  * { box-sizing: border-box; }
  /* Anchor jumps (#services, #contact) landed underneath the fixed header, which
     is 87px on desktop. scroll-padding-top offsets every fragment target at once
     rather than needing scroll-margin on each one. */
  html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; scroll-padding-top: 104px; }
  @media (max-width:820px){ html { scroll-padding-top: 92px; } }
  body { margin:0; font-family: var(--fb); color: var(--ink); background: var(--paper); line-height:1.65; -webkit-font-smoothing:antialiased; }
  h1,h2,h3,h4 { font-family: var(--fd); font-weight:800; line-height:1.04; margin:0 0 .4em; letter-spacing:-0.02em; }
  h1 { font-size: clamp(2.6rem, 6.4vw, 5.2rem); }
  h2 { font-size: clamp(2rem, 4.2vw, 3.2rem); color: var(--navy); }
  h3 { font-size:1.28rem; font-weight:700; letter-spacing:-0.01em; }
  p { margin:0 0 1em; }
  a { color: var(--steel-deep); text-decoration:none; }
  a:hover { text-decoration:underline; }
  img { max-width:100%; display:block; }
  .container { max-width: var(--container); margin:0 auto; padding:0 32px; }
  .kicker { font-family: var(--fb); font-size:.76rem; font-weight:700; letter-spacing:.2em; text-transform:uppercase; color: var(--steel); margin-bottom:16px; display:inline-flex; align-items:center; gap:10px; }
  .kicker::before { content:""; width:26px; height:2px; background: var(--steel); display:inline-block; }
  .btn { display:inline-flex; align-items:center; gap:10px; padding:15px 28px; border-radius: var(--radius); font-family:var(--fb); font-weight:700; font-size:.96rem; cursor:pointer; transition: transform .14s ease, background .2s, color .2s, border-color .2s; border:2px solid transparent; letter-spacing:.01em; }
  .btn .arw { transition: transform .2s ease; }
  .btn:hover .arw { transform: translateX(4px); }
  .btn-primary { background: var(--steel); color:var(--navy); }
  .btn-primary:hover { background:var(--accent-hover); text-decoration:none; }
  .btn-dark { background: var(--navy); color:var(--asi-white); }
  .btn-dark:hover { background: var(--navy-2); text-decoration:none; }
  .btn-outline { background:transparent; color: var(--navy); border-color: var(--line); }
  .btn-outline:hover { border-color: var(--navy); text-decoration:none; }
  .btn-light { background: var(--asi-white-a10); color:var(--asi-white); border-color: var(--asi-white-a55); backdrop-filter: blur(4px); }
  .btn-light:hover { background:var(--asi-white); color: var(--navy); text-decoration:none; }

  /* Loader */
  #loader { position:fixed; inset:0; z-index:100; background: var(--navy); color:var(--asi-white); display:flex; flex-direction:column; align-items:center; justify-content:center; gap:22px; transition:opacity .7s ease; }
  #loader.hide { opacity:0; pointer-events:none; }
  #loader .mark { font-family:var(--fd); font-weight:800; font-size:1.35rem; letter-spacing:.02em; text-transform:uppercase; }
  #loader .mark b { color: var(--steel); }
  #loader .bar { width:min(240px,60vw); height:3px; background:var(--asi-white-a16); border-radius:3px; overflow:hidden; }
  #loader .bar span { display:block; height:100%; width:0%; background:var(--steel); transition:width .2s ease; }
  #loader .pct { font-size:.72rem; letter-spacing:.2em; text-transform:uppercase; color:var(--asi-white-a55); }

  /* Nav */
  /* The header is OPAQUE BY DEFAULT and a page opts out of it, not the other way
     round. It used to be transparent by default, with the solid background added
     by a scroll handler that lives in the hero script -- and the hero script only
     ships on the homepage. So on all nineteen other pages the header stayed
     transparent forever and page content scrolled underneath it.
     Defaulting to opaque also means inner pages need no JavaScript for this, and
     there is no flash of transparent header before a script runs. */
  header.nav { position:fixed; top:0; left:0; right:0; z-index:60;
               background:var(--asi-petrol-a95); backdrop-filter:blur(10px);
               box-shadow:0 1px 0 var(--asi-white-a07);
               transition:background .3s ease, box-shadow .3s ease; }
  .nav-inner { display:flex; align-items:center; justify-content:space-between; padding:20px 32px; max-width:1320px; margin:0 auto; }
  .brand { font-family:var(--fd); font-weight:800; font-size:1.22rem; color:var(--asi-white); text-transform:uppercase; letter-spacing:.01em; }
  .brand b { color: var(--steel); }
  .nav-links { display:flex; gap:32px; align-items:center; }
  .nav-links a { color:var(--asi-white-a90); font-weight:600; font-size:.92rem; transition:color .25s; }
  .nav-links a:hover { color:var(--asi-white); text-decoration:none; }
  .nav-links a.cta { background: var(--steel); color:var(--navy); padding:11px 22px; border-radius:var(--radius); }
  .nav-links a.cta:hover { background:var(--accent-hover); }
  /* Homepage only, while the scroll-story is still behind the header. The hero
     script removes this once you are past it. */
  header.nav.over-hero { background:transparent; backdrop-filter:none; box-shadow:none; }
  @media (max-width:820px){ .nav-links a:not(.cta){ display:none; } .nav-inner{ padding:16px 20px; } }

  /* Scroll story */
  .scrollstory { position:relative; }
  .stage { position:sticky; top:0; height:100vh; height:100svh; overflow:hidden; background: var(--navy); }
  #seq { position:absolute; inset:0; width:100%; height:100%; display:block; }
  .stage::after { content:""; position:absolute; inset:0; pointer-events:none;
    background: linear-gradient(180deg, rgba(6,12,20,.62) 0%, rgba(6,12,20,.06) 24%, rgba(6,12,20,0) 46%, rgba(6,12,20,.18) 68%, rgba(6,12,20,.82) 100%); }
  /* The base veil darkens the top for the nav and the bottom for the chapter copy, and is
     deliberately clear from 24% to 46% so the photograph reads. On desktop that is fine —
     the landscape frames are dark where the intro sits, and Hector's note said desktop
     "runs great".

     On mobile it is not, and this is a trap rather than a bug: the intro sits at 22%-60%,
     squarely inside the clear band, so its legibility depends entirely on what the crop
     happens to put behind it. The old landscape frames passed by luck. The portrait crop
     moved the work light into that band and white text fell to 2.09:1 — a WCAG AA failure
     introduced by a change that touched no CSS at all.

     So: hold a floor under the intro band on mobile only. Text legibility must not be a
     property of which frame is on screen. Desktop keeps the approved gradient untouched.

     Tuned to the least darkening that clears AA rather than the most: the point of the
     portrait crop was to put detail on screen, and a veil heavy enough to hit 13:1 hides
     the pixels it cost 340 KB to deliver. These stops are the lightest that still clear
     4.5:1 on every line of the intro copy, with the photograph still reading. */
  @media (max-width:820px){
    .stage::after {
      background: linear-gradient(180deg,
        rgba(6,12,20,.62)  0%,
        rgba(6,12,20,.34) 16%,
        rgba(6,12,20,.30) 46%,
        rgba(6,12,20,.16) 62%,
        rgba(6,12,20,.26) 74%,
        rgba(6,12,20,.84) 100%);
    }
  }

  .stage-content { position:absolute; inset:0; z-index:2; }
  .intro { position:absolute; left:0; right:0; top:22vh; padding:0 32px; color:var(--asi-white); }
  .intro .inner { max-width: var(--container); margin:0 auto; }
  .intro h1 { color:var(--asi-white); text-shadow:0 2px 40px var(--asi-shadow-a40); max-width:15ch; }
  .intro .lead { max-width:540px; font-size:clamp(1.05rem,1.8vw,1.28rem); color:var(--asi-white-a90); }
  /* The hero kicker is the one that sits on a photograph rather than a flat band, so it
     needs more than the shared .kicker treatment. Kenneth, 2026-08-26: "hard to read".
     Three things were working against it — 13.1px, .26em of tracking, and orange over a
     mid-tone photo with no shadow under it. Wide tracking reads as deliberate on a solid
     ground and as mush over an image.

     Bigger, tighter, and lifted off the photo with the same shadow the h1 already uses.
     Section kickers elsewhere are untouched: they sit on solid colour, where .82rem is
     legible and the wider tracking still looks right. A hero eyebrow being larger than a
     section eyebrow is correct hierarchy, not an inconsistency. */
  .intro .kicker {
    font-size: clamp(.92rem, 1.35vw, 1.06rem);
    letter-spacing: .17em;
    font-weight: 700;
    text-shadow: 0 2px 18px var(--asi-shadow-a45);
    /* Measured against the hero photograph: orange on that warm mid-tone wood is a
       contrast ratio of 2.21:1, which fails WCAG AA at ANY size — 3.0 is the floor for
       large text and 4.5 for normal. That is why it read as "hard to see" rather than
       "small". White measures 5.77:1 on the same pixels.

       The accent is not lost: the rule before the text stays --steel, and the h1 below
       already runs white with "Build it." in orange. This applies that same pattern
       rather than departing from it. */
    color: var(--asi-white);
  }
  .intro .kicker::before { height:3px; width:30px; }

  /* Narrow phones: it already wrapped to two lines at .26em. Pulling the tracking in
     further keeps it at two while the type itself gets bigger, rather than pushing it
     to three. */
  @media (max-width:520px){
    .intro .kicker { letter-spacing: .12em; font-size:.92rem; }
  }


  .intro .hero-cta { display:flex; gap:14px; margin-top:28px; flex-wrap:wrap; }
  .scrollhint { position:absolute; bottom:28px; left:50%; transform:translateX(-50%); color:var(--asi-white-a75); font-size:.68rem; letter-spacing:.24em; text-transform:uppercase; display:flex; flex-direction:column; align-items:center; gap:10px; }
  .scrollhint .mouse { width:22px; height:36px; border:2px solid var(--asi-white-a55); border-radius:12px; position:relative; }
  .scrollhint .mouse::before { content:""; position:absolute; top:6px; left:50%; transform:translateX(-50%); width:3px; height:7px; border-radius:2px; background:var(--asi-white-a85); animation:wheel 1.6s infinite; }
  @keyframes wheel { 0%{opacity:0;transform:translate(-50%,0);} 40%{opacity:1;} 100%{opacity:0;transform:translate(-50%,10px);} }
  /* THE CHAPTER OVERLAYS MUST NOT BE HIT-TESTABLE.
     They are stacked over the hero and faded in and out with opacity as you
     scroll. opacity:0 hides an element visually but leaves it catching pointer
     events, so an inactive chapter sat invisibly on top of the hero CTAs and
     swallowed every click on "Start Your Project" and "What We Do". The buttons
     were never broken -- nothing was reaching them. They contain no links, so
     they never need to receive a click. */
  .chapters { position:absolute; inset:0; pointer-events:none; }
  .chapter, .chapter * { pointer-events:none; }
  .chapter { position:absolute; left:0; right:0; bottom:15vh; padding:0 32px; opacity:0; will-change:opacity,transform; }
  .chapter .inner { max-width:var(--container); margin:0 auto; color:var(--asi-white); }
  .chapter .num { font-family:var(--fb); font-weight:700; font-size:.78rem; letter-spacing:.24em; color:var(--steel); margin-bottom:12px; }
  .chapter h2 { color:var(--asi-white); font-size:clamp(2.1rem,5vw,3.6rem); margin:0 0 .18em; text-shadow:0 2px 30px var(--asi-shadow-a45); }
  .chapter p { max-width:500px; color:var(--asi-white-a90); font-size:clamp(1rem,1.5vw,1.15rem); margin:0; }
  .progress-rail { position:absolute; right:32px; top:50%; transform:translateY(-50%); display:flex; flex-direction:column; gap:14px; z-index:3; }
  .progress-rail i { width:8px; height:8px; border-radius:50%; background:var(--asi-white-a28); transition:background .3s, transform .3s; }
  .progress-rail i.on { background:var(--steel); transform:scale(1.4); }
  @media (max-width:820px){ .progress-rail{ display:none; } .chapter{ bottom:16vh; } }

  /* Stats band */
  .statband { background: var(--navy); color:var(--asi-white); }
  .statband .container { display:grid; grid-template-columns:repeat(4,1fr); gap:0; }
  .statband .stat { padding:52px 28px; border-right:1px solid var(--asi-white-a12); }
  .statband .stat:last-child { border-right:none; }
  .statband .n { font-family:var(--fd); font-weight:800; font-size:2.7rem; color:var(--steel); line-height:1; letter-spacing:-.02em; }
  .statband .l { color:var(--asi-white-a72); font-size:.95rem; margin-top:10px; }
  @media (max-width:820px){ .statband .container{ grid-template-columns:1fr 1fr; } .statband .stat{ border-right:none; border-bottom:1px solid var(--asi-white-a12); padding:34px 24px; } }

  /* Sections */
  section.block { padding:76px 0; }
  @media (max-width:820px){ section.block { padding:52px 0; } }
  section.block.mist { background: var(--mist); }
  .head { max-width:760px; margin:0 0 36px; }
  .head.center { margin-left:auto; margin-right:auto; text-align:center; }
  .head p { color:var(--muted); font-size:1.14rem; }
  .head.center .kicker { justify-content:center; }
  .reveal { opacity:0; transform:translateY(26px); transition:opacity .7s ease, transform .7s ease; }
  .reveal.in { opacity:1; transform:none; }

  .services-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(272px,1fr)); gap:2px; background:var(--line); border:1px solid var(--line); }
  .service-card { background:var(--paper); padding:36px 30px; transition:background .25s ease; position:relative; }
  .service-card::after { content:""; position:absolute; left:0; top:0; height:3px; width:0; background:var(--steel); transition:width .3s ease; }
  .service-card:hover { background:var(--mist); }
  .service-card:hover::after { width:100%; }
  /* Drawn icons, not emoji — see partials/service-icons.njk for why. They take
     their colour from the card via currentColor, so there is no second place to
     keep the accent in sync. */
  .service-card .ico { display:block; margin-bottom:16px; color:var(--steel); }
  .service-card .ico svg { width:30px; height:30px; display:block; }
  .service-card:hover .ico { color:var(--steel); }
  @media (prefers-reduced-motion: no-preference) {
    .service-card .ico { transition: transform .25s ease; }
    .service-card:hover .ico { transform: translateY(-2px); }
  }
  .service-card h3 { color:var(--navy); margin-bottom:8px; }
  .service-card p { color:var(--muted); font-size:.97rem; margin:0; }

  /* Full-bleed statement band */
  .cta-band { position:relative; color:var(--asi-white); background: var(--navy); overflow:hidden; }
  .cta-band .bg { position:absolute; inset:0; background-size:cover; background-position:center; opacity:.32; }
  .cta-band .veil { position:absolute; inset:0; background:linear-gradient(90deg, rgba(14,27,42,.94) 0%, rgba(14,27,42,.72) 55%, rgba(14,27,42,.5) 100%); }
  .cta-band .container { position:relative; padding:104px 32px; }
  .cta-band h2 { color:var(--asi-white); max-width:16ch; }
  .cta-band p { color:var(--asi-white-a85); max-width:520px; font-size:1.12rem; }
  .cta-band .hero-cta { display:flex; gap:14px; margin-top:26px; flex-wrap:wrap; }

  /* Process */
  .steps { display:grid; grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); gap:2px; background:var(--line); border:1px solid var(--line); }
  .step { background:var(--paper); padding:34px 30px; }
  .step .k { font-family:var(--fd); font-weight:800; font-size:2.2rem; color:var(--mist-2); line-height:1; }
  .step h3 { color:var(--navy); margin:8px 0 8px; }
  .step p { color:var(--muted); font-size:.96rem; margin:0; }

  /* About split */
  .split { display:grid; grid-template-columns:1.05fr 1fr; gap:64px; align-items:center; }
  @media (max-width:900px){ .split{ grid-template-columns:1fr; gap:38px; } }
  .split .frame-art { border-radius:6px; overflow:hidden; border:1px solid var(--line); box-shadow:0 30px 60px rgba(14,27,42,.16); background:var(--navy); aspect-ratio:4/3; }
  .split .frame-art img { width:100%; height:100%; object-fit:cover; }
  .mini-stats { display:grid; grid-template-columns:repeat(3,1fr); gap:22px; margin-top:36px; }
  .mini-stats .n { font-family:var(--fd); font-weight:800; font-size:1.9rem; color:var(--steel); line-height:1; }
  .mini-stats .l { color:var(--muted); font-size:.86rem; margin-top:6px; }

  /* Contact */
  .contact-grid { display:grid; grid-template-columns:1fr 1.1fr; gap:56px; align-items:start; }
  @media (max-width:860px){ .contact-grid{ grid-template-columns:1fr; } }
  form.contact-form { background:var(--paper); padding:36px; border-radius:8px; border:1px solid var(--line); box-shadow:0 18px 44px rgba(14,27,42,.08); }
  form label { display:block; margin-bottom:16px; font-weight:600; font-size:.84rem; color:var(--ink); }
  form input, form textarea { width:100%; padding:13px; border:1px solid var(--line); border-radius:var(--radius); font-family:var(--fb); font-size:1rem; margin-top:7px; background:var(--mist); }
  form input:focus, form textarea:focus { outline:2px solid var(--steel); border-color:transparent; background:var(--asi-white); }
  /* font-family is NOT inherited by <button>. Without this the submit button --
     the primary conversion CTA, on all 20 pages -- rendered in the browser's
     default Arial while every other control used Barlow. */
  form button { width:100%; padding:15px; border-radius:var(--radius); border:none; background:var(--steel); color:var(--navy); font-family:var(--fb); font-weight:700; cursor:pointer; font-size:1rem; }
  form button:hover { background:var(--accent-hover); }
  .contact-info p { margin:0 0 22px; }
  .contact-info strong { color:var(--steel-deep); display:block; font-size:.76rem; text-transform:uppercase; letter-spacing:.12em; margin-bottom:5px; }
  .contact-info a.big { color:var(--navy); font-size:1.3rem; font-family:var(--fd); font-weight:700; }
  .badge-row { display:flex; gap:10px; flex-wrap:wrap; margin-top:10px; }
  .badge { font-size:.78rem; font-weight:600; color:var(--navy); background:var(--mist); border:1px solid var(--line); padding:8px 14px; border-radius:50px; }

  /* Footer */
  footer { background:var(--navy); color:var(--asi-white-a80); padding:66px 0 28px; }
  footer h4 { color:var(--asi-white); font-family:var(--fd); font-weight:700; margin:0 0 14px; font-size:1.05rem; }
  .footer-grid { display:grid; grid-template-columns:2fr 1fr 1fr; gap:36px; margin-bottom:36px; }
  @media (max-width:720px){ .footer-grid{ grid-template-columns:1fr; gap:28px; } }
  footer a { color:var(--asi-white-a80); }
  footer a:hover { color:var(--asi-white); }
  .footer-bottom { border-top:1px solid var(--asi-white-a14); padding-top:24px; font-size:.85rem; color:var(--asi-white-a58); display:flex; justify-content:space-between; flex-wrap:wrap; gap:12px; }
  .csr-credit { opacity:.72; }

  /* Reduced-motion / fallback */
  /* REDUCED MOTION. The frame sequence is replaced by a still and the five
     chapters become ordinary stacked content.
   *
   * Every layer that was absolutely positioned FOR the sequence has to be
   * unwound here -- not most of them. Two were missed: `.stage-content`
   * (position:absolute; inset:0) and `.intro` (position:absolute; top:22vh).
   * With the chapters flowed but those two still absolute, the hero copy
   * painted directly on top of all five chapters, and `height:90vh` with
   * `overflow:hidden` clipped whatever spilled past one screen.
   *
   * That was the entire homepage for anyone with Reduce Motion switched on --
   * an accessibility setting, so the people most likely to hit it are the least
   * able to work around it. It never showed up in review because it is
   * invisible unless you have the setting on. Found on a client's iPhone
   * 2026-09-09.
   *
   * The stage keeps its background so white text stays on dark ground, and
   * `.stage::after` still lays its scrim over the poster. */
  .no-seq .stage { position:relative; height:auto; overflow:visible; padding:104px 0 80px; }
  .no-seq .stage-content { position:static; }
  .no-seq .intro { position:static; top:auto; }
  .no-seq #seq { display:none; }
  .no-seq .stage.poster { background:var(--navy) center/cover no-repeat; }
  .no-seq .scrollhint { display:none; }
  .no-seq .progress-rail { display:none; }
  /* #driver is the tall spacer that gives the sequence its scroll length, sized
     by JS in layout(). The reduced-motion path returns before layout() runs, so
     it is already 0 -- but stating it here means the fallback cannot inherit a
     screenful of dead scroll from whatever the JS did or did not get to. */
  .no-seq #driver { display:none; }
  .no-seq .chapters { position:static; }
  .no-seq .chapter { position:static; opacity:1; bottom:auto; margin-top:64px; }
  /* The chapters were click-through while they were overlays on the canvas.
     As real content they are not, or their links stop working. */
  .no-seq .chapter, .no-seq .chapter * { pointer-events:auto; }
  .no-seq .reveal { opacity:1; transform:none; }
  @media (prefers-reduced-motion:reduce){ html{ scroll-behavior:auto; } .scrollhint .mouse::before{ animation:none; } }

  /* ═══════════════ ASI BRAND LAYER ═══════════════
     Appended, not edited in. Deleting this block plus reverting :root
     returns the page to the published build exactly.            */


  /* lockup — Beam mark + ASI + the tagline standing on it */
  .brand { display:flex; align-items:center; gap:12px; }
  .brand .beam { height:34px; width:auto; flex:none; }
  .brand-txt { display:flex; flex-direction:column; line-height:1; }
  .brand-name { font-family:var(--fd); font-weight:700; font-size:1.85rem;
                letter-spacing:.035em; text-transform:uppercase; white-space:nowrap; }
  .brand-tag  { font-family:var(--fd); font-weight:600; font-size:.63rem;
                letter-spacing:.2em; text-transform:uppercase;
                color:var(--on-dark-subtle); margin-top:7px; white-space:nowrap; }
  @media (max-width:420px){ .brand-tag { letter-spacing:.14em; font-size:.56rem; } }

  #loader .mark { font-family:var(--fd); font-weight:700; font-size:2.1rem;
                  letter-spacing:.035em; text-transform:uppercase; }
  #loader .mark-tag { font-family:var(--fd); font-weight:600; font-size:.66rem;
                letter-spacing:.2em; text-transform:uppercase;
                color:var(--on-dark-subtle); margin-top:8px; }

  /* hero headline — the tagline. Third clause carries the amber. */
  .h1-accent { color:var(--steel); }

  /* footer lockup — mirrors the header: ASI over the full name */
  .foot-brand { display:flex; flex-direction:column; line-height:1; margin-bottom:14px; }
  .foot-brand__n { font-family:var(--fd); font-weight:700; font-size:1.85rem;
                   letter-spacing:.035em; text-transform:uppercase; color:var(--bone); }
  .foot-brand__s { font-family:var(--fd); font-weight:600; font-size:.63rem;
                   letter-spacing:.2em; text-transform:uppercase;
                   color:var(--on-dark-subtle); margin-top:7px; }

  /* header phone, sitting beside Get a Quote. Bone on the dark nav = 12.7:1 */
  .nav-links a.nav-phone { display:inline-flex; align-items:center; gap:8px;
               font-family:var(--fd); font-weight:600; font-size:1.02rem;
               letter-spacing:.05em; color:var(--bone); white-space:nowrap;
               padding:9px 6px; margin-right:6px; min-height:44px; }
  .nav-links a.nav-phone:hover { color:var(--steel); text-decoration:none; }
  .nav-phone__i { color:var(--steel); font-size:1.05em; line-height:1; }

  .nav-phone__i svg { display:block; }

  /* the CTA must never wrap to two lines */
  .nav-links a.cta { white-space:nowrap; }

  /* Below 820px the theme hides every nav link except .cta. Tap-to-call is
     the highest-value action on a phone, so keep it — icon only, 44px target.
     Higher specificity + later in the sheet beats `.nav-links a:not(.cta)`. */
  @media (max-width:820px){
    .nav-links a.nav-phone { display:inline-flex; gap:0;
                             padding:10px; margin-right:4px;
                             min-width:44px; justify-content:center; }
    .nav-phone__n { display:none; }
    .nav-links a.cta { padding:11px 16px; font-size:.94rem; }
  }
  @media (max-width:380px){
    .nav-links a.cta { padding:10px 13px; font-size:.88rem; }
    .brand .beam { height:28px; }
    .brand-name { font-size:1.5rem; }
  }

  /* scrolled nav keeps its translucency but on petrol, not the old navy */

  /* kickers ride Condensed */
  .kicker { font-family:var(--fd); font-weight:600; font-size:.82rem; letter-spacing:.26em; }

  /* ─────────── DARK-DOMINANT: petrol runs the whole page ───────────
     The published theme alternated bone/mist bands between the dark hero,
     stat band and footer. Carrying petrol straight through removes that
     flicker, and it resolves the spec's open dominance question in favour
     of dark. Because nothing sits on a light ground any more, ember is no
     longer needed — amber is legal everywhere at 5.45:1 on petrol.      */
  body                            { background:var(--navy); color:var(--on-dark); }
  section.block                   { background:var(--navy); }
  section.block.mist              { background:var(--navy-2); }

  section.block h2,
  section.block h3,
  section.block h4                { color:var(--bone); }
  section.block p,
  section.block li                { color:var(--on-dark-muted); }

  section.block .kicker           { color:var(--steel); }
  section.block .kicker::before   { background:var(--steel); }
  .service-card::after            { background:var(--steel); }
  .mini-stats .n                  { color:var(--steel); }
  .mini-stats .l                  { color:var(--on-dark-subtle); }

  .service-card                   { background:var(--navy-2); border:1px solid var(--hairline); }
  .service-card:hover             { background:var(--petrol3); }
  .service-card h3                { color:var(--bone); }
  .service-card p                 { color:var(--on-dark-muted); }

  .split .frame-art               { border-color:var(--hairline); }

  /* form: dark fields, bone text, amber focus */
  form label                      { color:var(--bone); }
  form input, form textarea       { background:var(--navy-2); border-color:var(--hairline);
                                    color:var(--bone); }
  form input::placeholder,
  form textarea::placeholder      { color:var(--on-dark-min); }
  form input:focus,
  form textarea:focus             { background:var(--petrol3); outline:2px solid var(--steel); }

  .contact-info strong            { color:var(--steel); }
  .contact-info a.big             { color:var(--bone); }
  a                               { color:var(--steel); }

  /* RULE 03 — amber buttons take petrol text, never white/near-black drift */
  .btn-primary, .nav-links a.cta, form button { color:var(--navy); }
  .btn-ghost                      { color:var(--bone); border-color:var(--asi-bone-a38); }

  /* one ground now, so one focus ring */
  *:focus-visible                 { outline:2px solid var(--steel); outline-offset:2px; }

  /* hardcoded navy veil -> petrol, so the CTA band matches the new ground */
  .cta-band .veil { background:linear-gradient(90deg,
      rgba(11,47,56,.94) 0%, rgba(11,47,56,.72) 55%, rgba(11,47,56,.5) 100%); }

  /* on-dark text tokens: measured values instead of translucent white */
  .statband .l { color:var(--on-dark-muted); }
  .cta-band p  { color:var(--on-dark-muted); }

  /* square corners, flat — override any residual rounding */
  .service-card, .btn, .btn-primary, .btn-ghost, .nav-links a.cta,
  form input, form textarea, form button, .split .frame-art {
    border-radius:0;
  }
  .split .frame-art { box-shadow:none; border:1px solid var(--line); }

  /* ═══════════ MULTI-PAGE LAYER ═══════════
     Added when the single index.html became a 20-page site. Colour comes
     from tokens.css only — the brand SVG fills and the footer's inline
     rgba() moved here so no component carries a literal colour. */

  /* Beam mark: was two hardcoded hex fills inline in the header SVG */
  .brand .beam .beam-a { fill: var(--steel); }
  .brand .beam .beam-b { fill: var(--bone); }

  /* Footer text: was inline style="color:var(--asi-white-a80)" */
  .foot-lede { color: var(--asi-white-a80); margin: 0; max-width: 360px; }
  .foot-list { margin: 0; }

  /* Current page in the nav — not colour-only, carries an underline too */
  .nav-links a[aria-current="page"] { color: var(--steel); text-decoration: underline;
                                      text-underline-offset: 5px; }

  /* ─────────── Interior page furniture (non-home templates) ─────────── */
  .page-hero { background: var(--navy); padding: 156px 0 72px; }
  .page-hero h1 { color: var(--bone); font-size: clamp(2.2rem, 5.2vw, 3.9rem); max-width: 22ch; }
  .page-hero .lead { color: var(--on-dark-muted); max-width: 62ch;
                     font-size: clamp(1.04rem, 1.7vw, 1.2rem); margin: 0; }
  .page-hero .hero-cta { display: flex; gap: 14px; margin-top: 28px; flex-wrap: wrap; }
  @media (max-width: 820px){ .page-hero { padding: 116px 0 56px; } }

  .crumbs { font-size: .8rem; letter-spacing: .04em; color: var(--on-dark-subtle);
            margin-bottom: 20px; }
  .crumbs a { color: var(--on-dark-subtle); }
  .crumbs a:hover { color: var(--steel); }
  .crumbs span[aria-hidden] { opacity: .45; margin: 0 6px; }

  /* Long-form copy */
  .prose { max-width: 72ch; }
  .prose > *:first-child { margin-top: 0; }
  .prose h2 { font-size: clamp(1.6rem, 2.6vw, 2.2rem); margin: 1.9em 0 .5em; }
  .prose h3 { margin: 1.6em 0 .4em; color: var(--bone); }
  .prose p, .prose li { color: var(--on-dark-muted); }
  .prose ul, .prose ol { padding-left: 1.1em; margin: 0 0 1.2em; }
  .prose li { margin-bottom: .5em; }
  .prose strong { color: var(--bone); font-weight: 600; }
  .prose a { color: var(--steel); text-decoration: underline; text-underline-offset: 3px; }

  /* Scope list — what's actually in the bid */
  .scope-list { list-style: none; padding: 0; margin: 0;
                display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
                gap: 0 40px; }
  .scope-list li { position: relative; padding: 16px 0 16px 30px;
                   border-bottom: 1px solid var(--hairline); color: var(--on-dark-muted); }
  .scope-list li::before { content: ""; position: absolute; left: 0; top: 25px;
                           width: 12px; height: 2px; background: var(--steel); }

  /* Cards that are links */
  a.service-card { display: block; text-decoration: none; }
  a.service-card:hover { text-decoration: none; }

  .muted-text { color: var(--on-dark-muted); }
  .contact-info a.big--sm { font-size: 1.05rem; }

  /* Visible unsourced-claim placeholder. Deliberately ugly — a VERIFY marker
     that blends in is a defect waiting to ship. */
  .verify { display: inline-block; font-family: var(--fb); font-weight: 600;
            font-size: .82rem; letter-spacing: .04em; color: var(--asi-petrol);
            background: var(--steel); padding: 2px 8px; }

  /* ─────────── Notes (blog) index and pagination ───────────
     Added with the blog system. Every colour here is a token measured against
     the ground it sits on — these sit on the standard dark band. */
  .post-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
  .post-item { background: var(--navy-2); border: 1px solid var(--hairline);
               padding: 30px 32px; }
  .post-item h2 { margin: 6px 0 10px; font-size: 1.5rem; }
  .post-item h2 a { color: var(--bone); text-decoration: none; }
  .post-item h2 a:hover { color: var(--steel); }
  .post-item p { color: var(--on-dark-muted); margin: 0 0 10px; }
  .post-meta { font-family: var(--fd); font-size: .76rem; letter-spacing: .16em;
               text-transform: uppercase; color: var(--on-dark-muted); margin: 0; }
  .post-more { font-family: var(--fd); font-weight: 700; letter-spacing: .04em;
               text-transform: uppercase; font-size: .82rem; color: var(--steel);
               text-decoration: none; }
  .post-more:hover { text-decoration: underline; }

  .pager { display: flex; align-items: center; justify-content: space-between;
           gap: 18px; margin-top: 28px; flex-wrap: wrap; }
  .pager-link { font-family: var(--fd); font-weight: 700; text-transform: uppercase;
                letter-spacing: .04em; font-size: .84rem; color: var(--bone);
                border: 1px solid var(--hairline); padding: 12px 20px;
                text-decoration: none; }
  .pager-link:hover { background: var(--petrol3); border-color: var(--steel); }
  .pager-count { font-family: var(--fb); font-size: .9rem; color: var(--on-dark-muted); }

  /* A MENTION of the marker, not a live placeholder. Deliberately not the amber
     badge: /terms/ and /gallery/ describe the convention, and painting those the
     same as a real missing fact tells the reader those pages are incomplete. */
  .verify-ref { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
                font-size: .86em; color: var(--bone); background: var(--asi-bone-a16);
                padding: 1px 6px; border-radius: 0; }

  /* ─────────── Project photography ─────────── */
  .shot { margin: 0; border: 1px solid var(--hairline); background: var(--navy-2); }
  .shot img { width: 100%; height: auto; display: block; }
  .shot figcaption { font-family: var(--fb); font-size: .84rem; color: var(--on-dark-subtle);
                     padding: 12px 14px; border-top: 1px solid var(--hairline); }

  /* Service-page hero shot: sits beside the intro copy on wide screens */
  .prose-with-shot { display: grid; grid-template-columns: 1.15fr .85fr; gap: 48px;
                     align-items: start; }
  .prose-with-shot .prose { max-width: none; }
  /* The copy column is much taller than the photo. Sticky keeps the shot
     beside what you are reading instead of leaving a column of dead space. */
  .prose-with-shot .shot { position: sticky; top: 104px; }
  @media (max-width: 900px){ .prose-with-shot .shot { position: static; } }
  @media (max-width: 900px){ .prose-with-shot { grid-template-columns: 1fr; gap: 32px; } }

  /* Gallery grid — square-cropped tiles, hairline gutters, no rounding */
  .shot-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
               gap: 2px; background: var(--hairline); border: 1px solid var(--hairline); }
  .shot-grid .shot { border: none; }
  .shot-grid .shot img { aspect-ratio: 4 / 3; object-fit: cover; }
  .shot-grid .shot figcaption { border-top: 1px solid var(--hairline); }

  /* A row of two, for in-body pairs on a service page */
  .shot-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 2px;
               background: var(--hairline); border: 1px solid var(--hairline);
               margin: 2.2em 0; }
  .shot-pair .shot { border: none; }
  .shot-pair .shot img { aspect-ratio: 4 / 3; object-fit: cover; }
  @media (max-width: 700px){ .shot-pair { grid-template-columns: 1fr; } }

  .foot-social { margin: 14px 0 0; display: flex; gap: 18px; flex-wrap: wrap; }
  .foot-social a { font-family: var(--fd); font-size: .84rem; letter-spacing: .12em;
                   text-transform: uppercase; color: var(--on-dark-subtle); }
  .foot-social a:hover { color: var(--steel); text-decoration: none; }

  /* ═══════════ READABILITY FIXES (2026-08-17) ═══════════
     Three real defects Kenneth caught on the staging build. */

  /* 1. THE FORM WAS UNREADABLE. form.contact-form kept the pre-rebrand bone
        background while the dark layer set form label { color: bone }. Bone
        text on a bone card — 1:1 contrast, labels literally invisible. The
        card becomes dark so the existing on-dark text colours are correct. */
  form.contact-form { background: var(--navy-2); border: 1px solid var(--hairline);
                      box-shadow: none; border-radius: 0; padding: 32px; }
  form label { color: var(--bone); font-size: .8rem; letter-spacing: .04em;
               text-transform: uppercase; font-family: var(--fd); font-weight: 600; }
  form input, form textarea, form select {
    background: var(--navy); border: 1px solid var(--hairline); color: var(--bone);
    color-scheme: dark;                 /* native select popup renders dark too */
    font-family: var(--fb); font-size: 1rem; text-transform: none; letter-spacing: 0;
  }
  form select { width: 100%; padding: 13px; margin-top: 7px; border-radius: 0;
                appearance: none; background-image: none; cursor: pointer; }
  form input:focus, form textarea:focus, form select:focus {
    background: var(--petrol3); outline: 2px solid var(--steel); outline-offset: 0;
  }
  form input::placeholder, form textarea::placeholder { color: var(--on-dark-min); }
  /* Two fields per row on desktop, stacked on a phone */
  .field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px; }
  @media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }
  /* Inline submit feedback. Empty by default, so it occupies no space until the
     script has something to report; the accent rule is the only colour cue and
     the wording carries the meaning, so it does not rely on colour alone. */
  /* Scoped under .contact-form deliberately: `section.block p` (0,1,2) already
     paints every paragraph in a band muted, and would otherwise win over a bare
     .form-status (0,1,0) and mute the confirmation the visitor needs to read. */
  .contact-form .form-status { margin: 18px 0 0; font-family: var(--fb);
                 font-size: .95rem; line-height: 1.55; color: var(--bone); }
  .contact-form .form-status:empty { display: none; }
  /* --on-dark-min is the AA floor measured on petrol; this card is petrol-2,
     which is lighter, and the same token drops to 4.38:1 here. One step up. */
  .contact-form .form-status.is-pending { color: var(--on-dark-muted); }
  .contact-form .form-status.is-ok,
  .contact-form .form-status.is-error { border-left: 3px solid var(--steel);
                 padding-left: 12px; }

  /* The honeypot must be unreachable, not merely invisible */
  form input[name="_gotcha"] { position: absolute; left: -9999px; width: 1px;
                               height: 1px; opacity: 0; pointer-events: none; }

  /* ─────────── TOUCH TARGETS ───────────
     Measured at 375px: 21 controls under the 44px minimum, including the two
     links most likely to be tapped one-handed — the phone number and the email
     address in the contact block. Most of this site's traffic is mobile.

     Two techniques, because the right one depends on the layout:

     STACKED links get real spacing. The footer nav sat at 19px tall with 7px
     between rows; growing an invisible hit area there would have made each link
     overlap its neighbours, which trades a small target for a wrong one.

     Links sharing a LINE get an invisible expanded hit area instead, so the
     breadcrumbs and the social row keep their intended visual size. Nothing sits
     directly above or below them to collide with. */

  /* stacked — genuine spacing */
  .foot-list a,
  .contact-info a.big {
    display: inline-flex; align-items: center; min-height: 44px;
  }
  /* one per row, so widening a short word like "Blog" collides with nothing */
  .foot-list a { padding: 2px 0; min-width: 44px; }

  /* same-line — expand the touch area, leave the type alone */
  .crumbs a, .foot-social a, .csr-credit a, .foot-legal a { position: relative; }
  .crumbs a::after, .foot-social a::after, .csr-credit a::after, .foot-legal a::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    min-width: 44px; width: 100%; height: 44px;
    transform: translate(-50%, -50%);
  }

  /* one and four pixels short respectively — not worth a technique */
  .nav-links a.cta { min-height: 44px; display: inline-flex; align-items: center; }
  .brand { min-height: 44px; }

  /* PHOTO TILES THAT ARE ALSO NAVIGATION.
     The gallery used to be twelve dead figures: a visitor who saw a kitchen they
     liked had nowhere to click. Each tile now opens the scope page for that
     trade, which is better for the reader and puts twelve descriptive internal
     links on a page that had almost none. */
  a.shot--link { display:block; text-decoration:none; color:inherit; background:var(--navy-2); }
  .shot-frame { display:block; overflow:hidden; }
  a.shot--link img { display:block; width:100%; }
  .shot-cap { display:flex; align-items:baseline; justify-content:space-between; gap:12px;
              font-family:var(--fb); font-size:.84rem; line-height:1.45;
              color:var(--on-dark-subtle); padding:12px 14px;
              border-top:1px solid var(--hairline); }
  .shot-arw { color:var(--steel); flex:none; opacity:0; transform:translateX(-4px); }
  a.shot--link:hover .shot-cap,
  a.shot--link:focus-visible .shot-cap { color:var(--bone); background:var(--petrol3); }
  a.shot--link:hover .shot-arw,
  a.shot--link:focus-visible .shot-arw { opacity:1; transform:translateX(0); }
  a.shot--link:focus-visible { outline:2px solid var(--steel); outline-offset:-2px; }
  @media (prefers-reduced-motion: no-preference) {
    a.shot--link img { transition: transform .55s cubic-bezier(.2,.6,.2,1); }
    a.shot--link:hover img { transform: scale(1.045); }
    .shot-cap, .shot-arw { transition: color .2s ease, background .2s ease,
                                       opacity .2s ease, transform .2s ease; }
  }

  /* Photo grids run the full width at three across. The head-in-a-left-rail
     treatment is for prose: a four-line head beside a twelve-photo grid leaves
     the rail empty for most of the section and squeezes the photos into a narrow
     strip that runs down the page forever. */
  .shot-grid--3 { grid-template-columns: repeat(3, 1fr); }
  @media (max-width: 900px) { .shot-grid--3 { grid-template-columns: repeat(2, 1fr); } }
  @media (max-width: 600px) { .shot-grid--3 { grid-template-columns: 1fr; } }

  /* SECTION HEAD BESIDE THE CONTENT, NOT STACKED ABOVE IT.
     A section head plus a 72ch body column left roughly 45% of every wide screen
     empty to the right of the text while the heading burned its own full-width
     band above. Moving the head into a left rail uses that empty column and takes
     a heading's height plus its margin out of every section it is applied to.
     The head sticks while you read past it, so you never lose which section you
     are in. Single column below 1000px, where there is no spare width to use. */
  @media (min-width: 1000px) {
    .block--split > .container { display:grid; grid-template-columns:minmax(200px,270px) 1fr;
                                 gap:0 60px; align-items:start; }
    .block--split .head { margin-bottom:0; max-width:none; position:sticky; top:112px; }
    .block--split .head p { font-size:1rem; }
  }
  @media (prefers-reduced-motion: reduce) { .block--split .head { position:static; } }

  /* Three stages, so the grid is told three rather than fitting as many as will
     go. auto-fit gave four across and stranded the fifth card alone on its own
     row — visible on every service page. */
  .steps--3 { grid-template-columns: repeat(3, 1fr); }
  @media (max-width: 900px) { .steps--3 { grid-template-columns: 1fr; } }

  /* "Build it" carries the four build stages the page used to spend four cards
     on. Same detail, one card. */
  .step-items { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 9px; }
  .step-items li { position: relative; padding-left: 18px; font-size: .92rem;
                   line-height: 1.5; color: var(--on-dark-muted); }
  .step-items li::before { content: ""; position: absolute; left: 0; top: .62em;
                           width: 7px; height: 1px; background: var(--steel); }

  /* 2. EMPTY BOXES. .steps and .services-grid painted --line (a LIGHT colour)
        as the container background and let it show through the 2px gaps. Any
        cell the grid does not fill — 4 steps in a 3-column row — rendered as a
        large pale rectangle. Container goes transparent and each card carries
        its own hairline, so unfilled cells are simply not there. */
  .steps, .services-grid { background: transparent; border: 0; }
  .step { background: var(--navy-2); border: 1px solid var(--hairline); }
  .step .k { color: var(--steel); }          /* was --mist-2: pale on pale */
  .step h3 { color: var(--bone); }
  .step p  { color: var(--on-dark-muted); }

  /* 3. CTA BAND. The photograph sat at opacity .32 under a veil, so it read as
        a wash rather than an image, and the copy on top was low contrast. More
        image, and a veil that stays dense where the text actually is. */
  .cta-band .bg { opacity: .55; }
  .cta-band .veil { background: linear-gradient(90deg,
      var(--asi-petrol-a94) 0%, var(--asi-petrol-a94) 38%,
      var(--asi-petrol-a72) 62%, var(--asi-petrol-a50) 100%); }
  .cta-band h2 { color: var(--bone); }
  .cta-band p  { color: var(--on-dark); }

  /* 4. Same oversight as .step: .badge kept its pre-rebrand light pill
        (bone fill, --line border) and read as a foreign element on a dark
        page. Readable, but not the brand. */
  .badge { background: var(--navy-2); border: 1px solid var(--hairline);
           color: var(--on-dark-muted); border-radius: 0; }

  /* ═══════════ FACT BAND (2026-08-18) ═══════════
     /about/ used to spend four stacked H2 sections and roughly 700px of a 72ch
     column on service area, hours, licensing and location. They are facts, not
     argument — nobody reads them, they get looked up. So they are cards: four
     across on desktop, two on tablet, one on mobile, equal height, scanned in
     one pass. Same hairline-on-navy-2 treatment as .step and .post-item, so it
     is the existing card language rather than a new one. */
  .factgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(258px, 1fr));
              gap: 2px; align-items: stretch; }
  .fact { background: var(--navy-2); border: 1px solid var(--hairline);
          padding: 28px 26px; display: flex; flex-direction: column; min-width: 0; }
  .fact h3 { font-family: var(--fd); font-weight: 700; font-size: .92rem;
             letter-spacing: .16em; text-transform: uppercase; color: var(--steel);
             margin: 0 0 18px; }
  /* The note is the last thing in every card and is pinned to the bottom, so a
     four-card row keeps one baseline no matter how long the list above it runs. */
  .fact-note { margin: 18px 0 0; margin-top: auto; padding-top: 16px;
               border-top: 1px solid var(--hairline);
               font-size: .88rem; line-height: 1.55; color: var(--on-dark-subtle); }
  .fact-note a { color: var(--steel); }

  /* Label / value rows. dl rather than a table: these are name-value pairs and
     they have to collapse to one column on a narrow card without reflowing. */
  .fact-rows { margin: 0; display: grid; gap: 12px; }
  /* min-width:0 on both the row and the value: admin@allseasonimprovements.com
     is one unbreakable token, and without this the 1fr column refuses to go
     below its intrinsic width, pushes the card past the container and puts a
     horizontal scrollbar on the whole page. */
  .fact-rows > div { display: grid; grid-template-columns: 5.6em minmax(0, 1fr);
                     gap: 10px; align-items: baseline; min-width: 0; }
  .fact-rows dt { font-family: var(--fd); font-weight: 600; font-size: .84rem;
                  letter-spacing: .08em; text-transform: uppercase;
                  color: var(--on-dark-subtle); }
  .fact-rows dd { margin: 0; color: var(--on-dark); font-size: .96rem; line-height: 1.45;
                  min-width: 0; overflow-wrap: anywhere; }
  .fact-rows dd a { color: var(--steel); }
  @media (max-width: 340px){ .fact-rows > div { grid-template-columns: 1fr; gap: 2px; } }
  /* Label above value, for cards whose values are long unbreakable strings. */
  .fact-rows--stack > div { grid-template-columns: 1fr; gap: 3px; }
  /* normal, not break-word: the <wbr> in the address is the only break point
     we want. Anything looser puts a line break in the middle of the domain. */
  .fact-rows--stack dd { overflow-wrap: normal; }

  /* Confirmed credentials. A tick list, not a dl: these are statements, not
     name-value pairs, and there is no number behind them to put in a value
     column. Licensed and insured were confirmed by Kenneth on 2026-08-18. */
  .ticks { list-style: none; margin: 0; padding: 0; display: grid; gap: 11px; }
  .ticks li { position: relative; padding-left: 26px; color: var(--on-dark);
              font-size: 1rem; line-height: 1.4; }
  .ticks li::before { content: ""; position: absolute; left: 2px; top: .18em;
                      width: 6px; height: 12px; border: solid var(--steel);
                      border-width: 0 2px 2px 0; transform: rotate(40deg); }

  /* Stacked profile links in the contact card's note slot. */
  .profile-links { display: grid; gap: 7px; }
  .profile-links a { display: inline-block; }

  /* Ten towns as ten bullets was ten lines. As chips they wrap into three or
     four and the card stops being the tallest thing on the page. */
  .chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
  .chips li { font-family: var(--fb); font-size: .86rem; line-height: 1.2;
              color: var(--on-dark); background: var(--ground);
              border: 1px solid var(--hairline); padding: 7px 10px; }

  /* The eight scopes on /about/ are navigation, not prose — a row of links that
     used to occupy a full band with its own H2 and lead. Chips carry the same
     links in two lines. Same chip as the service-area list, plus a hit state. */
  .scope-chips { margin-top: 44px; padding-top: 34px; border-top: 1px solid var(--hairline); }
  /* Scoped under section.block rather than !important: `section.block h2`
     (0,1,1) sets --bone and would otherwise win against a bare class. */
  section.block .scope-chips__h { font-family: var(--fd); font-weight: 700;
                    font-size: .92rem; letter-spacing: .16em; text-transform: uppercase;
                    color: var(--steel); margin: 0 0 16px; }
  .chips--links li { padding: 0; }
  .chips--links li a { display: block; padding: 9px 13px; color: var(--on-dark);
                       text-decoration: none; }
  .chips--links li:hover, .chips--links li:focus-within { border-color: var(--steel); }
  .chips--links li a:hover, .chips--links li a:focus-visible { color: var(--steel);
                                                               text-decoration: none; }
  .chips--links li a:focus-visible { outline: 2px solid var(--steel); outline-offset: -2px; }

  /* ═══════════ FOOTER RATING (2026-08-18) ═══════════
     Kenneth, 2026-08-18: the rating comes off /about/ and lives here instead —
     one line, every page, straight to the profile. Figure still comes from the
     single site.reviews constant, so it cannot drift. */
  .foot-rating { margin: 16px 0 0; }
  .foot-rating a { display: inline-flex; align-items: baseline; gap: 8px;
                   font-family: var(--fb); font-size: .9rem; color: var(--bone);
                   border: 1px solid var(--hairline); padding: 9px 14px;
                   text-decoration: none; }
  .foot-rating a:hover,
  .foot-rating a:focus-visible { border-color: var(--steel); color: var(--steel);
                                 text-decoration: none; }
  .foot-rating .stars { color: var(--steel); letter-spacing: .04em; }
  .foot-rating .arw { color: var(--steel); }

  /* ═══════════ GALLERY FILTER (2026-08-18) ═══════════
     Radio inputs + :has(), no JavaScript. Every photograph stays in the DOM, so
     crawlers and JS-off visitors see all of them, and there is no getElementById
     for check.mjs rule 10 to police. A browser without :has() shows the whole
     grid, which is the correct fallback for a gallery. The per-category display
     rules are generated in layouts/gallery.njk from each page's `cats`, so
     adding a category never touches this file. */
  .gal-filter { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 22px; }
  .gal-radio { position: absolute; width: 1px; height: 1px; opacity: 0;
               pointer-events: none; }
  .gal-chip { display: inline-flex; align-items: baseline; gap: 8px;
              font-family: var(--fd); font-size: .82rem; font-weight: 600;
              letter-spacing: .1em; text-transform: uppercase;
              color: var(--on-dark-muted); background: var(--navy-2);
              border: 1px solid var(--hairline); padding: 11px 16px;
              cursor: pointer; user-select: none; white-space: nowrap;
              transition: background .15s ease, color .15s ease; }
  .gal-chip:hover { background: var(--petrol3); color: var(--on-dark); }
  .gal-n { font-family: var(--fb); font-size: .76rem; letter-spacing: 0;
           color: var(--on-dark-min); }
  /* Amber fill takes petrol text, never bone — colour rule 2. */
  .gal-radio:checked + .gal-chip { background: var(--steel); color: var(--navy);
                                   border-color: var(--steel); }
  .gal-radio:checked + .gal-chip .gal-n { color: var(--navy); }
  .gal-radio:focus-visible + .gal-chip { outline: 2px solid var(--focus);
                                         outline-offset: 2px; }
  .gal-more { margin: 22px 0 0; font-family: var(--fd); font-size: .86rem;
              font-weight: 600; letter-spacing: .06em; }
  .gal-more a { color: var(--steel); text-decoration: none; }
  .gal-more a:hover { text-decoration: underline; }
  .gal-more span { color: var(--on-dark-min); margin: 0 6px; }
  @media (max-width: 600px) {
    .gal-filter { gap: 6px; margin-bottom: 18px; }
    .gal-chip { padding: 9px 12px; font-size: .74rem; letter-spacing: .06em; }
  }
