/* ============================================================================
   site-unify.css — makes index-v2.html read as the same site as /services-v2/.

   PURELY ADDITIVE. Loaded LAST, after every existing stylesheet, and linked
   ONLY from index-v2.html. index.html never sees it, so the working homepage
   cannot break.

   ── HOW THIS FILE FIGHTS THE CASCADE ─────────────────────────────────────
   index-v2.html loads, in order:
     style.css · hero.css · sections.css · marquee-banner.css · mokko-bridge.css
     · reveals.css · panel-fixes.css · hero-phone.css · palette.css · THIS FILE

   It does NOT load bto-tokens.css or bto-os-home.css. Rules quoted from those
   two files in earlier review notes DO NOT APPLY HERE — verified against the
   page's own <link> tags with CDP getMatchedStylesForNode, not read off source.

   Every section head on this page is governed by an ID-carrying rule in
   mokko-bridge.css § P5/M2 (`#engine.er-sec .er-head`, `#faq .ms-ah-wrapper
   .content__title`, `#work.bto-work .ms-ah-wrapper .content__title`). A
   class-only selector cannot beat an ID no matter how late it loads. So the
   rules below are prefixed with `body` and carry the same ID — one notch above
   the incumbent. THAT is why the selectors look heavy.

   *** THERE ARE EXACTLY THREE `!important` DECLARATIONS IN THIS FILE, *** all in
   one rule (§8, the footer/social ghost pill), all forced by an `!important` in
   palette.css:422 — specificity cannot beat an !important, so it has to be
   matched. Everything else wins on specificity alone. If you add a rule and it
   doesn't take, raise specificity before reaching for !important, and run
   cascade.mjs to see what actually beat you.
   (This header previously claimed zero. It was written before §8 existed and was
   wrong by the time it was read — corrected 2026-07-30. Three more overrides in
   the 2026-07-30 pass silently lost the cascade because I trusted a comment or a
   source file instead of resolving the live match: the footer nav links, the
   copyright line and work.html's card radius. All three are annotated in place
   with the incumbent that beat them.)

   ── WHAT THE REFERENCE ACTUALLY IS (measured at 1440×900, not assumed) ────
   THREE faces, each with one job:
     Antonio       display + card interiors + micro-tags   (61 nodes)
     ProximaNova   body prose only                         (51 nodes)
     Neue Montreal chrome only — logo, nav, buttons        (10 nodes)
   Display spec:  700 · uppercase · letter-spacing -0.05em · line-height .95–1
   Palette MP071: #F0EEEB canvas · #13181B ink · #003A6C navy · #FFBF65 amber
   Amber:         6 elements TOTAL. Flat fills, border-radius 0, rotation on a
                  wrapper (-3deg). Never a gradient, never a glow.
   Radii:         40px pills (13×) · 28px cards. Two values, that's the system.
   ============================================================================ */

@import url("https://fonts.googleapis.com/css2?family=Antonio:wght@100..700&display=swap");

/* ---------- 1 · tokens, aligned to the services page ---------------------- */
:root {
  --u-canvas:  #F0EEEB;   /* Magical Moonlight — the room                     */
  --u-ink:     #13181B;   /* Neverything                                      */
  --u-navy:    #003A6C;   /* Ateneo Blue                                      */
  --u-surface: #CCD5DA;   /* Polar Drift                                      */
  --u-accent:  #FFBF65;   /* Sea Buckthorn — FILL ONLY, never text on light   */
  --u-accent-tx: #854D00; /* accent AS TEXT on a light ground. 5.93:1.        */
                          /* #FFBF65 as text on #F0EEEB is 1.41:1 — illegible.*/
  --u-on-dark: #F0EEEB;   /* ink on a dark ground                             */
  --u-on-dark-mute: #B7B6B5;
  /* the ghost-pill pair, as tokens so they can follow their GROUND.
     §8 has to use !important to beat palette.css:422, and an !important
     declaration cannot be re-specified later by specificity. Routing it
     through a custom property means the dark footer redefines the VALUE
     (§15c) instead of needing a second !important to fight the first. */
  --u-pill-ink: #13181B;
  --u-pill-bd:  rgba(19, 24, 27, .24);
  --u-display: "Antonio", "Archivo", sans-serif;
  --u-prose:   "ProximaNova", "Neue Montreal", sans-serif;
  --u-track:   -0.05em;   /* the reference's display tracking, as a ratio.    */
                          /* Reference declares -0.35vw = -5.04px on its 96px */
                          /* head = -0.0525em. Copying the vw value would give */
                          /* -0.076em on this page's 66px heads — too tight.  */
                          /* The em ratio is what reproduces, so use that.    */
}

/* ---------- 2 · the body face -------------------------------------------- */
/* The reference sets prose in ProximaNova; this page was setting it in Neue
   Montreal. Same size (18px) and near-identical line-height (28 vs 28.8), so
   this is a face swap, not a re-scale — reflow risk is low and was measured.

   2026-07-30 — THE FOLLOW-UP I FLAGGED LAST PASS HAD ALREADY BITTEN. The src
   was ../services-v2/fonts/ProximaNova-Regular.otf, which resolved only while
   these pages lived at site root. Both pages now also serve from site/v2/, where
   the same relative path resolves to /v2/services-v2/fonts/ — a directory that
   does not exist. Measured result on .../v2/: the face errored and all 55 prose
   nodes silently fell through to Neue Montreal, the next family in --u-prose. It
   looked fine, which is why nobody caught it.
   The .otf now lives in site/fonts/, which is a real directory at site root AND
   is symlinked into v2/ as `fonts`. So ../fonts/ resolves from bto-css/ in BOTH
   contexts, and the cross-build coupling is gone for good.
   Weight range 400–500 matches the services build so the prose face and its
   weight behaviour are identical on all three pages. */
@font-face {
  font-family: "ProximaNova";
  src: url("../fonts/ProximaNova-Regular.otf") format("opentype");
  font-weight: 400 500;
  font-display: swap;
}

/* ---------- 3 · display type --------------------------------------------- */
/* Antonio is condensed: at the same font-size it occupies ~20% less width.
   Three of this page's display heads carry NO class of their own and are
   reachable only through their parent — verified by walking the live DOM.
   .hbv-copy h1 is the hero, .footer-title h1 is the close, .er-head is the
   Engine Room. Missing these is what made the first pass look half-applied. */
body .hbv-copy h1,
body .footer-title h1,
body .footer-title,
body .footer-title *,
body .er-head,
body .hero-title,
body .content__title,
body .bto-rail-title,
body .bto-banner-title,
body .bto-pw-title,
.ms-hero-subtitle {
  font-family: var(--u-display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--u-track);
  line-height: 0.95;
}

/* 3b · the four heads whose incumbent rule carries an ID or two classes.
   Same declarations as above — restated at higher specificity because the
   generic block LOSES to these, measured:
     .bto-banner .bto-banner-title              (0,2,0)  mokko-bridge.css:890
     #work.bto-work .ms-ah-wrapper .content__title (1,3,0)  mokko-bridge § M2
     #faq .ms-ah-wrapper .content__title        (1,2,0)  mokko-bridge § M2
     #engine.er-sec .er-head                    (1,2,0)  mokko-bridge § P5.6
   all of which pin font-weight:600 and letter-spacing:-.02em. */
/* 3c · THE STATIC PAGES' SECTION HEADS — added 2026-08-06.
   Founder: "the about page does not look like our website".

   MEASURED, at 1440x900, Antonio headings by size/weight, footer excluded:
     services   124/700 104/700 96/700 66/700 63/700 52/700 37/700 21/700 20/700 17/700
     home       295/700  96/700 60/700 52/700 31/700
     about       96/700  90/700 52/500  x9      <-- every section head on the page
     contact     90/700  52/700 x2  52/500 x3

   Every Antonio heading on home and services is 700, INCLUDING at exactly 52px.
   This file's own header states the reference: "Display spec: 700 - uppercase -
   letter-spacing -0.05em - line-height .95-1". So 500 here is off-spec, not a
   second tier, and it is the single biggest reason about.html read as a
   different site: nine of its ten section heads were the wrong weight.

   The incumbent is `.ms-ah-wrapper .content__title` (0,2,0) from Mokko's
   style.css. `body.bto-static .ms-ah-wrapper .content__title` is (0,3,0) plus
   an element, so it wins on specificity - no !important, per this file's rule.

   NOTE THE MISSING SPACE, it is the whole rule: `body.bto-static`, not
   `body .bto-static`. `.bto-static` sits ON the <body> element, so the
   descendant form asks for a .bto-static INSIDE body, which does not exist and
   silently matches nothing. Written that way first; the probe still read 500
   across all nine heads and that is the only reason it was caught.

   ONLY font-weight is declared. Family, letter-spacing (-2.592px on 51.84px =
   exactly -0.05em) and text-transform were MEASURED as already on-spec, so
   restating them would be noise that could only drift. Line-height is
   deliberately not touched: reveals.css:180 documents `.ms-ah-wrapper
   .content__title { line-height: 0.9 }` as load-bearing for the wipe's clip
   geometry, and this rule has no business moving it.

   SCOPE: `.bto-static` is about + contact + founder. It corrects about (9) and
   contact (3). It does NOT touch founder's eleven `H3.svc-step-name` at 23px/500
   - those are `.svc-step-name`, not `.content__title`, a genuine small tier, and
   they are left exactly as they are. */
body.bto-static .ms-ah-wrapper .content__title { font-weight: 700; }

/* 3d · THE STATIC PAGES' INHERITED INK — added 2026-08-06, same pass.
   MEASURED: `getComputedStyle(document.body).color` is
     home      rgb(19, 24, 27)   = #13181B, the MP071 ink
     services  rgb(19, 24, 27)
     about     rgb(0, 0, 0)      <-- PURE BLACK, the CSS initial value
   Nothing in the static chain declares a body colour, so black arrives by
   omission rather than by choice. bto-about.css:122-137 already caught the same
   class of bug on `.content__title` and wrote it up: pure black is not in
   MP071, the difference is small enough to be invisible and large enough to be
   wrong, and it will DRIFT on the theme toggle because `--fg-ink` follows the
   theme and `black` does not.

   This only reaches elements that declare no colour of their own - every
   section head, lead, prose and row on these pages already resolves to a
   palette token, so the visible delta is small. It is the floor being correct
   rather than the paint. */
body.bto-static { color: var(--fg-ink); }

body .bto-banner .bto-banner-title,
body #work.bto-work .ms-ah-wrapper .content__title,
body #faq .ms-ah-wrapper .content__title,
body #engine.er-sec .er-head {
  font-family: var(--u-display);
  font-weight: 700;
  letter-spacing: var(--u-track);
  line-height: 0.95;
  text-transform: uppercase;
}

/* the small uppercase eyebrow — same register as the services page's kickers */
body .heading-title,
body .bto-pw-eyebrow,
body .ms-hero-title {
  font-family: var(--u-display);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
}

/* ==========================================================================
   4 · THE AMBER SLAB — the page's signature device, and the headline fix
   ==========================================================================
   THE BUG, and how it was found: the block rendered with a dark wash that was
   heaviest through the middle and lifted at the right end. It was NOT a
   gradient, a mask or an overlay — six ablation runs killing .hb-viewer::before,
   ::after, both, the rotation and the z-order left it unchanged.

   ROOT CAUSE: hero-phone.css:289 puts `text-shadow: 0 4px 30px rgba(0,0,0,.7)`
   on `.hbv-copy h1`. text-shadow INHERITS, so the .u-mark span carries it too —
   and a span paints its own background FIRST, then its text's shadow ON TOP.
   Result: 30px of blurred black haze deposited over the amber plate, densest
   where the glyphs are. That is why it read as a left-to-right fade: the right
   end of the block is past the final full stop, so no glyphs, no haze.

   PROOF: sampling the brightest amber per column across the block gave
   L=240 M=236 R=254 (spread 18). With `text-shadow:none` on .u-mark the same
   scan gives 255/255/255 — spread 0, exactly #FFBF65.

   The shadow is correct for white type over video. It is wrong inside the slab,
   where dark ink sits on a solid plate that supplies its own contrast.
   Art-direction law being satisfied: "amber is flat or it isn't amber." */
.u-mark {
  background: var(--u-accent);
  color: var(--u-ink);
  display: inline-block;
  /* the services block's padding ratio. It reads as a BLOCK; the old
     0.02em/0.3em/0.12em hugged the letterforms. */
  padding: 0.06em 0.28em 0.14em;
  border-radius: 0;              /* reference measures 0px. It is a cut plate. */
  transform: rotate(-3deg);      /* reference .hero-text-scroll rotates -3deg  */
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  text-shadow: none;             /* ← THE FIX. See the block comment above.    */
}

/* On the dark hero the ink must be forced, or Mokko's white heading colour
   inherits straight through and the text vanishes into the plate. */
.hbv-copy h1 .u-mark {
  color: var(--u-ink);
  text-shadow: none;             /* restated: .hbv-copy h1 is where it enters  */
  margin-top: 0.12em;
}

/* ---------- 5 · the accent AS TEXT --------------------------------------- */
/* `em.s` is this page's accent-word tag. It resolves through --dc-accent and
   was rendering mid-grey #555859, which is why "NEEDS" and "ROOM." read as
   switched-off words.

   🔴 DO NOT set these to #FFBF65. On the #F0EEEB canvas that is 1.41:1 —
   an illegible headline. Accent-as-text on light is #854D00 (5.93:1). */
body h1 em.s,
body h2 em.s,
body h3 em.s,
body .bto-banner .bto-banner-title em.s {
  color: var(--u-accent-tx);
  font-style: normal;
}

/* ...but inside a DARK panel the polarity flips: there, #FFBF65 is 10.98:1 and
   #854D00 would be a brown word on near-black. Scoped, not global. */
body #engine.er-sec .er-head em.s,
body .footer-title em.s,
body footer.ftb h3 em.s {
  color: var(--u-accent);
}

/* eyebrows: the services page's kicker register */
body .hbv-copy .kick,
body .bto-rail-head .kick,
body .er-kick,
body .fq-kick {
  font-family: var(--u-display);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--u-accent);
}

/* ==========================================================================
   6 · THE DARK BEAT — the Engine Room goes back to dark
   ==========================================================================
   Measured tonal rhythm before this rule:  D · L · L · M · L · D
   The Engine Room was landing on #CCD5DA, not on the #0a0f0c it was designed
   for and not on the canvas either. Cause: `#engine.er-sec { background:
   var(--bto-surface-c) }` (mokko-bridge.css:1422) where --bto-surface-c
   resolves through --bg-panel-dark, which palette.css aliases to --bg-panel2
   (#CCD5DA) in the light theme. So the "dark panel" token is currently light.

   NOTE for the record: this file's previous `body .er-sec {background:
   --u-canvas}` rules never applied at all — they lost to the ID rule. They
   were dead code, not the cause. They are gone now either way.

   Its ink is ALREADY on-dark (`color: var(--bto-on-c)` → --fg-on-dark), so
   restoring the ground actually RESOLVES a latent mismatch rather than
   creating work. The section's content is six photographs, which hold their own
   luminance and read on any ground. Position 4 of 6 — two-thirds down, right
   before the close — is where the reference puts its dark showreel.

   Beats `#engine.er-sec` (1,1,0) with (1,1,1). No !important required. */
body #engine.er-sec {
  background: var(--u-ink);
  color: var(--u-on-dark);
}
/* the head and its eyebrow were inked for the light grey — flip them */
body #engine.er-sec .er-head,
body #engine.er-sec .heading-title,
body #engine.er-sec .er-kick {
  color: var(--u-on-dark);
}
/* the six tile chips are a dark pill on a photo; on the new dark ground their
   hairline disappears. Lift the border, keep the pill (see §8 note on the two
   chip languages still shipping). */
body #engine.er-sec .er3-tag {
  color: var(--u-on-dark);
  background: rgba(19, 24, 27, .82);
  border-color: rgba(240, 238, 235, .28);
}

/* ---------- 7 · surfaces -------------------------------------------------- */
/* Only the genuinely light sections are re-grounded to the warm canvas. The
   hero and the Engine Room keep their dark treatment — matching the reference
   means matching its light/dark RHYTHM, not flattening the page to one tone.
   `.er-sec` is deliberately absent from this list. */
body .project-area,
body .fq-sec,
body .bto-rail-block {
  background-color: var(--u-canvas);
}

/* ==========================================================================
   8 · THE ACCENT BUDGET — amber rarely and large, never often and small
   ==========================================================================
   Measured 42 accent-bearing elements on this page against 7 on the reference.
   The overspend was entirely this file's own earlier pass, which had made every
   work-card chip (20×) and every lane-rail button (14×) an amber pill. At 11px
   and 10.4px those are not a brand device, they are a UI control that happens
   to be yellow. Both are demoted below; the hero slab and the one nav CTA stay.

   ⚠ OPEN, NOT DECIDED HERE: the page ships TWO chip languages — `.er3-tag`
   (dark pill, Engine Room) and `.bto-work-cap .tag` (work cards). Making the
   work chips achromatic moves them toward the dark-pill language but does not
   merge them. Which one survives is a founder call; flagged in the report. */
body .bto-work-cap .tag {
  background: transparent;
  color: var(--u-ink);
  border: 1px solid rgba(19, 24, 27, .22);
  border-radius: 40px;
  padding: 4px 12px;
  font-family: var(--u-display);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 11px;
}
body .bto-work-cap h4 {
  font-family: var(--u-display);
  text-transform: uppercase;
  letter-spacing: -0.005em;
  font-weight: 700;
}
/* the 14 lane-rail "Open service" buttons become ghost pills.
   NOTE: no border-radius here on purpose. `.bto-banner .bto-pw-btn` already
   sets `var(--pin-r-full, 9999px)` — a token that MEANS "fully round" and
   renders identically to 40px on a 44px-tall element. Declaring 40px at lower
   specificity would be dead code; declaring it at higher specificity would
   replace a semantic token with a magic number for zero visible gain. */
body .bto-pw-btn {
  background: transparent;
  color: var(--u-ink);
  border: 1px solid rgba(19, 24, 27, .28);
  padding: 8px 18px;
  font-family: var(--u-display);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-weight: 500;
}
body .bto-pw-btn svg { fill: var(--u-ink); }

/* The standing amber action needs NO rule here: palette.css:422-428 already
   paints `.btn.btn--primary` / `.bto-nav-cta` with
   `background: var(--pin-primary) !important` and --pin-primary resolves to
   #FFBF65. Declaring it again would be dead code that merely LOOKS load-bearing.
   Verified by reading the computed value, not by assuming this file won.

   The problem is that the same class also carries the footer's FOUR social
   links — so palette's rule was minting four more 32px amber slivers, the exact
   "medium and often" failure the accent law forbids. A social link is not a
   primary action.

   ⚠️ !important REQUIRED, and this is the only one in the file. Forced by
   palette.css:422 `.btn.btn--primary { background: … !important }`. Specificity
   alone cannot beat an !important declaration, so this matches it at (0,3,1). */
body .social-area .btn.btn--primary,
body .social-area li a.btn {
  background: transparent !important;
  border-color: var(--u-pill-bd) !important;
  color: var(--u-pill-ink) !important;
}

/* ---------- 9 · body prose ----------------------------------------------- */
/* Prose only. Nav, buttons, chips and the logo stay on Neue Montreal — the
   reference sets those in Neue Montreal too, so they are already correct and
   moving them would be a regression, not a match. */
body .bto-rv-w,
body .bto-work-lead,
body .bto-footer-lead,
body .bto-pw-desc,
body .fq-a p,
body .hbv-copy p,
body .er-sec p,
body .project-area p,
body .fq-sec p {
  font-family: var(--u-prose);
}

/* ---------- 10 · radii ---------------------------------------------------- */
/* The reference runs two values: 40px pills and 28px cards. This page was
   running eleven (16, 20, 24, 32, 45, 50%, 99, 9999, 37.374, and two partials).
   Pulling the card family to 28 and the pill family to 40 is most of what makes
   a component set read as one system. */
body .bto-pw-card {
  border: none;
  overflow: hidden;
}
/* the lane cards' radius is pinned by `.bto-banner .bto-pw-card` (0,2,0) inside
   a `min-width:993px` query. Matched here at (0,2,1) INSIDE THE SAME QUERY so
   the mobile card keeps its own smaller 16px — the reference's small-card
   radius — instead of inheriting a desktop number. */
@media (min-width: 993px) {
  body .bto-banner .bto-pw-card { border-radius: 28px; }
}
body .bto-media,
body .swiper-slide .bto-media {
  border-radius: 28px;
  overflow: hidden;
}
body .btn,
body .back-to-top,
body .bto-nav-btn,
body .bto-rail-nav .bto-nav-btn { border-radius: 40px; }

body .bto-pw-eyebrow { color: var(--u-navy); }

/* the FAQ question rows join the display register */
body .fq-sec .fq-q,
body .fq-sec h3 {
  font-family: var(--u-display);
  text-transform: uppercase;
  letter-spacing: -0.005em;
}

/* ==========================================================================
   11 · SECTION HEADS — from LABEL to DESTINATION
   ==========================================================================
   The gap a computed-style diff cannot see. On the reference, `WORK / THAT /
   SHIPPED` occupies a whole viewport at ~250px across three offset lines — you
   scroll THROUGH the headline. Here, `06 — QUESTIONS / STRAIGHT ANSWERS.` gets
   the top-left corner at 66px with 38px of air before the first row, and the
   right half of the viewport empty. Same face, same colour, different RANK.

   Full parity is the rebuild the founder rejected. This is the cheapest
   available approximation: roughly 2.5× the air, roughly 2× the head.

   ⚠️ THIS BLOCK IS THE PAGE'S HEIGHT BUDGET. It is the ONLY thing in this file
   that materially changes page height, and it does so ON PURPOSE. Everything
   above it costs +2px in total. If the page needs to come back down, tighten
   the three clamps here — nothing else in this file needs to move. A halfway
   setting that keeps most of the effect:
       padding-block: clamp(96px, 12vh, 150px)
       .fq-list margin-top: clamp(56px, 7vh, 100px)
       head font-size: clamp(48px, 7vw, 110px)

   Specificity: beats `#engine.er-sec` / `#faq.fq-sec` (1,1,0) and
   `#faq .ms-ah-wrapper .content__title` (1,2,0) — mokko-bridge § P5.1 / M2. */
body #engine.er-sec,
body #faq.fq-sec {
  padding-block: clamp(120px, 18vh, 220px);   /* from a flat 64px            */
}

body #faq .fq-list {
  margin-top: clamp(72px, 10vh, 140px);       /* from 38px                   */
}

/* The three section heads mokko-bridge § M2/P5.6 deliberately pinned to ONE
   expression, `clamp(40px, 4.6vw, 68px)`, "so the two scale together at every
   width". Keeping them on one expression respects that; only the numbers move.
   The banner head is NOT in this list — it is a panel headline with a rail
   beneath it, not a section label, and its width is already committed. */
body #faq .ms-ah-wrapper .content__title,
body #work.bto-work .ms-ah-wrapper .content__title,
body #engine.er-sec .er-head {
  font-size: clamp(56px, 9vw, 150px);         /* from clamp(40px,4.6vw,68px) */
}

/* ==========================================================================
   12 · INK, NOT BLACK — and three selectors that need to reach further
   ==========================================================================
   The reference uses pure #000000 for exactly NOTHING. Every dark glyph on it
   is #13181B. This page was rendering its display heads, its rail titles and
   its brand-strip names in true black, inherited from Mokko's base
   `h1,h2,h3,h4,h5,h6 { color: var(--color-contrast-higher) }`. At display scale
   that difference is small per-glyph and large per-page: true black on a warm
   #F0EEEB canvas reads colder and harder than the reference does.

   Scoped to the heads that sit on LIGHT grounds only. `.er-head` is excluded —
   it is on the dark panel now and already carries on-dark ink. `.hbv-copy h1`
   is excluded — it is white type over video. */
body #work.bto-work .ms-ah-wrapper .content__title,
body #faq .ms-ah-wrapper .content__title,
body .bto-banner .bto-banner-title,
body .bto-rail-title,
body .bto-rail-head .bto-rail-title {
  color: var(--u-ink);
}

/* 12b · the work-card chips. §8 demoted them off amber, but their TYPE spec
   was still losing: `#work.bto-work .bto-work-cap .tag` (1,3,0) pins
   font-family:inherit and `#work .bto-work-cap .tag` (1,2,0) pins the colour.
   Restated at (1,3,1) so the achromatic pill actually gets the label register
   it was specified with. */
body #work.bto-work .bto-work-cap .tag {
  font-family: var(--u-display);
  color: var(--u-ink);
  background: transparent;
  border: 1px solid rgba(19, 24, 27, .22);
}

/* 12c · REGRESSION GUARD — `.heading-title` is a <p>, so §9's prose rules
   (`body .project-area p`, `body .fq-sec p`) were catching the section
   EYEBROWS and setting them in ProximaNova. An eyebrow is display register,
   not prose. Restated above the prose rules' specificity to take them back. */
body .project-area .heading-title,
body .fq-sec .heading-title,
body #work .heading-title,
body #faq .heading-title {
  font-family: var(--u-display);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
}

/* ---------- 13 · what is deliberately NOT touched ------------------------ */
/* .main-header        — already unified, measured to match, leave alone
   .ms-menu*           — Mokko's slide-out; the React header mirrors it
   .hbp-* (the phone)  — the Instagram mock is DELIBERATELY -apple-system +
                         Grand Hotel. That is iOS chrome cosplay, not our type
                         system. 199 nodes; changing them breaks the illusion.
   the hero video      — its own art direction, not a palette question
   .hb-stage ground    — recolouring the near-black stage to canvas is the
                         Creative Director's highest-leverage idea and an
                         explicit FOUNDER CALL. Not taken here.                */

/* ==========================================================================
   12 · HEADER PARITY — the founder's call: "even the header is different"
   He is right, and my earlier note that it was "correct behaviour" was wrong.
   A visitor sees a white logo on a shapeless dark bar on one page and a black
   logo in a frosted pill on the other. Same nav, two identities.

   Mokko's mechanism (style.css:7095-7109): `.main-header__inner::after` IS the
   pill — 40px radius, blur(8px), bg at 0.7 alpha — but it sits at `opacity: 0`
   until main.js adds `.move` on scroll. The services page shows its pill from
   the first frame. So: show it from the first frame here too.
   ========================================================================== */

/* the pill, always on — not only after scrolling */
body .main-header .main-header__inner::after { opacity: 1; }

/* With the pill always present the ground is light, so the LIGHT logo (white
   type, meant for the transparent-over-video state) is now invisible against
   it. Mokko renders both marks and swaps them; force the ink one in both slots
   rather than fighting the swap logic. */
body .main-header__inner .logo-light .bto-logo,
body .main-header__inner .logo-dark  .bto-logo { color: #13181B; }
body .main-header__inner .bto-logo__tag { color: rgb(85, 88, 89); opacity: 1; }

/* the search / theme controls read as bare glyphs at scroll-top; the services
   header sits each in a 36px #F7F7F7 disc. Measured off the scrolled state. */
body .header__search-icon,
body .main-header--widgets > div {
  border-radius: 40px;
}

/* --- 12b · two defects the first parity pass produced ---------------------- */

/* 1. Mokko's pill uses `hsla(var(--color-bg-*), 0.7)` — 70% alpha. On the
      services page that sits over a cream canvas and reads as clean frosted
      white. Here it sits over DARK VIDEO, so the same rule reads murky grey.
      Same CSS, opposite result, because the ground differs. Pin an explicit
      light value with enough alpha to be independent of what is behind it. */
body .main-header .main-header__inner::after {
  background-color: rgba(248, 247, 245, 0.92);
  filter: none;                 /* Mokko's contrast(0.958) greys it further */
}

/* 2. Mokko ships BOTH logo marks and cross-fades them. Forcing colour on both
      left the light mark stacked behind the dark one — visible as a pale slab
      behind "VISION IN MOTION". Hide the light mark outright; with the pill
      always on, the transparent-over-video state it exists for never occurs. */
body .main-header__inner .logo-light { display: none; }

/* 12c · Mokko cross-fades the two marks by OPACITY, not display: at scroll-top
   .logo-dark is display:block but opacity:0, and .logo-light is the visible
   one. Hiding .logo-light above therefore removed the logo entirely — verified
   in the live DOM, not assumed. With the pill now permanently light, the ink
   mark is the correct one, so bring it up to full. */
body .main-header__inner .logo-dark { opacity: 1; }


/* ############################################################################
   ############################################################################
   14–18 · THREE-PAGE CONSISTENCY PASS — 2026-07-30
   Added after auditing  /v2/  ·  /v2/work.html  ·  /v2/services/  side by side
   at 1440x900 and 390x844, scroll-scrubbed, computed values only.

   The previous passes compared the HOMEPAGE to the services page. work.html had
   never been measured against either. It turned out to be the outlier on almost
   every axis: two faces where the others run three, ONE accent instance against
   5 and 7, a display head in the wrong face entirely, and a footer missing two
   of its four blocks. Most of what follows is work.html joining the system.
   ############################################################################
   ########################################################################## */


/* ==========================================================================
   15 · THE FOOTER — the one nobody had audited

   Measured, before this block:

                        HOME              WORK              SERVICES
     height             720px             493px             1159px (a section)
     blocks             title·lead·       title·bottom      close head·social·
                        3 cols·bottom     (no lead/cols)    nav·newsletter·©
     ground             cream             cream             #13181B
     h2                 Antonio 48/600    Antonio 48/600    Antonio 96/700
                        ls +1px #000000   ls +1px #000000   ls -5.04px #F0EEEB
     nav items          5                 4                 6 — as <p>, 0 links
     social             4 pills, real URL 3 pills, all "#"  3 icon circles, no <a>
     links in footer    13                8                 0
     copyright          "© 2026 BY THE     "© 2026 By The    "Copyright © 2025
                        OWL — ALL RIGHTS   Owl — vision in   Spylt - All Rights
                        RESERVED"          motion. All…"     Reserved"
     footer logo mark   inline BTO svg    mokko_icon.png    (none)

   Three of those are defects rather than inconsistencies and are fixed in
   markup, not here: the template's brand name in the services copyright, the
   template's logo image in work's footer, and work's three dead href="#"
   socials. This block handles what CSS owns.

   THE ONE OPINIONATED CALL IN THIS PASS, stated plainly so it is easy to undo:
   the footer plate goes DARK. Reasons, in order of weight —
     1. It is the only surviving L→L repeat in the tonal score, which I flagged
        last pass and left as a founder call. It is now clearly a THREE-page
        inconsistency, not a homepage nicety: services closes on #13181B and the
        other two close on cream. Two pages disagree with the reference.
     2. The CD's locked nine-panel score puts panel 09 "The close" on DARK.
     3. It is the plate, NOT the section. `.footer-inner` already carries a 40px
        radius and 40px padding, so painting it produces a dark rounded card in
        a cream room — the CD's §2.3 "the darkness is the card, never the room",
        which is the services page's governing idea. Painting `footer.ms-footer`
        instead would have made the room dark and broken that rule.
     4. It gives work.html the dark beat it completely lacked (it measured L L
        across 5,620px — the "thirteen sections, one colour" failure).
   TO REVERT: delete 15b and 15c. Nothing else depends on them.
   ========================================================================== */

/* 15a · the footer head joins the display system.
   Incumbent: `.ms-footer .container .footer-title h2` (mokko style.css:21005,
   specificity 0,3,1) pins font-size 48px / font-weight 600 / line-height 1.4 /
   letter-spacing +1px. My §3 rule matches this node too but at (0,1,1) and
   loses. Restated at (0,3,2) by prefixing `body`. Size is left at Mokko's 48px
   deliberately — the head is a footer head, not a section destination, and
   growing it is a composition change, not a parity fix.
   letter-spacing goes from +1px to the system's -0.05em: a POSITIVE tracking on
   a condensed display face at 48px is the one value here that reads as a
   different typeface rather than a different size. */
body .ms-footer .container .footer-title h2 {
  font-family: var(--u-display);
  font-weight: 700;
  letter-spacing: var(--u-track);
  line-height: 1.05;
}

/* 15b · the plate goes dark. ONE declaration. */
body .ms-footer .footer-inner {
  background-color: var(--u-ink);
}

/* 15c · and everything inside it is re-inked for the new ground.
   Every pair below was contrast-checked against #13181B:
     #F0EEEB on #13181B = 13.72:1   (heads, links, email, copyright)
     #B7B6B5 on #13181B =  7.62:1   (lead, eyebrows, secondary line)
   Nothing in the footer drops below AA at its size after the flip. */
body .ms-footer .footer-inner .footer-title h2,
body .ms-footer .footer-inner .footer-nav-area a,
body .ms-footer .footer-inner .copyright-area .top,
body .ms-footer .footer-inner .bto-footer-cols a,
body .ms-footer .footer-inner .bto-footer-cols p:not(.bto-eyebrow) {
  color: var(--u-on-dark);
}
body .ms-footer .footer-inner .bto-footer-lead,
body .ms-footer .footer-inner .copyright-area .bottom {
  color: var(--u-on-dark-mute);
}
/* eyebrows. Incumbent `.bto-footer-cols .bto-eyebrow` (0,2,0) at
   mokko-bridge.css:410 + :717 — beaten at (0,2,1). */
body .bto-footer-cols .bto-eyebrow { color: var(--u-on-dark-mute); }

/* the 28px email link and its hover.
   FIRST ATTEMPT LOST, and this is the one that mattered: I grouped `.ms-sl` into
   the block above at (0,3,1). The incumbent is `.ms-footer .container
   .footer-title .ms-sl` (0,4,0) in palette.css:1171 → `var(--fg-ink)` #13181B.
   So the biggest link in the footer stayed dark ink ON the new dark ground —
   #13181B on #13181B, 1:1, invisible. Caught by re-measuring the footer after
   the flip, not by reading the rule. Matched at (0,4,1).
   Its :hover (palette.css:1172) resolves to `var(--fg-accent)` #854D00, which is
   2.61:1 on #13181B — a fail even at large-text AA. The on-dark accent token is
   #FFBF65 at 10.98:1, so the hover keeps its "goes to accent" behaviour and
   simply uses the correct value for the ground it is now on. */
body .ms-footer .container .footer-title .ms-sl { color: var(--u-on-dark); }
body .ms-footer .container .footer-title .ms-sl:hover { color: var(--u-accent); }
/* the hairline under the head was #81818133 — 20% of a mid grey. On cream it
   read as a rule; on ink it vanishes. Lift it to the on-dark ink at the same
   alpha so it still reads as a hairline and not as a seam. */
body .ms-footer .footer-inner .footer-title {
  border-bottom-color: rgba(240, 238, 235, 0.2);
}
/* social pills: ghost outlines inked for a light ground. Same device, flipped.
   (These were demoted OFF amber in §8 — they stay achromatic, see the accent
   law: four amber slivers in a footer is not a primary action.) */
/* the four social pills. FIRST ATTEMPT LOST TWICE OVER, and the fix is a token
   rather than an override:
     a. the pill's own colour/border come from §8 of this file, which carries
        !important (it has to — palette.css:422 does). Specificity cannot beat
        an !important, so re-declaring here at any weight failed. Redefining
        --u-pill-ink / --u-pill-bd on this subtree changes what §8's !important
        RESOLVES TO, which needs no second !important. Custom properties inherit,
        so one declaration covers every pill in the footer.
     b. the label is a child <div class="ms-btn__text"> with its OWN colour from
        `a.btn .ms-btn__text` (palette.css:791, 0,2,1) — it does not inherit, so
        the token alone does not reach it. Targeted directly at (0,5,3). */
body .ms-footer .footer-inner {
  --u-pill-ink: var(--u-on-dark);
  --u-pill-bd:  rgba(240, 238, 235, 0.24);
}
body .ms-footer .footer-inner .social-area li a.btn .ms-btn__text,
body .ms-footer .footer-inner .social-area .btn.btn--primary .ms-btn__text {
  color: var(--u-on-dark);
}

/* the five footer nav links. Incumbent `.ms-footer .container
   .footer-bottom-area .footer-nav-area li a` (0,4,2) at style.css:21087 →
   var(--color-contrast-higher) = #000000. Beaten at (0,4,3). */
body .ms-footer .container .footer-bottom-area .footer-nav-area li a {
  color: var(--u-on-dark);
}

/* the copyright line. Incumbent `.ms-footer .container .copyright-area .inner
   p.top` (0,5,1) at style.css:21147 → #000000. Beaten at (0,5,2). */
body .ms-footer .container .copyright-area .inner p.top {
  color: var(--u-on-dark);
}
/* the footer's owl mark is `fill: currentColor`, so it follows the re-ink for
   free — but work.html's slot ships an <img> instead of the svg, which cannot.
   That is fixed in work.html's markup, not here. */
body .ms-footer .footer-inner .copyright-area .logo-icon { color: var(--u-on-dark); }


/* ==========================================================================
   16 · TYPE — work.html's display heads join the system

   work.html measured only TWO faces (Neue Montreal 51 nodes, Antonio 2) where
   home runs three and services runs three. Both Antonio nodes were in the
   shared footer. In other words: nothing on the work page's own body was set in
   the display face at all. Its page title and all 17 card titles were Neue
   Montreal — a grotesque standing in for a condensed display face.
   ========================================================================== */

/* 16a · the page title. Incumbent `.page-header-content .page-header__title`
   (mokko style.css:12311, specificity 0,2,0) sets font-size 90px / weight 600
   and nothing else, so the face came from `h1..h6 { font-family:
   var(--font-heading) }` at :2334 → Neue Montreal.
   Measured before: Neue Montreal 90px / 600 / ls 0 / lh 108 / #000000 / no
   transform. Every one of those six values is off-system.
   Size stays at 90px: the homepage's equivalent rank (`.content__title`) runs
   129.6px, but that head owns a viewport and this one sits above a filter bar.
   Matching the FACE, weight, tracking, case and ink is the parity fix; matching
   the size would be a layout change. Beaten at (0,2,1) with `body`. */
body .page-header-content .page-header__title {
  font-family: var(--u-display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--u-track);
  line-height: 0.95;
  color: var(--u-ink);          /* was #000000 — MP071 ink is #13181B */
}

/* 16b · the 17 work-card titles. The homepage's equivalent component is
   `.bto-pw-title` — Antonio 700 / uppercase / ls -1.4px / 28.1px. work.html's
   was Neue Montreal 800 / 24px / no transform / ls 0.
   Incumbent is `.portfolio_wrap .filter .item--inner .ms-p-content
   .ms-p-content__inner h3` (style.css:11405, specificity 0,5,1) — it only sets
   padding and text-align, but the sizing comes from Mokko's h3 chain, so this
   is matched at (0,5,2) to be safe against the whole chain.
   Colour is deliberately NOT touched: these sit on photographic card grounds
   and are already #FFFFFF, which is correct on-image ink. */
body .portfolio_wrap .filter .item--inner .ms-p-content .ms-p-content__inner h3,
body .portfolio_wrap .ms-p-content .ms-p-content__inner h3 {
  font-family: var(--u-display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -0.05em;
  line-height: 1.05;
}

/* 16c · the eyebrow / kicker tracking — ONE value for every eyebrow.
   Before this pass the site ran three: `.heading-title` at 0.18em (set by §3 and
   §12c of this file), `.bto-footer-cols .bto-eyebrow` at 0.14em (mokko-bridge
   :410 and :717) and the reference's kickers at 0.2em.
   I first wrote 0.2em here to match the reference. That was wrong: it would have
   left 0.18em and 0.2em BOTH live in this one file — creating a third value
   while claiming to remove one. 0.18em is the value already used for every other
   eyebrow on these pages, and the difference from the reference's 0.2em is
   0.24px at 12px. Consolidating on the value already here is worth more than
   matching the reference's third decimal.
   Incumbent is (0,2,0), so this is matched at (0,2,1) — `body .bto-eyebrow`
   at (0,1,1) lost, verified by re-measuring.
   The FACE is left as Neue Montreal: the reference sets kickers in Antonio, but
   a kicker is chrome and these pages use Neue Montreal for all chrome. Flagged
   as the one open question in the type reconciliation rather than swapped. */
body .bto-footer-cols .bto-eyebrow,
body .bto-banner .bto-eyebrow { letter-spacing: 0.18em; }


/* ==========================================================================
   17 · RADII — collapsing the residuals onto the two-value system
   The system is 40px pills · 28px cards (measured off the reference).
   Before: home 9 distinct values · work 5 · services 4.
   ========================================================================== */

/* 17a · work's card image frames were 32px — the single most-repeated radius on
   the page (17 instances) and not a system value. The equivalent component on
   the homepage (`.bto-pw-card`) is 28px, 34 instances.
   FIRST ATTEMPT LOST THE CASCADE: I wrote `body .portfolio_wrap figure.ms-p-img`
   (0,2,2) against an incumbent of `.portfolio_wrap .portfolio-feed .item--inner
   figure` (0,3,1) at mokko style.css:9228. Caught by re-measuring, not by
   reading. Matched at (0,3,2). */
body .portfolio_wrap .portfolio-feed .item--inner figure { border-radius: 28px; }

/* 17b · work's filter bar was 45px. It is a pill. Pills are 40px.
   Incumbent `.ms-portfolio-filter-area.project .button-group` (0,3,0),
   style.css:10017 — beaten at (0,3,1). */
body .ms-portfolio-filter-area.project .button-group { border-radius: 40px; }

/* 17c · the Engine Room tiles were 16px on a ~300px card — the only 16px on the
   page, and cards are 28px. Its chips were 99px; pills are 40px. Both render
   identically to their system values at these sizes, so this is a token-hygiene
   fix with no visual delta — worth doing because 16px and 99px are what a future
   pass would copy when it adds the next tile. */
body #engine .er3-tile { border-radius: 28px; }
body #engine .er3-tag  { border-radius: 40px; }


/* ==========================================================================
   18 · FOCUS — one visible ring, three pages
   Measured before: the two static pages showed `outline: rgb(67,94,229) solid
   2px` — a BLUE (#435EE5) that appears nowhere in MP071 and nowhere else on the
   site; services showed the browser default (`auto 1px`, which is #005FCC in
   Chrome on the logo). Three treatments, two of them accidental, none of them
   ours.
   One ring, in the ink, with an offset so it clears the element on both light
   and dark grounds. Scoped to :focus-visible so it never appears on a mouse
   click — this ADDS a keyboard affordance rather than taking one away. */
body a:focus-visible,
body button:focus-visible,
body [role="button"]:focus-visible,
body input:focus-visible,
body summary:focus-visible {
  outline: 2px solid var(--u-ink);
  outline-offset: 3px;
}
/* on a dark ground the ink ring is invisible; flip it there. */
body .ms-footer .footer-inner a:focus-visible,
body .ms-footer .footer-inner button:focus-visible,
body #engine a:focus-visible,
body #engine button:focus-visible,
body .ms-menu-wrapper a:focus-visible {
  outline-color: var(--u-on-dark);
}


/* ==========================================================================
   19 · HEADER — the last two rows of the parity table

   19a · THE 10px SCROLL DROP. Measured, header bounding box at 1440:

                    scroll 0            scroll 600 / 1400
       home         [60, 20, 1320, 60]  [60, 30, 1320, 60]
       work         [60, 20, 1320, 60]  [60, 30, 1320, 60]
       services     [60, 20, 1320, 60]  [60, 20, 1320, 60]

   At rest all three are pixel-identical — logo at x80/y28, CTA at x1110, menu
   pill at x1243 with its right edge on 1360. The moment you scroll past 50px,
   main.js adds `.move` to `.main-header__inner` (mokko/assets/js/main.js:246)
   and style.css:7110 translates it down 10px. The React header does not, so a
   visitor moving between pages meets a 10px jump in the one element that is
   supposed to be the constant.

   Killing the transform here rather than reproducing it in React, because:
     · one CSS rule against a scroll listener on a GSAP-pinned page;
     · a persistent nav pill that shifts DOWN when you start scrolling is
       Mokko's quirk, not a decision anyone made for this brand;
     · it makes the header genuinely static at every scroll position on all
       three pages, which is the stronger end state.
   `.move` also sets position/left/right/margin, which are what keep the pill
   centred once scrolled — those are LEFT ALONE. Only the transform goes.
   Incumbent `.main-header .main-header__inner.move` (0,3,0) → beaten at (0,3,1).
   (Its `padding: 0.5rem 1.5rem` never lands — measured padding stays 0px 20px at
   every scroll position, so the 4px horizontal shift it implies does not happen
   and needs no fix. Verified, not assumed.) */
body .main-header .main-header__inner.move { transform: none; }
