/* ============================================================
   home.css — the home page shell, and nothing else.
   Loaded ONLY by Website/index.html, after tokens → base → topbar.

   THE SEVENTH FILE, AND WHY THAT IS NOT A CONTRADICTION.
   The rest of styles/ holds to "one file per scope, no catch-all" —
   a rule aimed at a page-local CATCH-ALL that used to mix its own
   :root block, shared components and a theme skin into one bag, so
   "where does this rule live" had two answers. This file is the
   opposite shape, and matches portal.css / analytic.css exactly:

     · one scope, declared up front;
     · EVERY selector prefixed .home, .home-* or [data-page="home"],
       checked comma-by-comma by verify_ui_tokens.mjs assertion 18;
     · ZERO variable declarations — tokens.css owns those, and
       assertion 17 covers this file too;
     · zero !important, zero ID selectors, zero @import.

   It also reuses rather than restates: .t-eyebrow / .t-micro / .num
   come from base.css, the whole top bar from topbar.css, every colour
   and space from tokens.css. The three literal one-offs below are
   marked as such where they appear; a fourth means they were never
   one-offs and belong in tokens.css.

   controls.css is deliberately NOT loaded by this page — a landing
   page has no controls, and .home-action is a marketing button that
   sizes to its label, not a .btn that fills a column.
   ============================================================ */

[data-page="home"] {
  height: auto;              /* base.css pins html,body to 100% for the app shell */
  min-height: 100%;
  color: var(--text-1);
  font-variant-numeric: tabular-nums;
}
/* THE BAR IS THE PRODUCT'S CONSTANT, SO IT DOES NOT FOLLOW THIS PAGE.
   It keeps topbar.css's own --sp-4 inset here, exactly as on /portal/ —
   the ONLY difference this page makes is sticky + a z-index.

   RULE: it does NOT take --inset-page, even though that would align the
   wordmark with the h1 on this page alone. Aligning the bar to per-page
   content would make it a per-page element: clicking Portal would move
   the wordmark and the whole nav sideways, and the top bar is the one
   element a reader expects NOT to move between pages.

   Two things fall out of this and are gone rather than merely unused:
   the `white-space: nowrap` guard on nav items (it existed because a
   132px-a-side bar could starve four labels; a 16px bar cannot), and the
   1200px breakpoint override that put the inset back (nothing left to put
   back). If you ever re-inset the bar, both have to come back with it. */
[data-page="home"] .topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  /* RULE: GIVE BACK THE SCROLLBAR'S WIDTH, OR THE NAV DOES NOT LINE UP WITH
     /portal/. This page scrolls and the portal does not, so this page has a
     vertical scrollbar and the portal has none. A scrollbar is taken out of
     the LAYOUT width, so `padding-right: 16px` measures 16px from the edge of
     a box that is already 8px narrower than the window — the four nav items
     end up 24px from the glass here and 16px there, and they visibly shift
     when you click through.

     `100vw - 100%` IS the conditional: 100vw counts the scrollbar, 100% (the
     body's content width) does not, so the difference is exactly the
     scrollbar's width — 8px here (--scrollbar-w), and genuinely 0 on a
     platform with overlay scrollbars, where nothing needs correcting and
     nothing is corrected. That is why this is a calc and not a hard-coded 8.

     max() floors it at 0 so a hypothetical scrollbar wider than --sp-4 can
     only remove the padding, never push the nav off the right edge. */
  padding-right: max(0px, calc(var(--sp-4) - (100vw - 100%)));
}

/* AND ITS GEOMETRY IS PORTAL'S, AT EVERY WIDTH. No padding override
   here, on purpose — topbar.css's own --sp-4 applies, so the bar on this
   page and the bar on /portal/ are the same object in the same place.

   THIS HAS BEEN BOTH WAYS AND THE TRADE-OFF IS REAL, so it is written down
   rather than rediscovered. The page content is a centred, capped band
   (--content-max); the bar is full bleed. On a wide window those two CANNOT
   both be true:

     bar flush (now)    → matches /portal/ exactly, no jump when you click
                          through; but on a 2500px window the wordmark sits
                          ~490px left of the h1 it used to line up with.
     bar follows content → wordmark and nav land on the content edges; but
                          the whole bar shifts sideways between this page
                          and /portal/, and the bar is the one element a
                          reader expects not to move.

   "Matches portal" is the chosen trade-off, after weighing both. To go
   back to the other one, the rule would be:
     padding-inline: max(var(--sp-4), calc((100% - var(--content-max)) / 2));
   and the nav's white-space:nowrap guard has to come back with it. */

/* RULE: scroll-padding-top IS WHAT MAKES THE ANCHORS LAND CORRECTLY.
   The top bar is sticky, so it sits OVER the document rather than pushing it
   down. Without this, jumping to #demo puts that section's top at y=0 — which
   is 48px UNDERNEATH the bar, so the portal frame loses its top border and its
   PORTAL·BETA badge entirely, and the section reads as already scrolled past.

   One declaration on the scroll container, not scroll-margin-top on each
   section, so any section added later is covered without a second edit.

   WARNING: THE THREE BEHAVIOURS BELOW ALL FALL OUT OF THIS ONE NUMBER —
   there is no per-section special case, and none should be added:
     · Hero      target = 48 - 72 = -24  → the browser clamps to 0, so the
                                            first section lands at the very
                                            top of the page.
     · Demo/Steps target lands 72px down → 24px of air under the 48px bar, so
                                            the frame's top edge and badge are
                                            fully visible.
     · Progress  target exceeds max scroll → clamps to the bottom, so the last
                                            section lands flush at the end of
                                            the document.
   Raising --h-topbar moves all four together, which is the point. */
html:has([data-page="home"]) {
  height: auto;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--h-topbar) + var(--sp-5));
}

@media (prefers-reduced-motion: reduce) {
  html:has([data-page="home"]) { scroll-behavior: auto; }
}

/* ============================================================
   SECTION RAIL — the reader's position, left margin
   Four marks and nothing else. A dot is a 9px square with a 1px
   border: filled amber for the section you are in, hollow grey
   otherwise. The name is not printed — it is a tooltip on hover
   or keyboard focus. A permanent label is a second navigation
   competing with the top bar, for four words nobody needs until
   they reach for them.

   The rail is centred in the page's LEFT MARGIN — the margin
   exists for it, so the dot sits in the middle of it rather than
   clinging to the window edge.

   RULE: THE left CALC IS LOAD-BEARING, NOT A MAGIC NUMBER. Since the
   content band is capped (--content-max), on a wide window the page
   is centred and its left edge is nowhere near the window's. A rail
   pinned to the window would drift far from the text it is indexing
   on a wide screen.

   So: take the content band's left edge, `50% - (band/2)`, and step
   back half the inset to land in the middle of the margin. The max()
   is the floor — below the cap the first term goes negative and the
   original window-relative position takes over unchanged, which is
   exactly the pre-cap behaviour every window under 1704px still gets.
   ============================================================ */
.home-rail {
  position: fixed;
  left: max(
    calc((var(--inset-page) - 9px) / 2),
    calc(50% - (var(--content-max) / 2) - (var(--inset-page) / 2) - 4.5px)
  );
  top: 50%;
  transform: translateY(-50%);
  z-index: 25;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.home-rail-dot {
  position: relative;
  display: block;
  width: 9px;
  height: 9px;
  border: var(--border) solid var(--line-locked);
  background: transparent;
}
/* the mark stays 9px; the target does not — 25px is pointable */
.home-rail-dot::after { content: ""; position: absolute; inset: -8px; }

.home-rail-dot:hover { border-color: var(--text-3); }
.home-rail-dot[aria-current="true"] { border-color: var(--accent); background: var(--accent); }

/* ---- the name, on demand -------------------------------
   Sits on its own ground so it can cross the gutter without
   fighting the text underneath, and is never animated in: it has
   to be readable the instant the pointer lands. */
.home-rail-label {
  position: absolute;
  left: 19px;
  top: 50%;
  transform: translateY(-50%);
  display: none;
  white-space: nowrap;
  padding: 4px 7px;
  font-size: var(--fs-micro);
  font-weight: var(--fw-micro);
  letter-spacing: var(--ls-micro);
  text-transform: uppercase;
  color: var(--text-1);
  background: var(--overlay-bg);
  backdrop-filter: var(--overlay-blur);
  border: var(--border) solid var(--line);
  border-radius: var(--radius);
  pointer-events: none;
}
.home-rail-dot:hover .home-rail-label,
.home-rail-dot:focus-visible .home-rail-label { display: block; }
.home-rail-dot[aria-current="true"]:hover .home-rail-label { color: var(--accent); }

/* ============================================================
   PAGE MARGINS — full bleed UP TO A LIMIT
   Content stretches with the window and one fixed distance
   (--inset-page) is deducted from each side, exactly as the design
   was drawn — until the band would exceed --content-max, at which
   point it stops growing and the surplus becomes margin.

   Below 1704px (= 1440 + 2×132) nothing here has any effect and the
   page behaves precisely as it did before the cap existed. Above it,
   the cap is what stops the portal frame growing taller than the
   screen and the step copy running past 90 characters a line.

   BOTH KNOBS ARE IN tokens.css: --content-max (how wide) and
   --inset-page (the margin either side). Do not re-type either
   number here — the section rail below computes its position from
   both, and a literal would silently desynchronise it.
   ============================================================ */
.home-hero,
.home-demo,
.home-steps,
.home-roadmap,
.home-caveat {
  max-width: calc(var(--content-max) + 2 * var(--inset-page));
  margin-inline: auto;
  padding-inline: var(--inset-page);
}

/* RULE: THE FOOTER IS NOT IN THE LIST ABOVE, AND THAT IS THE POINT. It is a
   status bar: its plate must run edge to edge like the top bar's, so a
   max-width would leave it floating with bare page either side, visible
   ground beyond both ends of the plate.

   So the plate is full width and only its CONTENTS are inset — and the
   inset is computed to land on the same edges as every section above,
   rather than restated as a literal:
     below the cap  → (100% - content-max)/2 is small, max() picks
                      --inset-page, matching the sections' 132px
     above the cap  → the sections are centred, and this expression is
                      exactly their left edge
   Verified equal to the steps grid's left/right at 1440, 1704 and 2546. */
/* (the footer's own inset is set in its block at the bottom of this file,
   next to the rest of its appearance — a second rule up here only ended up
   losing to it on source order, which is exactly the kind of duplicate the
   PAGE MARGINS list exists to avoid.) */

/* ============================================================
   1. HERO
   Short of full height on purpose: the portal frame below must
   break the fold so the reader knows the page continues.
   ============================================================ */
.home-hero {
  /* The hero's content is vertically centred, so this number does two
     things at once: it lifts the block by half the change (less dead
     space above the eyebrow) and it brings the portal frame further up
     the fold — which is why this is calc(100vh - X) rather than 100vh in
     the first place. Raise X to tighten, lower it to loosen; the two
     effects always move together in a 2:1 ratio. */
  min-height: calc(100vh - 340px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(22px, 2.2vw, 34px);
  padding-top: clamp(48px, 5vw, 72px);
  padding-bottom: clamp(36px, 3.5vw, 52px);
}
.home-hero-eyebrow { color: var(--text-3); }

.home-hero-title {
  margin: 0;
  /* ch, not px: 1ch is exactly one character in a monospace face, so this
     says "28 characters a line" and stays true when --fs-page-hero changes.
     Display type wants a SHORTER measure than body copy, not the same one.
     28 puts the 72-character headline on three lines at every width from
     600 to 2560; 22 made it four, which is one line too many for a hero. */
  max-width: 28ch;
  font-size: var(--fs-page-hero);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: 0.005em;
  color: var(--text-1);
  text-wrap: pretty;
}

.home-hero-lede {
  margin: 0;
  max-width: 64ch;
  font-size: var(--fs-page-lead);
  font-weight: 400;
  line-height: var(--lh-page-body);
  letter-spacing: 0;
  color: var(--text-2);
  text-wrap: pretty;
}

/* ---- actions ---------------------------------------------
   Marketing buttons, not controls: they size to their label
   instead of filling a column, so they are NOT .btn. One amber
   button per screen — the second is a hairline. */
.home-actions { display: flex; align-items: center; gap: 10px; }

.home-action {
  padding: 14px 20px;
  font-size: var(--fs-page-body);
  letter-spacing: 0.04em;
  color: var(--text-2);
  border: var(--border) solid var(--line);
  border-radius: var(--radius);
}
.home-action:hover { color: var(--text-1); border-color: var(--line-locked); }

.home-action--primary {
  color: var(--bg-ground);
  background: var(--accent);
  border-color: var(--accent);
}
.home-action--primary:hover {
  color: var(--bg-ground);
  background: var(--accent-hi);
  border-color: var(--accent-hi);
}

/* ============================================================
   MEDIA FRAME — one rule for every image on the page
   A 1px-ruled box on panel ground. The image covers it; the hint
   sits behind and only shows while the asset is missing.
   ============================================================ */
.home-media {
  position: relative;
  overflow: hidden;
  border: var(--border) solid var(--line);
  background: var(--bg-panel);
}
/* 16:9. With the band capped at 1440, this is the native ratio of a
   screen recording or a full-window screenshot, so the asset drops in
   without being cropped. This frame's height is ENTIRELY derived — width
   comes from --content-max, height from this ratio. There is no height
   to set. */
.home-media--hero { aspect-ratio: 16 / 9; width: 100%; }

.home-media-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.home-media-hint {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
  text-align: center;
  font-size: var(--fs-page-hint);
  max-width: 46ch;
  letter-spacing: 0.04em;
  color: var(--text-off);
}

/* ============================================================
   2. PORTAL DEMO
   ============================================================ */
.home-demo { padding-bottom: clamp(64px, 7vw, 128px); }

.home-demo-badge {
  position: absolute;
  left: 14px;
  top: 14px;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 6px 10px;
  background: var(--overlay-bg-thin);
  backdrop-filter: var(--overlay-blur);
  border: var(--border) solid var(--line);
  color: var(--text-2);
  pointer-events: none;
}
.home-demo-dot {
  display: block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
}

.home-demo-caption {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0;
  padding-top: 14px;
  font-size: var(--fs-page-hint);
  color: var(--text-3);
}
.home-demo-caption-lead { color: var(--text-2); }
.home-demo-caption .num  { margin-left: auto; }

/* ============================================================
   SECTION HEAD — eyebrow + one sentence, used by every section
   ============================================================ */
/* RULE: NO max-width HERE, AND THAT IS DELIBERATE. A `ch` resolves against the
   font-size of the element it is declared ON, and this container has no
   font-size of its own — it inherits body's 12px. `54ch` here meant 54 × the
   TWELVE-pixel character (389px), not the twenty-six-pixel one, so the
   heading below wrapped to three lines instead of two. The measure belongs
   on the element that actually sets the size: .home-head-title. */
.home-head {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-bottom: calc(var(--sp-5) * 2);
}
.home-head .t-eyebrow { color: var(--text-3); }

.home-head-title {
  margin: 0;
  /* 34ch of the 26px character. Headings want a SHORTER measure than body
     copy — two lines of ~34 is easier to take in than one long run. */
  max-width: 34ch;
  font-size: var(--fs-page-head);
  font-weight: 500;
  line-height: 1.32;
  letter-spacing: 0.01em;
  color: var(--text-1);
  text-wrap: pretty;
}

/* ============================================================
   3. STEPS
   SIX cards, ALL THE SAME WIDTH. Equal width is the rule, because
   six steps are a sequence and a sequence whose first term is
   bigger reads as a hierarchy that is not there.

   Three per row, two rows — not one row of six. At the design width
   the content box is ~1656px, so six across is ~263px a card: the
   titles wrap to three lines and the figures stop being able to show
   anything. Three across is ~541px, which is what the original cards
   were drawn at. If you do want one strip of six, this is the one
   line to change (`repeat(6, 1fr)`) plus the min-height below.

   Card anatomy is unchanged: text block on top at its natural
   height, figure takes the rest — so figures in a row start at the
   same y even when the copy runs to two lines.
   ============================================================ */
/* Section padding scales with the window instead of being fixed. Fixed
   values read as generous at 1440 and cramped at 2560, because everything
   AROUND them grows while they do not. The clamp lower bound matches the
   narrow-screen padding used below the 900px breakpoint, so narrow
   screens are unaffected. */
.home-steps {
  border-top: var(--border) solid var(--line);
  padding-top: clamp(56px, 6vw, 112px);
  padding-bottom: clamp(64px, 7vw, 128px);
}

/* RULE: THE ONE PER-SECTION SCROLL EXCEPTION, AND IT IS ARITHMETIC, NOT TASTE.
   These two sections carry a border-top rule and up to 112px of padding
   above their heading. The shared scroll-padding-top lands the section's
   BORDER 72px down, which puts the heading at 72 + 112 = 184px and leaves
   the tail of the previous section on screen.

   scroll-margin-top participates in the same sum, so cancelling the padding
   there makes the landing depend on the HEADING instead of the border:

     heading on screen = scroll-margin-top + scroll-padding-top + padding-top
                       = (--h-topbar − padding-top) + 72 + padding-top
                       = --h-topbar + 72
                       = 120px, whatever the padding happens to be

   The padding term cancels, so this holds at every viewport width even
   though the clamp makes padding-top range 56→112. Verified at 1440 and
   2546: heading lands at 120px in both.

   WARNING: the clamp is repeated here rather than factored out because it must
   equal .home-steps' padding-top exactly for the cancellation to hold. If
   you change one, change the other — a mismatch does not error, it just
   drifts the landing by the difference.
   WARNING: NOT applied to .home-hero: its target is only 48px down the document,
   so a negative margin would scroll it PAST the top of the page instead of
   clamping to 0, and the first rail dot would stop landing at the very top. */
.home-steps,
.home-roadmap {
  scroll-margin-top: calc(var(--h-topbar) - clamp(56px, 6vw, 112px));
}

.home-steps-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  /* --sp-5 (24), not --sp-4 (16). At the capped band a card is 464px wide,
     and a 16px gutter between three cards that wide reads as one block with
     hairlines rather than three objects. */
  gap: var(--sp-5);
  align-items: stretch;
}

.home-step {
  display: flex;
  flex-direction: column;
  min-height: 480px;
  border: var(--border) solid var(--line);
  background: var(--bg-panel);
}

.home-step-text {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: 22px 22px 18px;
}

.home-step-meta { display: flex; align-items: center; gap: 10px; }

.home-step-chip {
  padding: 3px 7px;
  color: var(--accent);
  border: var(--border) solid var(--accent);
  background: var(--accent-fill);
  border-radius: var(--radius);
}
.home-step-kind { color: var(--text-off); }

.home-step-title {
  margin: 0;
  font-size: var(--fs-page-title);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--text-1);
}

.home-step-body {
  margin: 0;
  max-width: 52ch;
  font-size: var(--fs-page-body);
  line-height: var(--lh-page-body);
  color: var(--text-2);
  text-wrap: pretty;
}

.home-step-figure {
  /* Locked to the source screenshot ratio (2560×1600 = 8:5), not the flex-fill
     it used to be. flex:none is required — aspect-ratio only wins if nothing
     is telling the flex item to grow past it. Six steps sharing one figure
     class means all six lock the same way; see the align-items:stretch note
     on .home-steps-grid for the one side-effect (card heights can drift a
     few px if the text blocks are uneven lengths). */
  flex: none;
  aspect-ratio: 2560 / 1600;
  min-height: 0;
  border: none;
  border-top: var(--border) solid var(--line);
  background: #0d0f10;          /* one-off: half a step below --bg-panel, so a
                                   figure well reads as recessed inside a card */
}

/* ============================================================
   4. PROGRESS
   Four cells, hairline-separated by a 1px grid gap over --line
   rather than by four borders. Honest status only: a cell that is
   not built is not a link and never carries amber.
   ============================================================ */
.home-roadmap {
  border-top: var(--border) solid var(--line);
  padding-top: clamp(56px, 6vw, 112px);
}

/* The four cells used to be hairline-separated by a 1px grid gap over a
   --line background. They are now separated by real space
   (--home-part-gap), so the separator has to move onto the cells: each one
   carries its own border-top, which also keeps the top rule broken over
   the gaps rather than running through them as one unbroken line.
   Setting --home-part-gap to 0 gives four abutting cells, NOT the old
   hairline — the background trick is gone on purpose. */
.home-roadmap-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--home-part-gap);
  /* gap: 1px;
  background: var(--line);
  border-top: var(--border) solid var(--line); */
}

.home-part {
  /* position: relative;          only so --current's inset rule can anchor */
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 24px var(--sp-4) 22px;
  /* border-top: var(--border) solid var(--line); */
  background: var(--bg-panel);
  color: var(--text-1);
}
a.home-part:hover { background: var(--bg-inset); color: var(--text-1); }

/* the one live destination: an accent rule under the cell, never a
   filled or outlined box.

   It is a pseudo-element rather than a border-bottom because a border
   always spans the full cell, and at this cell width a full-bleed rule
   reads as a container edge instead of as an underline belonging to the
   text above it. Inset by --home-part-rule (the cell's own side padding
   by default), it lines up with the name and the note. */
.home-part--current { border-bottom: var(--border) solid var(--accent); }
/* .home-part--current::after {
  content: "";
  position: absolute;
  left: var(--home-part-rule);
  right: var(--home-part-rule);
  bottom: 0;
  border-bottom: var(--border) solid var(--accent);
} */

.home-part-top { display: flex; align-items: center; justify-content: space-between; }
.home-part-index { font-size: var(--fs-page-hint); color: var(--text-3); }

.home-part-status {
  padding: 3px 6px;
  border: var(--border) solid var(--line-strong);
  border-radius: var(--radius);
  color: var(--text-2);
}
.home-part-status--done    { color: var(--done); }
/* not reachable: dashed, per the system's disabled rule */
.home-part-status--pending { color: var(--text-page-locked); border-style: dashed; border-color: var(--line-locked); }
.home-part-status--live    { color: var(--accent); border-color: var(--accent); background: var(--accent-fill); }

.home-part-name {
  font-size: var(--fs-page-lead);
  font-weight: 500;
  letter-spacing: 0.01em;
}
.home-part--pending .home-part-name { color: var(--text-page-locked); }

.home-part-note {
  margin: 0;
  max-width: 42ch;
  font-size: var(--fs-page-hint);
  line-height: 1.6;
  color: var(--text-3);
}
.home-part--pending .home-part-note { color: var(--text-page-off); }
.home-part-note--bright { color: var(--text-2); }

.home-part-foot {
  margin-top: auto;
  padding-top: 18px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

/* Three 2px segments = the three stages of a part.
   data-filled (0-3) says HOW MANY are lit; data-tone (accent|done) says in
   WHICH COLOUR. The two attributes are independent and both are read for
   both tones.

   WARNING: an unconditional `[data-tone="done"] i:nth-child(-n+3)` selector
   would fill all three segments regardless of data-filled, and would
   out-specify the [data-filled="0"] reset (0,2,1 beats 0,1,1), so an
   explicit 0 would still show fully green. Both tones must stay
   symmetric, filled per-count exactly like the accent tone below, or
   "one attribute quietly cancels the other" becomes a trap the next time
   the progress cells are hand-edited. */
.home-part-meter { display: flex; gap: 3px; }
.home-part-meter i { flex: 1; height: 2px; background: #2a2d2f; }  /* one-off:
                                   an empty track, one notch under --line-strong */
.home-part-meter[data-filled="0"] i { background: #2a2d2f; }

.home-part-meter[data-tone="accent"][data-filled="1"] i:nth-child(-n+1) { background: var(--accent); }
.home-part-meter[data-tone="accent"][data-filled="2"] i:nth-child(-n+2) { background: var(--accent); }
.home-part-meter[data-tone="accent"][data-filled="3"] i:nth-child(-n+3) { background: var(--accent); }

.home-part-meter[data-tone="done"][data-filled="1"] i:nth-child(-n+1) { background: var(--done); }
.home-part-meter[data-tone="done"][data-filled="2"] i:nth-child(-n+2) { background: var(--done); }
.home-part-meter[data-tone="done"][data-filled="3"] i:nth-child(-n+3) { background: var(--done); }

.home-part-stage { color: var(--text-3); letter-spacing: 0.1em; }
.home-part--pending .home-part-stage { color: var(--text-page-off); }

/* ============================================================
   5. CAVEAT
   ============================================================ */
/* The last thing on the page before the footer, so it needs air on BOTH
   sides or it reads as squeezed between the progress grid and the foot —
   this is the section's own breathing room, not the height of anything
   inside it. */
.home-caveat {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  /* Top padding is the LARGER of the two, so the caveat sits just past
     the vertical middle of its own section: level with the eye at the end
     of a scroll, and clearly a closing statement rather than a footnote to
     the grid above it, with visible margin below rather than a gap
     pressed up against the progress grid.

     Both values still clamp down to roughly the old numbers on a narrow
     screen, where the section is short enough that the balance stops
     mattering and the vertical budget does. */
  padding-top: clamp(64px, 8vw, 112px);
  padding-bottom: clamp(48px, 5.5vw, 76px);
}
.home-caveat-chip {
  padding: 4px var(--sp-2);
  color: var(--accent);
  border: var(--border) solid var(--accent);
  background: var(--accent-fill);
  border-radius: var(--radius);
}
.home-caveat-text { font-size: var(--fs-page-hint); color: var(--text-3); }

/* ============================================================
   FOOTER — the portal's status bar, reused as the page footer:
   same 26px height, same tone, same field order habit.
   ============================================================ */
.home-foot {
  position: sticky;
  bottom: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  height: var(--h-status);
  /* Full-bleed plate, contents on the content band. The footer is
     deliberately NOT in the PAGE MARGINS list above: a max-width there would
     leave the bar floating with bare ground either side. A status bar has
     to run edge to edge, so only its contents are inset — and the inset is
     computed to
     equal the sections' left edge rather than restated as a literal, so the
     footer's first word stays under the h1 at every width. */
  padding-inline: max(var(--inset-page), calc((100% - var(--content-max)) / 2));
  border-top: var(--border) solid var(--line);
  background: var(--overlay-bg);
  backdrop-filter: var(--overlay-blur);
  font-size: var(--fs-page-hint);
  color: var(--text-3);
}
.home-foot-strong { color: var(--text-2); }
.home-foot-sep { display: block; width: 1px; height: 11px; background: var(--line); }
.home-foot-attr { margin-left: auto; }

/* ============================================================
   NARROW SCREENS
   Three breakpoints, in this order:
   1200 — the step grid drops 3 → 2 columns (so six steps become three
          rows of two), progress 4 → 2, top bar back to its 20px inset
    900 — the rail has no room; gutters collapse to the page margin
    620 — one column everywhere
   ============================================================ */
@media (max-width: 1200px) {
  .home-steps-grid { grid-template-columns: 1fr 1fr; }
  .home-step { min-height: 420px; }
  .home-roadmap-grid { grid-template-columns: repeat(2, 1fr); }
  /* The top-bar override that used to live here is GONE, not moved: the
     bar no longer takes --inset-page at any width, so there is nothing to
     put back. See the .topbar rule at the top of this file. */
}

@media (max-width: 900px) {
  .home-rail { display: none; }
  /* .topbar is no longer in this list — it is already at --sp-4 at every
     width, so naming it here would be a no-op that reads like a rule. */
  .home-foot,
  .home-hero,
  .home-demo,
  .home-steps,
  .home-roadmap,
  .home-caveat {
    padding-inline: var(--sp-4);
  }
  .home-hero { min-height: auto; padding-top: var(--sp-5); padding-bottom: var(--sp-5); }
  /* `.home-hero-title { font-size: 30px }` was here and is GONE: the 30px
     is now the lower bound of --fs-page-hero's clamp(), so it applies at
     this width and below without a second declaration to keep in sync. */
  .home-foot-attr { display: none; }
}

@media (max-width: 620px) {
  .home-steps-grid,
  .home-roadmap-grid { grid-template-columns: 1fr; }
  .home-step { min-height: 0; }
  .home-step-figure { aspect-ratio: 4 / 3; flex: none; }
  .home-actions { flex-direction: column; align-items: stretch; }
  .home-action { text-align: center; }
  .home-demo-caption { flex-direction: column; align-items: flex-start; gap: var(--sp-1); }
  .home-demo-caption .num { margin-left: 0; }
  .home-caveat { flex-direction: column; align-items: flex-start; }
}
