/* ==========================================================================
   gaia-2026.css — the Gaia 2026 token layer
   --------------------------------------------------------------------------
   ONE stylesheet. Loaded IMMEDIATELY AFTER main.css on every page. It changes
   no markup, no copy, no number and no label. It re-expresses main.css's
   VISUAL layer — colour, type, spacing, weight, radius, elevation, motion —
   in terms of the published Gaia design system in /css/ds/.

   HOW IT IS BUILT, and why in that order:

   PART 1  imports the design-system TOKEN files only.
   PART 2  REBINDS main.css's own :root custom properties to those tokens.
           This is where most of the restyle happens. main.css already routes
           almost every colour through --teal / --ink / --off / --teal-line,
           so rebinding those 14 properties repaints the whole estate without
           touching a single component selector, and reverting is one <link>.
   PART 3  a deliberately small set of COMPONENT OVERRIDES for the things a
           custom property cannot reach: type scale, weight, letter-spacing,
           line-height, radius, border weight, elevation, hit-target size and
           transition timing. 24 override blocks. Each one is annotated with
           WHY a rebind could not do it. Do not add a 25th without the same
           justification — every selector override is a future conflict.

   ⛔ ds/base.css IS DELIBERATELY NOT IMPORTED. It is an app reset: it sets
      p{margin:0}, a{text-decoration:none} and body{background:surface-page}.
      Loaded after main.css it would collapse every paragraph gap and strip
      every link underline estate-wide. Token files only.

   ⛔ WHAT THIS FILE DOES NOT TOUCH, on purpose:
      - .gs-* (/how-it-works/ scenario animations) — out of scope, and its
        colour + webfont carve-outs are granted rulings.
      - .pv-* product-UI colours (/datamate/, /automate/) — a render of the
        app is not the marketing palette.
      - --wrap (1100px). The DS --container-max is 1200px, but widening the
        reading column at 18px body makes the measure worse, and .masthead's
        1240px override is load-bearing. Left alone.
      - Every layout grid, breakpoint and clip-path in main.css.

   🔒 ACCENT #0FB5A6 STAYS RESERVED: CTAs, focus rings, hover, current-page
      underline, current journey step. This file adds no decorative accent.
   ========================================================================== */

/* ===== PART 1 — design-system tokens ===================================== */
@import url("ds/fonts.css");        /* Archivo, variable 100–900, Google Fonts */
@import url("ds/colors.css");
@import url("ds/typography.css");
@import url("ds/spacing.css");
@import url("ds/radius.css");
@import url("ds/elevation.css");
@import url("ds/motion.css");
@import url("ds/products.css");

/* ===== PART 1b — Archivo, SELF-HOSTED ===================================
   ⚠️ ds/fonts.css @imports Archivo from fonts.googleapis.com. That import is
   left exactly as published and still runs first — but it CANNOT be the only
   source, and this was caught by measurement, not assumed:

     document.fonts.check('600 48px Archivo')  -> true      ← LIES
     width of a string in "Archivo"            -> 560.38px
     width of the same string in a NONSENSE family name -> 560.38px

   Identical. The text was falling back and every screenshot taken before this
   block was added showed the fallback, not Archivo. fonts.check() returns true
   for a family it has never loaded, so it is worthless as evidence. The only
   honest test is to measure a string against a family name that certainly does
   not exist. Do that, not fonts.check().

   This matters far beyond a sandbox. main.css already argues the case, about
   icon fonts: "An icon font renders empty boxes when a corporate network blocks
   the CDN, which is precisely where a bid manager will open this." A Tier 1
   contractor's network blocking fonts.googleapis.com is the same event, and it
   would silently drop the whole estate back to Helvetica with nothing in the
   page to show it had happened.

   So the same variable faces are served from this repo. Same v25 files Google
   serves, same weight range, same unicode subsets, font-display:swap. Declared
   after the @import, so these win when both are available and cover it when the
   CDN does not answer. Paths are relative to /css/, hence ds/fonts/.
   ======================================================================== */
@font-face{font-family:'Archivo';font-style:normal;font-weight:100 900;font-display:swap;
  src:url('ds/fonts/archivo-normal-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Archivo';font-style:normal;font-weight:100 900;font-display:swap;
  src:url('ds/fonts/archivo-normal-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Archivo';font-style:normal;font-weight:100 900;font-display:swap;
  src:url('ds/fonts/archivo-normal-vietnamese.woff2') format('woff2');
  unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB}
@font-face{font-family:'Archivo';font-style:italic;font-weight:100 900;font-display:swap;
  src:url('ds/fonts/archivo-italic-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Archivo';font-style:italic;font-weight:100 900;font-display:swap;
  src:url('ds/fonts/archivo-italic-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Archivo';font-style:italic;font-weight:100 900;font-display:swap;
  src:url('ds/fonts/archivo-italic-vietnamese.woff2') format('woff2');
  unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB}

/* ===== PART 2 — rebind main.css's :root to the design system =============
   Same variable names main.css already uses, so every one of its ~600 rules
   picks these up with no selector fight. Semantic aliases, not raw ramp.
   ======================================================================== */
:root {
  /* Brand. #004650 and #0FB5A6 are unchanged values — rebound for provenance
     so the design system is the single source, not a literal in a stylesheet. */
  --teal:       var(--gaia-teal-700);      /* #004650 — identical */
  --accent:     var(--gaia-accent-500);    /* #0FB5A6 — identical */
  --accent-dark:var(--gaia-accent-600);    /* #0c9689 -> #0D9A8E */

  /* Footer ground goes deeper, not lighter: it should sit UNDER the page,
     and the extra depth buys contrast for the footer's small print. */
  --teal-dark:  var(--gaia-teal-900);      /* #003239 -> #00272D */

  /* Structure. The DS is explicit that structure comes from 1px borders and
     that shadows are near-invisible, so the rule colour becomes a true border
     token rather than a tinted teal line. */
  --teal-line:  var(--border-default);     /* #cfdcde -> #D5DBDA */
  --teal-wash:  var(--surface-brand-tint); /* #eef3f4 -> #F0F5F5 */

  /* Neutrals. The cool teal cast is the look — #1a1a1a was a flat black that
     read cold against the teal chrome. Three steps, not two:
       headings  --text-heading  #1A2B2E   (set in PART 3)
       body      --text-strong   #2A3B3E
       secondary --text-body     #5A6B6E   */
  --ink:        var(--text-strong);        /* #1a1a1a -> #2A3B3E */
  --ink-mid:    var(--text-body);          /* #4a5558 -> #5A6B6E */
  --off:        var(--surface-page);       /* #f7f8f8 -> #F7F8F7 */

  /* ⛔ --white IS DELIBERATELY NOT REBOUND, and this is the one real trap in
     this file. ds/colors.css declares --surface-card:var(--white), so writing
     `--white: var(--surface-card)` is a CYCLE: the property goes invalid, and
     every `color: var(--white)` in main.css — the hero h1, .section--teal, the
     white price card on the teal band, the nav, the footer — falls back to the
     body ink and paints #2A3B3E on #004650. Measured at 1.11:1. This happened
     on the first build. ds/colors.css already declares --white:#FFFFFF and it
     is imported above, so the DS is the source and no rebind is needed.
     Before adding any rebind here, check the DS alias does not point back at
     the main.css name you are rebinding. */

  /* Warning wash used by the modeller's stop panels. */
  --warn-wash:  var(--surface-warning-tint); /* #fff8e6 -> #FDF6EC */
  --warn-line:  var(--warn-500);             /* #e6c86a -> #D97706 */

  /* Grid gap == DS space-7. Same 24px, now tokenised. */
  --gap:        var(--space-7);

  /* Ink used on the accent CTA. Slightly deeper than the old #00312f, which
     lifts the CTA label from 5.54:1 to 5.98:1 on #0FB5A6. */
  --cta-ink:    var(--gaia-teal-900);
}

/* ===== PART 3 — component overrides (24 blocks) =========================
   Each block states why a :root rebind could not reach it.
   ======================================================================== */

/* [1] TYPEFACE. main.css hard-codes the system stack on body with no variable
   to rebind, so this has to be a selector. Everything else inherits. */
body {
  font-family: var(--font-core);
  font-size: 18px;
  line-height: var(--leading-relaxed);
  font-optical-sizing: auto;
  text-rendering: optimizeLegibility;
}

/* [2] HEADINGS. Weight and letter-spacing are literals in main.css, not
   variables. Archivo 700 shouts; 600 is the confident weight. Colour moves
   from brand teal to --text-heading so teal stops being the colour of
   everything and goes back to being the brand: chrome, data and links.
   Specificity is 0,0,1 — .section--teal h2, .hero h1, .pagehead h1 and
   .footer h2 all still win, so nothing on a teal ground changes. */
h1, h2, h3, h4 {
  color: var(--text-heading);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-snug);
}
h1 { line-height: 1.08; }
h4 { font-size: var(--text-lg); letter-spacing: var(--tracking-snug); }

/* [3] LINKS. text-decoration-thickness / underline-offset are literals.
   A 1px rule at a proper offset reads as a reference document, not a
   hyperlink soup. Hover takes the accent — an interaction state, permitted. */
a {
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--duration-fast) var(--ease-standard),
              text-decoration-color var(--duration-fast) var(--ease-standard);
}
a:hover { color: var(--text-link-hover); }

/* [4] LEDE + NOTES. Sizes are clamp() literals. The lede gains measure and
   loses a fraction of weight; notes take the secondary ink. */
.lede { line-height: 1.5; letter-spacing: var(--tracking-snug); }
.note, .small, .credibility, .pv-cap, .photo-cap, .mod-hint,
.cite__source, .rate-tag { line-height: var(--leading-relaxed); }
/* The award credit was set at full 18px body size, which put it level with the
   lede and made the hero read as two competing paragraphs. It is a credit, so
   it takes the label size and sits under the measurement note. Text unchanged. */
.hero__cred { font-size: var(--text-base); line-height: var(--leading-relaxed); }

/* [5] SECTION RHYTHM — "generous" is the brief. main.css sets 2.75rem / 3.75rem
   as literals; the DS ships --section-y (96px) and --section-y-tight (64px)
   for exactly this. No layout, no structure — vertical air only. */
.section { padding: var(--section-y-tight) 0; }
@media (min-width: 700px) { .section { padding: var(--section-y) 0; } }
.hero { padding: var(--space-9) 0 var(--space-11); }
@media (min-width: 900px) { .hero { padding: var(--space-12) 0 var(--space-13); } }
.pagehead { padding: var(--space-9) 0 var(--space-10); }
@media (min-width: 700px) { .pagehead { padding: var(--space-12) 0 var(--space-12); } }

/* [6] RADIUS. Every box in main.css carries `border-radius: 3px` as a literal.
   The DS applied roles are cards 12px, controls 10px. One grouped rule. */
.card, .callout, .callout--price, .formula, .jump, .needed, .gate, .diagram,
.wall__card, .pf-item, .paste-line, .net-block, .band-status, .mod-fieldset,
.shot-needed, .narrow-toggle, fieldset {
  border-radius: var(--radius-card);
}
.btn, .field input, .field select, .field textarea,
.mod-field input, .mod-field select, .gate__row input, .gate__extra input {
  border-radius: var(--radius-control);
}

/* [7] CARDS. The 4px teal top bar was the most dated thing on the estate and
   is a literal border-width. The DS: structure from 1px borders, elevation
   near-invisible. The card becomes a hairline frame with a whisper of shadow
   and the big teal figure does the anchoring it was always meant to do. */
.card {
  border: var(--border-width) solid var(--border-subtle);
  box-shadow: var(--shadow-xs);
  padding: var(--space-7) var(--space-7) var(--space-7);
}
.wall__card, .pf-item, .diagram {
  border-color: var(--border-subtle);
  box-shadow: var(--shadow-xs);
}
/* The lead card keeps every signal of its rank — teal frame, tinted ground,
   larger h3, the solid CTA, the RECOMMENDED flag — at 1px instead of 4px. */
.wall__card--lead { border-top-width: var(--border-width); }

/* [8] LEFT-EDGE RULES. 4px and 5px literals throughout. A 2px rule reads
   measured; a 5px slab reads like a template. --border-width-strong is the
   DS token for exactly this. */
.problem, .callout, .formula, .paste-line, .net-block, .band-status, .cite,
.cite--hero, .mod-warning, .callout--stop {
  border-left-width: var(--border-width-strong);
}
.problem { padding-left: var(--space-6); }
/* The boxed members of that family square off their LEFT corners only, so the
   2px rule reads as a deliberate spine rather than a coloured sliver stranded
   on a 12px radius. This is what keeps the price-callout edge and the
   modeller's unguaranteed-warning edge legible as signals at 2px. */
.callout, .formula, .paste-line, .net-block, .band-status, .mod-warning {
  border-radius: 0 var(--radius-card) var(--radius-card) 0;
}

/* [9] NUMBERS. This is the "people who measure things for a living" detail:
   every published figure gets tabular figures so columns of them line up and
   nothing shifts. Weight drops 700 -> 600 because Archivo at 54px+ does not
   need 700, and the tighter tracking is what makes a big number look drawn
   rather than typed. No value is altered anywhere. */
.figure__pc, .card__pc, .price-line, .result-hero__figure, .net-line,
.guarantee-value, .facts td, .wall__cost, .paste-line__text {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
.figure__pc, .card__pc, .price-line, .result-hero__figure {
  font-weight: var(--weight-semibold);
}
/* -0.04em is right for a standalone figure set at 45–70px ("89%"), where it is
   what makes the number look drawn. .price-line is a whole SENTENCE containing
   a number — "£25 per cabin or bay per week" — and at that tracking the words
   ran together. Sentences get the ordinary tight tracking. */
.figure__pc, .card__pc, .result-hero__figure { letter-spacing: -0.04em; }
.price-line { letter-spacing: var(--tracking-tight); }

/* [10] EYEBROWS / CAPS LABELS. letter-spacing and weight are literals; the DS
   has --tracking-caps for small caps-set labels. 600, not 700 — at 0.8rem
   uppercase, 700 turns into a smear. */
.eyebrow, .card__who, .card dt, .wall__flag, .step-flag, .cite__who,
.paste-line__label, .footer h2, .jump h2, .gp-thin__acts, .gp .act i {
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-caps);
}

/* [11] BUTTONS. min-height, weight, radius and transition are all literals.
   44px is rule 4 — the reader wears gloves. inline-flex so the label centres
   in the taller box without the padding guesswork. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--hit-min);
  padding: var(--space-4) var(--space-7);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-snug);
  color: var(--cta-ink);
  border-width: var(--border-width);
  transition: var(--transition-control);
}
.btn:hover { color: var(--cta-ink); box-shadow: var(--shadow-sm); }
.btn--nav { min-height: var(--hit-min); padding: var(--space-3) var(--space-6); }
.btn--ghost { color: var(--text-brand); }
.btn:active { transform: scale(var(--press-scale)); }
/* Links in running text. WCAG 2.5.5's inline exception covers them, but a
   20px-tall link is still a poor target for a gloved thumb, and some of them
   are real controls sitting alone in their own paragraph ("Read the Swynnerton
   case study").
   ⚠️ `p > a:only-child` was tried first and is WRONG: :only-child counts
   ELEMENT children only, so it also matched links surrounded by text and
   turned them into 44px inline-flex boxes — the hero award credit and the
   pricing line both grew a ragged 44px gap mid-sentence.
   Vertical padding on an INLINE box expands the hit area without touching the
   line box, so leading is unchanged and the target clears 44px either way.
   .btn, .nav a and .footer ul a all set their own padding at higher
   specificity, so none of them is affected. */
/* ⛔ REVERTED 5 Aug. padding-block:15px made the padded box ~11px TALLER THAN THE
   LINE BOX on each side (measured: 47px box in a 24.3px line), so an inline link
   claimed hit points on the body text ABOVE it — the gate hit-tested it and a
   gloved tap on the line above navigated away. 11 links on the homepage.
   That is worse than a small target: it is a wrong target.
   WCAG 2.5.5 exempts links in running text for exactly this reason. So inline
   links keep their natural box and get a visible, easier-to-hit affordance
   instead — a thicker underline with a real offset. Standalone action links that
   sit alone in a paragraph are the real controls, and THOSE get the height. */
p > a, dd > a, td > a, li > a, span > a, strong > a, blockquote a {
  text-underline-offset: 0.18em;
  text-decoration-thickness: 0.08em;
}
/* ⛔⛔ WITHDRAWN 6 Aug 2026, AND THE COMMENT THAT USED TO BE HERE STATED ITS OWN BUG
   AS ITS FIX. It read: ":only-of-type plus first+last child is the honest test —
   :only-child counts ELEMENT children only and matched links surrounded by text,
   which is the bug this replaces."
   ⛔ `:first-child` AND `:last-child` COUNT ELEMENT CHILDREN TOO. So the replacement
   matched exactly the same wrong links as the thing it replaced, and the comment
   asserted otherwise for two days. Found by an adversarial reviewer chasing an
   unrelated footer measurement, not by anything that was looking for it.

   MEASURED ACROSS 19 PAGES AT 375px, BEFORE REMOVING IT:
       494  links genuinely alone in their parent   -> 44px, correct
        54  links MID-SENTENCE, wrongly matched     -> 44px atomic box in a sentence
        51  of those 54 had a box reaching >=6px ABOVE the text ink
   The bleed measured 11–14px on a 15–20px ink height. Real examples, each a live
   defect: "or put DataMate on one of your sites, free" on / and /drying-room/ ·
   "PlantWorx Innovation Awards 2025" mid-sentence in the homepage award credit ·
   "info@gaiagroupuk.com" at the end of the legal address, on EVERY page ·
   eleven of them on /automate/ alone.
   ⚠️ This is the identical defect the note above records as REVERTED on 5 Aug —
   "a 47px box in a 24.3px line, so an inline link claimed hit points on the body
   text ABOVE it... worse than a small target: it is a wrong target." It was
   reverted in one form and reintroduced in another, in the same block, under a
   comment explaining why it must not be.

   ⛔ NO CSS SELECTOR CAN EXPRESS "ALONE IN ITS PARAGRAPH, TEXT NODES INCLUDED."
   :only-child, :only-of-type, :first-child, :last-child and :has() all see
   ELEMENTS. So this cannot be fixed by finding a cleverer selector — every
   attempt is the same bug again. The 44px goes to the places where being a
   standalone control is guaranteed by the CLASS CONTRACT, listed below, and
   inline links keep their natural box plus the thicker underline affordance
   above. WCAG 2.5.8 exempts a target "in a sentence", which is precisely these.
   ⭐ If a standalone action link ever needs the height and has no class, give it
   one in the markup. An opt-in in the HTML is honest; a selector that guesses is
   not. */
/* ⚠️ AND THEN THE WITHDRAWAL ITSELF WAS TOO BROAD. Measured, same 19 pages, the
   two selectors SEPARATELY instead of together — which is what should have been
   done in the first place, because "p or li" was never one population:

       p  > a:only-of-type:first-child:last-child    79 matches
              genuinely alone  27  (34%)
              MID-SENTENCE     52  (66%)     <- the rule is simply wrong here
       li > a:only-of-type:first-child:last-child   443 matches
              genuinely alone 443 (100%)
              MID-SENTENCE      0   (0%)     <- the rule is exactly right here

   ⭐ A list item whose only content is a link IS a standalone control — that is
   what a list of links is. A paragraph is where prose lives, and two thirds of
   the paragraph matches were a link sitting inside a sentence. So the fix is to
   keep the rule for `li` and drop it for `p`, not to drop both.
   ⛔ Dropping both cost eight real targets: the four document links on
   /technical/, the three "Read the ... case study" links, and the /questions/
   jump list all fell to 20–21px against the 24px minimum, and they are exactly
   the standalone controls the rule was written for. That is the cost of treating
   two different populations as one number.
   ⚠️ `p` KEEPS the thicker-underline affordance above and nothing else. WCAG
   2.5.8 exempts a target "in a sentence", which is what 66% of them are, and a
   44px box on the other 34% is not worth 52 tap-stealing boxes. If one of those
   27 needs the height, give it `.btn` or wrap it in an `li`. */
/* Measured the same way before adding it, on 14 pages: `h3 > a:only-of-type:
   first-child:last-child` returns 4 matches, 100% genuinely alone, 0 mid-sentence
   (h2, h4 and dt return none at all). A heading whose entire content is a link is
   a standalone control by definition — these are the four document cards on
   /technical/, which were sitting at 20-21px against the 24px minimum. */
h2 > a:only-of-type:first-child:last-child,
h3 > a:only-of-type:first-child:last-child,
h4 > a:only-of-type:first-child:last-child,
li > a:only-of-type:first-child:last-child,
.btn-row > a,
.wall__card > a,
.card > a:only-of-type:first-child:last-child {
  display: inline-flex;
  align-items: center;
  min-height: var(--hit-min);
}

/* [12] NAV. Two jobs at once. (a) 44px hit height — main.css leaves the
   anchors ~24px tall. (b) the current-page marker moves from a border-bottom
   to a text-decoration so it still hugs the baseline inside a 44px box
   instead of floating 10px under the word. Accent stays the position colour. */
.nav a {
  display: inline-flex;
  align-items: center;
  min-height: var(--hit-min);
  padding: 0;
  border-bottom: 0;
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  text-decoration: none;
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}
.nav a:hover, .nav a[aria-current="page"] {
  text-decoration: underline;
  text-decoration-color: var(--gaia-accent-500);
}
.subnav a { display: inline-flex; align-items: center; min-height: var(--hit-min); }

/* [13] BRAND LOCKUPS. Both are ~22–35px tall in main.css; rule 4 again. The
   masthead loses a little padding so the taller row does not push it about. */
.brand, .footer__brand { min-height: var(--hit-min); align-items: center; }
.masthead { padding: var(--space-4) 0; }

/* [14] FOOTER LINK LISTS. 26px rows in main.css. Padding on the anchor, the
   li margin removed so the list gets taller by the hit area only. */
.footer ul a {
  display: inline-flex;
  align-items: center;
  min-height: var(--hit-min);
  padding-block: 0;   /* the inline-link padding above is redundant here and
                         only made the footer taller than the 44px it needs */
  text-decoration: none;
}
.footer ul a:hover, .footer ul a:focus-visible { text-decoration: underline; }
.footer li { margin-bottom: 0; }
.footer { padding: var(--space-11) 0 var(--space-10); }

/* [15] FORM CONTROLS. --field-height (44px) is a DS token; main.css sets
   padding only, which lands the fields at ~40px. */
.field input, .field select, .field textarea,
.mod-field input[type="number"], .mod-field input[type="email"],
.mod-field select, .gate__row input, .gate__extra input {
  min-height: var(--field-height);
  border-color: var(--border-strong);
  border-width: var(--border-width);
  transition: var(--transition-control);
}
summary, .narrow-toggle { min-height: var(--hit-min); }

/* [16] FOCUS. main.css uses 3px outlines in three different colours. One
   ring, accent, everywhere — a permitted accent use and the single clearest
   signal a gloved thumb-tabbing reader gets. outline (not box-shadow) so it
   survives the clip-path chevrons. */
a:focus-visible, button:focus-visible, summary:focus-visible,
input:focus-visible, select:focus-visible, textarea:focus-visible,
[tabindex]:focus-visible {
  outline: var(--border-width-strong) solid var(--gaia-accent-500);
  outline-offset: var(--focus-offset);
}

/* [17] JOURNEY BAR + THIN STEPPER. main.css hard-codes #F4F7F7 / #C6D6D6 /
   #5E7376 / #9AAEB0 as literals inside these components, so they cannot be
   reached by a rebind. #9AAEB0 on #F4F7F7 measured 2.15:1 — an AA failure on
   the em-dash and the middot separators; --text-body takes them to 5.3:1. */
.gp, .gp-thin, .gp-chev { background: var(--surface-brand-tint); }
.gp { border-bottom-color: var(--border-default); }
.gp-thin, .gp-chev {
  border-top-color: var(--border-default);
  border-bottom-color: var(--border-default);
}
.gp-thin__line, .gp-lede, .gp .act u { color: var(--text-body); }
.gp-thin__dash, .gp-thin__sep { color: var(--text-body); }
.gp-thin__acts, .gp-thin__status strong, .gp-thin a { color: var(--text-brand); }
.gp-thin { padding: var(--space-4) 0; }

/* [18] JOURNEY CHEVRONS. The muted chevron fill and ink are component-local
   literals; these are the DS equivalents. The current step keeps the accent
   (a position state, rule 5) and the next step keeps brand teal. */
.gp { --gp-mute: var(--ink-150); --gp-muteink: var(--text-body); --gp-edge: var(--border-default); }
.gp-chev { --gpc-mute: var(--ink-150); --gpc-muteink: var(--text-body); --gpc-edge: var(--border-default); }
.gp .pl .now, .gp-chev__item--current .gp-chev__link { color: var(--cta-ink); }

/* [19] ELEVATION. main.css's one big shadow (the portal render) is a literal
   0 18px 44px. The DS caps elevation at --shadow-lg on purpose. */
.pv { box-shadow: var(--shadow-lg); border-color: var(--border-subtle); }

/* [20] TABLES. Border colour comes through --teal-line already; what a rebind
   cannot do is give a fact table the room and the tabular figures that make
   it readable as a reference. */
.facts th, .facts td { padding: var(--space-4) var(--space-4); }
.facts th { font-weight: var(--weight-semibold); color: var(--text-heading); }
.namelist li { padding: var(--space-4) 0; }

/* [21] CALLOUTS + WASHES. Padding literals; the callout is the page's
   confidence block and was cramped. */
.callout, .gate, .jump, .paste-line, .net-block { padding: var(--space-7); }
.formula { padding: var(--space-5) var(--space-6); font-weight: var(--weight-medium); }

/* [22] MOTION. main.css uses bare .2s / .25s transitions. Rebound to the DS
   duration and easing so state changes feel like one product. No entrance
   animation is added anywhere — 03_MARKETING names that as a template signal. */
.gp .pl a, .gp-chev__link, .btn, .card, .wall__card, .pf-item,
.narrow-toggle, .gp-sum::after {
  transition-duration: var(--duration-fast);
  transition-timing-function: var(--ease-standard);
}

/* [23] REDUCED MOTION. ds/motion.css zeroes the duration tokens, which covers
   everything above; this kills the transform on :active and any main.css
   transition that still carries a literal duration. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
  .btn:active { transform: none; }
}

/* [24] SELECTION + PRINT-ADJACENT POLISH. ::selection cannot be reached by a
   rebind because main.css never sets it. Small, and it is the kind of thing
   a quantity surveyor notices when he drags across a figure to copy it. */
::selection { background: var(--gaia-accent-100); color: var(--gaia-teal-900); }

/* ==========================================================================
   PART 4 — WHITE MASTHEAD AND WHITE HERO           RULED BY JAMIE, 5 Aug 2026
   ==========================================================================
   "Yeah, go white."

   The foundation agent flagged this as the one thing it would not decide alone,
   because it is brand identity, not token mapping. Jamie ruled it. This is the
   single most visible change on the estate and it is what makes the site match
   the Claude Design explorations he chose - light, white, generous, confident.

   ⛔ THE GUARANTEE BAND STAYS TEAL. Only the masthead and the hero go white.
   .section--teal is deliberately untouched: it is the one dark panel Jamie did
   not reject (harvest screenshot 13), it gives the page its only contrast beat,
   and D216 (1) puts the guarantee promise top and prominent. A white masthead,
   white hero, then one deep teal band reads as deliberate. All-white reads as
   unfinished.

   ⚠️ THE TEAL-ON-TEAL TRAP INVERTS INTO A WHITE-ON-WHITE TRAP. main.css sets
   colour: var(--white) on .hero h1, .brand, .nav a, .hero__cred a, .hero__alt a
   and .figure__pc because the ground used to be #004650. Every one of those is
   now white on white - invisible - so each is re-pointed here. DESIGN_BRIEF §5
   records the original trap; this is the same trap with the ground reversed.
   ========================================================================== */

/* [25] THE MASTHEAD. White ground, a hairline rule to separate it from the
   page, and the brand in ink. The wordmark and roundel both paint with
   currentColor (main.css:1618/:1623), so they follow the brand colour and no
   SVG needs touching. */
.masthead {
  background: var(--white);
  color: var(--text-heading);
  border-bottom: 1px solid var(--border-subtle);
}
.brand { color: var(--gaia-teal-700); }
.brand:hover { color: var(--gaia-teal-900); }

/* Nav links: ink, not white. The accent underline on the current page is kept -
   D191 permits accent for position state, and it is the only thing telling a
   reader where they are. */
.nav a { color: var(--text-body); }
.nav a:hover { color: var(--text-heading); border-bottom-color: var(--gaia-accent-500); }
.nav a[aria-current="page"] {
  color: var(--text-heading);
  border-bottom-color: var(--gaia-accent-500);
}

/* [26] THE MASTHEAD CTA GOES GHOST — D214 (1), applied at last.
   On a teal ground the solid accent button read as furniture. On white it would
   read as the page's primary action and compete with the hero's own CTA on all
   22 pages. D214 ruled it should be subordinate; main.css:204 only ever set
   padding and font-size, so it inherited the solid .btn fill. Fixed here. */
.masthead .btn--nav {
  background: transparent;
  color: var(--gaia-teal-700);
  border: 1.5px solid var(--gaia-teal-700);
}
.masthead .btn--nav:hover {
  background: var(--gaia-teal-700);
  border-color: var(--gaia-teal-700);
  color: var(--white);
}

/* [27] THE HERO. White ground, ink headline. The h1 keeps its size and weight
   from the type layer; only the colour changes. */
.hero { background: var(--white); color: var(--text-body); }
.hero h1 { color: var(--text-heading); }
.hero .lede { color: var(--text-body); }
/* ⚠️ --text-muted (#7C8B8D) is 3.54:1 on white and FAILS AA. The gate caught four
   instances: .hero__alt, the three .figure__label provenance blocks, .note and
   .hero__cred. --text-body (#5A6B6E) is 5.58:1 and passes. ⛔ Do not use
   --text-muted for anything a reader has to read on white — it is for a
   disabled state or a decorative rule, and the token name does not say so. */
.hero .note { color: var(--text-body); }
.hero .note strong { color: var(--text-strong); }
.hero .eyebrow { color: var(--gaia-accent-700); }

/* The subordinate route and the award credit. Both were white-on-teal links.
   Teal on white, underline kept from the global a{} rule, so they still read as
   links and still read as subordinate to the button above them (rule 17). */
/* ⛔⛔ THESE FOUR RULES WERE UNSCOPED AND IT WAS THE WORST BUG OF THE DAY.
   Written for the white hero, they applied to EVERY .hero__alt and .hero__cred on
   the estate — and 20 pages still carry a deep-teal .pagehead. On /pricing/,
   /automate/, /case-studies/ and its three studies, and /drying-room/ the link
   "put DataMate on one of your sites, free" rendered #004650 on #004650:
   1.00:1. LITERALLY INVISIBLE, down from 10.54:1. Found by the gate, on all
   seven, by measuring rather than by looking.
   ⭐ AND NOTE WHAT IT IS: the exact mirror of the white-on-white trap recorded
   two hours earlier in the same session. A colour written for one ground,
   applied globally, breaks on every other ground — and breaks SILENTLY, because
   nothing throws and the text is simply not there. Scoped to .hero, which is
   the only place it was ever tested. */
.hero .hero__alt { color: var(--text-body); }
.hero .hero__alt a, .hero .hero__cred a { color: var(--gaia-teal-700); }
.hero .hero__alt a:hover, .hero .hero__cred a:hover { color: var(--gaia-accent-600); }
.hero .hero__cred { color: var(--text-body); }

/* [28] THE PROOF FIGURES. 89% / 65% / 57% were white on teal. Deep teal on
   white is the strongest thing the page has and it now anchors the hero the way
   the result cards anchor the page below. The rule above them was a translucent
   white line, which is invisible on white - it becomes a real hairline. */
.hero .figures { border-top-color: var(--border-default); }
.hero .figure__pc { color: var(--gaia-teal-700); }
.hero .figure__label { color: var(--text-body); }   /* was --text-muted: 3.54:1, AA fail */

/* Ghost buttons inside the hero: main.css only styles .section--teal .btn--ghost
   for a dark ground, so the hero's ghost button was already correct on light -
   left alone deliberately. */

/* [29] MOBILE MASTHEAD — the thing the foundation agent said got WORSE.
   Ten nav links at 44px wrapped to three rows and took the header from 221px to
   279px, which is a third of a phone screen gone before the reader sees
   anything. Jamie's standing instruction of 5 Aug ("if there are any other
   decisions like that, just go with it") covers this.
   No HTML, no JavaScript: the nav becomes ONE horizontally scrollable row with
   its own scroll-snap. Every link stays reachable, stays 44px, and stays in the
   DOM for a crawler. */
@media (max-width: 899px) {
  /* ⚠️ FIRST ATTEMPT FAILED AND THE FAILURE IS WORTH KEEPING. Putting brand, nav
     and CTA on ONE nowrap row collapsed the nav to 0px wide (scrollWidth 707,
     clientWidth 0): brand 134px + CTA 210px + gaps already exceeds 375, so
     flex:1 1 auto had nothing left to take. Every nav link vanished from the
     phone. Measured, not guessed.
     So the nav gets its OWN row and scrolls sideways in it. Two rows at ~110px
     total, against 279px for the three wrapped rows this replaces. */
  .masthead__inner { flex-wrap: wrap; gap: 0.4rem 0.5rem; }
  /* ⚠️ margin-right:auto (main.css:159) ate the whole free row and pushed the CTA
     onto a line of its own - three rows again. Brand 134 + CTA 210 = 344, which
     fits inside 375, so they only need the auto margin gone. */
  .brand { flex: 0 1 auto; margin-right: 0.4rem; font-size: 1.1rem; }
  .masthead .btn--nav { order: 2; padding: 0.5rem 0.7rem; font-size: 0.86rem; }
  /* ⛔ THE HORIZONTAL SCROLLER IS REVERTED. It got the header to 119px by hiding
     SIX OF TEN NAV LINKS behind a sideways scroll with no affordance, and the
     last visible label was cut mid-word ("...Results A"). The gate called it
     what it is: a content-hiding change dressed up as a restyle. A tall header
     is a nuisance; an invisible navigation is a defect.
     ⭐ Jamie's standing instruction covers judgement, not hiding his own
     navigation — so this takes the honest route instead: every link visible,
     wrapped, but small and tight. The 44px comes from padding on the INLINE box
     (same technique as the inline links above), which grows the hit area without
     growing the line box — so ten links fit two rows instead of three. */
  .masthead .nav {
    flex: 1 0 100%;
    order: 3;
    min-width: 0;
  }
  .masthead .nav ul {
    flex-wrap: wrap;
    gap: 0.1rem 0.85rem;
    row-gap: 0.1rem;
  }
  .masthead .nav a {
    display: inline;
    min-height: 0;
    padding-block: 0.55rem;     /* 44px hit area, 0 added line height */
    font-size: 0.9rem;
  }
  .masthead .btn--nav { flex: 0 0 auto; }
}

/* ==========================================================================
   PART 5 — TWO THINGS THE SECOND GATE FOUND. Both are real; both are fixed here.
   ==========================================================================
   [30] THE ACCENT CTA LABEL WAS WHITE ON #0FB5A6 = 2.57:1 ON /automate/.
   main.css:109 sets `.section--teal a { color: var(--white) }` to rescue links on
   a teal ground. `.btn` is an `<a>`, so inside any teal-ish section it inherited
   WHITE and lost to the accent fill — 2.57:1 on a PRIMARY call to action, on the
   paid-product page. The homepage measured 6.16:1 because its accent button is
   not inside such a section, so the earlier "6.16:1" claim was true and
   incomplete at the same time.
   ⭐ The general shape, for the third time today: a colour set defensively for
   one ground leaks into a component that carries its own ground. --cta-ink wins
   here at any nesting depth, because a CTA's label colour is a property of the
   CTA, never of what it happens to be sitting on. */
.btn:not(.btn--ghost),
.section--teal .btn:not(.btn--ghost),
.section--alt .btn:not(.btn--ghost),
.pagehead .btn:not(.btn--ghost),
.hero .btn:not(.btn--ghost) { color: var(--cta-ink); }
.btn:not(.btn--ghost):hover,
.section--teal .btn:not(.btn--ghost):hover,
.section--alt .btn:not(.btn--ghost):hover,
.pagehead .btn:not(.btn--ghost):hover,
.hero .btn:not(.btn--ghost):hover { color: var(--cta-ink); }

/* ⛔⛔ THE COMMENT THAT USED TO SIT HERE SAID "ghost buttons on a dark ground keep
   white ... Not touched." IT WAS FALSE, and it is the sixth instance today of a
   colour written for one ground reaching another.
   A ghost button carries BOTH classes, so `.section--teal .btn` above matched it.
   Against main.css:213 `.section--teal .btn--ghost { color: var(--white) }` the
   specificity is IDENTICAL — 0,2,0 either way — so the later file simply won, and
   the label became --cta-ink #00272D on #004650. MEASURED 1.5:1. Invisible.
   ⚠️ It was live, not theoretical: the estate verifier found exactly this pair on
   /how-it-works/ before that page was moved to gaia-base.css, and it bit again the
   moment a ghost Modeller button was added to /automate/'s teal price section.
   Two fixes, because one is not enough:
     · `:not(.btn--ghost)` above, so the solid-CTA rule can no longer reach a ghost
       at all. A ghost button has no fill, so it never had the problem [30] fixes.
     · the explicit pair below, at higher specificity than either file's version, so
       a future edit to main.css cannot silently take it back.
   #FFFFFF on #004650 = 10.54:1. The border is the same white, so the button reads
   as a button and not as a floating label. */
/* ⛔⛔ SEVENTH INSTANCE, AND THE REASON THIS RULE NOW NAMES EVERY DARK GROUND AT ONCE.
   The fix above originally covered `.section--teal` ALONE. Within the hour a ghost
   button was added to the teal `.pagehead` on /for-cabin-suppliers/ and measured
   1:1 — #004650 on #004650, perfectly invisible, the same defect in a different
   container. Fixing one ground at a time is how this bug has survived seven times
   in one day: each fix is correct and each is too narrow, so the next container
   re-opens it.
   ⭐ SO THE RULE IS: enumerate every dark ground the estate has, in one place, and
   let a new dark container be a deliberate addition to THIS LIST rather than a
   silent re-run of the same bug. The estate's dark grounds are exactly:
       .section--teal   #004650      .pagehead        #004650
       .footer          #00272D      .section--dark   (reserved, same treatment)
   ⚠️ `.hero` is NOT in this list. It went WHITE on 5 Aug (D225) and a white label
   on it would be the mirror-image defect — that is the white-on-white trap, and it
   is why this list is grounds and not containers.
   #FFFFFF on #004650 = 10.54:1 MEASURED · on #00272D = 15.81:1. */
.section--teal a.btn.btn--ghost,
.section--teal .btn.btn--ghost,
.pagehead a.btn.btn--ghost,
.pagehead .btn.btn--ghost,
.footer a.btn.btn--ghost,
.footer .btn.btn--ghost,
.section--dark a.btn.btn--ghost,
.section--dark .btn.btn--ghost {
  color: var(--white, #fff);
  border-color: var(--white, #fff);
  background: transparent;
}
.section--teal a.btn.btn--ghost:hover,
.section--teal .btn.btn--ghost:hover,
.pagehead a.btn.btn--ghost:hover,
.pagehead .btn.btn--ghost:hover,
.footer a.btn.btn--ghost:hover,
.footer .btn.btn--ghost:hover,
.section--dark a.btn.btn--ghost:hover,
.section--dark .btn.btn--ghost:hover {
  background: var(--white, #fff);
  color: var(--gaia-teal-700, #004650);
  border-color: var(--white, #fff);
}

/* [31] TEXT ON A DARK GROUND. The eyebrow on a teal .pagehead is 1.89:1 and it
   exists on TWELVE pages. It was 1.37:1 before, so the layer improved it and
   left it failing.
   ⛔ AND THE FAILURE PROTOCOL IN §8.6 WOULD HAVE MADE IT WORSE: the table names
   only dark tokens, and this element is weight 600, so it sent the agent to
   --text-heading (#1A2B2E on #004650 = 1.37:1) — a WORSE ratio than the defect,
   which the agent would have reported as complied. Fixed at source here so no
   agent has to decide it. */
/* ⛔ `.hero .eyebrow` WAS IN THIS SELECTOR LIST AND IT WAS WRONG. The hero is
   WHITE now, so #7BD8CF on it is 1.55:1. It also contradicted block [27]'s own
   `.hero .eyebrow { --gaia-accent-700 }` at equal specificity, and being later in
   the file it won. Dead code today because no page has an eyebrow inside a .hero
   — but it is a loaded gun, and it broke this file's own rule that every colour
   rule is scoped to the ground it was tested on. Removed. */
.pagehead .eyebrow,
.section--teal .eyebrow { color: var(--gaia-accent-300); }   /* #7BD8CF on #004650 = 5.3:1 */
.pagehead .note,
.pagehead .lede { color: var(--dark-body, #B9D3D5); }   /* 6.7:1 on #004650 */

/* ⛔⛔ THE THIRD GATE'S FAIL, AND IT IS THE FOURTH TIME TODAY THIS SHAPE HAS BITTEN.
   main.css:74 sets `.lede { color: var(--ink) }`. PART 2 rebinds --ink to
   --text-strong (#2A3B3E). On a #004650 ground that is 1.11:1 — INVISIBLE TEXT,
   live on /datamate/, /awards/, /for-cabin-suppliers/ and /modeller-v2/. The
   pristine page was 1.65:1, so the layer made an already-failing element WORSE.
   The layer had scoped `.pagehead .lede` and `.hero .lede` to light tokens and
   simply never thought about the third dark ground. #DCEBEC on #004650 = 8.6:1. */
.section--teal .lede,
.section--teal p.lede { color: var(--dark-lede, #DCEBEC); }
.section--teal .note { color: var(--dark-body, #B9D3D5); }

/* =========================================================================
   PART 6 — THE HERO GUARANTEE STRIP        was gaia-hero-promise.css
   D216 ①, ruled by Jamie 5 Aug 2026: "the guarantee needs to be top and huge."
   Folded in here as promised. The separate file existed only because a gate was
   mid-review of this one, and a holding pattern that outlives its reason is just
   a second place to look. Both satellite files and both <link> tags are gone.
   ========================================================================= */
/* ⚠️ TIGHTENED. This strip is the FIRST thing on the page, above the
   demonstrator, so every pixel it takes is a pixel the demonstrator loses from
   the first screen (the D215 / D216 ① tension, recorded in index.html). It is a
   band, not a section: padding is deliberately mean. */
.hero-promise {
  background: var(--surface-brand, #004650);
  color: #fff;
  padding: clamp(0.95rem, 1.9vw, 1.5rem) 0 clamp(1rem, 2vw, 1.6rem);
}
.hero-promise__eyebrow {
  margin: 0 0 0.55rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  /* 5.3:1 on #004650 - the accent tint, not the accent itself, because #0FB5A6
     on #004650 is 2.6:1 and would fail AA at this size. */
  color: #7BD8CF;
}
/* "Huge" is the instruction. It is the largest type on the page after the H1,
   and it is deliberately NOT larger than the H1 - two competing display sizes
   in the first screen is how a page stops having a hierarchy. */
.hero-promise__big {
  margin: 0;
  max-width: 30ch;
  font-size: clamp(1.35rem, 3.5vw, 2.15rem);
  line-height: 1.12;
  letter-spacing: -0.024em;
  font-weight: 500;
  color: #fff;
}
.hero-promise__big strong { font-weight: 700; }
.hero-promise__cond {
  margin: 0.7rem 0 0;
  max-width: 62ch;
  font-size: 0.95rem;
  line-height: 1.55;
  /* #B9D3D5 on #004650 is 6.7:1. NOT #7C8B8D, which is 2.98:1 here - the exact
     pair the design gate failed this morning. */
  color: #B9D3D5;
}
.hero-promise__cond strong { color: #fff; font-weight: 600; }

@media (min-width: 900px) {
  .hero-promise .wrap { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: 0 clamp(2rem, 4vw, 4rem); align-items: end; }
  .hero-promise__eyebrow { grid-column: 1 / -1; }
  .hero-promise__cond { margin-top: 0; padding-bottom: 0.3rem; }
}

/* =========================================================================
   PART 7 — THE /pricing/ PRICE PAGEHEAD    was gaia-price-block.css
   From harvest screenshot 13. Same fold, same reason.
   ========================================================================= */
/* The headline now carries the price AND the guarantee, so it is long. It needs a
   tighter measure and a smaller top size than a normal pagehead h1, or it runs to
   five lines on a laptop and stops reading as a headline. */
.pagehead--price h1 {
  max-width: 34ch;
  font-size: clamp(1.55rem, 3.4vw, 2.5rem);
  line-height: 1.14;
  letter-spacing: -0.026em;
}
.pagehead--price .price-sub {
  max-width: 60ch;
  margin: 1.05rem 0 0;
  font-size: 1rem;
  line-height: 1.6;
  /* #B9D3D5 on the pagehead's #004650 is 6.7:1. ⛔ NOT #7C8B8D, which is 2.98:1
     on this ground - the exact pair the design gate failed this morning. */
  color: #B9D3D5;
}
.pagehead--price .price-sub strong { color: #fff; font-weight: 600; }
.pagehead--price .note--tight { color: #B9D3D5; }

/* =========================================================================
   PART 8 — TWO THINGS THE ESTATE VERIFIER MEASURED ON 25 PAGES
   Both are pre-existing (the blame pass confirms the layer neither caused nor
   worsened either). Fixed here because they are cheap and honest.
   ========================================================================= */

/* [32] RADIO AND CHECKBOX HIT AREA — 18×18px measured on /modeller/ and
   /modeller-v2/ at 375px. WCAG 2.5.8 asks for 24. These are the power-source
   choices on a tool a quantity surveyor uses on site, on a phone, one-handed,
   so this is a real miss rather than a technicality.
   ⚠️ min-width/min-height, NOT padding. A padded inline control grows its box
   OVER the line above it and steals that line's taps — measured earlier today
   on inline links (a 47px box inside a 24.3px line) and reverted. accent-color
   is set so the checked state is the brand teal rather than the browser blue,
   which is the only visual change here. */
input[type="radio"],
input[type="checkbox"] {
  min-width: 24px;
  min-height: 24px;
  accent-color: var(--gaia-teal-700, #004650);
}

/* ⛔ NOT FIXED HERE, AND THE REASON IS RECORDED SO NOBODY "FIXES" IT BADLY:
   eleven text links across seven pages measure 20–21px tall against the same
   24px minimum. Every one of them is a link sitting on a line with other links
   and a separator, e.g. /automate/:
       <p><a>The full wiring scheme…</a> &middot; <a>Circuit labelling</a></p>
   They are 180–316px WIDE, so only the height falls short, by 3–4px. The two
   ways to close that both cost more than they buy:
     · padding — creates the overlapping box described above. Already reverted once.
     · line-height — would have to go to about 1.6 on body copy estate-wide to
       reach 24px, and to 2.2 on the small print of /info-pack/, which changes
       the leading of every paragraph on the site to satisfy a 3px shortfall.
   ✅ RULED BY JAMIE, 7 Aug 2026: ACCEPTED AS IS, FOR NOW. "Let's just accept that
   for now with regards to how tall the PX is."
   The current figure is 55 focusable targets across 16 pages at 16–21px against
   the 24px minimum — larger than the eleven above because the over-broad hit rule
   in block [11] was withdrawn the same day (see its note), which was the right
   trade: it removed 51 targets that were actively STEALING taps from the line
   above, and a wrong target is worse than a small one.
   ⛔ ACCEPTED IS NOT FIXED. This stays on the board. The honest fix is an opt-in
   class in the markup on the links that are genuinely standalone — no selector can
   find them, and every attempt to guess has reintroduced the overlap. */

/* =========================================================================
   PART 12 — THE FOOTER LEGAL ADDRESS, from the adversarial review of PART 10
   The footer patch blamed +22.67px on ".footer__legal" on Archivo being wider
   than the fallback and called it unrecoverable. ⛔ THAT WAS FALSE and the
   reviewer disproved it by measurement: BOTH states load Archivo, both compute
   13.76px/21.33px, and the paragraph's natural nowrap width is 1005.58px in
   both — identical to two decimal places.
   The real cause was block [11]'s withdrawn rule turning the trailing
   "info@gaiagroupuk.com" into a 142.8 x 44.0 atomic box mid-sentence, which
   cannot line-break and forced a fourth line. 86.66 - 63.98 = 22.68px, the
   whole of the figure that was attributed to the typeface.
   ⚠️ Block [11]'s withdrawal above already fixes this, so this block is a BELT
   AND BRACES: if anyone reinstates a generic hit rule, the footer's legal
   address is the one place where the consequence is a whole extra line rather
   than a few stolen taps, and it is on every page.
   Specificity note from the reviewer, who lost a run to it: a plain
   `.footer__legal p > a` is (0,1,2) and LOSES to the (0,4,1) rule it corrects.
   ========================================================================= */
.footer__legal p > a:only-of-type:first-child:last-child {
  display: inline;
  min-height: 0;
}


/* ===== applied 6 Aug: landed as PART 13 / block [36]; the agent wrote "PART 9"
   because three agents measured in parallel against a growing file. Selectors do not
   intersect (.masthead* / .footer* / .gp-thin*) and each only needs to be LATER in the
   file than the block it overrides, which appending satisfies. ===== */
/* =========================================================================
   PART 13 — THE MOBILE MASTHEAD, THIRD PASS.   210.6px -> 153px at 375.
   Measured 6 Aug 2026 in Chromium at 320 / 360 / 375 / 390 / 412 / 430 / 599
   / 768 / 898 / 900 / 1400, on / , /pricing/ , /automate/ , /modeller/ ,
   /case-studies/ and /for-cabin-suppliers/.
   =========================================================================
   §9.5 still lists this as "211px of three undifferentiated rows … the weakest
   thing on a phone". It is a fifth of the screen spent on navigation, directly
   above a demonstrator whose whole point is to be the first thing seen.

   ⛔ ALL TEN LINKS STAY VISIBLE, IN THE DOM, AND REACHABLE WITHOUT A GESTURE.
   The horizontal scroller was reverted for hiding six of ten (§8.4) and nothing
   here brings it back: this block sets no display, no overflow, no visibility,
   no max-height and no content. It changes four numbers - outer padding, two
   gaps, and the label size - and it wins one whole 44px row back by making ten
   labels FIT in two rows instead of nearly-fitting in three.

   WHAT THE OLD 210.6px WAS MADE OF, AT 375, ALL MEASURED:
      12.0  .masthead padding-top                        (--space-4, block [13])
      44.0  row 1: brand 122.0x44 + ghost CTA 168.2x44, 8px apart.
            40.2px of that row is empty and cannot take a nav link.
       6.4  .masthead__inner row-gap                     (0.4rem, block [29])
     132.0  the nav: THREE 44px rows, 4 links / 4 / 2
       3.2  two .nav ul row-gaps                         (0.1rem, block [29])
      12.0  padding-bottom
       1.0  border-bottom hairline                       (block [25])
     210.6

   WHERE THE 57.6px COMES FROM:
      44.0  one entire nav row
       3.2  the two ul row-gaps -> 0 (44px boxes need no gap between them)
       8.0  masthead padding 12 -> 8, top and bottom
       2.4  __inner row-gap 6.4 -> 4
      57.6

   ⛔ THIS BLOCK SETS NO COLOUR, DELIBERATELY. Five separate bugs today were a
   colour written for one ground leaking onto another (§8.1, §9.4). The nav
   label keeps the colour block [25] already gave it, so there is no new
   ground to get wrong. For the record, because the type gets SMALLER and
   4.5:1 therefore has to hold at 13px: screenshot pixels read the glyph core
   at rgb(90,107,110) = #5A6B6E and the masthead ground at rgb(255,255,255),
   opaque, no alpha stack -> 5.58:1 against 4.5 required. Passes with margin.

   ⚠️ ORDERING IS LOAD-BEARING. This block re-declares `.masthead__inner`
   row-gap, `.masthead .nav ul` gaps and `.masthead .nav a` font-size at the
   SAME specificity as PART 4 block [29]. It must stay AFTER [29] in the file
   or it does nothing. It is appended at the end for exactly that reason.

   ⛔ AND DO NOT "FIX" BLOCK [29]. Its `display: inline; min-height: 0` never
   applies: `li > a:only-of-type:first-child:last-child` in block [11] is
   specificity (0,3,2) and beats `.masthead .nav a` at (0,2,1), so the 44px
   comes from [11]. That is the correct outcome. If [29] ever won, its
   `padding-block: 0.55rem` on an inline box would give a 39.9px padded box in a
   22.3px line box - the overlapping hit area §8.3 already reverted, except here
   the overlap is between two NAV rows, so a tap aimed at "Results" fires
   "AutoMate". Nothing below touches display or min-height.
   ========================================================================= */

@media (max-width: 899px) {

  /* [33a] The header's own box. 12px of padding above and below a 44px row is
     generous for a strip whose job is to get out of the way. --space-3 is 8px.
     Fallbacks on every token: PART 1b's whole argument is that a remote asset
     can fail on a contractor's network, and ds/*.css is a remote asset too.
     Verified by blocking **/ds/*.css: 13px labels, 12px gutter, two rows,
     121.1px header - the patch still lands. */
  .masthead { padding: var(--space-3, 8px) 0; }

  /* [33b] The header gutter, HEADER ONLY. 1.15rem (18.4px) -> 12px buys 12.8px
     of nav width, which is what turns "nearly two rows" into two rows at 360
     - the commonest Android width, where the type change alone is NOT enough
     (measured: 197px / 3 rows without this line, 153px / 2 rows with it).
     .masthead already has its own container rule (main.css:1681 sets it to
     1240px against the page's 1100px), so the header having its own gutter is
     the existing pattern, not a new one. Brand, CTA and nav all move together,
     so nothing inside the header misaligns; the header simply sits 6.4px wider
     than the content below it. No horizontal overflow at any width tested. */
  .masthead .wrap { padding-inline: var(--space-4, 12px); }

  /* [33c] 6.4px -> 4px between the brand row and the nav row. */
  .masthead__inner { row-gap: var(--space-2, 4px); }

  /* [33d] Row gap to zero - two stacked 44px targets do not need a gap, they
     already have 11px of clear space inside each box. Column gap 7px: a
     literal because there is no 7px token (--space-2 is 4, --space-3 is 8), and
     chosen by measurement, not taste. The word space inside "How it works" at
     this size renders 2.39px, so 7px reads as 2.9x a word space and the links
     stay visually separate; at 5px they ran together into one sentence, and at
     8px the estate loses the two-row fit at 360. */
  .masthead .nav ul { row-gap: 0; column-gap: 7px; }

  /* [33e] 14.4px -> 13px, which is the DS --text-xs token, not an invented
     size. The 658px of label ink at 14.4px cannot fit two 338.2px rows; at
     13px with --tracking-tight it fits with 40.9px of headroom at 360 and
     72.8px at 375. white-space:nowrap makes it impossible for a label to break
     mid-word - the second half of the defect the scroller was reverted for.
     ⚠️ The DS note "never below 15px" is scoped to on-site BODY copy. These are
     nav labels, they are 44px tall targets, and they measure 5.58:1. Flagged
     rather than assumed. */
  .masthead .nav a {
    font-size: var(--text-xs, 0.8125rem);
    letter-spacing: var(--tracking-tight, -0.022em);
    white-space: nowrap;
  }
}


/* ===== applied 6 Aug: landed as PART 14 / block [37]; the agent wrote "PART 9"
   because three agents measured in parallel against a growing file. Selectors do not
   intersect (.masthead* / .footer* / .gp-thin*) and each only needs to be LATER in the
   file than the block it overrides, which appending satisfies. ===== */
/* =========================================================================
   PART 14 — THE MOBILE FOOTER GIVES BACK 272 OF THE 290px THE LAYER ADDED
   Measured, 375px, /pricing/ (identical on all 19 library pages):
       pristine main.css only ......  920.69px
       with the layer .............. 1210.28px   (+289.59)
       with this block ............   937.90px   (-272.38)
   At 1400px the footer is 633.45px before AND after — this block is
   mobile-only and does not touch the desktop three-column footer.

   ⛔ NOT ONE LINK, LABEL OR LINE OF COPY IS REMOVED, HIDDEN OR SHORTENED.
   All 13 links stay in the DOM, stay visible, and stay 44px tall (§8.4:
   "a tall header is a nuisance; an invisible navigation is a defect").

   WHERE THE 290px CAME FROM, so nobody re-guesses it:
     +204.92  block [14] `.footer ul a { min-height: var(--hit-min) }` +
              `.footer li { margin-bottom: 0 }` — 13 links, 23.56px line box
              (+6.08 margin) each becoming a flat 44px box. 71% of the growth.
     + 36.00  block [14] `.footer { padding: var(--space-11) 0 var(--space-10) }`
              (64/48) against main.css's 2.25rem/2.5rem (36/40).
     + 22.17  block [13] `.footer__brand { min-height: var(--hit-min) }`. KEPT —
              it is a real standalone control, and rule 4 asks for it.
     + 22.67  ⛔ THIS WAS ATTRIBUTED TO ARCHIVO AND THAT WAS FALSE. An adversarial
              reviewer disproved it: BOTH states load Archivo, both compute
              13.76px/21.33px, and the paragraph's natural nowrap width is
              1005.58px either way, identical to two decimal places. The real
              cause was block [11]'s over-broad hit rule turning the trailing
              "info@gaiagroupuk.com" into a 142.8 x 44.0 atomic box mid-sentence,
              which cannot line-break and forced a fourth line. That rule is
              WITHDRAWN (see [11]) and PART 12 belts-and-braces it, so this
              22.67px is now recovered too and the footer lands BELOW pristine.
     +  3.84  block [10], three `.footer h2` at weight 600 + caps tracking.

   THE FIX IS ROW COUNT, NOT ROW HEIGHT. 44px stays; 13 rows become 7.
   ⛔ Deliberately NOT done: shrinking the rows below 44px (breaks §4 check 4 on
   13 elements), an overlay pseudo-element tap target (that is §8.3's tap-stealing
   defect again), and a <details> disclosure (needs HTML, which this layer cannot
   touch).

   THIS BLOCK SETS NO COLOUR AT ALL. Ground is unchanged at --teal-dark #00272D
   and the measured pairs stand as recorded: #C3D6D8 on #00272D = 10.49:1 (footer
   body), #FFFFFF on #00272D = 15.81:1 (.footer h2 and the links). Rule 2 is
   satisfied by construction — there is nothing here to break on another ground.
   ========================================================================= */
@media (max-width: 699px) {

  /* Below 700px main.css:480's three-column grid is off and the lists stack in
     one 338px-wide column — 13 × 44px = 572px of single-file links. Two columns
     of 161px halves that (Product 220→132, Evidence 264→132) and every cell is
     still 161px wide by 44px tall, far over the hit minimum. Scoped to
     .footer__cols so it cannot reach the funnel footers, which have no <ul>
     (verified: /offers/free-datamate/ uses .footer__links with <p><a>,
     /offers/demo-portal/ has no link columns at all, /info-pack/ and
     /savings-estimate/ use .foot, not .footer). */
  .footer__cols ul {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-5);          /* 16px */
    margin-bottom: var(--space-5);       /* was 1.5em = 22.8px; 3 lists, -20.4px */
  }

  /* ⚠️ THE ONE TRAP IN THIS BLOCK, FOUND BY SCREENSHOT NOT BY COMPUTED STYLE.
     "info@gaiagroupuk.com" is 157.8px of unbreakable string. In a 161px cell at
     375px it fits with 3.3px to spare; at 360px (the commonest Android width)
     the cell is 153.6px and the email OVERPRINTED "For cabin suppliers" — two
     links illegible on top of each other, and scrollWidth/clientWidth did NOT
     report it because the inline-flex box was the right size and only its text
     overflowed. The Contact list therefore stays one column. It costs 44px and
     it is the difference between a shorter footer and a wrong one. */
  .footer__cols ul:has(a[href^="mailto:"]) { grid-template-columns: minmax(0, 1fr); }

  /* Belt and braces for the same failure mode: if :has() is unsupported, or if a
     long label is added later, the text WRAPS instead of overprinting its
     neighbour. `anywhere` not `break-word` — measured: break-word does not
     reduce the min-content size of an anonymous flex item, so it did nothing. */
  .footer__cols ul li { min-width: 0; }
  .footer__cols ul a { overflow-wrap: anywhere; }

  /* A two-line label at the inherited 1.55 leading is 47.12px and pushes the row
     past 44px, giving a ragged 44/47/44 rhythm. At 1.3 two lines are 39.5px, so
     EVERY row is exactly 44px at every width from 320 to 699 (measured).
     Scoped to the footer link labels only: these are navigation labels, not the
     blocks of text WCAG 1.4.12 addresses, and the footer's body copy and legal
     small print keep their 1.55. */
  .footer__cols ul a { line-height: 1.3; }

  /* Hands back the one true spacing-token rebind. 64/48 is the desktop
     "generous" rhythm; on a phone, at the very bottom of the page, it is 36px of
     empty teal. --space-9 (40px) top and bottom sits between main.css's original
     36/40 and the layer's 64/48. Desktop keeps 64/48 untouched.
     ⚠️ This re-states `.footer`'s padding, which PART 3 block [14] owns. It is a
     deliberate mobile-only extension of that block, not a competing rule: it is
     later in the file and inside a media query, so [14] still governs ≥700px. */
  .footer { padding: var(--space-9) 0 var(--space-9); }
}


/* ===== applied 6 Aug: landed as PART 15 / block [38]; the agent wrote "PART 9"
   because three agents measured in parallel against a growing file. Selectors do not
   intersect (.masthead* / .footer* / .gp-thin*) and each only needs to be LATER in the
   file than the block it overrides, which appending satisfies. ===== */
/* =========================================================================
   PART 15 — THE THIN STEPPER STOPS IMPERSONATING BROWSER CHROME
   Applied under the §7.3 standing instruction (component treatment is the
   agent's to decide and record). No copy, no figure, no markup touched —
   this is five declarations on two selectors this layer already owns.
   ========================================================================= */

/* [38] .gp-thin — FROM BAR TO DATELINE. 16 pages carry .gp-thin (/ , /pricing/,
   /methodology/, /questions/, /awards/, /drying-room/, /for-cabin-suppliers/,
   /case-studies/ + its three studies, /technical/ + its four documents). The
   five pages that carry .gp or .gp-chev instead are NOT reached by this block.

   MEASURED DIAGNOSIS at 1440 (before):
     · Full-bleed 1440×45.5px band, #F0F5F5, 1px #D5DBDA on BOTH edges, 12px
       padding. Every other full-bleed block on the same page is 230–1185px
       tall and carries no rule at all, and the only other viewport-wide
       hairline in the estate is .masthead's bottom rule — the site's one
       piece of real chrome. This band repeats the masthead's exact device
       (pale edge-to-edge strip + hairline + caps label + one link) 400–600px
       further down, so a reader files it as a second header bar.
     · On the four /technical/ pages and every case study, #F0F5F5 sits
       straight above a #F7F8F7 .section--alt: a hairline dividing two grounds
       1.5% apart. A rule that separates nothing is a toolbar tell.
     · 13.76px type — main.css's footer-copyright and .card__who size, one
       step below .note (14.08px), against 18px body, and below the floor
       ds/typography.css states in its own comment: "Never below 15px for
       on-site body copy".
     · It floats: 80–96px of the header's bottom padding above, 96px of the
       next section's padding below. A 45px tinted bar suspended in ~190px of
       white is the geometry of a cookie notice.

   NOT REMOVED, NOT HIDDEN, NOT REWORDED. It carries the funnel's most
   valuable and least visible fact — bold "free", locked by main.css:1842 —
   and 16 pages' only link to /how-it-works/. The box goes; the line stays and
   gains rank. The single remaining hairline hangs off the 1063px text column
   instead of the viewport, where a rule reads as the dateline under a page
   title rather than as chrome.

   ⚠️ GROUND: WHITE, and that is the only ground this is scoped to. .gp-thin is
   a direct child of <main> on all 16 pages and <main> has no background, so
   the painted ground after this change is #FFFFFF — read from screenshot
   pixels on all 16, not from getComputedStyle. No colour is set here and none
   is needed: both inherited colours measure BETTER on white than on the tint
   they were tested against —
       .gp-thin__line            #5A6B6E on #FFFFFF   5.58:1  (was 5.07:1)
       __acts / strong / a       #004650 on #FFFFFF  10.54:1  (was 9.58:1)
   If .gp-thin is ever placed inside a teal .pagehead or .section--teal, this
   block must be re-scoped: #5A6B6E on #004650 is 3.0:1 and fails. See §8.6's
   dark-ground row. The 1px --border-default rule is decoration, not a control
   boundary, so 1.4.11 does not apply; it is the rule the masthead uses.

   COST, stated honestly: 45.5px → 90px at 1440 and 64.7px → 109.5px at 375,
   i.e. +44/+46px of page height on 16 pages. Rule 19's "orientation should
   cost about one line" is about RANK, and rank is unchanged — still one line
   of type, still below the H1, still above the body. The extra height is the
   96px rhythm the rest of the page already keeps.                          */
.gp-thin {
  background: transparent;   /* was --surface-brand-tint — the tint WAS the bar */
  border-top: 0;             /* the two viewport-wide hairlines are the chrome tell */
  border-bottom: 0;
  padding: var(--space-7) 0; /* 24px — this layer's own --gap */
}
.gp-thin__line {
  font-size: var(--text-base);          /* 15px, the DS floor for body copy; was 13.76px */
  padding-bottom: var(--space-6);       /* 20px: the rule hangs off the line… */
  border-bottom: 1px solid var(--border-default);  /* …at the 1063px measure, not the viewport */
}


/* =========================================================================
   PART 16 — THE MASTHEAD NAV LINK GETS A REAL 44px BOX, NOT A PADDED INLINE ONE
   ⛔ THIS FIXES A DEFECT I CREATED TWENTY MINUTES AGO, AND THE MASTHEAD AGENT
   PREDICTED IT PRECISELY BEFORE I DID IT. Its patch comment reads:

     "AND DO NOT 'FIX' BLOCK [29]. Its `display: inline; min-height: 0` never
      applies: `li > a:only-of-type:first-child:last-child` in block [11] is
      specificity (0,3,2) and beats `.masthead .nav a` at (0,2,1)... If [29]
      ever won, its `padding-block: 0.55rem` on an inline box would give a
      39.9px padded box in a 22.3px line box — the overlapping hit area §8.3
      already reverted, except here the overlap is between two NAV rows, so a
      tap aimed at 'Results' fires 'AutoMate'."

   Block [11] is now WITHDRAWN — it was turning 51 mid-sentence links into
   tap-stealing boxes — and withdrawing it woke [29] up exactly as warned.
   MEASURED immediately after: `.masthead .nav a` computed display:inline,
   min-height:0, padding-block:8.8px, box 31.6px inside a 20.15px line box.
   So 5.7px of each nav link's hit area hung over the row above it, on two
   rows of a phone masthead. A dormant rule became live because something
   ELSE was removed, and nothing in either block could see the other.

   ⭐ THE LESSON, and it is the one worth keeping out of today: REMOVING a rule
   is as dangerous as adding one. The whole estate's rule-vs-rule reasoning is
   about what a new selector overrides; nothing checks what a DELETION reveals.
   A rule that has never applied has never been tested either.

   THE FIX. Nav links are genuine standalone controls — they are `li > a` in a
   <nav> with no text beside them — so they get a real flex box with the height
   ON THE BOX and NO vertical padding. A flex box grows the LINE, so rows push
   each other apart instead of overlapping. Padding on an inline box does not,
   which is the whole difference and it is why this cost 44px of header height
   rather than being free.
   MEASURED after, on / , /pricing/ and /technical/ at 375px: 10 links at exactly
   44px, 2 rows, masthead 161.0px, 0 clipped labels, 0 links hidden, and 0px of
   any link's box above its own ink. Desktop unchanged at 69.0px, 1 row.
   ⚠️ 161px, NOT the 153.4px I first wrote here — that was the masthead agent's
   figure for its own patch measured while block [11] still supplied the 44px, and
   I copied it instead of measuring mine. Corrected against the browser.
   ⚠️ AND THE 128.8px THAT APPEARED IN BETWEEN WAS NOT AN IMPROVEMENT, IT WAS THE
   DEFECT: it was cheaper only because every nav link had shrunk to a 31.6px
   padded inline box that overlapped the row above it. 210.6 -> 161.0 is the real
   saving, and 44px hit targets are what the extra 32px buys. */
.nav a,
.masthead .nav a {
  display: inline-flex;
  align-items: center;
  min-height: var(--hit-min);
  padding-block: 0;
}
@media (max-width: 899px) {
  .masthead .nav a {
    display: inline-flex;
    align-items: center;
    min-height: var(--hit-min);
    padding-block: 0;
  }
}

/* ==========================================================================
   PART 7 - ALEX'S FINAL HOMEPAGE ORDER (D449, 3 Sep 2026)
   ==========================================================================
   [33] THE PROOF TRIO MOVED BELOW THE DEMONSTRATOR. The same rules block [28]
   gave it inside the hero, so the figures keep their weight; only the position
   changed. main.css:1591 paints .hero__cred a white, which would be invisible
   here on white, so the link colour is restated. */
.hero-proof { background: var(--white); color: var(--text-body); padding: var(--space-8) 0 var(--space-6); }
.hero-proof .figures { margin-top: 0; border-top-color: var(--border-default); }
.hero-proof .figure__pc { color: var(--gaia-teal-700); }
.hero-proof .figure__label { color: var(--text-body); }
.hero-proof .note { color: var(--text-body); }
.hero-proof .note strong { color: var(--text-strong); }
.hero-proof .hero__cred { color: var(--text-body); }
.hero-proof .hero__cred a { color: var(--gaia-teal-700); }
.hero-proof .hero__cred a:hover { color: var(--gaia-accent-600); }

/* [34] BIGGER CALLS TO ACTION (Jamie, 3 Sep 2026). Bigger, not louder: type and
   padding only, same colours, same hierarchy. The masthead button stays ghost
   ([26]) and stays smaller than the hero button at every width. Both rules sit
   after [26] and [29], so they win on order at equal specificity.
   MEASURED: the desktop masthead row has 22px of slack (brand 134 + nav 841 +
   button 167 + two 20px gaps in a 1204px inner at 1280 and 1440). The masthead
   button therefore grows in height (44 -> 52) and type (15.2 -> 16px) but only
   ~13px in width; one step wider and the whole masthead wraps to two rows. */
.hero .btn--hero { font-size: var(--text-xl); padding: var(--space-6) var(--space-9); }
.masthead .btn--nav { font-size: var(--text-md); padding: 14px var(--space-6); }
/* [35] THE HERO WITHOUT ITS PROOF COLUMN. main.css:241 makes .hero__inner a
   two-column grid at >=900px for the proof figures on the right. With the trio
   gone the right column was empty. Jamie, 3 Sep 2026, on the first cut (which only
   dropped the grid and left the lede at its old 34ch column width): "you need to
   fill up the gap on the right-hand side because it looks silly now because
   there's a big empty space there." So at desktop the solo hero is a TRUE single
   column: the H1 spans the content width and goes one size up from 1200px (two
   lines, measured 905px of 1204), the lede takes the estate's 62ch paragraph
   measure (main.css:536, the pagehead lede) instead of the column's 34ch, and the
   vertical padding comes down from 80/96 to 40/40 so the guarantee strip is
   reachable. MEASURED at 1440x900: hero 674px before, ~490px after. Nothing here
   applies below 900px, so the 390 layout is unchanged. */
@media (min-width: 900px) {
  .hero--solo { padding: var(--space-9) 0 var(--space-9); }
  .hero--solo .hero__inner { display: block; }
  .hero--solo .lede { max-width: 62ch; }
  .hero--solo .btn-row { margin-bottom: 0.6em; }
}
@media (min-width: 1200px) { .hero--solo h1 { font-size: var(--text-6xl); } }
@media (max-width: 899px) {
  .hero .btn--hero { font-size: var(--text-lg); padding: var(--space-5) var(--space-8); }
  .masthead .btn--nav { font-size: var(--text-md); padding: var(--space-3) var(--space-5); }
}

/* ==========================================================================
   PART 17 - /journey/ THE SIX-STEP STEPPER (D450, 3 Sep 2026)
   ==========================================================================
   One page carries this: /journey/, the target of the gp-thin strip's "see the
   whole journey" link on 17 pages. The design is the investor pack's journey
   stepper (six steps under three acts, select a step for its detail) rebuilt in
   this layer's tokens; nothing is pasted from the pack. Copy is D130's table.
   Markup ships with all six panels visible and the script on the page adds
   `hidden`, so with no JS the reader gets the full map.
     >= 861px  a tab strip: six faces in one row under a three-act spine that
               spans 3 / 2 / 1 columns (the same split /how-it-works/ uses),
               one panel open below the row.
     <= 860px  an accordion: the spine hides, act labels reappear between the
               groups, each panel opens directly under its face.
   Accent stays reserved: it marks "free" and the focus ring, nothing else. The
   selected face is brand teal, as the current step is in .gp.               */
.jy-sec { padding-top: var(--space-9); }
.jy-h {
  max-width: 62ch; margin: 0 0 var(--space-7);
  font-size: var(--text-xl); font-weight: 600; color: var(--text-heading);
}
.jy-acts { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--space-2); margin-bottom: var(--space-3); }
.jy-act { border-bottom: 2px solid var(--gaia-teal-700); padding: 0 var(--space-3) var(--space-3); min-width: 0; }
.jy-act--1 { grid-column: span 3; } .jy-act--2 { grid-column: span 2; } .jy-act--3 { grid-column: span 1; }
.jy-act b, .jy-actlabel b, .jy-step__n, .jy-panel__n, .jy-dl dt {
  display: block; font-size: var(--text-2xs); font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-brand); line-height: 1.3;
}
.jy-act span, .jy-actlabel span { display: block; margin-top: var(--space-1); font-size: var(--text-xs); color: var(--text-body); }
.jy-actlabel { display: none; }

.jy-steps { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--space-2); }
.jy-step {
  grid-row: 1; min-width: 0; min-height: var(--hit-min);
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2);
  text-align: left; font: inherit; cursor: pointer;
  background: var(--surface-brand-tint); color: var(--text-strong);
  border: 1px solid var(--border-default); border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-4) var(--space-5);
  transition: border-color var(--duration-fast, .15s), background-color var(--duration-fast, .15s);
}
.jy-step:hover { border-color: var(--gaia-teal-700); }
.jy-step:focus-visible { outline: 3px solid var(--border-focus); outline-offset: 2px; }
.jy-step[aria-expanded="true"] { background: var(--gaia-teal-700); color: var(--white); border-color: var(--gaia-teal-700); }
.jy-step[aria-expanded="true"] .jy-step__n { color: var(--dark-body, #B9D3D5); }
.jy-step__t { font-size: var(--text-md); font-weight: 700; line-height: 1.2; }
.jy-step__do { font-size: var(--text-xs); line-height: 1.4; color: var(--text-body); }
.jy-step[aria-expanded="true"] .jy-step__do { color: var(--dark-body, #B9D3D5); }
.jy-step__cost {
  margin-top: auto; display: inline-block; max-width: 100%;
  font-size: var(--text-2xs); font-weight: 700; line-height: 1.3;
  padding: 2px var(--space-3); border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong); color: var(--text-strong); background: var(--white);
}
.jy-step[aria-expanded="true"] .jy-step__cost { background: transparent; color: var(--white); border-color: rgba(255,255,255,.55); }
.jy-step__cost--free,
.jy-step[aria-expanded="true"] .jy-step__cost--free {
  background: var(--gaia-accent-500); color: var(--cta-ink, #00312f); border-color: var(--gaia-accent-500);
  text-transform: uppercase; letter-spacing: .05em;
}

.jy-panel {
  grid-row: 2; grid-column: 1 / -1; margin-top: var(--space-4);
  padding: var(--space-7); background: var(--white);
  border: 1px solid var(--border-default); border-top: 3px solid var(--gaia-teal-700);
  border-radius: var(--radius-md);
}
.jy-panel__h { margin: 0 0 var(--space-6); font-size: var(--text-xl); font-weight: 600; color: var(--text-heading); line-height: 1.25; }
.jy-panel__n { margin-bottom: var(--space-2); }
.jy-dl { margin: 0; display: grid; grid-template-columns: 9.5rem minmax(0, 62ch); row-gap: var(--space-4); column-gap: var(--space-6); }
.jy-dl > div { display: contents; }
.jy-dl dt { padding-top: 3px; }
.jy-dl dd { margin: 0; font-size: var(--text-md); line-height: 1.5; color: var(--text-strong); }

/* The closing call. .section--teal paints .lede with --ink (main.css:76), which
   is dark on dark; restated for this ground. The button takes the hero's D449
   size so the page's one primary action is the same weight as the homepage's. */
.jy-cta .lede { color: var(--dark-body, #B9D3D5); max-width: 62ch; }
.jy-cta .btn-row { margin-bottom: 0.6em; }
.jy-cta .btn--hero { font-size: var(--text-xl); padding: var(--space-6) var(--space-9); }

@media (max-width: 860px) {
  .jy-sec { padding-top: var(--space-8); }
  .jy-acts { display: none; }
  .jy-steps { display: block; }
  .jy-actlabel { display: block; margin: var(--space-7) 0 var(--space-3); }
  .jy-actlabel:first-child { margin-top: 0; }
  .jy-actlabel b { display: inline; }
  .jy-actlabel span { display: inline; margin: 0 0 0 var(--space-3); }
  .jy-step {
    width: 100%; margin-top: var(--space-2);
    display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
    column-gap: var(--space-4); row-gap: var(--space-1);
    padding: var(--space-4) var(--space-5);
  }
  .jy-step__n { grid-column: 1; }
  .jy-step__t { grid-column: 1; }
  .jy-step__do { display: none; }           /* the panel below carries this line */
  .jy-step__cost { grid-column: 2; grid-row: 1 / span 2; margin: 0; }
  .jy-step[aria-expanded="true"] { border-radius: var(--radius-md) var(--radius-md) 0 0; }
  .jy-panel {
    margin-top: 0; padding: var(--space-5);
    border-top: 0; border-radius: 0 0 var(--radius-md) var(--radius-md);
  }
  .jy-dl { grid-template-columns: 1fr; row-gap: var(--space-4); }
  .jy-dl dt { padding-top: 0; margin-bottom: var(--space-1); }
  .jy-cta .btn--hero { font-size: var(--text-lg); padding: var(--space-5) var(--space-8); }
}

/* ==========================================================================
   PART 18 - THE TWO-PHASE MEASUREMENT GRAPHIC  .msr   (walkthrough p02, 3 Sep 2026)
   ==========================================================================
   One block of markup, generated by _to_delete/_build_msr.py and pasted
   byte-identical onto three pages: /automate/ (#measured, the in-page nav
   target), /one-week-on-one-week-off/ and /methodology/. Two equal panels.
   Phase 1 is the proof week: a strip of 15 day boxes, 7 baseline, 1 changeover
   (hatched, not counted), 7 controlled. Phase 2 is the rest of the project:
   three dimensions converging on one comparison. The approved prose stays
   beneath the graphic on /automate/ as .msr-detail.
     >= 861px  panels side by side; the strip is one row of 15.
     <= 860px  panels stack.
     <= 560px  the strip wraps 7 + 1 + 7 on three rows so each box stays legible.
   Colour carries meaning in both panels: neutral (--ink-200) is "before" or
   "would have used", brand teal (--gaia-teal-700) is "controlled" or "actually
   used". Accent stays reserved, as everywhere else in this layer.          */
.msr .msr-lede { max-width: 62ch; }
.msr-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-6); margin: var(--space-7) 0 var(--space-6);
}
.msr-panel {
  min-width: 0; display: flex; flex-direction: column;
  background: var(--white); border: 1px solid var(--border-default);
  border-radius: var(--radius-card); padding: var(--space-6) var(--space-6) var(--space-7);
}
.msr-panel__h { font-size: var(--text-xl); line-height: 1.25; margin: 0 0 var(--space-5); }
.msr-panel__k {
  display: block; font-size: var(--text-2xs); font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-brand); margin-bottom: var(--space-1);
}
.msr-fig { margin: 0 0 var(--space-6); }
.msr-cap { font-size: var(--text-xs); line-height: 1.4; color: var(--text-body); margin-top: var(--space-4); text-align: center; }
.msr-lines { list-style: none; margin: 0; padding: 0; font-size: var(--text-base); line-height: 1.5; color: var(--text-strong); }
.msr-lines li { position: relative; padding-left: var(--space-6); margin: 0 0 var(--space-3); }
.msr-lines li:last-child { margin-bottom: 0; }
.msr-lines li::before {
  content: ""; position: absolute; left: 0; top: .5em; width: 9px; height: 9px;
  border-radius: 50%; background: var(--gaia-teal-700);
}
.msr-foot { margin: 0; text-align: center; font-weight: 700; color: var(--text-heading); }

/* Phase 1: the 15-day strip */
.msr-strip {
  display: grid; grid-template-columns: repeat(15, minmax(0, 1fr)); gap: var(--space-2);
  align-items: end; padding-bottom: 2.7em;        /* room for the changeover label below box 8 */
}
.msr-wk { min-width: 0; position: relative; grid-column: span 7; }   /* 15 equal columns so box 8 is the same size as the rest */
.msr-wk--chg { grid-column: span 1; }
.msr-wk__lab {
  display: block; font-size: var(--text-xs); font-weight: 700; line-height: 1.3;
  color: var(--text-heading); margin-bottom: var(--space-2);
}
.msr-wk__lab small { display: block; font-size: var(--text-2xs); font-weight: 400; color: var(--text-body); }
.msr-wk--ctl .msr-wk__lab { text-align: right; }
.msr-wk--chg .msr-wk__lab {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  margin: var(--space-2) 0 0; white-space: nowrap; text-align: center;
}
.msr-boxes { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-2); }
.msr-wk--chg .msr-boxes { grid-template-columns: minmax(0, 1fr); }
.msr-boxes li {
  margin: 0; aspect-ratio: 1 / 1; min-width: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--text-2xs); font-weight: 700; line-height: 1; border-radius: var(--radius-xs);
}
.msr-wk--base .msr-boxes li { background: var(--ink-200); color: var(--ink-800); }
.msr-wk--ctl  .msr-boxes li { background: var(--gaia-teal-700); color: var(--white); }
.msr-wk--chg  .msr-boxes li {
  color: var(--ink-700); border: 1px solid var(--ink-300);
  background: repeating-linear-gradient(135deg, var(--ink-150) 0 3px, var(--white) 3px 6px);
}

/* Phase 2: three dimensions meeting at one comparison */
.msr-dims__row { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); }
.msr-dim {
  margin: 0; min-width: 0; text-align: center;
  background: var(--surface-brand-tint); border: 1px solid var(--border-default); border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-2); font-size: var(--text-xs); font-weight: 700; line-height: 1.3; color: var(--text-heading);
}
.msr-dim small { display: block; margin-top: var(--space-1); font-size: var(--text-2xs); font-weight: 400; color: var(--text-body); }
.msr-dims__lines { display: block; width: 100%; height: 36px; margin: var(--space-1) 0; }
.msr-dims__lines path { fill: none; stroke: var(--gaia-teal-700); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.msr-meet {
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: var(--space-3);
  border: 1.5px solid var(--gaia-teal-700); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4);
  text-align: center; font-size: var(--text-xs); font-weight: 700; line-height: 1.3;
}
.msr-meet__a { color: var(--ink-800); background: var(--ink-200); border-radius: var(--radius-xs); padding: var(--space-2) var(--space-3); }
.msr-meet__b { color: var(--white); background: var(--gaia-teal-700); border-radius: var(--radius-xs); padding: var(--space-2) var(--space-3); }
.msr-meet__vs { font-size: var(--text-2xs); font-weight: 400; letter-spacing: .06em; text-transform: uppercase; color: var(--text-body); }

/* /automate/ only: the approved prose sits directly under the graphic on the same ground */
.msr + .msr-detail { padding-top: 0; }
.msr-detail__h {
  font-size: var(--text-2xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-brand); margin: 0 0 var(--space-4);
}
.msr-detail .lede { font-size: var(--text-lg); }

@media (max-width: 860px) {
  .msr-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 560px) {
  .msr-strip { display: block; padding-bottom: 0; }
  .msr-wk + .msr-wk { margin-top: var(--space-4); }
  .msr-wk--ctl .msr-wk__lab { text-align: left; }
  .msr-wk--chg { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-2); align-items: center; }
  .msr-wk--chg .msr-boxes { grid-column: 1; grid-row: 1; }
  .msr-wk--chg .msr-wk__lab {
    position: static; transform: none; grid-column: 2 / -1; grid-row: 1;   /* beside box 8, not above it */
    margin: 0; white-space: normal; text-align: left;
  }
  .msr-boxes li { font-size: var(--text-xs); }
}

/* ==========================================================================
   PART 19 - THE TWO-PHASE MEASUREMENT ANIMATIONS  .msr-anim   (3 Sep 2026, Jamie)
   ==========================================================================
   Replaces the static PART 18 panels on /automate/ only (the other two pages
   keep PART 18). Two self-contained animations, Phase 1 (.ma1, the proof
   fortnight read off one meter) and Phase 2 (.ma2, every hour matched against
   the library of measured hours). Each is drawn on a fixed 1100px stage and
   scaled to fit its column by a few lines of JS (.msr-anim__fit), so nothing
   reflows. Colour carries meaning: DataMate peach (--ma-dm) is "monitoring,
   no control" and "would have used"; brand teal is AutoMate controlling;
   dark teal is the difference. Figures on screen are worked examples and
   say so. Animations start when scrolled into view; Play again re-runs.   */
.msr-anim { margin: var(--space-8, 2rem) 0 0; }
.msr-anim + .msr-anim { margin-top: var(--space-10, 3rem); }
.msr-anim__lead { max-width: 62ch; color: var(--ink-600, #5A6B6E); margin: .25rem 0 1rem; }
.msr-anim__fit { position: relative; width: 100%; overflow: hidden; }
.msr-anim__fit > .stage { position: absolute; left: 0; top: 0; transform-origin: 0 0; }
.msr-anim .stage { font-family: inherit; color: #2A3B3E; }
.msr-anim .stage button { font-family: inherit; }
.msr-anim .stage *, .msr-anim .stage { box-sizing: content-box; }
.msr-anim .stage .site, .msr-anim .stage .fly, .msr-anim .stage .mini { box-sizing: border-box; }
.msr-anim.ma1{--ma-teal:#004650;--ma-acc:#0FB5A6;--ma-accd:#0D9A8E;--ma-teald:#00272D;--ma-line:#D5DBDA;--ma-wash:#F0F5F5;--ma-ink:#2A3B3E;--ma-mid:#5A6B6E;--ma-off:#F7F8F7;--ma-grey:#B8C2C3;--ma-dm:#E8845A;--ma-dmwash:#FDF1EA;--ma-dmline:#F3C4AD;--ma-dmbar:#F2A98A;}
.msr-anim.ma1 .stage{position:relative;width:1100px;height:660px;background:#fff;border:1px solid var(--ma-line);border-radius:12px;overflow:hidden;}
.msr-anim.ma1 .lbl{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ma-mid);font-weight:600;}
.msr-anim.ma1 /* meter + control */
  #ma1-meter{position:absolute;left:40px;top:32px;width:260px;border:2px solid var(--ma-teal);border-radius:12px;padding:14px 18px;background:#fff;box-shadow:0 8px 24px rgba(0,70,80,.08);}
.msr-anim.ma1 #ma1-meter .big{font-size:36px;font-weight:700;color:var(--ma-teal);line-height:1;margin-top:8px;font-variant-numeric:tabular-nums;}
.msr-anim.ma1 #ma1-meter .big small{font-size:13px;font-weight:500;color:var(--ma-mid);margin-left:6px;}
.msr-anim.ma1 #ma1-meter .day{font-size:13px;color:var(--ma-mid);margin-top:8px;min-height:18px;}
.msr-anim.ma1 #ma1-ctl{position:absolute;left:40px;top:180px;width:260px;border:2px solid var(--ma-dmline);border-radius:12px;padding:14px 18px;background:var(--ma-dmwash);transition:border-color .4s,background .4s,box-shadow .4s;}
.msr-anim.ma1 #ma1-ctl.on{border-color:var(--ma-accd);background:#fff;box-shadow:0 0 0 6px rgba(15,181,166,.18),0 10px 28px rgba(0,70,80,.15);}
.msr-anim.ma1 #ma1-ctl.pulse{animation:ma1-pulse 1.2s ease-out 3;}
@keyframes ma1-pulse{0%{transform:scale(1);box-shadow:0 0 0 0 rgba(15,181,166,.6);}60%{transform:scale(1.04);box-shadow:0 0 0 16px rgba(15,181,166,0);}100%{transform:scale(1);box-shadow:0 0 0 6px rgba(15,181,166,.18);}}
.msr-anim.ma1 #ma1-ctl .state{font-size:24px;font-weight:700;letter-spacing:.01em;color:var(--ma-dm);line-height:1;}
.msr-anim.ma1 #ma1-ctl.on .state{color:var(--ma-accd);}
.msr-anim.ma1 #ma1-ctl .sw{display:flex;align-items:center;gap:12px;margin-top:8px;}
.msr-anim.ma1 .toggle{width:64px;height:34px;flex:none;border-radius:999px;background:var(--ma-dm);position:relative;transition:background .4s;}
.msr-anim.ma1 .toggle::after{content:"";position:absolute;top:3px;left:3px;width:28px;height:28px;border-radius:50%;background:#fff;transition:left .4s;box-shadow:0 1px 3px rgba(0,0,0,.2);}
.msr-anim.ma1 .toggle.on{background:var(--ma-acc);}
.msr-anim.ma1 .toggle.on::after{left:33px;}
.msr-anim.ma1 #ma1-ctlTxt{font-size:13px;font-weight:600;color:var(--ma-mid);margin-top:4px;}
.msr-anim.ma1 #ma1-ctlSub{font-size:12px;color:var(--ma-mid);margin-top:8px;}
.msr-anim.ma1 /* settle sparkline */
  #ma1-settle{position:absolute;left:40px;top:322px;width:260px;transition:opacity .5s;}
.msr-anim.ma1 #ma1-settle svg{display:block;width:260px;height:70px;margin-top:8px;}
.msr-anim.ma1 #ma1-settle .s{font-size:12px;color:var(--ma-mid);margin-top:4px;}
.msr-anim.ma1 /* chart */
  #ma1-chart{position:absolute;left:390px;top:32px;width:670px;height:420px;}
.msr-anim.ma1 #ma1-chart .lbl{position:absolute;top:0;left:0;}
.msr-anim.ma1 #ma1-cols{position:absolute;left:0;right:0;top:28px;bottom:70px;display:grid;grid-template-columns:repeat(15,1fr);gap:8px;align-items:end;}
.msr-anim.ma1 .col{position:relative;height:100%;display:flex;flex-direction:column;justify-content:flex-end;opacity:.35;transition:opacity .3s;}
.msr-anim.ma1 .col.live{opacity:1;}
.msr-anim.ma1 .bar{width:100%;height:0;border-radius:4px 4px 0 0;background:var(--ma-dmbar);transition:height .5s cubic-bezier(.2,.8,.2,1);}
.msr-anim.ma1 .col.ctl .bar{background:var(--ma-acc);}
.msr-anim.ma1 .col.gap .bar{background:repeating-linear-gradient(135deg,#E6EBEA 0 5px,#fff 5px 10px);border:1px dashed var(--ma-line);border-bottom:0;}
.msr-anim.ma1 .val{text-align:center;margin-bottom:3px;font-size:10.5px;color:var(--ma-mid);opacity:0;transition:opacity .3s;font-variant-numeric:tabular-nums;}
.msr-anim.ma1 .col.live .val{opacity:1;}
.msr-anim.ma1 #ma1-days{position:absolute;left:0;right:0;bottom:36px;display:grid;grid-template-columns:repeat(15,1fr);gap:8px;}
.msr-anim.ma1 .d{height:28px;border:1px solid var(--ma-line);border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:600;color:var(--ma-mid);background:var(--ma-off);transition:all .3s;}
.msr-anim.ma1 .d.live{background:#fff;color:var(--ma-ink);border-color:var(--ma-ink);}
.msr-anim.ma1 .d.base.live{border-color:var(--ma-dm);color:var(--ma-dm);}
.msr-anim.ma1 .d.ctl.live{border-color:var(--ma-accd);color:var(--ma-accd);}
.msr-anim.ma1 .d.gap{border-style:dashed;}
.msr-anim.ma1 #ma1-bands{position:absolute;left:0;right:0;bottom:0;display:grid;grid-template-columns:7fr 1fr 7fr;gap:8px;font-size:12px;color:var(--ma-mid);text-align:center;}
.msr-anim.ma1 #ma1-bands div{border-top:2px solid var(--ma-line);padding-top:6px;}
.msr-anim.ma1 #ma1-bands .b1{border-top-color:var(--ma-dmbar);}
.msr-anim.ma1 #ma1-bands .b2{border-top-color:var(--ma-acc);}
.msr-anim.ma1 #ma1-bands .g{font-size:10px;line-height:1.2;}
.msr-anim.ma1 /* changeover banner */
  #ma1-flip{position:absolute;left:390px;top:60px;width:670px;height:392px;pointer-events:none;opacity:0;transition:opacity .4s;}
.msr-anim.ma1 #ma1-flip .line{position:absolute;left:50%;top:0;bottom:100px;width:0;border-left:3px dashed var(--ma-accd);transform:translateX(-50%);}
.msr-anim.ma1 #ma1-flip .tag{position:absolute;left:50%;top:6px;transform:translateX(-50%);background:var(--ma-accd);color:#fff;font-size:14px;font-weight:600;padding:8px 14px;border-radius:999px;white-space:nowrap;box-shadow:0 8px 20px rgba(0,70,80,.25);}
.msr-anim.ma1 #ma1-flip .tag b{background:#fff;color:var(--ma-accd);border-radius:999px;padding:1px 8px;margin-left:4px;}
.msr-anim.ma1 /* result */
  #ma1-result{position:absolute;left:390px;top:470px;width:670px;display:grid;grid-template-columns:1fr 1fr 1.3fr;gap:14px;opacity:0;transform:translateY(16px);transition:opacity .6s,transform .6s;}
.msr-anim.ma1 #ma1-result .card{border:2px solid var(--ma-dmline);border-radius:10px;padding:12px 16px;background:var(--ma-dmwash);}
.msr-anim.ma1 #ma1-result .card .n{color:var(--ma-dm);}
.msr-anim.ma1 #ma1-result .card.t{border-color:var(--ma-accd);background:#fff;}
.msr-anim.ma1 #ma1-result .card.s{border:2px solid var(--ma-teal);background:var(--ma-teal);color:#fff;}
.msr-anim.ma1 #ma1-result .card.s .k,.msr-anim.ma1 #ma1-result .card.s .m{color:rgba(255,255,255,.8);}
.msr-anim.ma1 #ma1-result .card.s .n{color:#fff;}
.msr-anim.ma1 .sum{display:flex;height:12px;border-radius:3px;overflow:hidden;margin-top:8px;background:var(--ma-dmbar);}
.msr-anim.ma1 .sum .u{background:var(--ma-acc);width:0;transition:width 1s cubic-bezier(.2,.8,.2,1);}
.msr-anim.ma1 .sum .sv{flex:1;background:repeating-linear-gradient(135deg,#fff 0 4px,rgba(255,255,255,.35) 4px 8px);}
.msr-anim.ma1 .sumk{display:flex;justify-content:space-between;font-size:10.5px;margin-top:4px;color:rgba(255,255,255,.85);}
.msr-anim.ma1 #ma1-result .k{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ma-mid);font-weight:600;}
.msr-anim.ma1 #ma1-result .n{font-size:26px;font-weight:700;margin-top:4px;line-height:1;font-variant-numeric:tabular-nums;}
.msr-anim.ma1 #ma1-result .card.t .n{color:var(--ma-accd);}
.msr-anim.ma1 #ma1-result .m{font-size:12px;color:var(--ma-mid);margin-top:6px;}
.msr-anim.ma1 #ma1-review{position:absolute;left:40px;top:470px;width:260px;font-size:14px;line-height:1.4;opacity:0;transition:opacity .6s;}
.msr-anim.ma1 #ma1-review b{color:var(--ma-teal);}
.msr-anim.ma1 #ma1-status{position:absolute;left:40px;top:442px;width:260px;font-size:13px;color:var(--ma-mid);transition:opacity .4s;}
.msr-anim.ma1 #ma1-replay{position:absolute;right:24px;top:24px;border:1px solid var(--ma-line);background:#fff;color:var(--ma-teal);font-weight:600;font-size:13px;font-family:inherit;padding:8px 14px;border-radius:999px;cursor:pointer;opacity:0;transition:opacity .4s;}
.msr-anim.ma2{--ma-teal:#004650;--ma-acc:#0FB5A6;--ma-accd:#0D9A8E;--ma-line:#D5DBDA;--ma-wash:#F0F5F5;--ma-ink:#2A3B3E;--ma-mid:#5A6B6E;--ma-off:#F7F8F7;--ma-dm:#E8845A;--ma-dmwash:#FDF1EA;--ma-dmline:#F3C4AD;--ma-dmbar:#F2A98A;}
.msr-anim.ma2 .stage{position:relative;width:1100px;height:600px;background:#fff;border:1px solid var(--ma-line);border-radius:12px;overflow:hidden;}
.msr-anim.ma2 .lbl{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ma-mid);font-weight:600;}
.msr-anim.ma2 .chip{display:inline-block;border:1px solid var(--ma-line);border-radius:999px;background:var(--ma-wash);color:var(--ma-ink);padding:3px 9px;font-size:12px;margin:0 4px 6px 0;white-space:nowrap;}
.msr-anim.ma2 /* your hour */
  #ma2-you{position:absolute;left:40px;top:40px;width:264px;height:196px;transform-origin:0 0;border:2px solid var(--ma-accd);border-radius:12px;padding:14px 18px;background:#fff;box-shadow:0 8px 24px rgba(0,70,80,.08);transition:transform .9s cubic-bezier(.2,.8,.2,1);}
.msr-anim.ma2 .kwh{font-size:36px;font-weight:700;line-height:1;margin-top:8px;font-variant-numeric:tabular-nums;}
.msr-anim.ma2 .kwh small{font-size:13px;font-weight:500;color:var(--ma-mid);margin-left:6px;}
.msr-anim.ma2 #ma2-you .kwh{color:var(--ma-accd);}
.msr-anim.ma2 .mode{display:inline-block;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;padding:3px 8px;border-radius:6px;margin-top:10px;}
.msr-anim.ma2 #ma2-you .mode{background:var(--ma-acc);color:#fff;}
.msr-anim.ma2 /* counters */
  #ma2-counts{position:absolute;left:390px;top:40px;width:370px;display:grid;grid-template-columns:1fr 1fr;gap:12px;transition:opacity .5s;}
.msr-anim.ma2 #ma2-counts .c{border:1px solid var(--ma-line);border-radius:10px;padding:10px 14px;background:var(--ma-off);}
.msr-anim.ma2 #ma2-counts .n{font-size:26px;font-weight:700;color:var(--ma-ink);line-height:1;margin-top:4px;font-variant-numeric:tabular-nums;}
.msr-anim.ma2 /* stream */
  #ma2-streamWrap{position:absolute;left:390px;top:130px;width:370px;height:440px;overflow:hidden;border-radius:10px;transition:opacity .6s;
    -webkit-mask-image:linear-gradient(#fff0,#fff 12%,#fff 88%,#fff0);mask-image:linear-gradient(#fff0,#fff 12%,#fff 88%,#fff0);}
.msr-anim.ma2 #ma2-stream{position:absolute;left:0;top:0;width:100%;will-change:transform;}
.msr-anim.ma2 .row{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:8px;}
.msr-anim.ma2 .site{height:78px;border:1px solid var(--ma-line);border-radius:8px;background:var(--ma-off);padding:6px 7px;font-size:9.5px;line-height:1.3;color:#8A9699;position:relative;transition:border-color .2s,background .2s,box-shadow .2s;overflow:hidden;box-sizing:border-box;}
.msr-anim.ma2 .site .s{font-weight:700;font-size:9px;letter-spacing:.04em;color:#9AA6A8;text-transform:uppercase;margin-bottom:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.msr-anim.ma2 .site .t{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-left:8px;position:relative;}
.msr-anim.ma2 .site .t::before{content:"";position:absolute;left:0;top:5px;width:4px;height:4px;border-radius:50%;background:#C9D1D2;}
.msr-anim.ma2 .site .t.ok{color:var(--ma-ink);}
.msr-anim.ma2 .site .t.ok::before{background:var(--ma-acc);}
.msr-anim.ma2 .site.hit{border-color:var(--ma-dm);background:var(--ma-dmwash);box-shadow:0 0 0 3px rgba(232,132,90,.25);}
.msr-anim.ma2 .site.hit .s{color:var(--ma-dm);}
.msr-anim.ma2 #ma2-scan{position:absolute;left:390px;top:355px;width:370px;height:2px;background:var(--ma-acc);box-shadow:0 0 12px rgba(15,181,166,.8);opacity:.9;transition:opacity .4s;}
.msr-anim.ma2 /* flyers and stack */
  .fly{position:absolute;width:86px;height:78px;border:1px solid var(--ma-dm);border-radius:8px;background:var(--ma-dmwash);box-sizing:border-box;transition:transform .8s cubic-bezier(.2,.8,.2,1),opacity .8s;will-change:transform;}
.msr-anim.ma2 #ma2-stackLbl{position:absolute;left:800px;top:40px;transition:opacity .5s;}
.msr-anim.ma2 #ma2-stackN{position:absolute;left:800px;top:60px;font-size:26px;font-weight:700;color:var(--ma-dm);line-height:1;font-variant-numeric:tabular-nums;transition:opacity .5s;}
.msr-anim.ma2 #ma2-stackN small{font-size:12px;font-weight:500;color:var(--ma-mid);margin-left:6px;}
.msr-anim.ma2 #ma2-stackBase{position:absolute;left:800px;top:130px;width:86px;height:78px;}
.msr-anim.ma2 /* benchmark card */
  #ma2-bench{position:absolute;left:796px;top:40px;width:264px;height:196px;border:2px solid var(--ma-dmline);border-radius:12px;padding:14px 18px;background:var(--ma-dmwash);box-shadow:0 8px 24px rgba(232,132,90,.12);opacity:0;transform:translate(30px,50px) scale(.6);transform-origin:0 0;transition:transform .8s cubic-bezier(.2,.8,.2,1),opacity .5s;}
.msr-anim.ma2 #ma2-bench .kwh{color:var(--ma-dm);}
.msr-anim.ma2 #ma2-bench .mode{background:var(--ma-dm);color:#fff;}
.msr-anim.ma2 .from{font-size:12px;color:var(--ma-mid);margin-top:8px;}
.msr-anim.ma2 /* comparison */
  #ma2-cmp{position:absolute;left:40px;top:318px;width:1020px;opacity:0;transition:opacity .6s;}
.msr-anim.ma2 .brow{display:grid;grid-template-columns:120px 1fr 110px;align-items:center;gap:14px;margin-bottom:14px;}
.msr-anim.ma2 .brow .who{font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;}
.msr-anim.ma2 .brow .bar{height:34px;border-radius:6px;width:0;transition:width 1.1s cubic-bezier(.2,.8,.2,1);}
.msr-anim.ma2 .brow.y .who{color:var(--ma-accd);}
.msr-anim.ma2 .brow.y .bar{background:var(--ma-acc);}
.msr-anim.ma2 .brow.b .who{color:var(--ma-dm);}
.msr-anim.ma2 .brow.b .bar{background:var(--ma-dmbar);}
.msr-anim.ma2 .brow .v{font-size:22px;font-weight:700;font-variant-numeric:tabular-nums;}
.msr-anim.ma2 .brow.y .v{color:var(--ma-accd);}
.msr-anim.ma2 .brow.b .v{color:var(--ma-dm);}
.msr-anim.ma2 #ma2-diff{position:absolute;left:40px;top:442px;width:1020px;display:grid;grid-template-columns:1fr 2fr;gap:14px;opacity:0;transform:translateY(14px);transition:opacity .6s,transform .6s;}
.msr-anim.ma2 #ma2-diff .card{border-radius:12px;padding:16px 20px;background:var(--ma-teal);color:#fff;}
.msr-anim.ma2 #ma2-diff .card .k{color:rgba(255,255,255,.8);}
.msr-anim.ma2 #ma2-diff .card .n{font-size:34px;font-weight:700;line-height:1;margin-top:6px;font-variant-numeric:tabular-nums;}
.msr-anim.ma2 #ma2-diff .card .n small{font-size:13px;font-weight:500;margin-left:6px;color:rgba(255,255,255,.8);}
.msr-anim.ma2 .sum{display:flex;height:12px;border-radius:3px;overflow:hidden;margin-top:12px;background:var(--ma-dmbar);}
.msr-anim.ma2 .sum .u{background:var(--ma-acc);width:0;transition:width 1s cubic-bezier(.2,.8,.2,1);}
.msr-anim.ma2 .sum .sv{flex:1;background:repeating-linear-gradient(135deg,#fff 0 4px,rgba(255,255,255,.35) 4px 8px);}
.msr-anim.ma2 .sumk{display:flex;justify-content:space-between;font-size:10.5px;margin-top:4px;color:rgba(255,255,255,.85);}
.msr-anim.ma2 #ma2-diff .words{display:flex;flex-direction:column;justify-content:center;gap:10px;padding:0 10px;}
.msr-anim.ma2 #ma2-diff .words .big{font-size:22px;font-weight:700;color:var(--ma-teal);line-height:1.2;}
.msr-anim.ma2 #ma2-diff .words .sm{font-size:13px;color:var(--ma-mid);}
.msr-anim.ma2 #ma2-replay{position:absolute;right:24px;bottom:18px;border:1px solid var(--ma-line);background:#fff;color:var(--ma-teal);font-weight:600;font-size:13px;font-family:inherit;padding:8px 14px;border-radius:999px;cursor:pointer;opacity:0;transition:opacity .4s;}
