/**
 * Element defaults, accessibility helpers, page hero, section heads, reveal.
 *
 * The reset itself now lives in tokens.css, which is PASIA's own file copied
 * whole from _mockups/css/tokens.css: box-sizing, the type stack, headings,
 * links, images, :focus-visible and the reduced-motion block are all declared
 * there. Only what that file does NOT cover belongs here, so nothing is
 * declared twice and there is one answer to "where does this element rule come
 * from".
 *
 * Everything from `SHARED SHELL` down is ported from _mockups/css/site.css,
 * which is the chrome all 27 signed-off pages are built on. Tokens only, no
 * literals: the mock-up CSS is already token-pure and it stays that way.
 */

/* Only the resets tokens.css does not already make. */
*,
*::before,
*::after {
  padding: 0;
}

ul,
ol {
  list-style: none;
}

img {
  height: auto;
}

button {
  font-family: inherit;
  cursor: pointer;
  border: none;
  background: none;
}

/* ===========================================================================
 * SHARED SHELL, from _mockups/css/site.css
 * ======================================================================== */

/* --- accessibility helpers ------------------------------------------------ */
/* `.screen-reader-text` is WordPress core's name for the same thing, and core
   emits it whether or not a theme has heard of it: the_posts_pagination()
   prints <h2 class="screen-reader-text">Search result pages</h2> above every
   paginated list. Unstyled, that h2 renders at display size on every archive
   and search page. Same rule, both names. */
.sr-only,
.screen-reader-text {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--s-2);
  top: -60px;
  z-index: var(--z-toast);
  background: var(--gold);
  color: var(--deep-forest);
  font-weight: 700;
  padding: .6em 1em;
  border-radius: var(--r-md);
  transition: top var(--dur-fast) ease;
}

.skip-link:focus,
.skip-link:focus-visible {
  top: var(--s-2);
  color: var(--deep-forest);
}

/* Keep the focus ring visible on the dark forest surfaces. The global ring in
   tokens.css is --gold-deep, which measures 5.21:1 on mist and 5.72:1 on paper
   but only 2.03:1 on --forest and 2.53:1 on --deep-forest, under the 3:1 that
   SC 1.4.11 asks of a focus indicator. --gold-bright is 7.20:1 on deep forest
   and 5.78:1 on forest.

   `.cta--dark` and `.stats-band` are on this list because both paint
   `background: var(--brand-primary)` and both contain links and buttons. They
   were missed on the first pass, which is the whole reason this is a list and
   not a per-component override: add the selector here when a section goes
   dark.

   `.pasia-cta-band` and `.pasia-cta-panel` were added by Phase 2a for the same
   reason: both paint forest or deep forest and both are mostly buttons and
   chips, which is to say they are almost entirely focusable. `.table thead`
   joined them in the audit round: it paints --forest and Phase 4 may well put
   a sort link in a column heading. */
.site-header :focus-visible,
.pg-hero :focus-visible,
.site-foot :focus-visible,
.site-drawer :focus-visible,
.pasia-search :focus-visible,
.cta--dark :focus-visible,
.stats-band :focus-visible,
.pasia-cta-band :focus-visible,
.pasia-cta-panel :focus-visible,
.table thead :focus-visible {
  outline-color: var(--gold-bright);
}

/* --- page hero: photo plus ridge overlay, wash at about 90% ---------------
 *
 * The mock-ups' signature interior surface, on 26 of the 27 pages. Phase 2a
 * gave it markup: it lives once in `parts/page-hero.php`, with the ridge SVG
 * split into `parts/page-hero-ridge.php`. Two entry points reach the same
 * partial. The `page-head` section passes its field values in, for the 17
 * section-stack pages; the nine hand-built Phase 4 templates pass theirs from
 * the queried object. One markup, one stylesheet, two callers.
 *
 * The hero gets no section stylesheet, deliberately. A section stylesheet is
 * enqueued only on pages whose stack contains that section (inc/enqueue.php),
 * so nine of the 26 consumers would never load it and the hero would render as
 * unstyled markup on them. Rules for shared partials live here or in
 * components.css. docs/pasia-section-inventory.md §0.
 *
 * TWO LOOSE ENDS THIS FILE CANNOT TIE OFF, WRITTEN HERE SO NEITHER AGENT
 * ASSUMES THE OTHER OWNS THEM. The audit round found the first version of this
 * comment claiming, in the present tense, that no page-head stylesheet existed.
 * One did: `assets/css/sections/page-head.css`, 106 lines, the STARTER's page
 * head, a light --surface-alt band with a hairline, which is a different
 * component from this one. Inventory §A.1 says to replace it entirely.
 *
 *   1. The `page-head` section agent owns deleting that stylesheet. At the time
 *      of writing it has done so in the working tree.
 *   2. Phase 4's 404.php agent owns removing `pasia_enqueue_section_style(
 *      'page-head' )` at 404.php:22, and 404.php still carries it. That call is
 *      the explicit per-template enqueue inventory §0 considered and rejected
 *      for failing silently, and 404.php is still the Phase 0 placeholder
 *      emitting `.page-head` markup rather than calling parts/page-hero.php.
 *
 * Neither blocks the other and the order does not matter:
 * pasia_enqueue_section_style() returns early when the file is missing, so a
 * delete before the call is removed cannot fatal.
 *
 * `wp pasia verify` warns when a section has no stylesheet, and for the two
 * partial-backed sections that state is correct and permanent, because the
 * whole point is that their rules are global. Do not answer it by recreating
 * the file. The registry has an opt-out for exactly this case:
 * `'has_stylesheet' => false` in the section's registration, which
 * inc/sections/registry.php:158 accepts and inc/cli/seed-sections.php:937
 * honours. Both `page-head` and `cta-panel` should declare it.
 *
 * THE RIDGE FILLS ARE SET BELOW, NOT ON THE SVG. This comment previously said
 * `.ridge`, `.r1`, `.r2` and `.sun` appeared in no stylesheet by design, with
 * fills set inline as the mock-ups do. That was true while nothing emitted the
 * hero and it is now wrong: the mock-ups carry fill="#123f38", "#0d352f",
 * "#082a25" and "#d8b25e" inline, which is a hard rule 1 breach the moment it
 * is copied into the theme. The tokens already existed, so the partial emits no
 * `fill` attribute at all.
 *
 * assets/js/main.js initParallax() looks for `.pg-hero-ridge .ridge` and
 * `.pg-hero-photo`. Both exist now on every page that renders the partial, so
 * it has something to find rather than returning early. Parallax stays
 * enhancement only: with JavaScript off the hero is complete and static.
 */
.pg-hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--deep-forest);
  color: var(--paper);
  padding: clamp(2.75rem, 6vw, 4.5rem) 0 clamp(3rem, 7vw, 5rem);
}

.pg-hero.tall {
  padding-block: clamp(3.5rem, 8vw, 6.5rem) clamp(3.5rem, 8vw, 6rem);
}

.pg-hero-photo {
  position: absolute;
  inset: 0;
  z-index: -3;
  background-size: cover;
  background-position: center;
  transform: scale(1.04);
}

.pg-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  background: linear-gradient(180deg, var(--hero-overlay), var(--hero-wash-deep));
}

.pg-hero-ridge {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

.pg-hero-ridge svg {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

/* The ridge fills, off the markup and onto tokens.
   Scoped to the hero on purpose. `.sun` is also a class inside the licence
   card's seal and watermark SVGs on accreditations.html, so a bare
   `.sun { fill: … }` would repaint a component that has nothing to do with
   the hero. Same reasoning for `.ridge`. */
.pg-hero-ridge .ridge.r3 { fill: var(--ridge-far); }
.pg-hero-ridge .ridge.r2 { fill: var(--ridge-mid); }
.pg-hero-ridge .ridge.r1 { fill: var(--ridge-near); }

/* Two concentric discs, the inner one warmer. Purely atmospheric, behind a
   90% wash, and aria-hidden on the wrapper. */
.pg-hero-ridge .sun { fill: var(--gold-bright); opacity: .16; }
.pg-hero-ridge .sun.core { opacity: .22; }

/* Every one of the 26 mock-up heroes sets this inline on the breadcrumb.
   One rule here instead, so the partial emits no style attribute. */
.pg-hero .crumbs { margin-bottom: var(--s-4); }

.pg-hero .eyebrow.on-dark { margin-bottom: var(--s-3); }

/* `overflow-wrap` ON BOTH THE HEADING AND THE BREADCRUMB, and it is measured
   rather than pre-emptive. Every title in this hero is client-controlled: the
   17 stack pages pass a `page-head` field, and `single-vacancy.php` and
   `single-helper.php` pass a post title straight in, so this is not a
   blog-only surface. Measured with one 79-character unbreakable token:

     .pg-hero h1   scrollWidth 1302px inside a 280px box at 320
                   scrollWidth 2060px inside a 687px box at 1440
     .crumbs       scrollWidth  530px inside a 280px box at 320

   `.pg-hero` sets `overflow: hidden` for the ridge SVG, so the document width
   never moved and no width sweep could see any of it: the title was simply cut
   off mid-word, silently, which is the worst of the three outcomes.

   TWO DIFFERENT VALUES, AND THE DIFFERENCE IS LOAD-BEARING. `break-word` is
   enough for the heading, which is an ordinary block: it breaks the word only
   when the word cannot fit a line by itself, which is the same conservative
   choice `.stat .n` makes in components.css. `.crumbs` is a FLEX row, so each
   crumb is a flex item with an automatic minimum of its own min-content width,
   and only `anywhere` is taken into account when min-content is computed.
   `break-word` there would inherit down, change nothing about the items'
   minimum size, and leave the row exactly as wide as it was. */
.pg-hero h1 {
  color: var(--paper);
  font-size: var(--text-3xl);
  max-width: 20ch;
  overflow-wrap: break-word;
}

.pg-hero.tall h1 {
  font-size: var(--text-hero);
  max-width: 16ch;
}

.pg-hero h1 em {
  font-style: italic;
  color: var(--gold-bright);
  font-weight: 500;
}

.pg-hero .lede {
  color: var(--on-dark-strong);
  font-size: var(--text-lg);
  max-width: 56ch;
  margin-top: var(--s-3);
}

/* THE BARE CLASS, because `.lede` has two owners and both were exposed. The
   rule above dresses the page-hero's lede and `sections/hero.css` dresses the
   front-page hero's, and neither carried an `overflow-wrap`, so the same
   client field was cut in both places: 463px at 320 and 130 to 184px at 1440,
   silently, inside each hero's `overflow: hidden`. Declaring it on the class
   here rather than twice is the same move `.head` and `.crumbs` already make in
   this file, and base.css is the one of the two stylesheets that is global.

   `break-word` AND NOT `anywhere`, which is the `.pg-hero h1` reasoning above
   rather than the `.head h2` reasoning: a lede is an ordinary block inside a
   hero that clips, so the document cannot move and min-content is not the
   mechanism. Both values were measured and both closed it; `break-word` is the
   one that leaves every intrinsic size in the theme untouched. */
.lede { overflow-wrap: break-word; }

/* `center` and `tall` are template-set modifiers, not fields: a modifier field
   would be the style switch the registry rejects (AGENTS.md §1.2). 404.php is
   the only caller that will need them, and it will set them itself. It does not
   call the partial yet, and nor does anything else: Phase 3 and Phase 4 wire
   the callers up, and until they do initParallax() still returns early on every
   real page even though the ridge now has somewhere to come from. */
.pg-hero.center { text-align: center; }
.pg-hero.center h1,
.pg-hero.center .lede { margin-inline: auto; }
.pg-hero.center .eyebrow { justify-content: center; }
/* `.crumbs` is a flex row, so text-align above does not reach it. The 404
   mock-up fixes that in its own style block; the modifier owns it here so a
   caller cannot get a half-centred hero. */
.pg-hero.center .crumbs { justify-content: center; }

/* --- breadcrumb ----------------------------------------------------------- */
/* `anywhere`, not `break-word`: see the note above `.pg-hero h1`. A crumb is a
   flex item and only `anywhere` changes the min-content size the item's
   automatic minimum is taken from. */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  gap: .5em;
  align-items: center;
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}

.crumbs a { color: var(--on-dark-strong); }
.crumbs a:hover { color: var(--gold-bright); }
.crumbs .sep { color: var(--on-dark-faint); }
.crumbs [aria-current="page"] { color: var(--paper); }

.crumbs.on-light a { color: var(--pine); }
.crumbs.on-light .sep { color: var(--ink-faint); }
.crumbs.on-light [aria-current="page"] { color: var(--ink-soft); }

/* --- section heads -------------------------------------------------------- */
.head {
  max-width: 64ch;
  margin-bottom: var(--s-6);
}

.head.center {
  margin-inline: auto;
  text-align: center;
}

.head.center .eyebrow { justify-content: center; }

/* THIS ONE MOVES THE PAGE, WHICH IS WHY IT IS `anywhere` AND WHY IT IS HERE.
   `.head` is the shared section head and its `h2` is a client-supplied
   heading on almost every section in the theme. Measured with one
   79-character unbreakable token, on a `.head h2` that no section stylesheet
   protects:

     front / service-cards   document.scrollWidth 1322 at a 320 viewport
                             (1002px of horizontal page scroll)
                             document.scrollWidth 2244 at 1440 (804px)
     our-team / team-roster  the same two figures

   That is whole-document overflow, not a clipped element: the page itself
   scrolls sideways. It is `anywhere` rather than `break-word` because a
   `.head` frequently sits inside a grid or flex track whose automatic minimum
   is the item's min-content width, and only `anywhere` is taken into account
   when min-content is computed. `break-word` was measured first and left the
   document at 1322 unchanged.

   WHY THE SHARED RULE AND NOT ANOTHER LOCAL ONE. Four sections had already
   answered this on their own roots, so `how-it-works`, `form-panel`,
   `checklist-pair` and `aftersales`' bands computed `anywhere` while
   `service-cards`, `team-roster`, `icon-cards` and the bare `.section` heads
   on `pricing` and `our-team` computed `normal`. Every section that uses
   `.head` is exposed and only the owner of `.head` can close the class.

   IT CONTRADICTS SEVERAL "ZERO HORIZONTAL OVERFLOW, VERIFIED CLEAN" AUDITS,
   and they were not wrong. They measured real pages, and no real page yet
   carries a token like this. The exposure arrives with the client's copy, so
   it has to be closed before the copy does.

   THIS IS NO LONGER THE ONLY COVER, and it was for too long. `.head` is
   optional, and the seven sections that render their heading outside one were
   moving the document by 185 to 1239px on exactly this measurement. The
   heading element itself now carries `overflow-wrap: anywhere` in tokens.css,
   which is the same value for the same reason across the whole class; this
   rule stays because it is more specific than the element and because a reader
   arriving at `.head` should find the argument, not an absence. */
.head h2 {
  font-size: var(--text-3xl);
  margin: var(--s-3) 0;
  overflow-wrap: anywhere;
}

/* The gold italic clause, on a LIGHT section heading.
   This one is authored rather than ported, and it is worth saying why. The
   inventory instructs a dozen sections to tell their client to "wrap words in
   <em> for the gold italic": hero, how-it-works, service-cards, faq,
   testimonials, feature-row and more. Nothing delivered it. Checked against
   all 27 mock-ups: every <em> inside an <h2> is inside a dark CTA band, all
   eight of them, and each band declares its own rule; `site.css` has
   `.pg-hero h1 em` and `.stat .n em` and no third. So a light-ground section
   heading has never exercised the treatment and every one of those client
   instructions was a promise nothing kept.
   --gold-deep, NOT the dark form's --gold-bright, which measures 1.83:1 on
   --mist and 1.67:1 on --surface-2. --gold-deep is 5.21:1 on mist, 5.72:1 on
   paper and 4.76:1 on the warm band, so it clears AA on all three grounds a
   section can land on. It is also what `.stat .n em` already uses.
   One rule here rather than a dozen in section stylesheets: this is the §F8
   case, and the agent that found it correctly declined to fix it locally. */
.head h2 em {
  font-style: italic;
  font-weight: 500;
  color: var(--gold-deep);
}

.head p {
  color: var(--ink-soft);
  font-size: var(--text-lg);
}

.section.tint   { background: var(--paper); }
.section.tint-2 { background: var(--surface-2); }

/* ===========================================================================
 * WORDPRESS SURFACES the mock-ups never had to account for.
 * ======================================================================== */

/* Plain page template: legal and long-form content, block editor on. */
.page-plain {
  padding-block: var(--s-7);
  padding-inline: var(--gutter);
}

.page-plain-head { margin-bottom: var(--s-5); }

.page-plain-head h1 { font-size: var(--text-3xl); }

/* THE PROSE RULES THAT USED TO SIT HERE HAVE MOVED, and they left for the same
   reason the list rules did before them: they were a rich-text treatment
   wearing one template's class name.

   The measure, the block rhythm, the `h2`/`h3` sizes and the underlined --pine
   links are now the RICH TEXT OUTPUT component in components.css, declared on
   `.page-plain-body` AND on `.pasia-rte`, which is the canonical rich-text
   wrapper. `.page-plain-body` keeps every rule it had, because page.php,
   singular.php and index.php all emit it without the canonical class; what
   changed is that three surfaces which carry `.pasia-rte` and nothing else
   stop rendering as a wall of text. `tokens.css` resets `* { margin: 0 }`, so
   a rich-text field in a bare wrapper had no block spacing whatsoever.

   Only the two rules that are genuinely this template's stay here: the page
   padding and the head. See components.css, RICH TEXT OUTPUT. */

/* ===========================================================================
 * REVEAL ON SCROLL
 * ======================================================================== */

/* `.in` is the mock-ups' class, `.is-visible` is the theme's. Both are
   honoured so markup lifted straight out of a mock-up keeps working and so
   assets/js/main.js does not have to know which it is looking at.

   AN ANIMATION, NOT A TRANSITION, and that is a structural fix rather than a
   preference. This used to be `transition: opacity …, transform …` on `.reveal`
   with `transition-delay` on four `.reveal-dN` classes. A section stylesheet
   loads AFTER base.css, so any component declaring its own `transition`
   SHORTHAND on the same element replaced the property list and reset the delay
   to 0s: the element snapped into view instead of fading, silently. Eight
   section stylesheets use that shorthand today and the number only goes up.
   `animation` occupies a different property, so the collision cannot happen at
   all, which is one edit here against a workaround in every section that ever
   trips it. Verified: no section stylesheet declares `animation`, so nothing
   collides with this today either. If one ever needs to, it must not put its
   own animation on a `.reveal` element.

   THE FOUR DELAY STEPS ARE GONE, and they should never have existed. Checked
   against the signed-off design rather than taken on trust:

     grep -c "reveal-d[0-9]" _mockups/*.html   zero, across all 27 files
     grep -rn "transition-delay" _mockups/     nothing
     _mockups/css/site.css:452                 one bare .reveal, no delay
     _mockups/js/site.js initReveals()         adds .in on intersection,
                                               no per-element stagger

   So the stagger appears nowhere in the design, in its CSS or in its JS.
   Inventory §0 says it "is what the mock-ups do on every page (20 occurrences
   on about.html)"; those 20 are all BARE `.reveal`. The convention was invented
   on top of the design and then written into thirty-four briefs.

   Deleting the rules is a no-op for the 28 `reveal-dN` still in section markup:
   they simply stop matching, everything in a group fades together, and no agent
   has to edit anything. Drop them from markup when you are next in the file
   rather than making a pass for it. Removing them also removes a real cost the
   path-cards agent measured: one `transition-delay` applied to EVERY property
   in the list, so a `reveal-d4` card waited 320ms before its border answered a
   hover.

   NO `fill-mode`, AND THAT WORD IS LOAD-BEARING. The first version of this ran
   `animation: pasia-reveal … both`. A filling animation keeps applying the last
   keyframe forever, and an animation outranks EVERY normal author declaration,
   inline styles included, so `transform: none` at 100% permanently beat any
   `:hover { transform: translateY(-4px) }` on the same element. Two sections
   were already live with a hover lift that silently did nothing: the fade
   worked, so nothing looked broken until somebody hovered. It also made a
   mitigation another agent wrote untrue, because writing a transition as
   longhands is no defence against an animation.

   With the default `fill-mode: none` the animation applies only while it runs.
   The resting state is therefore AUTHORED rather than filled: `opacity: 1` sits
   on `.in` as a normal declaration, and `transform` is left out of the resting
   state entirely so it falls back to the cascade the moment the run ends. Hover
   lifts, and anything else a section wants to do with `transform`, work again.
   A hover during the 600ms fade is still overridden, which is momentary and
   correct: the reveal owns the element while it is revealing. */
.reveal { opacity: 0; }

.reveal.in,
.reveal.is-visible {
  opacity: 1;
  animation: pasia-reveal var(--dur-slow) var(--ease-out-soft);
}

@keyframes pasia-reveal {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: none; }
}

/* Progressive enhancement, belt. `.reveal` starts at opacity 0 and only JS
   lifts it, so with scripting off everything below the hero would be rendered
   and unseeable. This was a HIGH audit finding across 23 of the 27 mock-ups.
   The braces are header.php's <noscript> block, which covers the browsers that
   do not support the scripting media feature. */
@media (scripting: none) {
  .reveal { opacity: 1; }
}

/* Reduced motion. The animation is cancelled rather than left to run at zero
   duration, and `.reveal` is lifted so an element that never receives `.in`
   is still visible. header.php's <noscript> block sets both with `!important`,
   which still wins here: an `!important` author declaration outranks an
   animation in the cascade. */
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; }

  .reveal.in,
  .reveal.is-visible { animation: none; }
}
