/* ============================================================================
   No Hype AI — "The Verdict": the SHARED layer.

   Spec of record:  you-tube-channels/channels/channel_06_ai_academy/00_strategy/
                    brand_kit.md  ·  brand_review_2026-08.md §3.1–§3.6
   Token source:    pixel-brand/engine/styles/editorial/tokens.css
   — the blocks marked "LIFTED" are copied from it rather than re-derived, so the
   site and the social assets cannot drift apart. If this file and the spec
   disagree, the spec is right and this file is a bug.

   WHY THIS FILE EXISTS (Phase 2, first change): the holding page and the 232-page
   site are two stylesheets. Phase 1 put the approved system in holding.css only.
   Duplicating it into main.css would have created two copies of a palette that is
   also the source for every rendered social asset — so the tokens and the
   components both stylesheets need live here, once, and holding.css and main.css
   each <link> it BEFORE their own file. Neither may redefine a token below.

   Loaded by:  _includes/layouts/holding.njk  ·  _includes/layouts/base.njk
   ========================================================================= */


/* ==========================================================================
   PART 1 · Brand tokens
   LIFTED verbatim from editorial/tokens.css. Raw values — never used directly
   by a component that can appear on both grounds; use Part 2's semantics.
   ========================================================================== */
:root {
  --ink: #0B0D10;
  --ink-2: #15181D;
  --paper: #F7F6F3;
  --paper-2: #EDEBE6;
  --lime: #CDF546;
  --lime-deep: #A8CC22;
  --crimson: #FF2E5B;
  --amber: #F2B01E;
  --graphite-400: #8A9099;
  --graphite-600: #4B5158;
  --font-display: 'Archivo', system-ui, sans-serif;   /* wdth 62–125, wght 100–900 */
  --font-body: 'Inter', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, monospace;

  /* cap-height / em, MEASURED from the shipped woff2 — not from a table.
     Everything sized "by cap-height" derives from these; a wrong value here
     silently fails an acceptance test. See docs/FONTS.md. */
  --cap-display: 0.676;   /* Archivo */
  --cap-mono: 0.676;      /* JetBrains Mono */
  --cap-body: 0.661;      /* Inter */

  /* verdict block, brand_review §3.4 — ratios are against the CAP-HEIGHT */
  --verdict-h-per-cap: 1.6;
  --verdict-padx-per-cap: 0.6;
  --verdict-track: 0.06em;

  /* page furniture */
  --measure: 62rem;
  --gutter: clamp(1.5rem, 5vw, 5rem);
}


/* ==========================================================================
   PART 2 · Ground semantics — the dark/light machinery

   Three grounds, not two:

     :root / [data-theme="dark"]   INK   — the default (rev 2: dark-first)
     [data-theme="light"]          PAPER — the user's explicit light choice
     html.reading                  PAPER — lesson and long-form pages, ALWAYS

   `html.reading` is the §3.6 hard exception: "lesson and long-form pages default
   to --paper regardless of theme preference. 219 lesson pages exist to be read;
   the brand does not get to make that harder." All three land on <html>, and
   `html.reading` is (0,1,1) against [data-theme]'s (0,1,0) — so the exception
   wins over a saved theme by specificity, not by rule order.

   Contrast, computed (WCAG 2.1) — every pairing below is ≥4.5:1 for body text:
     paper/ink 18.0 · graphite-400/ink 6.05 · graphite-400/ink-2 5.53
     ink/paper 18.0 · graphite-600/paper 7.43 · lime/ink 15.5 · ink/lime 15.5
     ink/crimson 5.37 · ink/amber 10.2
   NEVER lime text or a lime stroke on paper (1.2:1). On a light ground lime
   exists ONLY as a fill with ink text — which is what --accent-fill is for.
   ========================================================================== */

:root,
[data-theme="dark"] {
  /* drives the scrollbar, form controls and the canvas behind the body */
  color-scheme: dark;

  --bg-primary: var(--ink);
  --bg-secondary: var(--ink-2);      /* the flat alternative to a glow */
  --bg-tertiary: var(--ink-2);
  --text-primary: var(--paper);
  --text-secondary: var(--graphite-400);
  /* --text-muted is deliberately the same value as --text-secondary: the palette
     has two graphites and only one of them clears 4.5:1 on this ground. The
     hierarchy is carried by size and weight instead of by a third grey. */
  --text-muted: var(--graphite-400);
  --hairline: rgba(138, 144, 153, .22);   /* graphite-400, low alpha — a neutral, not an accent */
  --border-color: var(--hairline);

  /* --accent is the accent as TEXT or STROKE. --accent-fill is the accent as a
     FILL with text on it; it is lime on both grounds, because that is the one
     form lime is allowed to take on paper. */
  --accent: var(--lime);
  --accent-hover: var(--lime-deep);
  --accent-fill: var(--lime);
  --accent-fill-text: var(--ink);

  /* the system's signature 4px rule takes the ground's accent-as-stroke */
  --rule: var(--lime);

  /* a verdict block carrying no state: paper→ink here, ink→paper on light */
  --v-neutral-fill: var(--paper);
  --v-neutral-text: var(--ink);

  /* the mark recolours only when it is INLINED; as an <img> it falls back to
     these same on-ink values baked into mark.svg */
  --mark-box: var(--graphite-400);
  --mark-check: var(--lime);

  /* code is set on ink on BOTH grounds — an ink block on a paper page is the
     editorial convention, and it keeps one code treatment across the site */
  --code-bg: var(--ink);
  --code-text: var(--paper);
}

[data-theme="light"],
html.reading {
  color-scheme: light;

  --bg-primary: var(--paper);
  --bg-secondary: var(--paper-2);
  --bg-tertiary: var(--paper-2);
  --text-primary: var(--ink);
  --text-secondary: var(--graphite-600);
  --text-muted: var(--graphite-600);
  --hairline: rgba(75, 81, 88, .22);   /* graphite-600, low alpha */
  --border-color: var(--hairline);

  --accent: var(--ink);
  --accent-hover: var(--graphite-600);
  --accent-fill: var(--lime);
  --accent-fill-text: var(--ink);

  --rule: var(--ink);

  --v-neutral-fill: var(--ink);
  --v-neutral-text: var(--paper);

  --mark-box: var(--ink);
  --mark-check: var(--ink);

  --code-bg: var(--ink);
  --code-text: var(--paper);
}


/* ==========================================================================
   PART 3 · Type primitives
   LIFTED from editorial/tokens.css. `font-stretch` drives Archivo's wdth axis;
   `font-variation-settings` is set alongside it because some engines honour the
   axis more reliably that way.
   ========================================================================== */

/* Condensed uppercase. MARKETING SURFACES ONLY (§3.2) — the hero headline, a
   chapter card. Never a section heading. */
.display {
  font-family: var(--font-display);
  font-weight: 900; font-stretch: 62%;
  font-variation-settings: 'wdth' 62, 'wght' 900;
  text-transform: uppercase;
  letter-spacing: -.01em; line-height: .92;
  margin: 0;
}
/* Sentence-case headings keep the normal width axis. */
.heading {
  font-family: var(--font-display);
  font-weight: 700; font-stretch: 100%;
  font-variation-settings: 'wdth' 100, 'wght' 700;
  letter-spacing: -.02em; line-height: 1.06; margin: 0;
}
.mono { font-family: var(--font-mono); letter-spacing: .08em; }

/* --- two shared utilities --------------------------------------------------

   `.highlight` — a run of running text lifted to the top of the page's hierarchy.
   A lime FILL with ink text, which is 15.5:1 and is the ONE form lime may take on
   either ground; lime *text* on paper is 1.2:1 and prohibited, and every page that
   uses this can be either ground. It is deliberately NOT a verdict block: the
   block is "one per fixed canvas" and the pages that need this already spend
   theirs. `box-decoration-break: clone` so a highlight that wraps gets its padding
   on both lines instead of one long ragged band.

   `.visually-hidden` — present for a screen reader, absent on screen. Used by the
   consent gate's `role="status"` region, whose whole job is to announce a change a
   sighted user can already see. `clip-path` rather than `display:none`, which
   would take it out of the accessibility tree and silence it.
   ========================================================================== */
.highlight {
  background: var(--accent-fill);
  color: var(--accent-fill-text);
  font-weight: 600;
  padding: .1em .25em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}


/* ==========================================================================
   PART 4 · The verdict block (§3.4) — the owned device
   ONE per fixed canvas. Height 1.6 × cap-height, x-padding 0.6 × cap-height, 0
   radius, JetBrains Mono Bold uppercase +0.06em, ink text on an accent fill.
   The trailing letter-spacing on the last glyph is subtracted back out of the
   right padding so the optical padding really is 0.6 × cap.
   Set --v-size (font-size) and --v-fill on the element.
   ========================================================================== */
.verdict {
  --cap: calc(var(--v-size) * var(--cap-mono));
  --v-h: calc(var(--cap) * var(--verdict-h-per-cap));
  --v-pad: calc(var(--cap) * var(--verdict-padx-per-cap));
  display: inline-flex; align-items: center; justify-content: center;
  height: var(--v-h);
  padding-left: var(--v-pad);
  padding-right: calc(var(--v-pad) - var(--verdict-track));
  background: var(--v-fill, var(--lime));
  /* --v-text exists for the neutral block, which inverts by ground. Every
     accent fill (lime/crimson/amber) takes ink text on both grounds. */
  color: var(--v-text, var(--ink));
  font-family: var(--font-mono); font-weight: 700; font-size: var(--v-size);
  text-transform: uppercase; letter-spacing: var(--verdict-track);
  line-height: 1; border-radius: 0;
}
/* The block always sits on a rule of the same colour running to the nearest
   canvas edge. Thickness max(4px, 0.10 × block height) — at body sizes that
   resolves to the literal 4px the spec draws. Derived from --v-size so the rule
   can be a SIBLING of the block. */
.verdict-rule {
  height: max(4px, calc(var(--v-size, 40px) * var(--cap-mono)
                        * var(--verdict-h-per-cap) * .10));
  background: var(--v-fill, var(--lime));
}
.verdict-group { position: relative; width: max-content; }
.verdict-group > .verdict-rule {
  position: absolute; top: 100%; right: 0;
  /* "to the nearest canvas edge" — on a centred web column that is the left
     edge of the viewport, whatever the gutter is. body clips the overflow.
     A block that is NOT in a .verdict-group (the callout) gets a rule that
     stops at its own panel edge instead, which is its nearest canvas edge. */
  left: -100vw;
}
/* State picks the fill, not the lane (brand_review Part 10 #2). A lane colour
   only applies when the block carries a bare number or date. */
.lane-pass    { --v-fill: var(--lime);    }
.lane-fail    { --v-fill: var(--crimson); }
.lane-unknown { --v-fill: var(--amber);   }
.lane-evals   { --v-fill: var(--lime);    }
/* No state at all — a label, not a verdict. Inverts with the ground. */
.lane-neutral { --v-fill: var(--v-neutral-fill); --v-text: var(--v-neutral-text); }


/* ==========================================================================
   PART 5 · The hero lockup
   Shared by the holding page (src/holding/index.njk) and the real home page
   (_includes/layouts/home.njk) via _includes/components/home-hero.njk. There is
   exactly one hero in this codebase and this is what styles it.
   ========================================================================== */

.shell {
  max-width: var(--measure);
  margin-inline: auto;
  /* Top padding is deliberately much smaller than the bottom: 4.5rem of dead
     space above a 40px mark pushed the whole hero below the fold on a laptop.
     Measured 2026-08-09. */
  padding: clamp(1.25rem, 3vw, 2.25rem) var(--gutter) clamp(3rem, 8vw, 6rem);
}

/* --- kicker: the mark + wordmark lockup ------------------------------------
   The mark sits ON the kicker line rather than floating above it. Standing alone
   it read as an orphan and cost ~90px of vertical space for a piece of furniture;
   beside the words it stands for, it is the lockup brand_kit describes.
   ⚠️ Deviation for sign-off: brand_kit sets the wordmark in that lockup as
   Archivo 700, but the Phase 1 spec asks for the kicker in JetBrains Mono. This
   keeps the mono kicker. Switching to the letter of the lockup is one line —
   swap font-family to var(--font-display) and drop the tracking. Because the
   header lockup now shares this rule, that one line changes every surface. */
.kicker {
  display: flex;
  align-items: center;
  gap: clamp(.5rem, 1.5vw, .75rem);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: clamp(.8125rem, 2.6vw, 1rem);
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--accent);
  margin: 0 0 clamp(.625rem, 1.5vw, .875rem);
}
.kicker-mark {
  flex: none;
  width: clamp(28px, 6.5vw, 38px);
  height: auto;
}

/* --- headline: the approved question -------------------------------------- */
.hero-headline {
  font-size: clamp(2.5rem, 8.4vw, 5.5rem);
  color: var(--text-primary);
  text-wrap: balance;
  margin-bottom: clamp(.75rem, 1.75vw, 1rem);
}

/* the system's signature: a 4px rule under the headline */
.hero-rule {
  height: 4px;
  background: var(--rule);
  border: 0;
  margin: 0 0 clamp(1.25rem, 2.5vw, 1.75rem);
}

.standfirst {
  font-size: clamp(1.0625rem, 1rem + .5vw, 1.375rem);
  line-height: 1.55;
  color: var(--text-primary);
  max-width: 46ch;
  margin: 0 0 clamp(1rem, 2vw, 1.25rem);
}
/* The credential sentence is the quieter of the two — it supports the first
   rather than competing with it, and graphite keeps one voice dominant. Still
   body-legible on both grounds (6.05:1 on ink, 7.43:1 on paper). */
.standfirst--credential {
  color: var(--text-secondary);
  font-size: clamp(1rem, .95rem + .35vw, 1.1875rem);
  margin-bottom: clamp(1.25rem, 2.5vw, 1.75rem);
}

/* --- byline: photo beside the host + credential lockup --------------------
   The carousel close-slide pattern (brand_review §7.3), not a new device.
   Stacked by default, side-by-side only once there is room. The credential
   lockup's second line needs ~309px at the spec'd size, and a 390px phone has a
   342px content column — so the moment a photo sits beside it, the lockup takes
   a THIRD break it is not supposed to take. Stacking below 640px keeps the
   two-line lockup the 2026-08-09 decision specifies. */
.byline {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(.875rem, 3vw, 1.75rem);
}
@media (min-width: 640px) {
  .byline { flex-direction: row; align-items: center; }
}
/* Alpha cut-out sitting DIRECTLY on the field — no tile, no circle, no border.
   §3.5: the subject is lit out of the field, not pasted onto it.
   `object-fit: contain` because the source is already a 1:1 cut-out; nothing
   here may crop through the face. */
.byline-photo {
  flex: none;
  width: clamp(104px, 24vw, 160px);
  height: auto;
  object-fit: contain;
}
.byline-text { min-width: 0; }

.host {
  font-family: var(--font-display);
  font-weight: 700; font-stretch: 100%;
  font-variation-settings: 'wdth' 100, 'wght' 700;
  font-size: clamp(1.0625rem, 1rem + .35vw, 1.25rem);
  letter-spacing: -.01em;
  color: var(--text-primary);
  margin: 0 0 .35rem;
}
/* The credential lockup. Two spans, each its own line, each `nowrap` — the break
   is specified (brand_kit.md → Host / Presenter), so it is never left to the
   browser. Set in sentence case, matching the canonical string and the carousel
   close slide: uppercasing renders the degree as "MSC", which reads as an
   acronym rather than a qualification.
   Tracking is .06em, not the kicker's .12em: at .08em the 39-character second
   line needs 328px and overflows a 320px phone. */
.credentials {
  font-family: var(--font-mono);
  font-size: clamp(.75rem, 2.4vw, .8125rem);
  letter-spacing: .06em;
  color: var(--text-secondary);
  margin: 0;
}
.credentials .cred-line { display: block; }
/* Every leaf part is nowrap, so the only break opportunities on the whole lockup
   are the ones authored in the markup. Nothing here re-breaks on its own. */
.credentials .cred-line > span {
  display: inline-block;
  white-space: nowrap;
}

/* --- the launch statement, carried by the one verdict block ---------------- */
/* 2rem top, not 2.5: at 2.5 the verdict block's 4px rule was clipped by 7px on a
   1440x800 laptop, which reads as a rendering fault rather than as a fold. */
.launch { margin: clamp(1.5rem, 3vw, 2rem) 0 clamp(3rem, 8vw, 4.5rem); }
.launch-label {
  font-family: var(--font-mono);
  font-size: clamp(.6875rem, 2.2vw, .75rem);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 .75rem;
}
/* Set on the GROUP: the rule is a sibling of the block, and custom properties
   inherit — so this is the only element both of them can read it from. Setting
   it on .verdict instead leaves the rule on its 40px fallback and it comes out
   ~4.33px instead of the literal 4px the spec draws. */
.launch .verdict-group { --v-size: clamp(1.0625rem, 3.4vw, 1.5rem); }


/* ==========================================================================
   PART 6 · Sections, the email slot and the channel rows
   Also shared between the holding page and the home page.
   ========================================================================== */

.section {
  border-top: 1px solid var(--hairline);
  padding-top: clamp(2rem, 5vw, 3rem);
  margin-top: clamp(2.5rem, 6vw, 3.5rem);
}
.section-title {
  font-size: clamp(1.375rem, 1.2rem + 1vw, 1.875rem);
  color: var(--text-primary);
  margin-bottom: clamp(1.25rem, 3vw, 1.75rem);
}
.section-lede {
  color: var(--text-secondary);
  max-width: 52ch;
  margin: -.5rem 0 clamp(1.5rem, 3vw, 2rem);
}
/* A handle quoted inside running text is an identifier, so it is set in mono and
   never allowed to break mid-string. */
.lede-handle {
  font-family: var(--font-mono);
  font-size: .9em;
  letter-spacing: .02em;
  color: var(--text-primary);
  white-space: nowrap;
}

/* --- numbered list: mono numeral + one line each ---------------------------
   Moved out of holding.css in the Kit build: /confirmed/ renders this in the
   SITE build, where holding.css is not loaded, so the list came out as a plain
   browser-numbered <ol>. Shared now, which is what this file is for.

   Used by: src/holding/index.njk ("what the lessons cover") and
   components/confirmed-body.njk ("what arrives"). The real home page shows the
   ten course cards instead — same device, card scale. */
.lanes { list-style: none; margin: 0; padding: 0; }
.lanes li {
  display: grid;
  grid-template-columns: 2.75rem 1fr;
  gap: .25rem 1rem;
  padding: clamp(.875rem, 2.5vw, 1.125rem) 0;
  border-bottom: 1px solid var(--hairline);
}
.lanes li:last-child { border-bottom: 0; }
.lane-no {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(1.125rem, 4vw, 1.5rem);
  letter-spacing: .02em;
  color: var(--text-muted);
  line-height: 1.25;
  font-variant-numeric: tabular-nums;
}
.lane-name {
  font-family: var(--font-display);
  font-weight: 700; font-stretch: 100%;
  font-variation-settings: 'wdth' 100, 'wght' 700;
  font-size: clamp(1.0625rem, 3.6vw, 1.25rem);
  letter-spacing: -.01em;
  color: var(--text-primary);
  line-height: 1.25;
}
.lane-what {
  grid-column: 2;
  color: var(--text-secondary);
  font-size: clamp(.9375rem, 2.9vw, 1rem);
  line-height: 1.5;
}

/* --- email slot — built, empty in v1 (no form, no fake input) -------------- */
.signup {
  background: var(--bg-secondary);   /* the flat alternative to a bloom */
  padding: clamp(1.5rem, 4vw, 2.25rem);
}
.signup p { margin: 0 0 .75rem; }
.signup p:last-child { margin-bottom: 0; }

/* The three things subscribing gets you. Same mono-numeral device as `.lanes` and
   the course cards, at list scale — numbered because the offer is three specific
   things and a reader should be able to count them.

   `counter` rather than a browser-drawn marker: `list-style` puts the numeral in
   the UA's font at the UA's size, and this one has to be JetBrains Mono, tabular,
   and aligned with the text column. It also has to render identically in a build
   whose stylesheet zeroes every margin (main.css) and one that does not
   (holding.css), so every value below is set explicitly. */
.offer-list {
  list-style: none;
  counter-reset: offer;
  margin: 0 0 .75rem;
  padding: 0;
}
.offer-list li {
  counter-increment: offer;
  display: grid;
  grid-template-columns: 1.75rem 1fr;
  gap: .5rem;
  margin: 0 0 .5rem;
  padding: 0;
  color: var(--text-primary);
  line-height: 1.55;
}
.offer-list li:last-child { margin-bottom: 0; }
.offer-list li::before {
  content: counter(offer);
  font-family: var(--font-mono);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
  color: var(--text-muted);
}
.signup .status {
  font-family: var(--font-mono);
  font-size: clamp(.75rem, 2.4vw, .8125rem);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* --- the consent gate in front of the Kit embed ---------------------------
   components/home-signup.njk. Part of the page, NOT an overlay: the site has one
   third-party script, on one page, and a full-screen modal in front of a holding
   page is disproportionate — it reads as the hype the brand is against.

   The four states are driven entirely by `data-consent-state` on `.consent`,
   written by the gate's script. The server renders `nojs`, so with JavaScript off
   the visitor gets the honest state (nothing loaded, nothing stored, here is the
   hosted form) rather than two buttons that do nothing.

   Ground: `.signup` is --bg-secondary — --ink-2 on the dark default, --paper-2 in
   the light theme — so everything below is semantics only. Measured against those
   two grounds: paper/ink-2 16.7 · graphite-400/ink-2 5.53 · ink/paper-2 16.4 ·
   graphite-600/paper-2 6.86 · ink/lime 15.5 · ink/lime-deep 10.4. */
.consent {
  margin-top: clamp(1.25rem, 3vw, 1.75rem);
  padding-top: clamp(1.25rem, 3vw, 1.75rem);
  border-top: 1px solid var(--hairline);
}
.consent-block { display: none; }
.consent[data-consent-state="nojs"]      .consent-block--nojs,
.consent[data-consent-state="undecided"] .consent-block--undecided,
.consent[data-consent-state="granted"]   .consent-block--granted,
.consent[data-consent-state="denied"]    .consent-block--denied { display: block; }
/* The no-consent route is offered in every state except the one where the form is
   already on the page. */
.consent[data-consent-state="granted"] .consent-alt { display: none; }

.consent .consent-ask {
  color: var(--text-primary);
  margin: 0 0 clamp(.875rem, 2vw, 1.125rem);
  max-width: 52ch;
}

.consent-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .625rem .75rem;
  margin: 0 0 clamp(.875rem, 2vw, 1.125rem);
}
/* Accept and reject are the SAME button. Equal prominence is the requirement, so
   there is no quieter variant to be the reject: same fill, same type, same size,
   one click each. Lime fill with ink text is the one form lime may take on either
   ground, so this needs no per-theme variant. */
.consent-btn {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(.75rem, 2.4vw, .8125rem);
  letter-spacing: .06em;
  text-transform: uppercase;
  background: var(--accent-fill);
  color: var(--accent-fill-text);
  border: 0;
  border-radius: 0;
  padding: .75rem 1.125rem;
  cursor: pointer;
}
.consent-btn:hover { background: var(--lime-deep); }

/* The promise, stated before the choice rather than inside the form being asked
   about. Quieter than the question it follows — it is reassurance, not the ask. */
.consent-promise {
  color: var(--text-secondary);
  font-size: clamp(.875rem, 2.8vw, .9375rem);
  line-height: 1.6;
  margin: -.375rem 0 clamp(.875rem, 2vw, 1.125rem);
  max-width: 52ch;
}

.consent-foot,
.consent-alt {
  color: var(--text-secondary);
  font-size: clamp(.875rem, 2.8vw, .9375rem);
  line-height: 1.6;
  margin: clamp(.75rem, 2vw, 1rem) 0 0;
  max-width: 52ch;
}
/* Withdrawal, as a text button rather than a third lime one — see the comment in
   home-signup.njk. It still has to look like the control it is, so it takes the
   ground's accent-as-text and an underline it never drops. */
.consent-undo {
  font: inherit;
  color: var(--accent);
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: .15em;
  cursor: pointer;
}
.consent-undo:hover { color: var(--accent-hover); }

/* The status region carries `.visually-hidden` (Part 3) and needs nothing else —
   each state block already states its own state on screen, so an on-screen status
   line was the same sentence twice.
   Deliberately NOT `display: none` when empty: a `role="status"` region has to be
   in the accessibility tree BEFORE its text changes, or the change is not
   announced. Visually hidden costs no layout, so there is nothing to reclaim. */
.consent-status { margin: 0; }

/* No spacing rule on the mount: Kit's form ships 40px of its own padding top and
   bottom (measured on the injected DOM), and adding to that opens a gap that
   reads as a rendering fault. The mount is empty until consent, and an empty
   block contributes nothing. */

/* holding.css and main.css each supply :focus-visible for links, but not for
   these buttons, and the gate is the one place on the page where keyboard reach
   is a compliance question rather than a nicety. */
.consent :focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}


/* --- the Kit sign-up form -------------------------------------------------
   Kit's embed renders REAL DOM into the page (not an iframe), which is the only
   reason this is possible at all — its colours can be corrected from here.

   WHY !important, which is otherwise not used in this codebase: Kit bakes the
   form ground into an inline `style="background-color:#15181D"` attribute, and
   inline styles beat every selector; its other rules ship in a <style> injected
   INSIDE the form at runtime, at `.formkit-form[data-uid="…"] .formkit-input`
   specificity (0,3,0), so they also land after anything written here. This is a
   third-party widget overriding the page, so the page has to override back.

   WHAT IT FIXES: a hex typed into Kit's editor can only ever be right on ONE
   ground, and this site has two. Measured 2026-08-12 with the form's ground set
   to ink-2: on the light theme the disclaimer link came out ink-on-ink at
   1.09:1 — invisible — because the link colour follows --accent while the ground
   stayed baked. Everything below is theme-aware, so both grounds resolve.

   These selectors are deliberately the same for the HTML form-action embed, so
   switching embeds (docs/KIT-V2-CHECKLIST.md) changes nothing here. */
.signup .formkit-form,
.signup .formkit-background {
  background: var(--bg-secondary) !important;
  border: 0 !important;
  border-radius: 0 !important;
  opacity: 1 !important;
  box-shadow: none !important;
}
.signup .formkit-form { font-family: var(--font-body) !important; }

.signup .formkit-header,
.signup .formkit-header * {
  font-family: var(--font-display) !important;
  font-weight: 700 !important;
  font-stretch: 100% !important;
  font-variation-settings: 'wdth' 100, 'wght' 700 !important;
  letter-spacing: -.02em !important;
  color: var(--text-primary) !important;
}
.signup .formkit-subheader,
.signup .formkit-subheader * {
  font-family: var(--font-body) !important;
  color: var(--text-secondary) !important;
}
/* the disclaimer line, which is where the 1.09:1 failure showed up */
.signup .formkit-guarantee,
.signup .formkit-guarantee * { color: var(--text-muted) !important; }
.signup .formkit-guarantee a { color: var(--accent) !important; }

.signup .formkit-input {
  background: var(--paper) !important;   /* never pure #fff */
  color: var(--ink) !important;          /* never pure #000 */
  border: 1px solid var(--graphite-400) !important;
  border-radius: 0 !important;
  font-family: var(--font-body) !important;
}
/* Lime fill, ink text — the one form lime may take on either ground, so the
   button needs no per-theme variant. */
.signup .formkit-submit,
.signup .formkit-submit > span {
  background: var(--accent-fill) !important;
  color: var(--accent-fill-text) !important;
  border-radius: 0 !important;
  font-family: var(--font-mono) !important;
  font-weight: 700 !important;
  letter-spacing: .06em !important;
  text-transform: uppercase !important;
}
.signup .formkit-submit:hover,
.signup .formkit-submit:focus { background: var(--lime-deep) !important; }
.signup .formkit-form :focus-visible {
  outline: 2px solid var(--accent) !important;
  outline-offset: 2px !important;
}

/* --- channels -------------------------------------------------------------- */
/* 22rem is the floor because a row has to hold the icon, the platform name and
   `@no.hype.ai.expert` on ONE line. Narrower and the handle breaks mid-word,
   which reads as a bug rather than as a wrap. Borders sit on the rows, not on a
   grid gap — a gap-coloured grid paints a visible empty cell when the item count
   is odd. */
.channels {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* min() so the 22rem floor cannot itself overflow a narrower container — a bare
     minmax(22rem, 1fr) forces a 352px track and scrolls a 320px phone sideways. */
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  column-gap: clamp(1rem, 3vw, 2.5rem);
}
.channels li { border-bottom: 1px solid var(--hairline); }
.channels a {
  display: flex;
  align-items: center;
  /* On a very narrow phone the handle drops to its own line rather than breaking
     mid-string; `margin-left: auto` keeps it right-aligned either way. */
  flex-wrap: wrap;
  gap: .25rem .875rem;
  padding: clamp(.875rem, 2.5vw, 1.0625rem) clamp(.25rem, 1.5vw, .625rem);
  color: var(--text-primary);
  text-decoration: none;
}
/* The accent is the hover/focus state, not the resting one: five accented rows
   would blow the "≤12% of any surface" budget and put a second visual centre on
   the page next to the verdict block. */
.channels a:hover,
.channels a:focus-visible { background: var(--bg-secondary); color: var(--accent); }
.social-icon { flex: none; color: var(--text-muted); }
.channels a:hover .social-icon,
.channels a:focus-visible .social-icon { color: currentColor; }
.channel-name {
  font-family: var(--font-display);
  font-weight: 700; font-stretch: 100%;
  font-variation-settings: 'wdth' 100, 'wght' 700;
  font-size: 1rem;
  letter-spacing: -.01em;
}
.channel-handle {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: clamp(.75rem, 2.4vw, .8125rem);
  letter-spacing: .04em;
  color: var(--text-muted);
  /* the handle is long and the column is narrow — never let it force a scrollbar */
  overflow-wrap: anywhere;
  text-align: right;
}
.channels a:hover .channel-handle,
.channels a:focus-visible .channel-handle { color: currentColor; }


/* ==========================================================================
   PART 7 · Footer
   One footer for both the holding page and the site — the personal-capacity
   disclosure is a condition of OBA approval 00133783 and has to appear on
   every public surface, not just the holding page.
   ========================================================================== */
.site-footer {
  border-top: 1px solid var(--hairline);
  margin-top: clamp(3rem, 8vw, 5rem);
  padding-top: clamp(1.75rem, 4vw, 2.5rem);
  color: var(--text-secondary);
  font-size: clamp(.8125rem, 2.6vw, .875rem);
  line-height: 1.6;
}
/* layouts/base.njk renders the footer as a direct child of <body>, outside any
   container, so on the 235-page site it ran flush to the viewport edge while
   every other element sat on the measure. layouts/holding.njk renders it INSIDE
   .shell, which already supplies both — hence the direct-child scope, which
   would otherwise double the padding there. Same max-width + padding as .shell,
   and `box-sizing: border-box` is global, so the two align exactly. */
body > .site-footer {
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.site-footer p { margin: 0 0 .75rem; max-width: 62ch; }
.site-footer p:last-child { margin-bottom: 0; }
.site-footer strong { color: var(--text-primary); font-weight: 600; }
.site-footer .copyright {
  font-family: var(--font-mono);
  letter-spacing: .06em;
  text-transform: uppercase;
  font-size: clamp(.6875rem, 2.2vw, .75rem);
  /* the 62ch measure is for the disclosure paragraph; this line is a single
     statement and must not be wrapped by it */
  max-width: none;
}


/* ==========================================================================
   PART 8 · Long-form policy pages (/privacy/)

   Lives HERE rather than in main.css because /privacy/ is the one page built by
   BOTH Pages projects (src/privacy.njk), and the holding build ships only
   fonts.css, tokens.css and holding.css. Everything the page needs is therefore
   in the shared layer, and it renders identically under either shell.

   That also means it cannot lean on main.css's global element rules. The two
   stylesheets have different resets — main.css zeroes every margin, holding.css
   does not — so every element below sets its own margin and padding rather than
   inheriting a UA default that exists in one build and not the other.

   Colours are semantics only: the page is always `html.reading`, so it resolves
   to ink on paper 18.0:1, graphite-600 body 7.43:1 and ink links on paper —
   all inside brand_kit's contrast table, and no lime on paper anywhere.
   ========================================================================== */
.policy {
  /* Shorter than the 62rem shell: legal prose is read line by line, and 68
     characters is the top of the comfortable measure. */
  max-width: 68ch;
}

.policy-head { margin: 0 0 clamp(1.75rem, 4vw, 2.5rem); }
.policy h1 {
  font-size: clamp(2rem, 1.6rem + 2vw, 3rem);
  color: var(--text-primary);
  margin: 0 0 clamp(.75rem, 1.75vw, 1rem);
}
/* Sentence case, normal width — §3.2 keeps the condensed uppercase display axis
   on marketing surfaces. A privacy policy is not one. */
.policy h2 {
  font-size: clamp(1.1875rem, 1.1rem + .6vw, 1.5rem);
  color: var(--text-primary);
  margin: clamp(2.25rem, 5vw, 3rem) 0 clamp(.625rem, 1.5vw, .875rem);
}
.policy .hero-rule { margin: 0 0 clamp(.875rem, 2vw, 1.125rem); }

.policy-updated {
  font-family: var(--font-mono);
  font-size: clamp(.6875rem, 2.2vw, .75rem);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0;
}

.policy p {
  color: var(--text-secondary);
  line-height: 1.7;
  margin: 0 0 clamp(.875rem, 2vw, 1.125rem);
}
.policy strong { color: var(--text-primary); font-weight: 600; }
.policy em { font-style: italic; }

.policy ul {
  list-style: disc;
  /* padding, not margin: the marker has to sit inside the measure, and the
     holding build has no global list rule to fall back on. */
  padding: 0 0 0 1.35rem;
  margin: 0 0 clamp(.875rem, 2vw, 1.125rem);
}
.policy li {
  color: var(--text-secondary);
  line-height: 1.7;
  margin: 0 0 .5rem;
}
.policy li:last-child { margin-bottom: 0; }
.policy li::marker { color: var(--text-muted); }

/* Cookie names are identifiers, so they are set in mono — but as running text,
   not as a code block: main.css's `code` rule (a filled --bg-secondary chip)
   does not exist in the holding build, and half the site rendering a grey chip
   while the other half renders bare mono is exactly the drift tokens.css is
   here to prevent. */
.policy code {
  font-family: var(--font-mono);
  font-size: .875em;
  color: var(--text-primary);
  background: none;
  padding: 0;
  overflow-wrap: break-word;
}

/* Links inherit the ground's accent-as-text from each stylesheet's own `a`
   rule (ink on this paper ground) — the only thing added is a break for the
   long mailto address, which overflows a 390px measure otherwise. */
.policy a { overflow-wrap: break-word; }

/* --- the "Cookies and similar storage" table ------------------------------
   A table because a table is checkable — four rows, four columns, each cell one
   fact. It has to be legible in BOTH builds, so every selector below is scoped
   to `.policy` (0,1,1): main.css has its own bare `table`/`th`/`td` rules, it
   loads AFTER tokens.css, and an unscoped rule here would be overridden on the
   235-page site and not on the holding page — the exact drift this file exists
   to prevent.

   Below 40rem the table STACKS instead of scrolling. A four-column table in a
   342px content column (a 390px phone) is either a sideways scroll the reader
   has to discover or four unreadable columns; stacked, each row becomes a
   labelled block and the whole thing stays in the flow. The labels come from
   `data-label` on each cell, so the markup carries them once and the header row
   is simply hidden — there is no second copy of the column names.
   ========================================================================== */
.policy-table { margin: 0 0 clamp(1.25rem, 3vw, 1.75rem); }
.policy table {
  width: 100%;
  border-collapse: collapse;
  margin: 0;
  font-size: clamp(.8125rem, 2.6vw, .875rem);
  line-height: 1.55;
}
.policy th,
.policy td {
  text-align: left;
  vertical-align: top;
  padding: .625rem .75rem .625rem 0;
  border-bottom: 1px solid var(--hairline);
}
.policy th {
  font-family: var(--font-mono);
  font-size: clamp(.6875rem, 2.2vw, .75rem);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-primary);
  /* Explicit, not inherited: main.css fills `th` with --bg-secondary and the
     holding build has no such rule, so leaving it unset renders a grey header
     band on the 235-page site and a bare one on the holding page — the same
     source file, two different tables. */
  background: none;
  border-bottom-width: 4px;
  border-bottom-color: var(--rule);   /* the system's 4px rule, doing a job */
}
.policy td { color: var(--text-secondary); }
.policy td:first-child { color: var(--text-primary); }
.policy tbody tr:last-child td { border-bottom: 0; }
/* The identifier column is the one that must never break mid-string. */
.policy td code { white-space: nowrap; }

@media (max-width: 40rem) {
  .policy table,
  .policy tbody,
  .policy tr,
  .policy td { display: block; width: 100%; }
  /* Not `display:none` — the header is still the accessible name of each column
     for a screen reader reading the table linearly. */
  .policy thead {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .policy tbody tr {
    padding: .875rem 0;
    border-bottom: 1px solid var(--hairline);
  }
  .policy tbody tr:last-child { border-bottom: 0; padding-bottom: 0; }
  .policy tbody td {
    padding: 0 0 .375rem;
    border-bottom: 0;
  }
  .policy tbody td:last-child { padding-bottom: 0; }
  .policy tbody td::before {
    content: attr(data-label);
    display: block;
    font-family: var(--font-mono);
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-muted);
  }
  /* The name is its own label — "NAME: __cf_bm" is noise. */
  .policy tbody td:first-child::before { content: none; }
  .policy tbody td:first-child { padding-bottom: .625rem; }
}
