/*
Theme Name:   Penguin Project Solutions
Theme URI:    https://penguinprojectsolutions.com
Description:  Block child theme of Twenty Twenty-Five for Penguin Project Solutions — fit-out, renovation and refurbishment specialists. Brand tokens live in theme.json; page structure lives in patterns/.
Author:       Shoothill
Author URI:   https://shoothill.com
Template:     twentytwentyfive
Version:      0.1.0
Requires at least: 6.7
Tested up to: 7.0
Requires PHP: 8.1
License:      GPL-2.0-or-later
License URI:  https://www.gnu.org/licenses/gpl-2.0.html
Text Domain:  penguin-pps
*/

/* ---------------------------------------------------------------------------
   Nearly all styling belongs in theme.json, not here. Add CSS only for what
   theme.json genuinely cannot express, and say why in a comment.
   --------------------------------------------------------------------------- */

/* Bulleted and numbered lists indent 20px, not the browser's 40px (2026-10-07).
   Not in theme.json because core/list styles only reach list blocks. This has
   to cover the ACF scope-of-works lists and Complianz's cookie policy too.
   :where() keeps the specificity at zero, so every unbulleted list that sets
   its own padding (nav, footer links, cards, steps, contact details) still
   wins. */
:where(ul, ol) {
	padding-inline-start: 20px;
}

/* Cards in a row should match height regardless of copy length. The grid
   stretches every card to the tallest one; flex column keeps the media above
   the body inside it.

   Do NOT push the last child down with `margin-block-start: auto`. Every card
   ends with .pps-card__body, so an auto margin there bottom-anchors the title
   and copy together, and any card with shorter copy has its title pushed down
   out of line with its neighbours. The spare height belongs at the foot of the
   card, which is where flex-start leaves it. If a card ever gains a footer
   element (a "Read more" link, say), give that element its own class and put
   the auto margin on that — not on :last-child. */
.pps-card {
	display: flex;
	flex-direction: column;
	height: 100%;
}

/* Numbered process steps: the numeral is decorative, so it is a CSS counter
   rather than markup an assistive technology would read out. */
.pps-steps {
	counter-reset: pps-step;
}

.pps-step > h3::before {
	counter-increment: pps-step;
	content: counter(pps-step);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 2.25rem;
	block-size: 2.25rem;
	margin-inline-end: 0.75rem;
	border-radius: 50%;
	background: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--primary);
	font-size: 1rem;
	font-weight: 700;
	flex: 0 0 auto;
}

/* Accent rule used under section headings. */
.pps-rule::after {
	content: "";
	display: block;
	inline-size: 3.5rem;
	block-size: 4px;
	margin-block-start: 0.75rem;
	background: var(--wp--preset--color--accent);
}

.has-text-align-center.pps-rule::after {
	margin-inline: auto;
}

/* Placeholder blocks for content the client has not supplied yet. Deliberately
   conspicuous so nothing ships to production looking finished when it is not. */
.pps-placeholder {
	border: 2px dashed var(--wp--preset--color--secondary);
	background: var(--wp--preset--color--offwhite);
	padding: var(--wp--preset--spacing--40);
	text-align: center;
}

.pps-placeholder::before {
	content: "Placeholder — content to be supplied";
	display: block;
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--wp--preset--color--secondary);
	margin-block-end: 0.5rem;
}

/* ---------------------------------------------------------------------------
   Footer.

   Colours are set here rather than with core's has-white-color / has-text-color
   block classes. Those emit `!important` utility rules, and because our palette
   originally had a slug named "text", core's generic `has-text-color` MARKER
   class — which WordPress stamps on any block with a custom colour — resolved to
   `.has-text-color{color:var(--wp--preset--color--text)!important}` and beat the
   intended white, rendering the footer headings, address and copyright at
   1.24:1. The slug is now "body", but styling the footer from the theme's own
   classes avoids depending on that collision never recurring.
   --------------------------------------------------------------------------- */
/* Two type sizes in the whole footer, and nothing else.
 *
 * It previously had four. The root font size is `large` (~19px once fluid
 * typography has scaled it), so anything not explicitly sized inherited it —
 * which is why the Quick links and Legal lists rendered at 19px, larger than
 * the "Get in touch" address beside them at 15px, and larger than the headings
 * above them at 18px. Setting the body size on .pps-footer itself means every
 * link, list item and paragraph inherits one size; only the headings opt out.
 *
 * The old .pps-footer__legal-line rule set 0.875rem but never took effect —
 * `.pps-footer p` (0,1,1) outranked `.pps-footer__legal-line` (0,1,0), so the
 * copyright line rendered at 0.9375rem. It is dropped rather than fixed: with
 * one body size there is nothing left for it to say.
 */
.pps-footer {
	color: var(--wp--preset--color--white);
	font-size: var(--wp--preset--font-size--small);
}

.pps-footer__heading {
	color: var(--wp--preset--color--white);
	font-size: var(--wp--preset--font-size--large);
}

/* One vertical rhythm for all four columns.
 *
 * The global block gap is spacing-40 (2rem), which every column inherited — so
 * a heading sat 32px above the address beneath it. But .pps-footer-links zeroed
 * its own margins, so the link lists sat flush against their headings instead.
 * Side by side, "Quick links" started its content 24px higher than "Get in
 * touch" did, which is most of why the footer looked untidy.
 *
 * The gap is set on the children directly rather than by overriding
 * --wp--style--block-gap, so it does not depend on which layout class core
 * happens to emit for the column.
 */
.pps-footer .wp-block-column > * {
	margin-block-start: var(--wp--preset--spacing--30);
}

.pps-footer .wp-block-column > :first-child {
	margin-block-start: 0;
}

.wp-block-template-part footer a,
footer.wp-block-group a {
	color: var(--wp--preset--color--white);
	text-decoration: none;
}

.wp-block-template-part footer a:hover,
.wp-block-template-part footer a:focus-visible,
footer.wp-block-group a:hover,
footer.wp-block-group a:focus-visible {
	color: var(--wp--preset--color--accent);
	text-decoration: underline;
}

/* Footer link lists read as navigation, not prose — drop the bullets.
   The block-start margin is deliberately NOT zeroed here: that is what pulled
   these lists up out of line with the address column next to them. They take
   the same gap below their heading as everything else. */
.pps-footer-links {
	list-style: none;
	padding-inline-start: 0;
	margin-block-end: 0;
}

.pps-footer-links li {
	margin-block: 0 var(--wp--preset--spacing--20);
}

.pps-footer-links li:last-child {
	margin-block-end: 0;
}

/* The copyright line sits under the separator, outside the columns, so it needs
   its own gap — the column rhythm above does not reach it. */
.pps-footer__legal-line {
	margin-block: var(--wp--preset--spacing--40) 0;
}

/* The header CTA button must not pick up the footer/global link underline. */
.wp-element-button,
.wp-block-button__link {
	text-decoration: none;
}

/* Visible keyboard focus.
   One outline colour cannot serve both grounds: the accent yellow is 7.18:1 on
   charcoal but only 1.42:1 on white, well under the 3:1 that SC 1.4.11 needs. So
   the default is charcoal for the light sections, and the accent is reinstated
   only inside the dark ones. */
a:focus-visible,
.wp-element-button:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
	outline: 3px solid var(--wp--preset--color--primary);
	outline-offset: 2px;
}

.pps-section--dark :is(a, button, .wp-element-button):focus-visible,
.pps-hero :is(a, button, .wp-element-button):focus-visible,
.pps-cta :is(a, button, .wp-element-button):focus-visible,
header.wp-block-group :is(a, button, .wp-element-button):focus-visible,
footer.wp-block-group :is(a, button, .wp-element-button):focus-visible,
.wp-block-template-part > header :is(a, button, .wp-element-button):focus-visible,
.wp-block-template-part footer :is(a, button, .wp-element-button):focus-visible {
	outline-color: var(--wp--preset--color--accent);
}

/* ---------------------------------------------------------------------------
   Header sits on charcoal because the supplied logo is WHITE on transparent
   (mean RGB 253,253,253) — on a light header it would be invisible. This also
   matches penguinmep.com, which uses the same white mark on a dark bar.
   If a dark-ink version of the logo is ever supplied, this can be reverted.
   --------------------------------------------------------------------------- */
.pps-header {
	color: var(--wp--preset--color--white);
}

/* ---------------------------------------------------------------------------
   Sticky header — matching penguinmep.com.

   MEP runs Elementor Pro's sticky module on the nav row: `sticky: top` on
   desktop, tablet and mobile, with NO shrink and no colour change on scroll
   (its stylesheet carries no `.elementor-sticky--effects` rules). This is the
   same behaviour in ~10 lines of CSS and no JavaScript.

   It works because the header is a DIRECT child of .wp-site-blocks with no
   wrapper element — a sticky element only scrolls within its parent, so if a
   template-part <div> is ever introduced around it, the header will pin to that
   wrapper's height and appear not to stick at all.

   z-index 100 clears the hero, whose background image and text sit at 0/1/2.
   --------------------------------------------------------------------------- */
.pps-header {
	position: sticky;
	top: 0;
	z-index: 100;
	/* Moved out of parts/header.html so it can animate — see the note there. */
	padding-block: var(--wp--preset--spacing--30);
	transition: padding 0.25s ease;
}

/* ---------------------------------------------------------------------------
   The stuck header's height, in one place.

   THREE things depend on this number and only two of them are next to each
   other: the row's min-height below, and the scroll offset that keeps in-page
   anchors and the skip link out from under the pinned bar. They were separate
   literals — 67px and 5rem — with nothing linking them, so changing the header
   height silently mis-scrolled every anchor target on the site.

   The logo (125px) and the header button (44px) also have to fit inside it;
   those are in templates.css and the table in docs/deployment.md.

   The border width is stated inline in parts/header.html, which is why it is
   repeated rather than derived. Change it there and change it here.
   --------------------------------------------------------------------------- */
:root {
	--pps-header-stuck: 70px;
	--pps-header-border: 3px;
}

/* Sized against the STUCK header, not the tall one: following an anchor always
   scrolls, and scrolling is what makes it stick. The extra 10px is breathing
   room so the target does not sit flush against the bar. */
html {
	scroll-padding-top: calc(var(--pps-header-stuck) + 10px);
}

/* ---------------------------------------------------------------------------
   Stuck state — the strapline comes off the logo, the bar tightens.

   penguinmep.com gets this from two rows: a non-sticky top row carrying the full
   logo, and a sticky row below it carrying `Penguin-header-logo-no-sub-text`.
   Scroll down and the first row leaves, so the strapline "disappears". This
   header is one row, so the same end state is reached by swapping the image.

   `pps-logo-white-compact.png` is the supplied logo with the rule and the
   "FIT-OUT | RENOVATION | REFURBISHMENT" line erased — NOT cropped. The penguin's
   tail runs down past the strapline's baseline, so a rectangular crop takes the
   bottom off the bird. Erase the strapline's own pixels and trim, and the mark
   survives intact. tools/make-compact-logo.py regenerates it.

   The swap is a cross-fade between the real <img> and a background on the link,
   so both occupy the same box and nothing reflows mid-animation. `.is-stuck` is
   set by assets/js/header.js; with no JavaScript the full logo simply stays.
   --------------------------------------------------------------------------- */
.pps-header .wp-block-site-logo a {
	position: relative;
	display: block;
}

/* The transitions live on the BASE selectors, not inside .is-stuck. Declared
   only on the stuck rule they animate on the way in and snap back on the way
   out, because the property being animated is no longer declared once the class
   is removed. */
.pps-header .wp-block-site-logo,
.pps-header .wp-block-site-logo img {
	transition: opacity 0.25s ease, width 0.25s ease;
}

.pps-header .wp-block-site-logo a::after {
	content: "";
	position: absolute;
	inset: 0;
	background: url("assets/img/pps-logo-white-compact.png") left center / contain
		no-repeat;
	opacity: 0;
	transition: opacity 0.25s ease;
	pointer-events: none;
}

.pps-header.is-stuck .wp-block-site-logo img {
	opacity: 0;
}

.pps-header.is-stuck .wp-block-site-logo a::after {
	opacity: 1;
}

/* ---------------------------------------------------------------------------
   The stuck bar: 70px tall, 125px logo.

   Driven by min-height on the inner flex row rather than by computing paddings,
   because the row's height is whichever of the logo, the nav and the "Contact
   us" button is tallest, and that changes the moment any of their type sizes do.
   Stating the height once and letting the row centre its contents survives that.

     67px row + 3px accent border = 70px

   min-height, not height: below roughly 900px the nav and button wrap onto their
   own line, and the bar has to be free to grow rather than clip them.
   --------------------------------------------------------------------------- */
.pps-header.is-stuck {
	padding-block: 0;
}

.pps-header > .wp-block-group {
	transition: min-height 0.25s ease;
}

.pps-header.is-stuck > .wp-block-group {
	min-height: calc(var(--pps-header-stuck) - var(--pps-header-border));
}

.pps-header.is-stuck .wp-block-site-logo,
.pps-header.is-stuck .wp-block-site-logo img {
	width: 125px;
}

@media (prefers-reduced-motion: reduce) {
	.pps-header .wp-block-site-logo img,
	.pps-header .wp-block-site-logo a::after,
	.pps-header.is-stuck .wp-block-site-logo,
	.pps-header.is-stuck .wp-block-site-logo img {
		transition: none;
	}
}

/* ---------------------------------------------------------------------------
   Nav underline — MEP's `e--pointer-underline e--animation-fade`.

   An accent rule under the item that fades in on hover, on focus, and on the
   page you are currently on. The existing colour change stays: it is the
   brand's, and colour alone is not a safe hover signal anyway.

   The pseudo-element is always present and only its OPACITY animates, so the
   layout never shifts and nothing reflows on hover.
   --------------------------------------------------------------------------- */
.pps-header .wp-block-navigation-item__content {
	position: relative;
}

.pps-header .wp-block-navigation-item__content::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	bottom: -0.35em;
	height: 2px;
	background-color: var(--wp--preset--color--accent);
	opacity: 0;
	transition: opacity 0.3s ease;
}

.pps-header .wp-block-navigation-item__content:hover::after,
.pps-header .wp-block-navigation-item__content:focus-visible::after,
.pps-header .current-menu-item .wp-block-navigation-item__content::after {
	opacity: 1;
}

/* The underline is decoration on top of the colour change, so removing the
   transition leaves the state fully readable. */
@media (prefers-reduced-motion: reduce) {
	.pps-header .wp-block-navigation-item__content::after {
		transition: none;
	}
}

/* Inside the mobile overlay the items are stacked full-width, where a rule
   spanning the whole row reads as a divider rather than a pointer. */
.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation-item__content::after {
	content: none;
}

.wp-block-template-part > header a:not(.wp-element-button),
header.wp-block-group a:not(.wp-element-button) {
	color: var(--wp--preset--color--white);
	text-decoration: none;
}

/* The mobile menu toggle inherits currentColor, so it depends on .pps-header
   above being white. It was rendering at 1.24:1 — effectively invisible — while
   the header relied on core's has-text-color class. Stated explicitly here so a
   phone user can always see the control that opens the menu. */
.pps-header .wp-block-navigation__responsive-container-open,
.pps-header .wp-block-navigation__responsive-container-close {
	color: var(--wp--preset--color--white);
}

.wp-block-template-part > header a:not(.wp-element-button):hover,
.wp-block-template-part > header a:not(.wp-element-button):focus-visible,
header.wp-block-group a:not(.wp-element-button):hover,
header.wp-block-group a:not(.wp-element-button):focus-visible {
	color: var(--wp--preset--color--accent);
}

/* The mobile overlay menu inherits a light background from the parent theme;
   keep it charcoal so the white links stay readable when it opens. */
.wp-block-navigation__responsive-container.is-menu-open {
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--white);
}

.wp-block-navigation__responsive-container-close,
.wp-block-navigation__responsive-container-open {
	color: currentColor;
}

/* Accreditation logos arrive as JPEGs with white backgrounds; a hairline border
   stops them floating on the off-white section without looking like buttons. */
.pps-accreditation img {
	border: 1px solid var(--wp--preset--color--pale);
	background: var(--wp--preset--color--white);
}

/* ---------------------------------------------------------------------------
   Close the gaps between header, main and footer.

   The header, main and footer are direct children of .wp-site-blocks, and the
   parent theme applies the global block gap between ALL of its children:

       :where(.wp-site-blocks) > * { margin-block-start: var(--…--spacing--40) }

   That is 2rem, and because those three are the only children it renders as a
   2rem band of white BODY background above the hero and below the closing CTA —
   visible as horizontal white stripes on every page.

   The block gap is wanted everywhere else (it spaces blocks inside groups), so
   this zeroes it only between the top-level regions. Plain selectors are used
   because :where() carries zero specificity, so these win without !important.
   --------------------------------------------------------------------------- */
.wp-site-blocks > *,
.wp-site-blocks > * + * {
	margin-block-start: 0;
	margin-block-end: 0;
}

/* The parent also applies root padding to the wrapper, which would inset the
   full-width hero and footer from the top and bottom of the viewport. */
.wp-site-blocks {
	padding-top: 0;
	padding-bottom: 0;
}
