/* ═══════════════════════════════════════════════════════════════════════════
   SEQUOYAH POOLS — style.css
   Built to DESIGNSYSTEM.md v2.  Section numbers in comments refer to it.

   The reasoning is kept in the file on purpose (§13.6): stripping it saves
   under 20 KB brotli'd across the whole build, which is a quarter of one
   photograph, and the edge already compresses it.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── FACES ───────────────────────────────────────────────────────────────
   Newsreader carries a real `opsz` axis (6–72) alongside `wght` (200–800),
   verified with fontTools before the pairing was committed (§3.6).  There is
   NO font-variation-settings anywhere in this file: it would override
   font-weight rather than merge with it, and it would pin opsz to one value
   per breakpoint, which is the opposite of what the axis is for.
   font-optical-sizing:auto is the default and is what we want.
   ───────────────────────────────────────────────────────────────────────── */
@font-face{
  font-family:'SeqDisplay'; font-style:normal; font-weight:200 800;
  font-display:swap; src:url('fonts/display.woff2') format('woff2');
}
@font-face{
  font-family:'SeqDisplay'; font-style:italic; font-weight:200 800;
  font-display:swap; src:url('fonts/display-i.woff2') format('woff2');
}
@font-face{
  font-family:'SeqBody'; font-style:normal; font-weight:100 900;
  font-display:swap; src:url('fonts/body.woff2') format('woff2');
}
@font-face{
  font-family:'SeqMono'; font-style:normal; font-weight:400;
  font-display:swap; src:url('fonts/mono.woff2') format('woff2');
}
@font-face{
  font-family:'SeqMono'; font-style:normal; font-weight:500;
  font-display:swap; src:url('fonts/mono-500.woff2') format('woff2');
}


/* ─── TOKENS ───────────────────────────────────────────────────────────── */
:root{
  /* §1.1 — the ONLY horizontal inset on the page.  Fluid, so the margin grows
     with the viewport instead of stepping at breakpoints.  22px at the floor,
     not 28: below ~400px a 28px gutter costs 16% of the screen.

     FULL WIDTH, deliberately.  A 1280px cap was tried here and taken out: it
     is the conventional answer to a long line, but this page is carried by
     full-bleed photography, and a capped page puts a hard vertical edge down
     both sides of every picture on a wide screen.  What the cap was actually
     fixing — the line length of section headings — is fixed at the source
     instead, by the ch limits on the headings themselves (h2.d is 20ch, .wide
     26ch, .sub 42ch, .bd at --measure).  Those hold the line at any viewport
     width, and they do it without giving up the bleed. */
  --gut: clamp(22px, 2.6vw, 56px);

  /* §2.1 — a closed set.  A gap that is not on this scale is wrong. */
  --sp-1: 8px;   --sp-2: 16px;  --sp-3: 24px;  --sp-4: 32px;
  --sp-5: 48px;  --sp-6: 64px;  --sp-7: 96px;  --sp-8: 128px;

  /* §2.2 — the middle term carries a rem component so it does not collapse on
     narrow viewports and does not ignore user zoom.  The 128px ceiling was set
     against a heading scale that has since come down, and against a page that
     had no cap on its measure; now that the content stops at 1280 the band
     between two sections is the page's only pause, and 128px was not one.
     176px is: at 1440 the gap between sections is now larger than the tallest
     heading in them, which is what makes a section read as a section. */
  --sec: clamp(80px, 3.4rem + 4.6vw, 176px);

  --colgap: clamp(14px, 1.5vw, 26px);
  /* The inner margin of a framed full-bleed panel: how far the type sits
     in from the picture's own edge, distinct from --gut so that --gut is
     still declared exactly once per ancestor chain (§1.1). */
  --pad-in: clamp(20px, 2.6vw, 56px);

  /* §2.4 — one token frames the first screen: the distance from the top of the
     viewport to the menu items IS the distance from the bottom-most hero
     element to the bottom of the viewport. */
  --nav-pt: clamp(17px, 2vw, 26px);
  /* §7.3 — the gap above the hero copy and the gap below it.  Both derive from
     this one value; --nav-h (written by syncNavHeight) is the VISIBLE bottom of
     the bar, not its box. */
  --hero-gap: clamp(26px, 5vw, 88px);
  --nav-h: 80px;

  /* ─── Colour, derived not chosen (§4.1) ─────────────────────
     1. The Sequoyah mark was sampled BY PIXEL COUNT, not by eye.  Its dominant
        chroma is #0B1E4B at hue 220° (7788 of 8623 chromatic samples); its one
        secondary is #85AEDF at hue 210°.  Both artworks — the wordmark and the
        diver badge — return the same two values, so the pair belongs to the
        brand rather than to one file.
     2. The modal hue across the photography on this page is 212°.  It agrees
        with the mark, so the interface blue is also the water's blue and the
        pictures and the chrome belong to one world.
     3. --ink IS the sampled navy, unaltered: it measures 15.40:1 on paper.
     4. --accent is that hue held and darkened until it cleared 4.5:1 on BOTH
        grounds.  #3D6DB5 fell to 4.21 on the tint band, which is the ground
        that decides it; #356198 clears 6.05 on paper and 5.10 on the tint.
     5. --sky is the mark's own secondary, and it is now the ONLY accent used on
        a dark ground.  The mark has no third chroma, and inventing a warm
        sibling for the dark panels would be choosing rather than deriving, so
        the dark ground runs on a single accent.
     6. Text tints are --ink at alpha so they composite correctly over any
        ground.  The names record the intent; the values record what passed. */
  --paper:      #F7FAFD;
  --ink:        #0B1E4B;                 /* 15.40:1 on paper, 13.00:1 on tint */
  --ink-75:     rgba(11,30,75,.78);      /* body        7.88:1 */
  --ink-55:     rgba(11,30,75,.66);      /* secondary — 5.25:1 on paper AND
                                            5.07:1 on --tint.  The alternate
                                            ground is the one that decides this
                                            value, so it was measured there
                                            first and on paper second. */
  --ink-30:     rgba(11,30,75,.34);
  --rule:       rgba(11,30,75,.13);      /* NON-TEXT */
  --hair:       1px solid rgba(11,30,75,.13);

  --accent:     #356198;                 /* 6.05:1 on paper, 5.10:1 on tint */
  --accent-ink: #FFFFFF;                 /* 6.34:1 on --accent */
  --sky:        #88B1E0;                 /* the on-DARK sibling; 8.02:1 over the
                                            media ground, and measured again by
                                            the §4.5 pixel sampler over the
                                            COMPOSITED hero, not on paper */

  /* §4.2 — the alternate ground has to register.  #EDF2F7 is only 7.4% off
     paper and, at viewing distance after a full-bleed photograph, reads as no
     band at all.  This value is 9.3% off, which is where the band starts being
     a band without becoming a second page. */
  --tint:       #E8F0FA;

  /* §12 — the scrim's own darkest stop, painted on the frame so the panel is
     never a pale wash over an empty box while the photograph streams.  It is
     --ink pushed down, never a neutral: a neutral black behind a blue-cast
     photograph shows as a grey halo on the frame's rounded corners. */
  --media-dark: #071634;

  /* ONE radius, every frame (§8.2).  6px is a web-design radius — it reads as
     a card.  3px reads as a printed plate with its corners knocked off, which
     is the register this page is in. */
  --r-m: 3px;
  --lift:   0 1px 2px rgba(11,30,75,.05);
  --lift-h: 0 12px 30px rgba(11,30,75,.10);

  /* §5.1 — one curve, one duration.  Hierarchy is carried by distance, never
     by speed.  cubic-bezier(.22,1,.36,1) puts most of the travel in the first
     third: a 1.2s reveal is essentially in place by ~400ms and the remaining
     800ms is a decelerating settle the eye reads as weight. */
  --ease: cubic-bezier(.22, 1, .36, 1);
  --dur-rv: 1.2s;  --dur-fast: .22s; --dur-card: .3s;  --dur-img: .6s;
  --dur-nav: .34s; --dur-ground: .5s; --dur-panel: .5s;

  /* §5.2 — roughly 4× between the lightest and heaviest element. */
  --rv-text: 14px; --rv-side: 16px; --rv-card: 34px;
  --rv-plate: 56px; --rv-stage: 52px;

  /* §3.1 — the calc-style middles rather than pure vw: a pure-vw middle makes
     type shrink linearly toward nothing on narrow viewports, and the rem
     component sets a floor that also survives user zoom. */
  /* Tiny is not the same as refined.  A label at 10px is a label nobody reads,
     and the page had a lot of them; a caption at 12.5px is below the size at
     which the eye stops working for it.  Both steps go up by roughly one
     notch, and the tracking on the label step comes down slightly to pay for
     the extra width. */
  --s--2: clamp(10.5px, .60rem + .18vw, 12px);
  --s--1: clamp(.82rem, .79rem + .14vw, .92rem);
  --s-0:  clamp(.95rem, .92rem + .16vw, 1.04rem);
  --s-1:  clamp(1.22rem,1.10rem + .48vw,1.55rem);
  --s-2:  clamp(1.62rem,1.34rem + 1.14vw,2.40rem);
  --s-3:  clamp(2.06rem,1.58rem + 1.95vw,3.15rem);
  /* Section headings.  Was 3.90vw to a 5.10rem (81.6px) ceiling, which is past
     the size at which a two-line heading is read as a sentence rather than
     looked at as a picture.  4.30rem is 68.8px: still the loudest thing on its
     screen, and still read. */
  --s-4:  clamp(2.24rem,1.80rem + 3.15vw,4.30rem);

  /* §7.2 — the hero is inside a fixed-height box, so it cannot answer to width
     alone.  min() takes whichever is smaller: as large as the WIDTH allows,
     never larger than the HEIGHT can carry.  The 10.6vh coefficient is derived,
     not guessed: at the narrowest supported width everything else in the hero
     measures 47vh, leaving 53vh for a headline that wraps to five lines there. */
  /* §7.2 — min() takes whichever is smaller: as large as the WIDTH allows,
     never larger than the HEIGHT can carry.  Three terms, and each of them is
     doing a different job:

       the FLOOR, 2.85rem, is what the phone gets.  The vw term hands a 390px
       screen 37.7px, which is smaller than several headings further down the
       same page — the first line of the site should not be the quietest
       display type on it.  45.6px is the floor now, and .hz h1's <br> is
       dropped below 720 so the line can find its own breaks at that size.

       the WIDTH term sets the desktop size: 91.8px at 1440, up from 84.7.

       the HEIGHT term, 10.6vh, is the one that stops a wide short window from
       running the headline off the bottom of its own screen. */
  --s-hero: clamp(2.85rem, min(1.10rem + 5.15vw, 10.6vh), 6.10rem);
  /* The deck was one step off the body and read as a caption under a very large
     headline.  It is the hero's only sentence of prose; it takes the body size
     and a little more. */
  --s-deck: clamp(1.08rem, 1.00rem + .32vw, 1.22rem);

  /* §3.3 — two inverse relationships, both mandatory: as size goes up tracking
     goes more negative, and leading goes down toward 1. */
  --track-eb: .18em;
  --track-d: -.032em;
  --track-tight: -.045em;
  --lh-hero: .96;
  --lh-d: 1.06;
  --lh-b: 1.68;
  --measure: 62ch;
  --wd: 300;                             /* every display rule states its weight */

  --font-d: 'SeqDisplay', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
  --font-b: 'SeqBody', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
            Helvetica, Arial, sans-serif;
  --font-m: 'SeqMono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

@media (max-width: 980px){ :root{ --sec: 84px; } }
@media (max-width: 720px){ :root{ --sec: 64px; } }


/* ─── RESET ───────────────────────────────────────────────────────────────
   html{overflow-x:clip} contains the lateral reveal transforms without
   creating a scroll container, which position:sticky would notice. */
*, *::before, *::after{ box-sizing: border-box; }
html{ overflow-x: clip; -webkit-text-size-adjust: 100%; }
/* §5.11 — scroll-behavior is NEVER smooth in CSS: it also applies to
   scrollTo(), and the inertia loop plus the browser's own smoothing eased
   toward each other and overshot an anchor by 1609px.  Anchor smoothing is
   done in JS instead. */
body{
  margin: 0; width: 100%; max-width: 100%; overflow-x: hidden;
  background: var(--paper); color: var(--ink);
  font-family: var(--font-b); font-size: var(--s-0); font-weight: 400;
  line-height: var(--lh-b);
  -webkit-font-smoothing: antialiased;
}
img, svg, video{ display: block; max-width: 100%; }
picture{ display: contents; }
h1,h2,h3,h4,h5,h6,p,figure,blockquote,dl,dd,ol,ul{ margin: 0; padding: 0; }
ul,ol{ list-style: none; }
a{ color: inherit; text-decoration: none; }
button{ font: inherit; color: inherit; }
address{ font-style: normal; }

.skip{
  position: fixed; left: 0; top: -100px; z-index: 100;
  background: var(--ink); color: #fff; padding: 14px 20px;
  transition: top var(--dur-fast) var(--ease);
}
.skip:focus{ top: 0; }

/* §15.5 — measured on genuinely focused elements, never read through the
   invalid getComputedStyle(el, ':focus-visible'). */
:where(a, button, input, select, textarea, [tabindex]):focus-visible{
  outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 3px;
}
.hero :where(a, button):focus-visible,
.invite :where(a, button):focus-visible,
.band.stage :where(a):focus-visible{ outline-color: #fff; }


/* ─── GRID §1 ─────────────────────────────────────────────────────────────
   The only horizontal-padding utility that may exist.  `--gut` may appear in
   padding-left/right exactly ONCE in any ancestor chain: a section that
   carried .full *and* an inner wrapper that also padded sat inset by 2×--gut,
   which is invisible in isolation and glaring once you scroll past it. */
.full{ padding-left: var(--gut); padding-right: var(--gut); }

/* §1.2 form A — the band bleeds, its inner content re-establishes the gutter.
   Preferred over negative margins: no overflow risk, and never 100vw, which
   includes the scrollbar and widens the document by 15–17px. */
.band{ padding-inline: 0; }
.band > .inner{ padding-inline: var(--gut); }

section{ padding-block: var(--sec); position: relative; }
.hero{ padding-block: 0; }


/* ─── TYPE §3 ─────────────────────────────────────────────────────────── */
.d{
  font-family: var(--font-d);
  font-weight: var(--wd);                /* stated, never inherited: many
                                            variable display faces default to a
                                            small opsz at a heavy weight, and a
                                            rule that forgets its weight renders
                                            an 90px headline in letterforms drawn
                                            for 11px */
  letter-spacing: var(--track-d);
  line-height: var(--lh-d);
  text-wrap: balance;
}
h2.d{ font-size: var(--s-4); max-width: 20ch; }
h2.d.wide{ max-width: 26ch; }
h3.ch, h4.ch{
  font-family: var(--font-d); font-weight: 400; font-size: var(--s-1);
  line-height: 1.15; letter-spacing: -.02em; text-wrap: pretty; max-width: 30ch;
}

/* §3.5 — emphasis is carried by style and colour, never by weight.  Weight
   changes inside a display line break the optical rhythm of the setting. */
.d em{ font-style: italic; color: var(--accent); }
.d em.on-dark{ color: var(--sky); }

.bd{ font-size: var(--s-0); line-height: var(--lh-b); color: var(--ink-75);
     max-width: var(--measure); text-wrap: pretty; }
.bd + .bd{ margin-top: var(--sp-3); }

/* §3.4 — eyebrow, folio and label share one size, uppercase, wide positive
   tracking, and differ only by role. */
.eb{
  font-family: var(--font-m); font-size: var(--s--2); font-weight: 500;
  letter-spacing: var(--track-eb); text-transform: uppercase;
  line-height: 1.35; color: var(--ink-75); display: inline-block;
}
.fol{
  font-family: var(--font-m); font-size: var(--s--2); font-weight: 400;
  letter-spacing: var(--track-eb); text-transform: uppercase;
  line-height: 1.35; color: var(--ink-55);
}
.eb i, .fol i, .cap i{ font-style: normal; padding: 0 .35em; opacity: .55; }

/* The departmental marker.  This is the ONLY place a full-width 1px line is
   permitted, and it is a marker INSIDE a section, not a separator between two. */
.dept{ display: flex; align-items: center; gap: var(--sp-2); }
/* The number leads, and it is set in the same mono as the label it precedes
   rather than in a colour of its own — the em dash after it does the work of
   separating them, and one more colour on this line would be one too many. */
.dept .num{ margin-right: 2px; color: var(--ink-55); }
.dept .ln{ flex: 1; height: 1px; background: var(--rule); }
.dept > .fol:last-child{ text-align: right; }
.dept + *{ margin-top: var(--sp-6); }


/* ─── BUTTONS §5.6 ────────────────────────────────────────────────────── */
.btn{
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--font-m); font-size: var(--s--2); font-weight: 500;
  letter-spacing: var(--track-eb); text-transform: uppercase;
  /* The label step went up a notch with the rest of the small type, and the
     masthead CTA wrapped to two lines inside its own pill.  A button label is
     one line by definition — if it does not fit, the bar collapses. */
  white-space: nowrap;
  padding: 13px 14px 13px 22px; border-radius: 999px;
  background: var(--accent); color: var(--accent-ink); border: 1px solid transparent;
  transition: background-color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
.btn:hover{ background: #0A5C9C; }
.btn .ic{
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid currentColor;
  transition: transform var(--dur-card) var(--ease);   /* .3s, as Coral Sea */
}
/* The chip moves, the button does not.  If the whole button translates the
   cursor loses its target and the affordance gets worse. */
.btn:hover .ic{ transform: translateX(4px); }
.btn .ic svg{ width: 13px; height: 13px; fill: none; stroke: currentColor;
              stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }

/* §5.6 — a ghost button over photography keeps its ground transparent on
   hover.  A flash of solid colour against a photograph reads as a page glitch,
   not as feedback. */
.btn.ghost{
  background: transparent; color: #fff; border-color: rgba(255,255,255,.42);
}
.btn.ghost:hover{ background: transparent; border-color: #fff; }
.btn.ghost:hover .ic{ transform: translateX(5px); }   /* a touch further on a ghost */


/* ═══ MASTHEAD §6 ═════════════════════════════════════════════════════════
   Padding is CONSTANT between all three states.  The first version shrank its
   vertical padding when it took its ground; because padding is a layout
   property every state change reflowed the bar and the whole page read as if
   it were "loading" dozens of times per scroll.  Only transform and the ground
   itself may change — and `transition: all` is never used here. */
.nav{
  position: fixed; z-index: 60; top: 0; left: 0; right: 0;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  padding: var(--nav-pt) var(--gut) clamp(15px, 1.7vw, 22px);
  color: #fff;
  background: rgba(7,22,52,0);
  transition: transform var(--dur-nav) var(--ease),
              background-color var(--dur-ground) var(--ease),
              box-shadow var(--dur-ground) var(--ease),
              backdrop-filter var(--dur-ground) var(--ease);
}
/* §4.5 — the bar is a SIBLING of the hero, so it inherits none of the hero's
   scrim and an ancestor-walking contrast checker structurally cannot see the
   problem: it reported zero failures while these links measured 2.96:1.  The
   fix is a veil, never a colour change. */
.nav::before{
  content:''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  /* Two layers.  The vertical pass carries the bar; the horizontal one is a
     targeted veil over the right end, because that is where the hero's own
     wedge has fallen to .06 and the CTA still sits there.  Measured with the
     pixel sampler: the CTA was 4.17:1 with the single .55 vertical layer. */
  background:
    linear-gradient(180deg, rgba(7,22,52,.62) 0%, rgba(7,22,52,.34) 62%,
                            rgba(7,22,52,0) 100%),
    linear-gradient(270deg, rgba(7,22,52,.30) 0%, rgba(7,22,52,0) 36%);
  transition: opacity var(--dur-ground) var(--ease);
}
.nav.solid::before{ opacity: 0; }
.nav.up{ transform: translateY(-100%); }
.nav.solid{
  background: rgba(7,22,52,.94);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
          backdrop-filter: saturate(140%) blur(14px);
  box-shadow: 0 10px 30px rgba(4,20,34,.16);
}

.wordmark{ display: inline-flex; align-items: center; justify-self: start;
           position: relative; }
.wordmark::after{ content:''; position: absolute; left: 0; right: 0; top: -7px; bottom: -7px; }
/* The client's own mark, at its own 686:175 proportion.  Two files, not one
   recoloured with a filter: the glossy disc must not be touched, and only the
   wordmark beside it changes for the dark ground. */
.wordmark .mark{ width: clamp(126px, 11.6vw, 172px); height: auto; display: block; }
.navpanel .wordmark .mark{ width: 150px; }

/* 1fr auto 1fr is what centres the link list optically ON THE PAGE rather than
   in the leftover space between wordmark and CTA. */
.navlinks{ justify-self: center; display: flex; gap: clamp(2px, .7vw, 14px); }
.navlinks > li{ position: relative; }
.navlinks > li > a{
  /* Navigation has its own size, not the caption step.  The bar now aligns to
     the same 1280 measure as the content below it — which is what puts the
     wordmark on the same left edge as every section label — and inside that
     measure seven headings plus a button do not fit at 14.7px.  13px is the
     top of the range navigation belongs in anyway: it is chrome, and chrome
     that competes with the headline behind it is chrome set too large. */
  font-size: 13px; color: rgba(255,255,255,.9);
  display: inline-flex; align-items: center; gap: 7px;
  padding: 12px 7px;                             /* >= 44 in both axes */
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.navlinks > li > a:hover{ color: #fff; transform: translateY(-2px); }

/* The caret is drawn, not a glyph: a text chevron sits on the baseline and no
   font in this stack has one that optically centres against 13px of body. */
.cv{
  width: 6px; height: 6px; flex: none; margin-bottom: 2px;
  border-right: 1px solid currentColor; border-bottom: 1px solid currentColor;
  transform: rotate(45deg); opacity: .55;
  transition: transform var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}
.nav:not(.kb) .hassub:hover .cv,
.hassub.open .cv{ transform: rotate(225deg); opacity: 1; }

/* ─── The drop, §6.4 ──────────────────────────────────────────────────────
   :hover carries the mouse, with no script — and two things cannot be hovered
   at once, so hover alone is self-exclusive.  Keyboard opening is NOT
   :focus-within: clicking a heading leaves focus on it, so :focus-within held
   that drop open while the cursor opened a second one beside it, and two
   overlapping panels were on screen at the same time.  Focus is handled by
   script instead, through .open, which is enforced to one at a time.

   The padding-top is a hover BRIDGE: with the panel flush against the bar
   there is a dead pixel row between them and the menu closes as the cursor
   crosses it. */
.subnav{
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  padding-top: 9px; z-index: 5;
  visibility: hidden; pointer-events: none;
}
/* `.nav:not(.kb)` is what makes "one at a time" hold in BOTH directions.  A
   keyboard focus can open a drop while the cursor still rests on another
   heading — the mouse has not moved, so :hover is still true — and the count
   goes back to two.  Script sets .kb the moment focus drives the menu and
   clears it the moment a pointer does. */
.nav:not(.kb) .hassub:hover > .subnav,
.hassub.open > .subnav{ visibility: visible; pointer-events: auto; }
/* A paper card dropped out of a dark bar onto a photograph reads as a
   different application opening on top of the page.  The panel belongs to the
   masthead, so it takes the masthead's ground: the same navy, held back to .80
   so the picture behind it is still legible through it, blurred so what shows
   through is tone rather than detail, and grained so the translucency has a
   surface instead of looking like a flat wash. */
.subnav > ul{
  min-width: 236px; padding: var(--sp-2) 0;
  background: rgba(7,22,52,.80); color: #fff;
  -webkit-backdrop-filter: saturate(125%) blur(20px);
          backdrop-filter: saturate(125%) blur(20px);
  border: 1px solid rgba(255,255,255,.11);
  border-radius: var(--r-m);
  box-shadow: 0 26px 64px rgba(4,12,30,.46);
  display: flex; flex-direction: column;
  position: relative; overflow: hidden;
}
/* The grain.  fractalNoise at .9 is fine enough to read as a surface rather
   than as visible dots at 1x, and it is one 160px tile that repeats, so it
   costs nothing.  overlay rather than a flat opacity: it lightens where the
   panel is light and darkens where it is dark, which is what makes it read as
   the material of the panel rather than as a texture laid over it. */
.subnav > ul::before{
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: .34; mix-blend-mode: overlay;
}
/* The rows are transformed, so they already establish a stacking context and
   paint after the ::before that precedes them.  z-index is stated anyway
   because the ONLY reason they currently win is DOM order. */
.subnav li{ position: relative; z-index: 1; }
.subnav a{
  display: block; padding: 9px var(--sp-3);
  font-size: var(--s--1); color: rgba(255,255,255,.86); white-space: nowrap;
  transition: color var(--dur-fast) var(--ease);
}
.subnav a:hover{ color: #fff; }
/* Listed, not linked.  These pages are not in this build, so the row is held
   back and takes the default cursor — it reads as a contents entry rather than
   as a destination that failed. */
.subnav a[aria-disabled="true"]{ color: rgba(255,255,255,.64); cursor: default; }
.subnav a[aria-disabled="true"]:hover{ color: rgba(255,255,255,.86); }
/* Nothing fades (§5.3).  The panel takes its ground at once and the rows
   travel, on the same curve as everything else. */
.subnav li{ transform: translateY(7px); transition: transform .40s var(--ease); }
.nav:not(.kb) .hassub:hover .subnav li,
.hassub.open .subnav li{ transform: none; }
.subnav li:nth-child(2){ transition-delay: 30ms; }
.subnav li:nth-child(3){ transition-delay: 60ms; }
.subnav li:nth-child(4){ transition-delay: 90ms; }
.subnav li:nth-child(5){ transition-delay: 120ms; }
.subnav li:nth-child(6){ transition-delay: 150ms; }
.nav > .btn{ justify-self: end; padding-inline: 22px 8px; }

.navtoggle{
  display: none; width: 46px; height: 46px;
  grid-column: 3; justify-self: end;
  align-items: center; justify-content: center; flex-direction: column; gap: 6px;
  background: transparent; border: 0; cursor: pointer; padding: 0;
  -webkit-tap-highlight-color: transparent;
}
/* Two lines, not three.  With three the middle bar has to fade out during the
   morph and the fade is always visible as a flicker at the crossing point. */
.navtoggle span{ display: block; width: 24px; height: 1px; background: #fff;
                 transition: transform .4s var(--ease); }
.navtoggle[aria-expanded="true"] span:nth-child(1){ transform: translateY(3.5px) rotate(45deg); }
.navtoggle[aria-expanded="true"] span:nth-child(2){ transform: translateY(-3.5px) rotate(-45deg); }


/* ═══ MENU PANEL §6.5 ═════════════════════════════════════════════════════
   It ANIMATES both ways.  `hidden` is display:none and a display flip has
   nothing to transition, so script holds the attribute back until the close
   has finished playing. */
.navpanel{
  position: fixed; inset: 0; z-index: 70;
  background: var(--paper); color: var(--ink);
  overscroll-behavior: contain; overflow-y: auto;
  padding: 0 0 var(--sp-3);
  display: flex; flex-direction: column;
  transform: translateY(-100%);
  transition: transform var(--dur-panel) var(--ease);
}
.navpanel.open{ transform: none; }
.navpanel[hidden]{ display: none; }
/* Compact on purpose.  The panel is a single glance and a tap, not a page:
   every row that does not carry a link is height the reader has to travel
   past with a thumb.  Rows are 44px — the tap-target minimum — and nothing
   more. */
.navpanel .phead{ display: flex; align-items: center; min-height: 58px;
                  padding-inline: var(--gut); }
.pnav{ display: flex; flex-direction: column; margin-top: var(--sp-2);
       border-top: var(--hair); }
.pnav > li + li{ border-top: var(--hair); }       /* permitted: within one component */
.navpanel a:not(.btn){
  display: flex; align-items: center; min-height: 46px;
  padding: 4px var(--gut);
  font-family: var(--font-d); font-size: var(--s-1); font-weight: var(--wd);
  line-height: 1.1;
  transition: color var(--dur-fast) var(--ease);
}
.navpanel a:not(.btn):hover{ color: var(--accent); }

/* ─── A heading with pages under it ──────────────────────────────────────
   The heading itself stays a link — it goes to the section that covers it —
   and the disclosure is a SEPARATE control beside it.  Making the whole row
   the toggle would cost the reader the one destination on it that works. */
.prow{ display: flex; align-items: center; }
.prow > a{ flex: 1; min-width: 0; }
.pexp{
  width: 52px; align-self: stretch; flex: none;
  background: transparent; border: 0; cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center;
  position: relative; -webkit-tap-highlight-color: transparent;
}
/* A plus that becomes a minus by rotating one stroke onto the other — the same
   two-stroke construction as the burger, for the same reason: nothing has to
   fade out at the crossing point. */
.pexp span{
  position: absolute; width: 13px; height: 1px; background: var(--ink-55);
  transition: transform var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.pexp span:nth-child(2){ transform: rotate(90deg); }
.pexp[aria-expanded="true"] span{ background: var(--accent); }
.pexp[aria-expanded="true"] span:nth-child(2){ transform: rotate(180deg); }

.psub{ display: flex; flex-direction: column; padding-bottom: var(--sp-2); }
/* Load-bearing.  `[hidden]` is display:none from the UA sheet at (0,0,1);
   `.psub` is (0,1,0) and wins, so every group rendered open while its
   button still said collapsed. */
.psub[hidden]{ display: none; }
.psub a{
  min-height: 40px !important;
  padding-left: calc(var(--gut) + var(--sp-3)) !important;
  font-family: var(--font-b) !important; font-weight: 400 !important;
  font-size: var(--s--1) !important; color: var(--ink-55);
}
.psub a[aria-disabled="true"]{ cursor: default; }
.psub a[aria-disabled="true"]:hover{ color: var(--ink-75) !important; }
.psub li{ transform: translateY(6px); transition: transform .34s var(--ease); }
.psub.open li{ transform: none; }
.psub.open li:nth-child(2){ transition-delay: 28ms; }
.psub.open li:nth-child(3){ transition-delay: 56ms; }
.psub.open li:nth-child(4){ transition-delay: 84ms; }
.psub.open li:nth-child(5){ transition-delay: 112ms; }
.psub.open li:nth-child(6){ transition-delay: 140ms; }
.navpanel .pfoot{ margin-top: var(--sp-4); padding: 0 var(--gut) var(--sp-2);
                  display: flex; }
.navpanel .pfoot .btn{ flex: 1; justify-content: space-between;
                       padding: 15px 15px 15px 22px; }

/* Keyed to the CLASS, not to :not([hidden]): the attribute now lags the class
   by the length of the close.  140ms start — after the panel has travelled far
   enough to read as a surface — then 40ms steps, not 80: a 400ms cascade on
   six links feels sluggish when the user is waiting to tap. */
.pnav > li{ transform: translateY(10px); transition: transform .45s var(--ease); }
.navpanel.open .pnav > li{ transform: none; }
.navpanel.open .pnav > li:nth-child(1){ transition-delay: 140ms; }
.navpanel.open .pnav > li:nth-child(2){ transition-delay: 172ms; }
.navpanel.open .pnav > li:nth-child(3){ transition-delay: 204ms; }
.navpanel.open .pnav > li:nth-child(4){ transition-delay: 236ms; }
.navpanel.open .pnav > li:nth-child(5){ transition-delay: 268ms; }
.navpanel.open .pnav > li:nth-child(6){ transition-delay: 300ms; }
.navpanel.open .pnav > li:nth-child(7){ transition-delay: 332ms; }
.navpanel.open .pnav > li:nth-child(8){ transition-delay: 364ms; }
.navpanel.open .pnav > li:nth-child(9){ transition-delay: 396ms; }

/* The close control lives INSIDE the panel.  .nav establishes a stacking
   context, so no z-index on a descendant of it could ever lift a close button
   above a panel in the root stacking context — the menu opened with no visible
   way out. */
.navclose{
  position: absolute; top: clamp(12px,1.6vw,20px); right: calc(var(--gut) - 10px);
  width: 46px; height: 46px;
  display: flex; align-items: center; justify-content: center;
  background: transparent; border: 0; cursor: pointer; padding: 0;
}
.navclose::before, .navclose::after{
  content:''; position: absolute; width: 22px; height: 1px;
  background: var(--ink); transition: background-color var(--dur-fast) var(--ease);
}
.navclose::before{ transform: rotate(45deg); }
.navclose::after{ transform: rotate(-45deg); }
.navclose:hover::before, .navclose:hover::after{ background: var(--accent); }


/* ═══ MEDIA PANELS §12 ════════════════════════════════════════════════════
   A CSS gradient paints on the first frame; the photograph beneath it does
   not.  The frame therefore carries the artwork's own darkness, and the scrim
   waits for the picture. */
.media-panel{ position: relative; overflow: hidden; background-color: var(--media-dark); }
.media-panel .mp-art{ position: absolute; left: 0; right: 0; top: -4%; height: 108%;
                      z-index: 0; }
.media-panel .mp-art img,
.media-panel .mp-art video{ width: 100%; height: 100%; object-fit: cover; }
.media-panel::after{ content:''; position: absolute; inset: 0; z-index: 1;
                     pointer-events: none; }
/* The scrim arrives ON THE SAME FRAME as the picture, not after it.

   §12's gate exists because a gradient paints on the first frame and the
   photograph beneath it does not, so a scrim running .88 -> .08 over an empty
   box reads as a pale wash.  The gate is still right for the panels further
   down the page.  Two things about it were wrong here:

   1. It carried a .45s fade, so the darkening visibly washed IN a beat after
      the photograph had already arrived.  A gate is a gate: it either shows or
      it does not, and there is nothing to ease.
   2. The HERO does not need gating at all.  Its frame is painted in
      --media-dark, which IS the scrim's own darkest stop, so there is no pale
      wash to avoid — and its artwork is preloaded at high priority, so the
      picture is usually there before first paint anyway.  Holding its scrim
      back only ever made the gradient late. */
.js-media-wait .media-panel::after{ opacity: 0; }
.js-media-wait .media-panel.media-in::after{ opacity: 1; }
.js-media-wait .hero::after{ opacity: 1; }


/* ═══ HERO §7 ═════════════════════════════════════════════════════════════ */
.hero{
  /* NOT `height`.  A fixed height plus .media-panel's overflow:hidden means a
     stack that does not fit is silently cut off, and what gets cut is the
     bottom of the page's most load-bearing screen.  As a minimum it is still
     exactly one screen everywhere the content fits, and one screen plus the
     overflow everywhere it does not. */
  min-height: max(520px, 100svh);
  display: flex; align-items: stretch;
  /* §7.3 — gap A is the hero's own top padding MINUS the measured VISIBLE bar
     height.  Measuring from the bar's box was the mistake that cost three
     rounds: the bar carried 22px of invisible bottom padding, so the gap the
     eye saw was 111px against 89px while the code measured 89 against 89. */
  padding-top: calc(var(--nav-h) + var(--hero-gap));
  padding-bottom: var(--nav-pt);          /* §7.4 — the same token as .nav's top */
}
/* §4.6 — two layers: a horizontal wedge so the type side stays dark while the
   picture opens up to the right, plus a vertical pass for the header and the
   bottom line. */
.hero::after{
  /* Four layers, each with one job, so none of them has to be a curtain.  The
     first draft of this replaced the old flat wedge with a bottom-left radial,
     which looked better and measured worse: the eyebrow sits at 27% of the
     frame's height, where a veil anchored at 88% has already fallen off, and it
     came back 3.74:1 against the sky behind it.

     The type column runs from x=80 to x=670 at 1440 — 5% to 47% — at every
     height from the eyebrow to the dateline.  So the veil that carries it is a
     WEDGE, held to full strength across that column and gone by 78%, which
     leaves the right-hand two-fifths of the photograph — the part with the
     mountains in it — very nearly untouched. */
  background:
    /* 1. The type column.  Re-weighted when the hero went to video: the still
       it was tuned against is dark water where the headline's right end falls,
       and the film is bright sandstone and sky there.  Measured across four
       frames of the loop, the H1 came back 3.61-3.66 against a 3.0 floor —
       passing, but on a moving ground that is not a margin.  The mid stops
       move up and the falloff moves right; the mountains in the right two
       fifths are still untouched. */
    linear-gradient(90deg,  rgba(7,22,52,.90) 0%,  rgba(7,22,52,.86) 30%,
                            rgba(7,22,52,.66) 48%, rgba(7,22,52,.24) 66%,
                            rgba(7,22,52,0)   80%),
    /* 2. Depth into the corner the eye lands in first. */
    radial-gradient(90% 80% at 0% 100%,
                    rgba(7,22,52,.55) 0%, rgba(7,22,52,0) 70%),
    /* 3. The masthead — short, so it is not a band across the sky. */
    linear-gradient(180deg, rgba(7,22,52,.50) 0%, rgba(7,22,52,.10) 20%,
                            rgba(7,22,52,0)   38%),
    /* 4. A floor, so the foot of the picture does not run out bright. */
    linear-gradient(0deg,   rgba(7,22,52,.38) 0%, rgba(7,22,52,0) 16%);
}
/* The 34%-tall foot veil went with the proof strip it was drawn for.  Nothing
   sits in the bottom third of the hero at all now, and the floor stop in
   ::after is enough to keep the picture from running out bright. */

/* §5.8 — the hero drifts by TRANSLATING an oversized element.  A composited
   layer that is scaled is resampled by the GPU every frame: a scale(1.04)→1.0
   drift held 1.0398 at 500ms and 1.038 at 2.5s, so the picture was never once
   drawn at its true size and the client reported the hero as "very highly
   blurred".  108% tall with 4% slack at each end gives the drift room. */
@keyframes heroDrift{
  from{ transform: translate3d(0, 1.6%, 0); }
  to  { transform: translate3d(0, 0, 0); }
}
.hero .mp-art img,
.hero .mp-art video{ animation: heroDrift 24s cubic-bezier(.15,0,.85,1) forwards; }
/* When the video is armed it covers the still; until then it is [hidden]. */
.hero .mp-art video{ position: absolute; inset: 0; z-index: 1; }

.hz{
  position: relative; z-index: 2; width: 100%; color: #fff;
  display: flex; flex-direction: column; align-items: flex-start;
}
/* §7.3 — two auto margins share the leftover space equally, which is what
   keeps the two gaps identical at any screen height, including heights where
   there is no leftover at all. */
.hz .eb{ margin-top: auto; color: rgba(255,255,255,.90); }
.hz .row{ margin-bottom: auto; }

.hz h1{
  font-family: var(--font-d); font-weight: var(--wd);
  font-size: var(--s-hero); line-height: var(--lh-hero);
  letter-spacing: var(--track-tight);
  max-width: 18ch; margin-top: var(--sp-5);
  text-wrap: balance; text-shadow: 0 2px 52px rgba(0,0,0,.44);
}
/* The dateline.  Not emphasis inside the sentence — a line of its own, indented
   off the headline's left edge, at .58 of its size.  The indent is what makes
   it read as a subordinate line rather than as a fourth line of the same
   sentence; the italic and the colour are what make it read as the fact it is.
   text-indent, not margin: it has to move with the line box when the headline
   wraps differently at a narrower width. */
.hz h1 .dateline{
  display: block; font-style: italic; color: var(--sky);
  font-size: .58em; line-height: 1.25; letter-spacing: -.015em;
  margin-top: .22em; text-indent: .14em;
}
.hz .deck{
  font-size: var(--s-deck); line-height: 1.62; max-width: 36ch;
  color: rgba(255,255,255,.90); margin-top: var(--sp-5);
}
.hz .row{ display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-6);
          margin-top: var(--sp-6); }

/* ─── The editorial link §5.6b ────────────────────────────────────────────
   A rule under a word with an arrow after it, not a pill.  Two pills side by
   side inside a photograph is an application's gesture; this is a
   publication's.  The rule is the affordance, so it is drawn at rest rather
   than appearing on hover — a control that only announces itself when the
   cursor is already on it is not a control. */
.lnk{
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--font-m); font-size: var(--s--2); font-weight: 500;
  letter-spacing: var(--track-eb); text-transform: uppercase;
  color: #fff; padding: 14px 0 12px;      /* >= 44px tall with the rule */
  position: relative;
}
.lnk.quiet{ color: rgba(255,255,255,.78); }
.lnk::after{
  content: ''; position: absolute; left: 0; right: 0; bottom: 8px; height: 1px;
  background: currentColor; opacity: .5;
  transform-origin: left; transform: scaleX(1);
  transition: opacity var(--dur-card) var(--ease);
}
.lnk:hover{ color: #fff; }
.lnk:hover::after{ opacity: 1; }
.lnk .ar{ display: inline-flex; width: 22px; height: 8px; flex: none;
          transition: transform var(--dur-card) var(--ease); }
.lnk .ar svg{ width: 100%; height: 100%; fill: none; stroke: currentColor;
              stroke-width: 1; stroke-linecap: round; stroke-linejoin: round; }
.lnk:hover .ar{ transform: translateX(5px); }

/* The hero's dateline foot is gone with the location line it carried, and so
   is the scroll cue that sat opposite it.  The cue was a hairline travelling
   down inside another on a 2.6s infinite loop, and nothing else on this page
   or on the reference repeats: every piece of motion here is an arrival that
   finishes and stops.  One element looping for ever is the loudest thing in a
   quiet system, however small it is drawn. */

/* §7.7 — every element starts at a different moment and finishes on the SAME
   frame.  T = 1900ms; change a delay here and its duration must change with
   it.  The strip runs on the same CSS clock, not an IntersectionObserver whose
   timeline origin is whenever layout happened to settle — and it starts at
   delay 0 with a longer duration, because a strip that sits still for 700ms to
   buy the shared landing reads immediately as "not starting instantly". */
/* The reference's own keyframes and its own clock.  Every element starts at a
   different moment and finishes on the SAME frame — the stagger belongs in the
   entrances, not in the settle, so the hero resolves as one gesture rather
   than trailing off as several.  Change a delay here and its duration has to
   change with it. */
@keyframes heroUp{ from{ opacity: 0; transform: translate3d(0,30px,0); }
                     to{ opacity: 1; transform: translate3d(0,0,0); } }
/* opacity:0 on the element as well as in the keyframe: during the delay the
   animation is not yet applying, and without it the line would sit finished on
   screen and then jump back to its start when its turn came. */
.hz .eb  { opacity: 0; animation: heroUp 1.50s var(--ease) .40s forwards; }
.hz h1   { opacity: 0; animation: heroUp 1.40s var(--ease) .50s forwards; }
.hz .deck{ opacity: 0; animation: heroUp 1.30s var(--ease) .60s forwards; }
.hz .row { opacity: 0; animation: heroUp 1.20s var(--ease) .70s forwards; }
/* The bar's CONTENTS arrive; the bar itself never moves.  Animating .nav moved
   its ::before veil with it, so the darkening at the top of the hero slid down
   into place as a visible band instead of simply being part of the picture.
   The veil is a property of the ground, not an element of the intro. */
.nav .wordmark,
.nav .navlinks,
.nav > .btn,
.nav .navtoggle{ opacity: 0; animation: heroUp 1.30s var(--ease) .60s forwards; }
/* animation-fill-mode:forwards outranks the specified value, so once the intro
   has played a :hover transform on these has nothing to animate.  Hand it back. */
/* animation-fill-mode:forwards outranks the specified value, so once the intro
   has played a :hover transform on these has nothing to animate.  Handing the
   property back means also restoring the opacity the keyframe was holding. */
.hz .settled,
.nav .wordmark.settled, .nav .navlinks.settled,
.nav > .btn.settled, .nav .navtoggle.settled{ animation: none; opacity: 1; }




/* ═══ REVEALS ═════════════════════════════════════════════════════════════
   This is the motion system of sccpools.thecreativezohaib.com and
   coralseapools.thecreativezohaib.com, taken from their own stylesheets rather
   than inferred from how they look.  Both run one curve and one duration and
   carry hierarchy in DISTANCE alone:

     --ease      cubic-bezier(.22, 1, .36, 1)
     --dur-rv    1.2s          every scroll reveal, no exceptions
     text 14px · lateral 16px · card 34px · plate 56px · stage 52px

   Everything is OPAQUE from the first frame and arrives by moving.  An element
   dissolving up from zero opacity is indistinguishable from one still
   downloading, and on a page with sixty reveals that reads as "slow" however
   fast the page actually is.

   These rules sit LAST among the transitions they touch: the `transition`
   shorthand REPLACES the property rather than merging with it. */
.rv, .rv-l, .rv-r, .rv-s, .rv-up{
  opacity: 1;
  transition: transform var(--dur-rv) var(--ease);
}
.rv  { transform: translate3d(0, var(--rv-text), 0); }
.rv-l{ transform: translate3d(calc(var(--rv-side) * -1), 0, 0); }
.rv-r{ transform: translate3d(var(--rv-side), 0, 0); }
.rv-s{ transform: translate3d(0, var(--rv-stage), 0); }
.rv-up{ transform: translate3d(0, 30px, 0); }
.card.rv{ transform: translate3d(0, var(--rv-card), 0); }
.plate.rv-l, .story-pic.rv-l{
  transform: translate3d(calc(var(--rv-plate) * -1), 0, 0); }
.plate.rv-r, .story-pic.rv-r{
  transform: translate3d(var(--rv-plate), 0, 0); }
.rv.in, .rv-l.in, .rv-r.in, .rv-s.in, .rv-up.in{ transform: translate3d(0,0,0); }

/* The wipe, from the Coral Sea build: a frame revealed edge to edge rather
   than slid into place, for a picture that is already where it belongs. */

/* ─── Line-masked display type ─────────────────────────────────────────────
   A heading arrives one rendered line at a time from behind its own edge.
   The class names are the reference's: `.split` is the state, `.ln-mask` is
   the clipping line box, `.ln-in` is what moves.  `.ln-in` rather than `.in`
   matters — `.in` is the reveal state class, and using it for the inner span
   makes every `.in` query on the page return the heading's line wrappers too.

   110%, not 100%: the extra 10% keeps a descender out of sight until the line
   is genuinely arriving.  The mask's own bottom padding, pulled back by an
   equal negative margin, is what stops `overflow:hidden` clipping the
   descenders of the last line. */
.ln-mask{ display: block; overflow: hidden;
          padding-bottom: .06em; margin-bottom: -.06em; }
/* The offset is scoped to .split, which script adds.  Unscoped it was safe
   only because the splitter created every .ln-in itself at runtime; masks
   written into the HTML would have sat translated 110% inside their own
   overflow:hidden for anyone with script off, which is a heading that never
   appears.  The JS-built ones always carry .split, so nothing changes for
   them. */
.ln-in{ display: block; transition: transform 1.15s var(--ease); }
.split .ln-in{ transform: translate3d(0, 110%, 0); }
.split.in .ln-in{ transform: translate3d(0,0,0); }
.ln-mask:nth-child(2) .ln-in{ transition-delay: 90ms; }
.ln-mask:nth-child(3) .ln-in{ transition-delay: 180ms; }
.ln-mask:nth-child(4) .ln-in{ transition-delay: 270ms; }
.ln-mask:nth-child(5) .ln-in{ transition-delay: 360ms; }
/* A split heading must not ALSO translate: the lines carry the arrival. */
.split.rv, .split.rv-l, .split.rv-r, .split.rv-up{
  transform: none !important; transition: none !important;
}

/* ═══ THE HERITAGE STRIP ══════════════════════════════════════════════════
   A museum caption, not a statistics row.  What made the old version read as a
   dashboard was the furniture rather than the numbers: a rule above, a rule
   below, and a vertical rule between every pair, which is five lines drawn to
   separate four things that a column of white space separates perfectly well.
   One rule survives, above the set, because the strip does need to be marked
   off from the hero it follows.

   The number is the whole of it.  The label under it is small and the sentence
   under that is smaller, and the distance between them does the ranking. */
.record{ padding-block: var(--sec); }
.figs{
  display: grid; grid-template-columns: repeat(4, minmax(0,1fr));
  gap: var(--sp-5) var(--colgap);
  border-top: var(--hair); padding-top: var(--sp-6);
}
.fig{ min-width: 0; padding: 0; }
.fig b{
  display: block; font-size: var(--s-3); font-weight: 300;
  line-height: .92; letter-spacing: -.04em;
}
.fig .fol{ display: block; margin-top: var(--sp-3); color: var(--ink);
           font-weight: 500; max-width: 20ch; }
.fig p{ margin-top: var(--sp-2); font-size: var(--s--1); line-height: 1.62;
        color: var(--ink-55); max-width: 26ch; }


/* ═══ FULL-BLEED BAND ═════════════════════════════════════════════════════ */
/* The band is the photograph, inset to the gutter like every other image on
   the page, and framed with the same radius token.  `section{padding-block}`
   is zeroed here or it would paint 128px of --media-dark above and below the
   picture and read as two navy stripes. */
.band.stage{ position: relative; padding-block: 0; background: transparent; }
.band.stage .parallax-wrap{
  height: clamp(320px, 46vw, 620px);
  margin-inline: var(--gut);
  border-radius: var(--r-m);
  background: var(--media-dark);
}
/* The bottom stop is .84, not .52: with .52 the caption measured 3.60:1 against
   the lightest pixel of the lawn it sits on.  A veil, never a colour change. */
.band.stage::after{
  left: var(--gut); right: var(--gut); border-radius: var(--r-m);
  background: linear-gradient(180deg, rgba(7,22,52,.30) 0%, rgba(7,22,52,0) 32%,
                                      rgba(7,22,52,0) 52%, rgba(7,22,52,.84) 100%);
}
/* Inside the frame, not on the page gutter: the caption sits one inner margin
   in from the picture's own edge.  --gut still appears exactly once in this
   element's ancestor chain (§1.1) — the second term is its own token. */
/* `.band > .inner` (0,2,0) out-specifies a bare `.cap-over` (0,1,0), so the
   caption inherited the plain gutter and sat flush against the frame's own
   left edge.  Matched specificity, and the inner margin lands. */
.band > .inner.cap-over{
  position: absolute; left: 0; bottom: var(--sp-4); z-index: 2; color: #fff;
  padding-inline: calc(var(--gut) + var(--pad-in));
}

/* §5.8 — parallax by translating an oversized layer inside a clipped frame.
   8% slack at each end.  Travel is a share of each frame's OWN height, so a
   tall stage moves further than a small plate, which is what reads as depth. */
.parallax-wrap{ position: relative; overflow: hidden; display: block; }
/* `img.parallax-img`, not `.parallax-img`: moving from `> *` to a descendant
   selector when the images were wrapped in <picture> put this rule one element
   BELOW `.media-panel .mp-art img` on specificity, so the closing panel's
   artwork lost its 116% height, kept its parallax translate, and exposed 7px of
   --media-dark along the bottom edge of the section.  Any oversized layer that
   is also translated has to out-specify the plain cover rule. */
.parallax-wrap img.parallax-img{
  position: absolute; left: 0; top: -8%; width: 100%; height: 116%;
  object-fit: cover; transform: translate3d(0,0,0);
}


/* ═══ IMAGE FRAMES §8.2 ═══════════════════════════════════════════════════
   Every photograph on the page sits in a frame and EVERY frame is identical:
   one radius token, one placeholder ground, one overflow, one hover.  --tint
   must actually be declared or the frame falls back to transparent and flashes
   paper-white under its artwork on every load. */
.fr{
  position: relative; display: block; overflow: hidden;
  border-radius: var(--r-m); background: var(--tint);
}
.fr img{ width: 100%; height: 100%; object-fit: cover; }
.fr:not(.parallax-wrap) img{
  transition: transform var(--dur-img) var(--ease);
}
/* The one permitted scale() on a raster: transient, user-initiated, and it ends. */
.plate:hover .fr:not(.parallax-wrap) img{ transform: scale(1.03); }

.cap{ margin-top: var(--sp-2); font-size: var(--s--1); color: var(--ink-55); }
.cap b{ color: var(--ink); font-weight: 500; }
.cap b::after{ content: ' — '; color: var(--ink-30); font-weight: 400; }
.cap-line{ margin-top: var(--sp-2); display: block; }

/* Captions set over a photograph, with their own local veil. */
/* The gradient is tall AND heavy where the text actually sits.  Measured with
   the §4.5 pixel sampler over the brightest pool water in the set: at the old
   0 -> .74 ramp these labels ran 2.47 to 4.03:1.  The tall run-up is what keeps
   a scrim this dark from reading as a black bar across the picture. */
.fr .over{
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: var(--sp-7) var(--sp-3) var(--sp-3);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px;
  color: #fff;
  background: linear-gradient(180deg, rgba(7,22,52,0) 0%, rgba(7,22,52,.22) 38%,
                                      rgba(7,22,52,.76) 64%, rgba(7,22,52,.90) 100%);
}
.fr .over b{ font-size: var(--s--1); font-weight: 600; }
.fr .over .eb{ color: rgba(255,255,255,.9); }


/* ═══ EDITORIAL SPLIT §8.4 ════════════════════════════════════════════════ */
/* Renamed from `.split`: that name now belongs to the line-mask state class
   the reference uses, and one word cannot be both a layout grid and an
   animation state. */
.editorial{ display: grid; grid-template-columns: repeat(12, minmax(0,1fr));
            gap: var(--colgap); align-items: stretch; }
.story-copy{ grid-column: 1 / 7; }
/* The frame ends where the copy ends.  align-items:start left the picture
   floating with a column of dead space under it; stretching the figure and
   letting the frame take the slack bottom-aligns the two columns at every
   width.  Its right edge sits on the gutter line, like every other right edge
   on the page (§1.3) — no bleed. */
.story-pic{ grid-column: 8 / 13; display: flex; flex-direction: column; }
.story-pic .fr{ flex: 1; min-height: clamp(360px, 38vw, 620px); }

.story-copy h2.d{ margin-bottom: var(--sp-5); }
.story-copy .pull{ margin-top: var(--sp-4); padding-top: var(--sp-4);
                   border-top: var(--hair); }
.hall-h{ display: block; margin-top: var(--sp-6); color: var(--ink-55); }
.hall{ margin-top: var(--sp-3); border-top: var(--hair); }
.hall > div{ display: grid; grid-template-columns: minmax(0,.34fr) minmax(0,1fr);
             gap: var(--sp-3); padding: var(--sp-3) 0; border-bottom: var(--hair); }
/* The list ends with its last item, not with a rule under it. */
.hall > div:last-child{ border-bottom: 0; padding-bottom: 0; }
.hall dt{ font-family: var(--font-d); font-weight: 400; font-size: var(--s-1);
          line-height: 1.1; letter-spacing: -.02em; }
.hall dd{ font-size: var(--s--1); line-height: 1.55; color: var(--ink-55); }


/* ═══ SECTION HEADER BLOCK ════════════════════════════════════════════════ */
.sechead{ display: grid; grid-template-columns: minmax(0,1.15fr) minmax(0,.85fr);
          gap: var(--colgap); align-items: start; }
.sechead .sub{ max-width: 42ch; padding-top: .7em; }
.sechead + *{ margin-top: var(--sp-8); }
h2.d.wide + *{ margin-top: var(--sp-8); }

/* ─── The manifesto ──────────────────────────────────────────────────────
   One section on the page is allowed to be nothing but type.  Four one-word
   lines, set larger than any heading and much tighter, so the four ways of
   building are read as a list of materials rather than as a sentence about
   them.  The last takes the italic, because the vanishing edge is the one that
   is a decision rather than a technique.

   No max-width in ch: the measure here is four words, and a character-count
   cap would only ever be doing nothing. */
h2.d.manifesto{
  /* The SAME size, weight and tracking as every other section heading — it
     inherits --s-4 from h2.d and states nothing of its own.  It was set to a
     scale of its own (up to 115px, weight 300) and that made section 02 read as
     a different page; what makes this one a manifesto is the four one-word
     lines and the italic on the last, not a larger type size.

     Only two things are its own: no ch cap, because the measure here is four
     words and a character count would never be doing anything; and slightly
     tighter leading, because --lh-d is set for lines of running text and one
     word per line at that leading reads as four separate headings. */
  line-height: .98;
  max-width: none; text-wrap: nowrap;
}
h2.d.manifesto em{ color: var(--accent); }
/* The paragraph sits BESIDE the statement and centres against it, not under
   it.  Four one-word lines make a tall, narrow block with a great deal of air
   to its right; a paragraph dropped beneath it leaves that air unused and puts
   two stacked left edges where one would do.  align-items:center is the whole
   of it: the note's optical middle lands on the middle of the four lines, so
   the pair reads as one object. */
.manifesto-wrap{
  display: grid; grid-template-columns: minmax(0,.78fr) minmax(0,1fr);
  gap: var(--colgap); align-items: center;
}
.manifesto-note{ max-width: 46ch; }
.manifesto-wrap + *{ margin-top: var(--sp-8); }
@media (max-width: 980px){
  /* Below the width at which the two columns each hold their own measure, the
     note goes under the statement and the pair stops being centred — there is
     nothing left to centre against. */
  .manifesto-wrap{ grid-template-columns: minmax(0,1fr); align-items: start; }
  .manifesto-note{ margin-top: var(--sp-5); max-width: none; }
}
@media (max-width: 720px){
  h2.d.manifesto{ line-height: 1.0; }
  .manifesto-wrap + *{ margin-top: var(--sp-7); }
}


/* ═══ GALLERY §8.7 ════════════════════════════════════════════════════════ */
.gal{ display: grid; grid-template-columns: repeat(12, minmax(0,1fr));
      gap: var(--colgap); }
.plate{ display: flex; flex-direction: column; min-width: 0; }
.plate .fr{ flex: 1; }
/* Four tiles, not six: Sequoyah build four ways and the mosaic says exactly
   that.  Two stacked tiles beside one tall one, then the infinity edge across
   the full measure — the tile whose entire subject is a horizon line is the
   one that earns the width. */
.p-edge{ grid-column: 1 / 8;  grid-row: 1 / 3; }
.p-nat { grid-column: 8 / 13; grid-row: 1; }
.p-det { grid-column: 8 / 13; grid-row: 2; }
.p-ind { grid-column: 1 / 13; grid-row: 3; }
.p-nat .fr, .p-det .fr{ aspect-ratio: 16 / 11; }
.p-ind .fr{ aspect-ratio: 24 / 8; }


/* ═══ MANUFACTURER MARKS ═════════════════════════════════════
   Six marks, once, under the work they are installed in — not a scrolling
   carousel, and not six stray logos dropped into the footer.  They are set to
   one optical weight rather than one pixel height: the supplied files arrive at
   wildly different ink coverage, and matching their boxes makes the heavy ones
   shout.  One max height, one opacity, so the row reads as a single line of
   small print, which is what it is. */
.brands{ margin-top: var(--sp-7); padding-top: var(--sp-4); border-top: var(--hair); }
.brands-h{ display: block; color: var(--ink-55); }
.brand-row{
  list-style: none; margin: var(--sp-5) 0 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center;
  /* Flush left and flush right, evenly distributed between — the same two edges
     every other row on the page ends on.  The gap is the MINIMUM, not the
     spacing: space-between spends whatever is left over after the seven marks,
     and the gap only takes over if the row ever runs out of room to spend. */
  justify-content: space-between;
  gap: var(--sp-4) var(--sp-5);
}
/* Each mark is FITTED into one box rather than set to one height: their ink
   boxes run 131x17 to 155x50, and matching heights would set the widest,
   thinnest mark three times the size of the tallest.  The li carries the box so
   the flex gap measures between boxes, not between ragged artwork. */
.brand-row li{
  flex: 0 0 auto; min-width: 0;
  height: 34px; display: flex; align-items: center;
}
.brand-row img{
  display: block; max-height: 34px; max-width: 140px;
  width: auto; height: auto; object-fit: contain;
  /* The build keys their white plate to alpha, so there is no blend mode here
     and no pale rectangle when the ground is not pure white.  The opacity holds
     six other companies' marks below the work they are installed in. */
  opacity: .66;
  transition: opacity var(--dur-card) var(--ease);
}
.brand-row li:hover img{ opacity: 1; }
/* SGM is the seventh mark they name and the only one they do not supply a file
   for.  Set, not drawn: a traced lookalike of somebody else's wordmark is worse
   than type, and at 26px in the mono face it sits at the same optical weight as
   the six that are artwork. */
.brand-word{
  font-family: var(--font-m); font-weight: 500; font-size: 15px;
  letter-spacing: .16em; color: var(--ink); opacity: .66;
  transition: opacity var(--dur-card) var(--ease);
}
.brand-row li:hover .brand-word{ opacity: 1; }

/* ─── Two rows below 1200, both of them flush ───────────────────────────
   Seven marks fit on one line down to about 1200 and no further — measured, not
   picked.  Two things were tried before this one:

     LETTING FLEX WRAP.  It broke them 5/2 at 980, and a two-item row under
     space-between is two marks with the whole measure of hole between them.

     A PLACED FLEX BREAK, so both lines could keep space-between and get exactly
     even gaps.  It does not work: flex items sized from an image's intrinsic
     width do not shrink far enough to fit four marks into 346px, and the row
     wrapped to four lines instead of two.

   So the marks are PLACED, on a twelve-column grid: four above, three below,
   each row starting on the left gutter and ending on the right one.  The spans
   in the second row are 5/4/3 rather than 4/4/4 because SGM is a three-letter
   wordmark and the other two are 130px of artwork — equal cells left SGM
   stranded 89px from CoverStar, and sizing the cell to the mark closes it. */
@media (max-width: 1200px){
  .brand-row{
    display: grid; grid-template-columns: repeat(12, minmax(0,1fr));
    gap: var(--sp-5) 0; align-items: center;
  }
  .brand-row li:nth-child(1){ grid-column: 1 / 4;   justify-self: start; }
  .brand-row li:nth-child(2){ grid-column: 4 / 7;   justify-self: center; }
  .brand-row li:nth-child(3){ grid-column: 7 / 10;  justify-self: center; }
  .brand-row li:nth-child(4){ grid-column: 10 / 13; justify-self: end; }
  .brand-row li:nth-child(5){ grid-column: 1 / 6;   justify-self: start; }
  .brand-row li:nth-child(6){ grid-column: 6 / 10;  justify-self: center; }
  .brand-row li:nth-child(7){ grid-column: 10 / 13; justify-self: end; }
  /* max-width:100% is what makes the fit box the CELL rather than a fixed
     140px: at 390 a quarter of the measure is about 86px, and a mark that does
     not scale into that runs into the one beside it. */
  .brand-row img{ max-width: 100%; }
  /* And the air between them.  Scaling each mark to fill its whole track put
     Hayward's last letter against Pentair's first — the boxes did not overlap,
     which is why the geometry check passed, but there was no gap to see.  The
     padding is INSIDE the cell and comes off the two edge items of each row, so
     the row still starts and ends on the section's own two edges. */
  .brand-row li{ padding-inline: 8px; }
  .brand-row li:nth-child(1), .brand-row li:nth-child(5){ padding-left: 0; }
  .brand-row li:nth-child(4), .brand-row li:nth-child(7){ padding-right: 0; }
}
@media (max-width: 720px){
  .brand-row{ gap: var(--sp-4) 0; }
  .brand-row li{ height: 30px; }
  .brand-row img{ max-height: 30px; }
  .brand-word{ font-size: 13px; letter-spacing: .14em; }
}

/* The line under the mosaic that the rest of the section has no room for:
   commercial work, and the retail counter.  Set at the section's own measure
   and set apart from the tiles above it, because it is an aside rather than a
   fifth way of building. */
.also{ margin-top: var(--sp-6); max-width: 56ch; }
.also b{ font-weight: 500; color: var(--ink); }


/* ═══ THE CRAFT ═══════════════════════════════════════════════════════════ */
.craft{ display: grid; grid-template-columns: repeat(3, minmax(0,1fr));
        gap: var(--colgap); }
.cr{ min-width: 0; }
.cr .fr{ aspect-ratio: 4 / 3; }
.cr .cap-line{ padding-bottom: var(--sp-3); border-bottom: var(--hair); }
.cr .idx{ display: block; margin-top: var(--sp-3); color: var(--accent); }
.cr .ch{ margin-top: var(--sp-1); }
.cr .bd{ margin-top: var(--sp-2); }


/* ═══ SERVICES §8.5 ═══════════════════════════════════════════════════════ */
.band.tinted{ background: var(--tint); padding-block: var(--sec); }
.stages-h{ display: block; margin-top: var(--sp-7); color: var(--ink-55); }
.stages{ position: relative; margin-top: var(--sp-4); padding-top: var(--sp-5); }
.stages .conn{ position: absolute; inset: 0; width: 100%; height: 100%;
               overflow: visible; pointer-events: none; }
/* §8.6 — the drawn path is the ONLY line in this component.  A static track
   under it makes the drawn part indistinguishable from the undrawn part. */
.drawn-path{ stroke: var(--accent); stroke-width: 1; stroke-linecap: round; }
.stage-row{ display: grid; grid-template-columns: repeat(4, minmax(0,1fr));
            gap: var(--colgap) var(--sp-5); }
.node{ min-width: 0; position: relative; }
.node b{
  display: inline-block; font-family: var(--font-m); font-size: var(--s--2);
  font-weight: 500; letter-spacing: .12em; color: var(--ink-55);
  padding: 5px 9px; border-radius: 999px;
  background: var(--tint); border: 1px solid var(--rule);
  transition: transform var(--dur-rv) var(--ease),
              box-shadow var(--dur-rv) var(--ease), color var(--dur-rv) var(--ease),
              border-color var(--dur-rv) var(--ease);
}
/* A ring on an unchanged ground, never a fill: filling each node solid as the
   line reached it turned every passed node into a dark blob and the drawn line
   disappeared behind them. */
.node.lit b{ border-color: var(--accent); color: var(--accent);
             box-shadow: 0 0 0 1px var(--accent); }
/* The badge grows very slightly under the cursor as well as taking its ring —
   scale on a 1px-bordered circle is cheap and it is what makes the row feel
   live rather than lit.  Never a fill (§5.7). */
.node b:hover{ transform: scale(1.05); border-color: var(--accent);
               color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
.node .ch{ margin-top: var(--sp-2); }
.node .bd{ margin-top: var(--sp-2); max-width: 30ch; }


/* ═══ THE SERVING LEDGER ═════════════════════════════════════════════════
   The counties, set as one record rather than as a page per town.  Two columns
   of three on a hairline ledger, so the whole map costs half the height that
   six stacked rows would.

   No map graphic.  A styled map tile is a picture of a map, not an answer to
   "do you come to my address"; the list of towns IS the answer, and it is
   readable, searchable and correct on a phone. */
.arch{ display: grid; grid-template-columns: repeat(2, minmax(0,1fr));
       column-gap: var(--sp-7); }
/* Two columns, not three.  The county name and the word after it belong in one
   cell: split across two grid tracks, "Anderson" and "County" collided at the
   width the track was sized for, and widening that track to fit the longest
   county left the short ones swimming. */
.aw{
  display: grid;
  grid-template-columns: minmax(0,.46fr) minmax(0,1fr);
  gap: var(--sp-2) var(--sp-3); align-items: baseline;
  padding-block: var(--sp-3);
  border-top: var(--hair);
}
.aw:nth-last-child(-n+2){ border-bottom: var(--hair); }
.aw-name{ display: flex; align-items: baseline; gap: .38em; min-width: 0;
          flex-wrap: wrap; }
.aw b{ font-family: var(--font-d); font-weight: var(--wd); font-size: var(--s-1);
       line-height: 1; letter-spacing: -.03em; }
.aw .an{ font-family: var(--font-d); font-weight: 400; font-size: var(--s-1);
         line-height: 1.15; letter-spacing: -.02em; color: var(--ink-55); }
/* Set left, not right.  Right-aligning a list that runs to three lines gives it
   a ragged LEFT edge, which is the edge the eye reads down. */
.aw .eb{ color: var(--ink-55); line-height: 1.75; }
.aw:hover .an{ color: var(--accent); }
.aw .an{ transition: color var(--dur-card) var(--ease); }

/* ═══ THE QUESTIONS §9 ════════════════════════════════════════════════════
   <details>/<summary> for the semantics — keyboard, the accessibility tree and
   find-in-page for free — with `open` driven from script, because a closed
   <details> does not render its content and there is nothing to transition
   from (§9.1). */
.faq-wrap{ align-items: start; }
/* §9.6 — the picture SPANS the accordion's box rather than sitting beside it at
   an offset, so the first question is top-aligned with it and the last is
   bottom-aligned with it in every state.  A fixed aspect ratio cannot also be a
   fixed height, and the height is the thing that has to match, so the frame
   takes --faq-h and object-fit takes the crop. */
.faq-pic{ grid-column: 1 / 5; margin: 0; align-self: start; }
.faq-pic .fr{ height: var(--faq-h, auto); min-height: 320px; }

/* §9.4 — opening a question must not change the section's height.  The list is
   pinned to its COLLAPSED height, measured after the fonts land; the closed
   rows then give up 8px of padding a side, and flex-grow spends whatever is
   left over so the last row's bottom edge lands in the same place every time.
   Compression guarantees the space exists; flex-grow spends it. */
.faq-list{
  grid-column: 6 / 13; height: var(--faq-h, auto);
  display: flex; flex-direction: column;
  border-top: var(--hair);
}
.faq details{
  display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0;
  border-bottom: var(--hair);
}
.faq details.opening{ flex: 0 0 auto; }
.faq summary{
  flex: 1 1 auto; display: flex; align-items: baseline; gap: var(--sp-3);
  padding-block: var(--sp-4); cursor: pointer; list-style: none;
  transition: padding var(--dur-card) var(--ease), color var(--dur-fast) var(--ease);
}
.faq summary::-webkit-details-marker{ display: none; }
.faq-list:has(details.opening) summary{ min-height: 0; padding-block: var(--sp-3); }
.faq summary:hover .qt{ color: var(--accent); }
.faq .qn{ flex: none; padding-top: .25em; }
.faq .qt{
  flex: 1; min-width: 0;
  font-family: var(--font-d); font-weight: 400; font-size: var(--s-1);
  line-height: 1.18; letter-spacing: -.02em; text-wrap: pretty;
  transition: color var(--dur-fast) var(--ease);
}
.faq details.opening .qt{ color: var(--accent); }

/* §9.7 — a plus that becomes a minus by ROTATING the vertical stroke into the
   horizontal one as it goes, so the change reads as one movement rather than as
   a stroke blinking out. */
.faq .tg{ width: 24px; height: 24px; flex: none; position: relative; align-self: center; }
.faq .tg::before, .faq .tg::after{
  content: ''; position: absolute; top: 50%; left: 50%; background: var(--accent);
  transform: translate(-50%,-50%);
  transition: transform var(--dur-card) var(--ease), opacity var(--dur-card) var(--ease);
}
.faq .tg::before{ width: 16px; height: 1px; }
.faq .tg::after { width: 1px;  height: 16px; }
.faq details.opening .tg::after{ transform: translate(-50%,-50%) rotate(90deg); opacity: 0; }

/* §9.2 — a MEASURED height, never grid-template-rows: 0fr -> 1fr.  `1fr`
   resolves against the container's free space, and where that space is
   indefinite the track takes its base size, which for an overflow:hidden child
   is zero plus its padding — every answer opened to a 32px window over 96px of
   text, and only on mobile, because the pinned desktop list made the free space
   definite and hid the bug where it was written. */
/* Scoped to .js, which the inline head script sets before first paint.
   Unscoped, an answer stayed pinned at height:0 with its content at opacity:0
   even when <details> had been opened natively — so with script off the
   accordion looked like it worked and answered nothing.  Script off, the five
   questions are simply five open <details>. */
.js .faq .ans{ height: 0; overflow: hidden; transition: height var(--dur-panel) var(--ease); }
.js .faq .ans > .inner{
  opacity: 0; transform: translateY(-4px);
  transition: opacity .4s var(--ease), transform .4s var(--ease);
}
:root:not(.js) .faq .ans > .inner{ padding-bottom: var(--sp-4); }
/* The one permitted short fade (§5.3): the text arrives AFTER the space has
   been made for it, rather than sliding down inside a box that is itself still
   moving. */
.faq details.opening .ans > .inner{
  opacity: 1; transform: none; padding-bottom: var(--sp-4);
  transition: opacity .4s var(--ease) .15s, transform .4s var(--ease) .15s;
}
.faq .ans .bd{ max-width: 52ch; }


/* ═══ CLOSING INVITATION §8.10 ════════════════════════════════════════════ */
.invite{ position: relative; display: grid; align-items: center;
         min-height: clamp(560px, 66vw, 860px); padding-block: var(--sp-8);
         background: transparent; }
/* Artwork, scrim and veil all inset to the gutter and share the frame radius,
   so the panel reads as a picture on the page rather than as a band across it. */
.invite .mp-art,
.invite::after,
.invite::before{ left: var(--gut); right: var(--gut); border-radius: var(--r-m); }
.invite .mp-art{ overflow: hidden; background: var(--media-dark); }
.invite > .inner{ padding-inline: calc(var(--gut) + var(--pad-in)); }
.invite::after{
  background:
    linear-gradient(90deg,  rgba(7,22,52,.86) 0%, rgba(7,22,52,.70) 40%,
                            rgba(7,22,52,.34) 74%, rgba(7,22,52,.16) 100%),
    linear-gradient(180deg, rgba(7,22,52,.42) 0%, rgba(7,22,52,.14) 40%,
                            rgba(7,22,52,.46) 100%);
}
.invite .mp-art{ top: -8%; height: 116%; }
.iz{ position: relative; z-index: 2; color: #fff; width: 100%; }
/* One step down from the section scale.  At --s-4 this ran to three lines,
   filled the panel top to bottom, orphaned "ground" onto the last line away
   from the emphasis it belongs to, and left the whole right two-thirds of the
   photograph empty.  At this size it sets in two lines with the italic intact
   and the panel has air above and below it again. */
.invite h2.d{
  font-size: clamp(1.95rem, 1.35rem + 2.35vw, 3.5rem);
  max-width: 30ch; color: #fff;
}
.invite h2.d em{ color: var(--sky); }      /* 8.02:1 on the media ground */
.invite .bd{ color: rgba(255,255,255,.88); margin-top: var(--sp-5); max-width: 46ch; }
.invite .row{ display: flex; flex-wrap: wrap; align-items: center;
              gap: var(--sp-3) var(--sp-5); margin-top: var(--sp-5); }
/* The number itself is in the href, never set as page copy. */
.invite .tel{
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--font-m); font-size: var(--s--2); font-weight: 500;
  letter-spacing: var(--track-eb); text-transform: uppercase; color: #fff;
  padding: 13px 4px;
  transition: color var(--dur-fast) var(--ease);
}
.invite .tel .ic{
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255,255,255,.42);
  transition: transform var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
.invite .tel .ic svg{ width: 14px; height: 14px; fill: none; stroke: currentColor;
                      stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.invite .tel:hover .ic{ transform: translateX(4px); border-color: #fff; }


/* ═══ FOOTER §10 ══════════════════════════════════════════════════════════ */
.foot{ padding-block: var(--sp-6) var(--sp-4); border-top: var(--hair); }
/* Every track is minmax(0,…): the default minimum is min-content, and one
   unbreakable string in any column widens that track, shrinks the others, and
   moves the right edge of the last column off the gutter line — at some
   viewport widths only, which is why it survives review.  The asymmetric
   fractions are deliberate: the brand column carries a paragraph and needs
   measure, the link lists are short and need less. */
.foot .top{
  display: grid;
  grid-template-columns: minmax(0,1.45fr) minmax(0,.8fr) minmax(0,.85fr) minmax(0,1.15fr);
  gap: var(--sp-5); align-items: start;
}
.fmark{ display: inline-block; }
.fmark img{ width: 190px; height: auto; }
.fbrand .bd{ margin-top: var(--sp-3); max-width: 42ch; font-size: var(--s--1); }
.faddr{ margin-top: var(--sp-3); font-size: var(--s--1); color: var(--ink-55); }
.fico{ display: flex; gap: 10px; margin-top: var(--sp-3); }
.fico a{
  width: 44px; height: 44px; border-radius: 50%; border: var(--hair);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink-75);
  transition: color var(--dur-card) var(--ease), border-color var(--dur-card) var(--ease);
}
.fico a:hover{ color: var(--accent); border-color: var(--accent); }
.fico svg{ width: 17px; height: 17px; fill: none; stroke: currentColor;
           stroke-width: 1.35; stroke-linecap: round; stroke-linejoin: round; }

.foot h2.eb{ display: block; color: var(--ink); margin-bottom: var(--sp-3); }
/* §10.5 — compactness is a requirement.  A footer link list that inherits the
   body's 1.68 line-height is a paragraph; the gap is set on the list, not on
   the leading, so every pair sits at exactly one distance. */
.foot ul{ display: flex; flex-direction: column; gap: 10px; }
.foot ul li{ line-height: 1.35; }
.foot ul a{ display: inline-block; padding-block: 5px; margin-block: -5px;
            font-size: var(--s--1); color: var(--ink-75);
            transition: color var(--dur-card) var(--ease); }
.foot ul a:hover{ color: var(--accent); }

.nl-blurb{ margin-top: var(--sp-2); font-size: var(--s--1); color: var(--ink-55);
           max-width: 34ch; }

.hours-h{ margin-top: var(--sp-5); }
.hours > div{ display: flex; justify-content: space-between; align-items: baseline;
              gap: var(--sp-2); padding: 5px 0; }
.hours dt{ font-size: var(--s--1); color: var(--ink-75); }
.hours dd{ font-family: var(--font-m); font-size: var(--s--2); letter-spacing: .1em;
           color: var(--ink-55); white-space: nowrap; }

/* §10.3 — exactly three items.  space-between puts the first on the left
   gutter, the last on the right gutter and the third optically centred.
   baseline, not center: the three are different lengths and may wrap. */
/* §10.3 — exactly three items on a space-between row.  The first lands on the
   left gutter, the last on the right gutter, and the third sits optically
   centred between them; equal space, not equal width, which is what keeps the
   two outer items flush with every other edge on the page.
   align-items:baseline, not center — the three are different lengths and wrap
   at different points on a narrow screen. */
.foot .bot{
  margin-top: var(--sp-6); padding-top: 20px; border-top: var(--hair);
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--sp-4);
  font-family: var(--font-m); font-size: var(--s--2); letter-spacing: .1em;
  line-height: 1.7; text-transform: uppercase; color: var(--ink-55);
}
.foot .bot span{ flex: 0 1 auto; min-width: 0; }
/* Two items now, not three: "we speak English and Spanish" was the middle one,
   and it is already the last clause of the footer's own paragraph above this
   line and a hallmark in section 01.  With two, space-between is the whole
   rule and the centring is not needed. */
.foot .bot span:last-child{ text-align: right; }
/* Set apart from the credentials above it, not tucked under them. */
.copy{ margin-top: var(--sp-4); text-align: center;
       font-family: var(--font-m); font-size: var(--s--2);
       letter-spacing: .1em; text-transform: uppercase; color: var(--ink-55); }


/* ═══ RESPONSIVE §11 ══════════════════════════════════════════════════════
   §11.5 component matrix, as built:

   Component      >=1180      980–1180   720–980    360–720    <360
   Proof strip    5 across    5 across   5 across   2/2/1 grid 2/2/1
   Record         4           4          2          2          2
   Gallery        3-col mosaic 2         2          2          2
   Craft          3           3          3          2*         1 wide
   Services       3           3          2          2*         1 wide
   (* two-up from 480 to 720; below 480 each item turns sideways, picture left)
   Stages         4 (1 row)   4          2 (2 rows) 2 (2 rows) 2
   Archive        4           4          3          2          2
   Voices         7/5 split   7/5        stacked    stacked    stacked
   Footer         4           2          2          2          2
   ═══════════════════════════════════════════════════════════════════════ */

@media (max-width: 1180px){
  .story-copy{ grid-column: 1 / 7; }
  .story-pic{ grid-column: 7 / 13; }
}

/* §11.3 — display:none on a navigation element is only ever half a change.
   The replacement is written in the same rule.  1360, not 980, and the number
   is measured rather than picked.  The list is centred ON THE PAGE, and the
   estimate button is 98px wider than the wordmark, so the right-hand gap runs
   out first: 0px at 1310, 12px at 1361, 46px at 1440.  Below 1360 the whole
   bar becomes the button, and the panel behind it carries the same seven
   headings with every sub-page under them. */
@media (max-width: 1360px){
  .navlinks{ display: none; }
  .nav > .btn{ display: none; }
  .navtoggle{ display: flex; }
}

@media (max-width: 980px){

  .figs{ grid-template-columns: repeat(2, minmax(0,1fr));
         gap: var(--sp-5) var(--sp-4); padding-top: var(--sp-5); }

  /* §11.2 — the image goes first: it is the thing that earns the scroll. */
  .editorial{ grid-template-columns: minmax(0,1fr); }
  /* §9.4 — take the pin OFF where the picture stacks above the list: there is
     nothing left to line up with, and the pin becomes a clamp.  A question at
     this width runs to three lines, so the resting padding is already the
     compressed padding, there is no headroom to reclaim, and the opening answer
     would be squeezed. */
  .faq-pic{ grid-column: 1 / -1; }
  .faq-pic .fr{ height: auto; aspect-ratio: 16 / 10; min-height: 0; }
  .faq-list{ grid-column: 1 / -1; height: auto; margin-top: var(--sp-5); }
  .faq details, .faq details.opening, .faq summary{ flex: 0 0 auto; }
  .faq-list:has(details.opening) summary{ padding-block: var(--sp-4); }
  /* §11.2 — the picture is INTERLEAVED into the copy on a phone, not stacked
     above it: it lands after the line about the firm never changing hands and
     before the hallmarks, so the reader gets the story, then the evidence, then
     the record.  Above the copy it was just a picture with a wall of text under
     it.

     display:contents on the copy column is what makes that reachable — `order`
     only sorts siblings, and the picture is a sibling of the COLUMN, not of the
     paragraphs inside it.  Dissolving the column promotes its children to grid
     items alongside the picture, and then three order values are enough.  The
     `.bd + .bd` and `h2 + *` margins are DOM-adjacency and are untouched by it. */
  .story-copy{ display: contents; }
  .story-copy > *{ order: 1; }
  .story-pic{ order: 2; grid-column: 1 / -1;
              margin: var(--sp-5) 0; }
  .story-copy > .hall-h, .story-copy > .hall{ order: 3; }
  /* A little taller than 16/10, not a lot: the frame is the one picture in the
     section and it now has copy above AND below it, so it can afford the height
     — but past about 16/11 it starts pushing the hallmarks off the screen. */
  .story-pic .fr{ flex: none; min-height: 0; aspect-ratio: 16 / 11; }

  .sechead{ grid-template-columns: minmax(0,1fr); gap: var(--sp-4); }
  .sechead .sub{ padding-top: 0; }

  /* §8.7 — 2-up on phones and tablets.  One-up galleries are the second
     biggest contributor to an endless mobile page after one-up card grids. */
  .gal{ grid-template-columns: repeat(2, minmax(0,1fr)); }
  .plate{ grid-column: auto !important; grid-row: auto !important; }
  /* 40/51, which is 4/3 with 70% more height.  At 165px a column these were
     124px tall — a thumbnail of a pool rather than a picture of one.  They are
     210px now, and the section is still 2-up. */
  .gal .fr{ aspect-ratio: 40 / 51 !important; }

  .stage-row{ grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--sp-5) var(--colgap); }
  /* Two columns all the way down, never one.  Six counties in a single column
     is six full-width rows of two lines each — the longest block on the page
     for the least information on it.  What gives way instead is the ROW: the
     town list drops under the county name and takes the column's full measure,
     so the pair reads as one unit at 165px just as it does at 600px. */
  .arch{ column-gap: var(--sp-4); }
  .aw{ grid-template-columns: minmax(0,1fr); gap: 6px; padding-block: var(--sp-3); }
  .aw .eb{ grid-column: 1; line-height: 1.7; }

  /* §10.4 — the mobile footer pairing is SPECIFIED, not a fallback.  auto-fit
     produced a subscribe form beside a list of opening times, at half width
     each, both broken.  Two-up, not one-up: four stacked columns is the longest
     block on a mobile page and the reader has already decided to leave. */
  /* Compressed.  The footer is the longest block on a mobile page and the
     reader has already decided to leave by the time they reach it, so every
     value here is the smallest that still reads as separation: the column gap
     drops a step, the brand paragraph loses its body leading, the link rows
     tighten, and the hours stop being a table with air in it. */
  .foot{ padding-block: var(--sp-5) var(--sp-3); }
  .foot .top{ grid-template-columns: minmax(0,1fr) minmax(0,1fr);
              gap: var(--sp-4) var(--sp-3); }
  .fbrand{ grid-column: 1 / -1; }
  .flinks-1{ grid-area: 2 / 1; }
  .flinks-2{ grid-area: 2 / 2; }
  .fwide{ grid-area: 3 / 1 / auto / -1; }
  .fmark img{ width: 158px; }
  .fbrand .bd{ margin-top: var(--sp-2); line-height: 1.62; max-width: none; }
  .faddr{ margin-top: var(--sp-2); line-height: 1.55; }
  .fico{ margin-top: var(--sp-2); gap: 8px; }
  .fico a{ width: 40px; height: 40px; }
  .foot h2.eb{ margin-bottom: var(--sp-2); }
  /* Compact, but not cramped: the leading came back up a step after the first
     pass read as squeezed rather than tight. */
  .foot ul{ gap: 4px; }
  .foot ul li{ line-height: 1.45; }
  .nl-blurb{ margin-top: var(--sp-1); line-height: 1.6; max-width: none; }
  .hours-h{ margin-top: var(--sp-4); }
  .hours > div{ padding: 4px 0; }
  .hours dt{ line-height: 1.5; }
  .foot .bot{ margin-top: var(--sp-4); padding-top: 14px; }
}

@media (max-width: 720px){
  /* The §4.6 wedge is a desktop device: it keeps the type side dark while the
     picture opens up to the right.  On a phone the copy spans the FULL width,
     so there is no right-hand side to open up — every line of type ends where
     the wedge has fallen to .06.  A first attempt at a light vertical-only
     scrim measured 2.31:1 on the standfirst and 2.94 on the h1.  What a phone
     needs is a near-uniform veil at ~.65, and this is it.  The photograph is
     background here, not a picture with a text column beside it. */
  .hero::after{
    background:
      linear-gradient(180deg, rgba(7,22,52,.76) 0%,  rgba(7,22,52,.68) 22%,
                              rgba(7,22,52,.64) 58%, rgba(7,22,52,.80) 100%);
  }
  .craft{ grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--sp-5) var(--colgap); }
  /* Two columns all the way down.  The one-column fallback was written for the
     award ledger, where "APSP International Design Awards" could not be broken
     and ran past the gutter; a county name and a middot-separated town list
     both wrap freely, so the constraint that produced it is gone. */
  .aw{ padding-block: var(--sp-2); }
  /* Shallower crops on a phone: a 4/3 frame in a full-width stack is a third of
     the screen before a word of the item has been read. */
  .cr .fr{ aspect-ratio: 3 / 2; }
  .cr .bd, .node .bd{ font-size: var(--s--1); line-height: 1.6; }
  .band.stage .parallax-wrap{ height: clamp(260px, 62vw, 420px); }
  /* The band's TOP scrim stop comes off below 980, and the reason is that the
     stop is a percentage of a frame that is 2.4x shorter here.  At 620px the
     .30 -> 0 ramp covers 198px and reads as depth; at 260px it covers 83px and
     lands directly under a 78px solid masthead, so the two fuse into 161px of
     continuous navy — 62% of the picture's height against 49% on desktop, and
     it reads as a thick bar stuck to the top of the photograph rather than as
     a photograph.

     Nothing is lost by removing it.  The caption sits at the BOTTOM of this
     frame and is carried by the .84 stop, which stays; the top stop was only
     ever decorative. */
  .band.stage::after{
    background: linear-gradient(180deg, rgba(7,22,52,0) 0%, rgba(7,22,52,0) 34%,
                                        rgba(7,22,52,.58) 64%, rgba(7,22,52,.93) 100%);
  }

  .hall > div{ padding: var(--sp-2) 0; }
  .faq summary{ padding-block: var(--sp-3); gap: var(--sp-2); }
  .faq .qt{ font-size: var(--s-0); }
  .story-copy h2.d{ margin-bottom: var(--sp-4); }
  .hall-h, .stages-h{ margin-top: var(--sp-5); }

  .hz h1{ margin-top: var(--sp-3); }
  .hz .deck{ margin-top: var(--sp-3); }
  .hz .row{ margin-top: var(--sp-4); }
  /* The forced break belongs to the desktop composition.  At 45.6px "pools in
     Knoxville" is about 370px of type against 346px of measure at 390 — kept,
     it would run off the side; dropped, the line finds its own breaks and the
     headline can be the size it should be. */
  .hz h1 br{ display: none; }
  .hz h1{ max-width: 13ch; }
  .hz .eb{ font-size: 11px; }
  .hz .row{ gap: var(--sp-2) var(--sp-5); }

  /* Still three columns, still flush left and flush right — just tighter, and
     each item wraps inside its own column rather than the row breaking up. */
  .foot .bot{ gap: var(--sp-2); font-size: 9px; letter-spacing: .06em;
              line-height: 1.5; }
}

/* §7.2 — the hero is a fixed-height box, so it has to answer to HEIGHT as well
   as width.  --s-hero already did; --hero-gap did not, so a 1280x720 laptop
   paid the same 64px twice that a 1280x1000 monitor did and the proof strip
   went over the edge.  Compressing the gaps is the first move, because it
   keeps the hero to one screen; .hero's min-height is the safety net for the
   screens where even a compressed stack cannot fit. */
@media (max-height: 880px){
  :root{ --hero-gap: clamp(18px, 3.4vh, 56px); }
  .hz h1{ margin-top: var(--sp-3); }
  .hz .deck{ margin-top: var(--sp-3); }
  .hz .row{ margin-top: var(--sp-4); }
}
@media (max-height: 700px){
  .hz .deck{ margin-top: var(--sp-2); }
  .hz .row{ margin-top: var(--sp-3); }
}

@media (max-width: 560px){
  .fr .over{ padding: var(--sp-4) var(--sp-2) var(--sp-2); }
  .fr .over b{ font-size: 12px; }
  .hall > div{ grid-template-columns: minmax(0,.42fr) minmax(0,1fr); gap: var(--sp-2); }
  /* At two columns on a 390px screen each track is ~165px.  The county name
     takes the body size rather than the display size — at --s-1 "Anderson"
     alone filled the track and "County" wrapped under it on three of the six
     rows, which broke the alignment the ledger exists for. */
  .arch{ column-gap: var(--sp-3); }
  .aw b, .aw .an{ font-size: var(--s-0); letter-spacing: -.01em; }
  .aw .eb{ font-size: 9.5px; letter-spacing: .08em; }
}

/* Below 480 the two-up column stops holding a 30-word paragraph — about 22
   characters a line, half the §3.2 minimum measure — so each item takes the
   full width with its picture ABOVE its text.  The frames go shallow (16:9)
   rather than 4:3, because a deep crop in a full-width stack costs a third of
   the screen before a word of the item has been read, and that is what makes a
   one-up mobile page feel endless. */
@media (max-width: 480px){
  .craft{ grid-template-columns: minmax(0,1fr); gap: var(--sp-6); }
  .cr .fr{ aspect-ratio: 16 / 9; }
}

@media (max-width: 400px){
  .btn{ padding: 11px 11px 11px 18px; gap: 9px; }
  .btn .ic{ width: 26px; height: 26px; }
  .hz .row{ gap: var(--sp-1); }
}

@media (max-width: 360px){

  .gal{ grid-template-columns: repeat(2, minmax(0,1fr)); }
  .figs{ grid-template-columns: repeat(2, minmax(0,1fr)); }
}

/* §11.4 — anything whose only reveal is :hover must show its content by
   default on a touch reader, or the component is dead on a phone. */
/* §15.2 on the device the rule exists for. */
@media (pointer: coarse){
  /* §15.2 wants 44px targets and §10.5 wants a compact footer.  The hit area
     grows without the layout growing: padding on the anchor, an equal negative
     margin on it, so the box is 44px and the row still measures 22. */
  .foot ul{ gap: 0; }
  .foot ul a{ padding-block: 11px; margin-block: -11px; }
  .invite .tel{ padding: 12px 0; }
}

@media (hover: none){
  .fr .over{ opacity: 1; transform: none; }
  .btn:hover{ background: var(--accent); }
  .btn.ghost:hover{ background: transparent; border-color: rgba(255,255,255,.42); }
}


/* ═══ REDUCED MOTION §5.10 ════════════════════════════════════════════════
   Neutralised at the TOKEN and base-class level, not per component: a
   component-level opt-out means every new component is a new chance to forget,
   and the failure is invisible to anyone not running the setting, so it ships.
   What stays on is the masthead hide/return — that is an affordance, not
   decoration. */
@media (prefers-reduced-motion: reduce){
  .rv, .rv-l, .rv-r, .rv-s, .rv-up,
  .card.rv, .plate.rv-l, .plate.rv-r,
  .story-pic.rv-l, .story-pic.rv-r{
    opacity: 1 !important; transform: none !important; transition: none !important;
  }
  .ln-mask{ overflow: visible !important; }
  .hz .eb, .hz h1, .hz .deck, .hz .row,
  .nav .wordmark, .nav .navlinks, .nav > .btn, .nav .navtoggle{
    animation: none !important; opacity: 1 !important; transform: none !important;
  }
  .hero .mp-art img, .hero .mp-art video{ animation: none !important; transform: none !important; }
  .lnk .ar, .lnk::after{ transition: none !important; }
  .navpanel, .navpanel li, .subnav li{
    transition: none !important; transform: none !important; }
  .faq .ans, .faq .ans > .inner, .faq .tg::before, .faq .tg::after{
    transition: none !important; }
  .faq .ans > .inner{ opacity: 1 !important; transform: none !important; }
  .cv{ transition: none !important; }
  .navpanel.open{ transform: none !important; }
  .navpanel:not(.open):not([hidden]){ transform: translateY(-100%) !important; }
  .ln-in{ transform: none !important; transition: none !important; }
  .parallax-img{ transform: none !important; }
  *:hover{ transform: none !important; }
  img{ transform: none !important; }
  .drawn-path{ transition: none !important; stroke-dashoffset: 0 !important; }
}
