/*
 * Madruga — design system · THE LEDGER
 * -----------------------------------
 * The page is the artefact: a printed audit document. Cream stock, brown-black ink,
 * hairline rules, numbered sections, tabular figures, and ONE stamp red reserved for
 * failure and open gates. Every neutral carries a little hue — a 0-chroma grey next to
 * this stock reads as a cold patch, so there are none, and a spec pins that.
 *
 * Load-bearing rules, kept from docs/product/Madruga Design Doc v2.dc.html §2:
 *
 *   D1  Never a number without its denominator. "4 of 61 clicks · 6.5%", not "4 signups".
 *   D3  Colour marks a STATE, never a QUANTITY. Bars, fractions and traces are grey.
 *       In the Ledger there is exactly one hue, --stamp, and it means the engine
 *       concluded something failed, or a gate is still open. Nothing else is coloured.
 *   §4  No border radius above 0. Depth is 1px rules and a single surface step.
 *       NO SHADOWS, NO GRADIENTS, NO ICONS, NO ILLUSTRATIONS.
 *   §17 Every non-disabled text colour clears 4.5:1 against the surface it sits on.
 *
 * Deliberate departures from the design doc, authorised for this direction:
 *   - The wordmark is uppercase and letterspaced. §4b's "the gate meter IS the logo"
 *     (lowercase over a 62%-filled rule) is retired.
 *   - The four-hue signal set is collapsed to --stamp. §17 already sanctions this:
 *     "State is never colour alone… a monochrome screenshot loses nothing." Every
 *     state still ships its uppercase name in text, so INVEST vs CUT LOOSE survives.
 *   - Marketing surfaces render evidence as tables rather than progress bars. The
 *     app's real withheld verdicts keep their gate meters (D2).
 *
 * ONE GROUND. There is no dark mode and no [data-theme] override — a document does
 * not invert. Do not reintroduce a prefers-color-scheme block; a spec pins its absence.
 *
 * With Propshaft, `stylesheet_link_tag :app` bundles every file in this directory.
 */

/* ------------------------------------------------------------------ Tokens --- */
:root {
  /* Two surfaces, and only two. Paper is the ground; paper-2 is an inset — table
     header rows, callouts, wells. There is no third "card" surface: a card is
     paper with a rule around it. */
  --paper:      oklch(0.958 0.016 84);
  --paper-2:    oklch(0.928 0.018 84);

  /* Ink. Brown-black, not neutral — the stock is warm, and a neutral black on it
     reads blue. Four steps, each with a job. ink-4 is the faintest and is used for
     RULES and borders only — at 0.52 it does not clear 4.5:1 on paper, so it never
     carries text (§17). Row numbers and other faint text take ink-3. */
  --ink:        oklch(0.19 0.008 60);   /* text, all borders, inverted surfaces */
  --ink-2:      oklch(0.31 0.008 60);   /* body copy */
  --ink-3:      oklch(0.44 0.010 60);   /* labels, section numbers, faint text */
  --ink-4:      oklch(0.52 0.008 60);   /* input borders, notches — never text */

  --rule:       oklch(0.87 0.012 70);   /* internal hairlines */
  --rule-soft:  oklch(0.84 0.012 70);   /* under section headers */

  /* The stamp. Failure and open gates ONLY: kill-pending, kill-confirmed, an
     unmet gate, at-risk, an alert. If it appears anywhere else it stops meaning
     anything, and the one signal this system has left is gone. */
  --stamp:      oklch(0.55 0.19 25);   /* borders, underlines */
  --stamp-ink:  oklch(0.50 0.19 25);   /* text — clears 5.9:1 on paper */

  /* Inverted surfaces. Used exactly three times: the Solo pricing column, the
     closing block, and the nav CTA cell. */
  --on-ink:     oklch(0.958 0.016 84);
  --on-ink-2:   oklch(0.78 0.012 84);

  /* Type. Archivo for anything read as a sentence or a label; Geist Mono strictly
     for machine output — figures, dates, slugs, state names, row numbers. */
  --font-sans: "Archivo", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Rhythm. ONE gutter, paid by every row on the page: the nav wordmark, every
     section header, every heading, the closing block and the footer wordmark all
     start on the same line. The old --section-x did this job AND the padding between cells
     in a split row, which is why those two edges disagreed. They are separate now,
     and only the OUTER edges of a split row are allowed to spend --gutter. */
  --gutter:  clamp(20px, 4.5vw, 56px);  /* page edge → content, every row */
  --cell-x:  32px;                      /* between cells inside a split row */
  --measure: 52ch;                      /* cap the MEASURE, never the page */
  --section-y: 64px;

  /* One radius, stated once. Zero, everywhere, forever. */
  --r: 0;
}

/* ------------------------------------------------------------------ Base --- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink-2);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.58;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection { background: var(--ink); color: var(--on-ink); }

/* Links are ink with a rule under them. There is no accent colour to spend. */
a { color: var(--ink); text-decoration: none; }
a:hover { color: var(--ink); }

img, svg { max-width: 100%; display: block; }

/* --------------------------------------------------------------- The page --- */
/* The page runs the full width of the viewport and the horizontal rules run with
   it, edge to edge. It used to sit in a centred 1280px box with a border around it,
   which read as a widget floating in a field; a document does not have an outer
   border, it has ruled rows. Only the CONTENT inside the rules is inset, by
   --gutter. Marketing views opt in with `content_for :framed, true`. */
.page--framed {
  background: var(--paper);
}

/* ------------------------------------------------------------ Typography --- */
h1, h2, h3, h4 {
  color: var(--ink);
  font-weight: 700;
  margin: 0 0 0.5em;
  letter-spacing: -0.03em;
  line-height: 1.02;
}

h1 { font-size: clamp(38px, 5.6vw, 66px); line-height: 0.98; max-width: 15ch; }
h2 { font-size: clamp(28px, 3.6vw, 38px); line-height: 1.02; }
h3 { font-size: 21px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.15; }
h4 { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }

p { margin: 0 0 1em; max-width: var(--measure); line-height: 1.58; }

/* The mono eyebrow is retired. Every section opens with a named header row
   (.sec-head), and elsewhere the eyebrow itself renders as that same row.
   Same type, same rule, so the two read as one device. */
.eyebrow {
  font-family: var(--font-sans);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 32px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--rule-soft);
  max-width: none;
}

.lede { font-size: 17.5px; color: var(--ink-2); line-height: 1.55; }
.muted { color: var(--ink-3); }
.mono { font-family: var(--font-mono); }
.center { text-align: center; }

/* -------------------------------------------------------- Section header --- */
/* The section name over a hairline. This replaces the mono eyebrow everywhere.
   The name is decoration in the accessibility tree — the heading that follows
   carries the meaning. */
.sec-head {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding-bottom: 10px;
  margin-bottom: 32px;
  border-bottom: 1px solid var(--rule-soft);
}
.sec-head__name {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ------------------------------------------------------------- Layout ------ */
.container {
  width: 100%;
  padding-inline: var(--gutter);
}

/* Sections are separated by a full-bleed 1px ink rule, never by whitespace alone. */
.section { padding-block: var(--section-y); border-bottom: 1px solid var(--ink); }
.section:last-of-type { border-bottom: 0; }
.section--surface { background: var(--paper-2); }
.section--well { background: var(--paper-2); }
/* Legacy modifier: the rule is now unconditional, so this is a no-op kept so the
   dozen views that emit it keep validating. */
.section--bordered { border-top: 0; }

/* Inverted, full-bleed. Used exactly once on the marketing site: the closing block. */
.section--invert { background: var(--ink); color: var(--on-ink-2); border-bottom: 0; }
.section--invert h1, .section--invert h2, .section--invert h3 { color: var(--on-ink); }
.section--invert p { color: var(--on-ink-2); }
.section--invert a { color: var(--on-ink); }

.grid { display: grid; gap: 24px; }
/* minmax(0, 1fr), never 1fr. A bare `1fr` is `minmax(auto, 1fr)`, so a track holding
   anything unbreakable — the report's `white-space: pre` block, a wide table — sizes to
   that thing's min-content and pushes the grid past the viewport, squeezing the other
   track down to one word per line. The columns must stay equal whatever is inside them. */
@media (min-width: 860px) {
  .grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.grid > * { min-width: 0; }

/* Split header: H2 left, the intro paragraph right, bottom-aligned. The second of
   the two section shapes (the other is a bordered table), alternated so the page
   never falls into a single repeating rhythm. */
.split { display: grid; gap: 24px; margin-bottom: 40px; }
.split > :last-child { margin-bottom: 0; }
@media (min-width: 860px) {
  .split { grid-template-columns: 0.85fr 1.15fr; gap: 56px; align-items: end; }
  .split h2 { margin-bottom: 0; }
}

/* ------------------------------------------------------------- Buttons ----- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 15px 26px;
  border-radius: var(--r);
  border: 1px solid var(--ink);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
  white-space: nowrap;
}

.btn--primary { background: var(--ink); color: var(--on-ink); }
.btn--primary:hover { background: var(--ink-2); color: var(--on-ink); }

.btn--secondary { background: transparent; color: var(--ink); }
.btn--secondary:hover { background: var(--paper-2); color: var(--ink); }

/* The quiet one. Still a rectangle in a rule, so it can sit inside a button pair
   without the pair looking half-drawn. */
.btn--ghost { background: transparent; color: var(--ink); }
.btn--ghost:hover { background: var(--paper-2); }

.btn--block { width: 100%; }
.btn--sm { padding: 11px 18px; font-size: 11.5px; }

/* On an inverted ground the fill and the ground swap. */
.section--invert .btn--primary,
.invert .btn--primary { background: var(--on-ink); color: var(--ink); border-color: var(--on-ink); }
.section--invert .btn--primary:hover,
.invert .btn--primary:hover { background: var(--on-ink-2); color: var(--ink); }

/* A button PAIR is one bordered box split by a single rule, not two floating
   buttons with a gap. The individual buttons drop their own borders. */
.btnpair { display: inline-flex; border: 1px solid var(--ink); margin-top: 28px; }
.btnpair .btn { border: 0; }
.btnpair .btn + .btn { border-left: 1px solid var(--ink); }

/* Social sign-in buttons. Google and Apple both publish an exact white / exact black
   for these, and we depart from it on purpose: a #fff button on cream stock is a cold
   patch, which is precisely the thing this ground exists to avoid. Shape, wording and
   logo still follow the guidelines — only the neutrals come from our tokens. */
.provider-btns { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; }
.provider-btns form.button_to { margin: 0; width: 100%; }
.provider-btn { display: flex; align-items: center; justify-content: center; gap: 11px; width: 100%; padding: 13px; border-radius: var(--r); border: 1px solid var(--ink); background: var(--paper); color: var(--ink); font-weight: 600; font-size: 15px; cursor: pointer; font-family: inherit; transition: filter 120ms ease; }
.provider-btn:hover { filter: brightness(0.96); }
.provider-btn--apple { border-color: var(--ink); background: var(--ink); color: var(--on-ink); }
.provider-btn--apple:hover { filter: brightness(1.4); }
.provider-btn__logo { flex: 0 0 auto; display: block; }
.provider-caption { text-align: center; color: var(--ink-3); font-size: 13px; margin: 4px 0 0; }

/* --------------------------------------------------------------- Forms ----- */
label {
  display: block;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 7px;
}

input[type="text"], input[type="email"], input[type="number"], input[type="password"],
input[type="date"], input[type="url"],
textarea, select {
  width: 100%;
  font-family: var(--font-sans);
  font-size: 16px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--ink-4);
  border-radius: var(--r);
  padding: 12px 14px;
  transition: border-color 120ms ease;
}
input:focus, textarea:focus, select:focus { border-color: var(--ink); }
input::placeholder, textarea::placeholder { color: var(--ink-3); }

/* Focus ring: 2px ink at 2px offset, NEVER removed (§17). There is no accent
   colour left to spend on it, and ink on paper is the strongest ring available. */
:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.field { margin-bottom: 16px; }
.field-hint { font-size: 13px; color: var(--ink-3); margin-top: 6px; }
.field-error { font-size: 13px; color: var(--stamp-ink); margin-top: 6px; }

/* --------------------------------------------------------------- Cards ----- */
/* A card is paper inside a rule. There is no lifted surface and no shadow. */
.card {
  background: var(--paper);
  border: 1px solid var(--ink);
  border-radius: var(--r);
  padding: 26px;
}
.card__kicker {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 14px;
}
.card__title { font-size: 21px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.15; color: var(--ink); margin: 0 0 10px; }
.card__body { font-size: 15px; color: var(--ink-2); margin: 0; max-width: 46ch; }

/* Seamed grid: cells of ONE bordered grid sharing 1px dividers — not three cards
   floating with gutters between them. The seam is the grid background showing
   through a 1px gap; one rule, no extra elements. */
.grid--seam { gap: 1px; background: var(--ink); border: 1px solid var(--ink); }
.grid--seam > .card { border: 0; padding: 30px 28px; }

/* A decision: a mono index in its own column, the imperative on one line, the
   arithmetic under it. Numbered because the review asks for at most three and
   the count is the point. */
.decision { display: flex; gap: 16px; padding: 14px 0; border-bottom: 1px solid var(--rule); }
.decision:last-child { border-bottom: 0; }
.decision__index { font-family: var(--font-mono); font-size: 12px; color: var(--ink-3); padding-top: 2px; }
.decision__action { font-size: 16px; font-weight: 600; color: var(--ink); margin: 0 0 6px; }

/* Marker lists. An em-dash in mono, never a checkmark and never an icon. */
.checks { list-style: none; margin: 0; padding: 0; }
.checks li { position: relative; padding-left: 28px; margin-bottom: 9px; font-size: 15px; color: var(--ink-2); }
.checks li::before {
  content: "—";
  position: absolute; left: 0;
  font-family: var(--font-mono);
  color: var(--ink-3);
}
/* Its inverse, for "what you can't do" lists. Same shape so the two read as a pair;
   the glyph differs and the cell text always says which it is (§17). */
.checks--x li::before { content: "×"; color: var(--stamp-ink); }
/* No marker at all. An archived community is not an achievement. */
.checks--bare li { padding-left: 0; }
.checks--bare li::before { content: none; }

/* --------------------------------------------------------- Disclosure ------ */
/* Collapsed help. A native <details>, so it toggles with no JS — which is what we
   want under the enforced CSP, where an inline <script> is blocked silently. */
.disclosure {
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: var(--r);
}
.disclosure > summary {
  padding: 13px 16px;
  color: var(--ink-2);
  font-size: 14px;
  cursor: pointer;
  list-style: none;
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::before { content: "▸ "; color: var(--ink-3); }
.disclosure[open] > summary { color: var(--ink); border-bottom: 1px solid var(--rule); }
.disclosure[open] > summary::before { content: "▾ "; }
.disclosure__body { padding: 6px 16px 16px; color: var(--ink-2); font-size: 14px; line-height: 1.7; }
.disclosure__body ol { margin: 0; padding-left: 20px; }
.disclosure__body li { margin: 12px 0; }
.disclosure__body code {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink);
  overflow-wrap: anywhere;
}

/* ------------------------------------------------------ Warning callout ---- */
/* Carries the stamp: a callout is the instrument reporting a problem. */
.callout {
  background: var(--paper-2);
  border: 1px solid var(--stamp);
  border-left-width: 2px;
  border-radius: var(--r);
  padding: 18px 20px;
  color: var(--stamp-ink);
  font-size: 14px;
}
.callout__label { font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; }

/* ------------------------------------------------------- Signed-in chrome -- */
/* The alarm and the nav were BOTH position:sticky at top:0. Two sticky elements at
   the same offset stack by z-index rather than vertically, so on scroll the nav
   parked underneath the opaque alarm and disappeared. One wrapper owns the stick
   and the two stack inside it normally. .nav stays sticky on its own for the
   marketing pages, which have no wrapper. */
.app-chrome { position: sticky; top: 0; z-index: 60; }
.app-chrome .nav { position: static; }

/* --------------------------------------------------------------- Nav -------- */
/* A bordered strip, not a floating bar. Three cells divided by 1px rules:
   wordmark | links | CTA. Opaque paper — a translucent blur belongs to a product
   that wants to look like glass, and this one wants to look like paper. */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--paper);
  border-bottom: 1px solid var(--ink);
}
/* The cells run edge to edge, so the strip carries no container padding of its own
   and each cell pays for its own. The FIRST cell pays --gutter on its left and the
   LAST cell pays --gutter on its right; everything between them uses --cell-x. That
   is what lands the wordmark on the same line as every section header and heading below
   it. A uniform padding on all four sides of every cell is the bug this replaces. */
.nav__inner {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: 0;
  height: 64px;
  padding-inline: 0;
}
.nav__inner > .logo {
  display: inline-flex;
  align-items: center;
  padding-left: var(--gutter);
  padding-right: var(--cell-x);
  flex: 0 0 auto;
}
/* align-items is not optional here: the default `stretch` lets each item place its
   own text, and the trigger is a <button> whose UA line-height is `normal` rather
   than the inherited one — so "Product" sat 3.5px below the four links beside it. */
.nav__links {
  display: none;
  gap: 26px;
  align-items: center;
  margin-right: auto;
  padding-inline: var(--cell-x);
  border-left: 1px solid var(--ink);
}
.nav__links a {
  color: var(--ink-3);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.nav__links a:hover { color: var(--ink); }
@media (min-width: 860px) { .nav__links { display: flex; } }

/* The CTA cell: filled solid ink, no radius, and inset from the right page edge by
   exactly --gutter — the same line the footer's copyright ends on. The buttons
   inside lose their own borders and stretch. */
.nav__inner > .row {
  gap: 0;
  flex: 0 0 auto;
  flex-wrap: nowrap;
  align-items: stretch;
  padding-right: var(--gutter);
  border-left: 1px solid var(--ink);
}
.nav__inner > .row .btn { border: 0; height: 100%; }
.nav__inner > .row .btn + .btn { border-left: 1px solid var(--ink); }
.nav__inner > .row form.button_to { margin: 0; display: flex; }

/* The signed-in pillars are a cell of the same strip, not a group floating in the
   middle of it — space-between would otherwise centre them against nothing. */
.nav__inner > .pillars {
  padding-inline: var(--cell-x);
  border-left: 1px solid var(--ink);
  margin-right: auto;
}

/* Account menu (signed-in app chrome). Inline dropdown on desktop; sheet on mobile.
   It is the app's counterpart to the marketing CTA cell: flush to the right edge,
   divided by a rule, with the trigger filling it rather than sitting inside it as
   a second box. */
.account { position: relative; display: inline-flex; align-items: stretch; border-left: 1px solid var(--ink); }
.account__trigger {
  display: inline-flex; align-items: center; gap: 8px;
  padding-left: var(--cell-x); padding-right: var(--gutter);
  background: transparent; border: 0; border-radius: var(--r);
  color: var(--ink); font-size: 11.5px; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase; cursor: pointer;
  transition: background 0.15s;
}
.account__trigger:hover { background: var(--paper-2); }
.account__label { display: none; }
.account__caret { width: 14px; height: 14px; display: none; transition: transform 0.15s; }
.account__trigger[aria-expanded="true"] .account__caret { transform: rotate(180deg); }
.account__burger { width: 22px; height: 22px; }
@media (min-width: 860px) {
  .account__label, .account__caret { display: inline-flex; }
  .account__burger { display: none; }
}

.account__menu {
  position: absolute; right: 0; top: 100%; min-width: 232px; z-index: 60;
  display: flex; flex-direction: column; padding: 0;
  background: var(--paper); border: 1px solid var(--ink); border-radius: var(--r);
}
.account__menu[hidden] { display: none; }
.account__email {
  padding: 12px 14px; font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-3);
  border-bottom: 1px solid var(--rule); overflow: hidden; text-overflow: ellipsis;
}
.account__menu form { margin: 0; }
.account__item {
  display: block; width: 100%; text-align: left; padding: 11px 14px; border: 0;
  border-radius: var(--r);
  background: transparent; color: var(--ink-2); font-family: inherit; font-size: 14px; cursor: pointer;
}
.account__item:hover { background: var(--paper-2); color: var(--ink); }

/* Project switcher: a second cell of the nav strip, built from the account menu's
   parts so there is one dropdown idiom in the chrome rather than two. It filters
   Communities only — the note inside says so, because a switcher that silently does
   nothing on Today would read as broken. */
.switcher .account__label { display: inline-flex; text-transform: none; letter-spacing: 0.02em; font-size: 13px; }
.switcher .account__burger { display: none; }
.switcher .account__caret { display: inline-flex; }
.account__item--on { color: var(--ink); font-weight: 600; }
.account__item--on::before { content: "— "; font-family: var(--font-mono); }
.switcher__note {
  padding: 12px 14px; margin: 0; border-top: 1px solid var(--rule);
  font-size: 12.5px; line-height: 1.5; color: var(--ink-3); max-width: 260px;
}

body.nav-locked { overflow: hidden; }
/* Mobile: the menu drops to a full-width sheet under the bar. */
@media (max-width: 859px) {
  .account { position: static; }
  .account__menu {
    left: 0; right: 0; top: 100%; min-width: 0; border-radius: var(--r);
    border-left: 0; border-right: 0;
  }
}

/* -------------------------------------------------------------- Footer ----- */
/* Mirrors the nav exactly: three bordered cells — wordmark | links | copyright. */
.footer { border-top: 1px solid var(--ink); }
.footer .container {
  display: flex;
  align-items: stretch;
  flex-wrap: wrap;
  padding-inline: 0;
  min-height: 64px;
}
.footer .logo { display: inline-flex; align-items: center; padding-left: var(--gutter); padding-right: var(--cell-x); flex: 0 0 auto; }
.footer__links {
  display: flex; flex-wrap: wrap; align-items: center; gap: 22px;
  margin: 0; margin-right: auto;
  padding: 14px var(--cell-x);
  border-left: 1px solid var(--ink);
}
.footer__links a {
  color: var(--ink-3);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.footer__links a:hover { color: var(--ink); }
/* The copyright is machine-produced (a year and a legal entity), so it is mono. */
.footer__legal {
  display: inline-flex; align-items: center;
  margin: 0; padding: 14px var(--gutter) 14px var(--cell-x);
  border-left: 1px solid var(--ink);
  font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-3);
  max-width: none;
}

/* ------------------------------------------------------------- Flash ------- */
.flash { border-radius: var(--r); padding: 12px 16px; margin-bottom: 16px; font-size: 14px; border: 1px solid var(--ink); }
.flash--notice { color: var(--ink); }
.flash--alert { color: var(--stamp-ink); border-color: var(--stamp); background: var(--paper-2); }

/* Wide content scrolls inside its own box. The page body never scrolls sideways —
   the review is read on a phone, and a document that pans is a document that gets
   closed. */
.scroller { overflow-x: auto; }

/* --------------------------------------------------------- Utilities ------- */
.stack > * + * { margin-top: 16px; }
.row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* Fields in a row carry hints of different lengths, so centring staggers their
   inputs down the page. Align the tops instead. */
.row--fields { align-items: flex-start; }
.narrow { max-width: 760px; margin-inline: auto; }
.auth-card { max-width: 440px; margin: 64px auto; }

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

/* --------------------------------------------------------------- Board ----- */
/* A framed panel with a header strip, wrapping a .rows table. Used on the landing
   page to SHOW the product rather than describe it. The strip names the table and
   its counts, and nothing else — it carried an invented "synced 06:04" integration
   line, which promised a surface the communities screen does not have. */
.board { border: 1px solid var(--ink); background: var(--paper); }
.board__head {
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px;
  padding: 11px 16px;
  background: var(--paper-2);
  border-bottom: 1px solid var(--ink);
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-3);
}
.board__head-title { color: var(--ink); font-weight: 500; }
.board__body { padding: 0; }
.board__foot {
  padding: 12px 16px; border-top: 1px solid var(--rule);
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-3); max-width: none;
}
.board__scroll { overflow-x: auto; }
.board .rows th, .board .rows td { padding-inline: 16px; }
.board .rows th:first-child, .board .rows td:first-child { padding-left: 16px; }
.board .rows tr:first-child td { border-top: 0; }

/* ------------------------------------------------------------- Table ------ */
/* Numbers in a table are the point. Header row on the inset surface, hairline row
   rules, tabular figures so columns of digits line up. */
.rows { width: 100%; border-collapse: collapse; font-size: 14px; }
.rows th {
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3);
  text-align: left; padding: 11px 12px 11px 0;
  background: var(--paper-2); border-bottom: 1px solid var(--ink);
}
.rows td { padding: 13px 12px 13px 0; border-top: 1px solid var(--rule); vertical-align: top; }
.rows tbody tr:first-child td { border-top: 0; }
.rows tr:hover td { background: var(--paper-2); }
.rows__primary { color: var(--ink); font-weight: 600; }
.rows__sub { font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-3); }
.rows__funnel { font-family: var(--font-mono); color: var(--ink-2); white-space: nowrap; }

/* Machine-produced tabular data: mono, tabular figures, right-aligned counts. */
.rows--mono { font-family: var(--font-mono); font-size: 12.5px; }
.rows--mono td { color: var(--ink-2); }
.rows__num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.rows__end { text-align: right; }

/* ---------------------------------------------------- one product's funnel ---

   One row per product inside a community's row. The figures are mono and the step
   names are the small grey the rest of the app gives to a denominator — the number
   is the fact, the word is what it counts. Grey throughout: colour marks a state the
   engine concluded, and a funnel concludes nothing on its own (§2). */
.funnelline { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; margin-bottom: 6px; }
.funnelline:last-child { margin-bottom: 0; }
.funnelline__product { font-weight: 600; color: var(--ink); font-size: 13px; }
.funnelline__steps {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 7px;
  font-family: var(--font-mono); font-size: 12px;
  font-variant-numeric: tabular-nums; color: var(--ink-2);
}
.funnelline__label { color: var(--ink-3); }
.funnelline__arrow { color: var(--ink-3); }
.funnelline__none { font-family: var(--font-mono); font-size: 12px; color: var(--ink-3); }

/* A HANDFUL OF SHORT COLUMNS IN A FULL-BLEED CONTAINER.

   `.rows` is `width: 100%`, which is right for a table whose cells hold prose — but a
   table of seven short figures hands the slack to whichever column the browser picks,
   and on a wide screen that drops a hand's width of empty page between two columns you
   are meant to read across. Here every column sizes to its content and the LAST one
   absorbs what is left, so the figures stay one block. The `100%` on the last cell is
   how a table is told "you get the remainder" — it resolves against the leftover space,
   not the table. */
.rows--hug th, .rows--hug td { white-space: nowrap; }
.rows--hug th:last-child, .rows--hug td:last-child { width: 100%; }
.rows--hug th, .rows--hug td { padding-right: 28px; }
.rows--hug th:last-child, .rows--hug td:last-child { padding-right: 0; }

/* The sentence under a row: why the tier is what it is. Prose, so it wraps and takes a
   measure — the nowrap above is for figures and would otherwise run it to the page edge. */
.rows__reason { display: block; white-space: normal; max-width: 88ch; }

/* A cell that reports a failure. The one hue this system has left.
   Qualified past `.rows--mono td`, which is (0,1,1) and would otherwise repaint
   every cell in the table ink-2 — including this one. */
.rows td.rows__stamp { color: var(--stamp-ink); }

/* ------------------------------------------------------- Ledger rows ------ */
/* The other section shape: a bordered grid with hairline row rules. Mono row
   number, name in 600, consequence in ink-2. Used for stage chains and for any
   list that is really a table wearing a list's clothes. */
.lrows { border: 1px solid var(--ink); }
.lrow {
  display: grid;
  grid-template-columns: 34px 190px 1fr;
  gap: 20px;
  align-items: baseline;
  padding: 16px;
  border-top: 1px solid var(--rule);
}
.lrow:first-child { border-top: 0; }
.lrow__num {
  font-family: var(--font-mono); font-size: 11.5px;
  font-variant-numeric: tabular-nums; color: var(--ink-3);
}
.lrow__name { font-weight: 600; color: var(--ink); }
.lrow__body { color: var(--ink-2); font-size: 15px; margin: 0; max-width: none; }
.lrow__end { margin-left: auto; font-family: var(--font-mono); font-size: 12px; color: var(--ink-3); }
@media (max-width: 859px) {
  .lrow { grid-template-columns: 34px 1fr; gap: 6px 20px; }
  .lrow__body { grid-column: 2; }
}

/* ---------------------------------------------------- Asymmetric split ---- */
@media (min-width: 860px) {
  .grid--aside { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); gap: 56px; align-items: start; }
  .grid--aside--flip { grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); gap: 56px; align-items: start; }
}

/* -------------------------------------------------------- Numbered steps -- */
/* A real ordered list with a zero-padded mono counter, so the sequence survives
   CSS being off and a screen reader reads it as a list. Now cells of one bordered
   grid rather than a stack floating in whitespace. */
.steps {
  list-style: none; margin: 0; padding: 0; counter-reset: step;
  display: grid; gap: 0; border: 1px solid var(--ink);
}
.steps > li {
  counter-increment: step;
  display: grid; grid-template-columns: 34px 1fr; gap: 20px;
  padding: 22px 20px; border-top: 1px solid var(--rule);
}
.steps > li:first-child { border-top: 0; }
.steps > li::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--font-mono); font-size: 11.5px;
  font-variant-numeric: tabular-nums; color: var(--ink-3); padding-top: 4px;
}
.steps h3 { font-size: 21px; font-weight: 600; margin: 0 0 7px; }
.steps p { margin: 0; color: var(--ink-2); font-size: 15px; }

/* ---------------------------------------------------------------- Q&A ----- */
.qa { display: grid; gap: 0; border: 1px solid var(--ink); }
.qa > div { padding: 22px 20px; border-top: 1px solid var(--rule); }
.qa > div:first-child { border-top: 0; }
.qa h3 { font-size: 19px; font-weight: 600; margin: 0 0 6px; }
.qa p { margin: 0; color: var(--ink-2); font-size: 15px; max-width: none; }

/* --------------------------------------------------------- Nav dropdown --- */
/* flex, not block: as a block the inline-flex trigger sits on a text line box and
   inherits its half-leading. Making the wrapper a flex container takes inline
   layout out of the picture. */
.nav__menu { position: relative; display: flex; align-items: center; }
.nav__trigger {
  display: inline-flex; align-items: center; gap: 6px;
  /* line-height too — a button does not inherit it, and `normal` is a different
     box height from the one the sibling links get. font-family alone is not
     enough to match them. */
  font-family: inherit; font-size: 11.5px; line-height: inherit;
  font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-3);
  background: transparent; border: 0; padding: 0; cursor: pointer;
}
.nav__trigger:hover,
.nav__trigger[aria-expanded="true"] { color: var(--ink); }
.nav__caret { width: 12px; height: 12px; transition: transform 0.15s; }
.nav__trigger[aria-expanded="true"] .nav__caret { transform: rotate(180deg); }

.nav__panel {
  position: absolute; left: 0; top: 100%; z-index: 60; min-width: 560px;
  display: grid; grid-template-columns: 1fr 1fr; gap: 0;
  padding: 0;
  background: var(--paper); border: 1px solid var(--ink); border-radius: var(--r);
}
.nav__panel[hidden] { display: none; }
.nav__panel-item { display: block; padding: 14px 16px; border-top: 1px solid var(--rule); }
.nav__panel > .nav__panel-item:nth-child(1),
.nav__panel > .nav__panel-item:nth-child(2) { border-top: 0; }
.nav__panel-item:hover { background: var(--paper-2); }
.nav__panel-title { display: block; font-size: 14px; font-weight: 600; color: var(--ink); }
.nav__panel-gloss { display: block; font-size: 13px; color: var(--ink-3); margin-top: 2px; }
.nav__panel-foot {
  grid-column: 1 / -1; padding: 14px 16px;
  background: var(--paper-2);
  border-top: 1px solid var(--ink); font-size: 13.5px;
}

/* Mobile: the marketing nav collapses to a sheet under the bar, the same device
   the signed-in account menu uses. The Product panel un-positions and joins the flow. */
.nav__mobile { display: inline-flex; align-items: center; padding-left: var(--cell-x); padding-right: var(--gutter); border-left: 1px solid var(--ink); }
.nav__toggle {
  display: inline-flex; padding: 8px; cursor: pointer;
  background: transparent; color: var(--ink);
  border: 1px solid var(--ink); border-radius: var(--r);
}
.nav__burger { width: 22px; height: 22px; }
.nav__sheet {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 60;
  display: grid; gap: 0; padding: 0;
  background: var(--paper); border-bottom: 1px solid var(--ink);
}
.nav__sheet[hidden] { display: none; }
.nav__sheet a {
  display: block; padding: 14px var(--gutter); color: var(--ink-2);
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  border-top: 1px solid var(--rule);
}
.nav__sheet a:first-child { border-top: 0; }
.nav__sheet a:hover { background: var(--paper-2); color: var(--ink); }
/* Hide the WRAPPER, not just its children. .nav__inner is space-between, so a
   wrapper whose contents are display:none is still a flex item — it reserved a
   slot for nothing, and the CTAs sat short of the container edge. */
@media (min-width: 860px) { .nav__mobile { display: none; } }
@media (max-width: 859px) {
  .nav__panel { position: static; min-width: 0; grid-template-columns: 1fr; border: 0; padding: 0; background: transparent; }
  /* The sheet already carries Sign in and Subscribe, so the bar is logo +
     hamburger. Keeping both CTAs inline crushes the bar at 380px and gives the
     same action two hit targets. */
  .nav__inner > .row { display: none; }
}

/* --------------------------------------------------------------- Blog ------ */
/* The index is a stack of 1px rules, not cards: the same depth device as the rest
   of the site, and it keeps five posts from reading as a product grid. */
.post-row { padding-block: 30px; border-bottom: 1px solid var(--rule); }
.post-row:first-child { border-top: 1px solid var(--ink); }
.post-row__meta,
.post__meta {
  display: flex; gap: 14px; flex-wrap: wrap; margin: 0 0 10px; max-width: none;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-3);
}
/* A tag is a filing label, so it earns emphasis from weight rather than colour —
   and it must never borrow .state, which is the verdict enum's. */
.post-row__tag, .post__tag { color: var(--ink); font-weight: 600; }
.post-row__title { font-size: 27px; font-weight: 700; line-height: 1.08; letter-spacing: -0.03em; margin: 0 0 8px; max-width: 34ch; }
.post-row__title a { color: var(--ink); }
.post-row__title a:hover { text-decoration: underline; text-underline-offset: 4px; }
.post-row__excerpt { font-size: 15.5px; color: var(--ink-2); margin: 0; max-width: 62ch; }

/* Tag filter. Each is a real URL (?tag=…), so these are links rather than toggles —
   no JS under a nonce-only CSP, and a filtered view stays linkable and crawlable.
   Radius above 0 is forbidden, so the current tag is marked by ink, not by shape. */
.tagbar { display: flex; gap: 0; flex-wrap: wrap; border: 1px solid var(--ink); }
.tagbar a {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 9px 14px; border-left: 1px solid var(--rule);
  border-radius: var(--r); color: var(--ink-3);
}
.tagbar a:first-child { border-left: 0; }
.tagbar a:hover { background: var(--paper-2); color: var(--ink); }
.tagbar a[aria-current="page"] { background: var(--ink); color: var(--on-ink); }

/* h1 is sized for a landing hero. A post title is a sentence. */
.post__title { font-size: clamp(32px, 4.5vw, 46px); line-height: 1.05; }

/* Article body — the one surface on the site that is prose rather than UI, so it
   gets a reading measure and a vertical rhythm of its own. */
.post__body { font-size: 17.5px; line-height: 1.6; max-width: var(--measure); }
.post__body > * + * { margin-top: 1.1em; }
.post__body p { max-width: none; }
.post__body h2 { font-size: 27px; margin: 2em 0 0.5em; }
.post__body h3 { font-size: 20px; margin: 1.7em 0 0.4em; }
.post__body ul, .post__body ol { padding-left: 22px; }
.post__body li + li { margin-top: 8px; }
.post__body a { text-decoration: underline; text-underline-offset: 3px; }
.post__body blockquote {
  margin: 1.6em 0; padding: 2px 0 2px 20px;
  border-left: 2px solid var(--ink); color: var(--ink-2);
}
.post__body code {
  font-family: var(--font-mono); font-size: 0.88em; padding: 1px 5px;
  background: var(--paper-2); border: 1px solid var(--rule); border-radius: var(--r);
}
.post__body pre {
  font-family: var(--font-mono); font-size: 13px; line-height: 1.7; padding: 16px;
  overflow-x: auto; background: var(--paper-2); border: 1px solid var(--ink);
  border-radius: var(--r);
}
.post__body pre code { background: none; border: 0; padding: 0; }
/* Tables carry the PRD's own tables — gates, stages, loops — so they inherit the
   rule-only depth rather than getting a border box. */
.post__body table { width: 100%; border-collapse: collapse; font-size: 15px; }
.post__body th, .post__body td { padding: 9px 12px 9px 0; text-align: left; border-bottom: 1px solid var(--rule); }
.post__body th {
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3);
  border-bottom: 1px solid var(--ink);
}

.post__foot {
  display: flex; justify-content: space-between; align-items: center;
  gap: 20px; flex-wrap: wrap; font-size: 15px; color: var(--ink-3); max-width: none;
  padding-top: 24px; border-top: 1px solid var(--ink);
}

/* -------------------------------------------------------------- Plans ------ */
/* One bordered grid, three columns, 1px dividers, equal height. Solo is the
   inverted column — the second of the two inverted surfaces on the site. */
.plans {
  display: grid; gap: 1px;
  background: var(--ink); border: 1px solid var(--ink);
}
/* Auto-fit, not repeat(3): there are two plans now that the free tier is gone, and a fixed third
   column left an empty solid-ink cell sitting on the right of every pricing block. */
@media (min-width: 860px) { .plans { grid-auto-flow: column; grid-auto-columns: 1fr; } }
.plan { background: var(--paper); padding: 30px 28px; display: flex; flex-direction: column; }
.plan__name {
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3);
  margin-bottom: 18px;
}
.plan__price {
  font-size: 44px; font-weight: 700; letter-spacing: -0.03em; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--ink);
  margin-bottom: 18px;
}
.plan__unit { font-size: 14px; font-weight: 500; letter-spacing: 0; color: var(--ink-3); }
.plan__body { font-size: 15px; color: var(--ink-2); margin: 0; max-width: none; }
/* The columns carry copy of unequal length, so the calls to action sink to the
   bottom rather than floating at three different heights. */
.plan .btn, .plan form.button_to { margin-top: auto; }
.plan form.button_to .btn { margin-top: 0; }
/* The confirmation line sits directly above the call to action. Its colour comes
   from the column, never from an inline style — on the inverted column an inline
   `color: var(--ink)` is black on black, which is how it shipped once already. */
.plan .mono { margin-top: auto; margin-bottom: 12px; font-size: 13px; color: var(--ink); }

/* The inverted column. Solid ink, and everything inside it flips. */
.plan--marked { background: var(--ink); }
.plan--marked .plan__name { color: var(--on-ink-2); }
.plan--marked .plan__price { color: var(--on-ink); }
.plan--marked .plan__unit { color: var(--on-ink-2); }
.plan--marked .plan__body { color: var(--on-ink-2); }
.plan--marked .mono { color: var(--on-ink-2); }
.plan--marked .btn { border-color: var(--on-ink); color: var(--on-ink); }
.plan--marked .btn--primary { background: var(--on-ink); color: var(--ink); }
.plan--marked .btn--primary:hover { background: var(--on-ink-2); color: var(--ink); }
.plan--marked .btn--secondary:hover { background: var(--ink-2); color: var(--on-ink); }

/* ------------------------------------------------------------- Mobile ------ */
@media (max-width: 859px) {
  .footer .container { display: block; }
  /* Stacked, every footer cell is its own row, so each one pays the page gutter on
     both sides — there is no longer a cell beside it to share an edge with. */
  .footer .logo,
  .footer__links,
  .footer__legal { border-left: 0; padding-inline: var(--gutter); }
  .footer__links { border-top: 1px solid var(--rule); }
  .footer__legal { border-top: 1px solid var(--rule); }
}
@media (max-width: 640px) {
  :root { --section-y: 44px; }   /* --gutter clamps itself; only the rhythm tightens */
  body { font-size: 15.5px; }
  h1 { font-size: clamp(32px, 10vw, 46px); }
  .lede { font-size: 16.5px; }
  .nav__inner { height: 56px; }
  .auth-card { margin: 32px auto; }
  .btnpair { display: flex; flex-direction: column; width: 100%; }
  .btnpair .btn + .btn { border-left: 0; border-top: 1px solid var(--ink); }
  /* Never let a wide element force horizontal page scroll. */
  .trace { overflow-x: auto; }
}

/* ============================================================================
   MADRUGA COMPONENTS
   The verdict card, gate meter and readiness meter are owned by the engine.
   ============================================================================ */

/* ------------------------------------------------------------- Wordmark ---- */
/* Uppercase and letterspaced, as a masthead is. This retires design doc §4b's
   "the gate meter IS the logo" — the lowercase wordmark over a fixed 62% fill.
   The evidence now lives in the tables, which is where a document keeps it. */
.logo {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.16em;
  line-height: 1;
  text-transform: uppercase;
  color: var(--ink);
}
.logo:hover { color: var(--ink); }
/* The footer sets the mark a step smaller, but NOT looser: tracking is what makes the nav and
   footer wordmarks read as the same mark rather than two sizes of the same word. */
.footer .logo { font-size: 13px; }

/* ------------------------------------------------------------ State chip --- */
/* The literal enum value, uppercase mono — the user is a developer who will see
   this string in their logs and in the Sunday email, and matching it removes a
   translation layer (§5). A rectangle, like everything else. */
.state {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
}
.state::before {
  content: "";
  width: 7px; height: 7px;
  background: currentColor;
  flex: 0 0 auto;
}
/* State is NEVER colour alone — the uppercase name always ships with it, so a
   monochrome screenshot loses nothing (§17). That rule is what lets the four-hue
   signal set collapse to one: only the two FAILURE states take the stamp, and
   every other state is ink plus its name. */
.state--watching { color: var(--ink-3); }
.state--working, .state--scaling { color: var(--ink); }
.state--exhausted { color: var(--ink-3); }
.state--kill-pending { color: var(--stamp-ink); }
.state--kill-confirmed { color: var(--stamp-ink); }
.state--unseeded { color: var(--ink-3); }

/* ------------------------------------------------------------ Metric ------- */
/* A figure a machine produced. Always rendered with its denominator alongside
   (D1) — this class styles the number, the template supplies the population. */
.metric {
  font-family: var(--font-mono);
  font-size: 26px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  letter-spacing: -0.01em;
}
.metric__unit { font-size: 13px; color: var(--ink-3); letter-spacing: 0; }

/* ------------------------------------------------------------ Trace -------- */
/* §5.2: it reads like a test run because the buyer reads test runs all day, and
   it is the honesty proof — a user who suspects the verdict is vibes can check
   the arithmetic in four seconds. */
.trace {
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.75;
  background: var(--paper-2);
  border: 1px solid var(--ink);
  padding: 16px 18px;
  white-space: pre-wrap;
  overflow-x: auto;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.trace__comment { color: var(--ink-3); }
.trace__met { color: var(--ink); }
/* An OPEN gate is the one quantity that still earns the stamp: it is the reason a
   verdict is being withheld. */
.trace__open { color: var(--stamp-ink); }

/* ------------------------------------------------------------ Verdict ----- */
/* §5: one component, seven states, rendered identically everywhere. THE ONE RULE
   is that a verdict never renders without its evidence in the same viewport — no
   tooltip-only evidence, no "view details" for the gates. If there isn't room for
   the evidence, the state doesn't render either; show the raw funnel instead. */
.verdict {
  background: var(--paper);
  border: 1px solid var(--ink);
  padding: 24px;
}
.verdict__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 4px;
}
.verdict__where {
  font-family: var(--font-mono); font-size: 12px; color: var(--ink-3);
}
/* A sentence, never a label. "Not enough evidence to judge this cell yet" — the
   sentence form is what makes withholding read as a decision the instrument made
   rather than a hole in it (§5.2). "Insufficient data" alone is banned (§17). */
.verdict__headline {
  font-size: 20px; color: var(--ink); font-weight: 600; letter-spacing: -0.02em;
  margin: 14px 0 6px; max-width: none;
}
.verdict__detail { font-size: 15px; color: var(--ink-2); margin: 0 0 20px; max-width: none; }
/* Only the UNMET quantities carry the stamp, in the order clicks → days → effort.
   Generated from the gate objects — never hand-written per state. */
.verdict__remainder { color: var(--stamp-ink); font-family: var(--font-mono); font-size: 13px; }
/* The bottleneck sentence sits behind a 2px ink rule: it is the one line that says
   what to do next. */
.verdict__bottleneck {
  font-size: 14px; color: var(--ink-2); margin: 18px 0 0;
  padding: 2px 0 2px 16px; border-left: 2px solid var(--ink); max-width: none;
}
.verdict__gates-label {
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3);
  margin-bottom: 12px;
}
/* Marked by a heavier rule, not by a hue. */
.verdict--marked { border-width: 2px; }

/* -------------------------------------------------------- Gate meter ------ */
/* §7.1 / D2: the app's withheld verdicts still DRAW their missing evidence rather
   than stating it. Continuous bars, grey, about the instrument.
   Deliberately shaped UNLIKE the readiness meter (discrete notches, about you) —
   conflating the two is the most likely design failure in the build.
   NOTE: the marketing site renders its evidence as tables instead; this component
   is the product's, and D2 keeps it. */
.meter { display: grid; grid-template-columns: 1fr auto; gap: 5px 12px; margin-bottom: 14px; }
.meter__label {
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3);
}
.meter__value {
  font-family: var(--font-mono); font-size: 12.5px; font-variant-numeric: tabular-nums;
  color: var(--ink-2); text-align: right;
}
.meter__track {
  grid-column: 1 / -1; height: 6px; background: var(--paper-2);
  border: 1px solid var(--rule);
}
/* Grey until cleared. A met gate turns to solid ink and STAYS VISIBLE — hiding a
   satisfied gate makes the card shrink as it progresses, which reads as losing
   ground (§5). Fill clamps at 100%; the true over-count stays in the numerals. */
.meter__fill { height: 100%; background: var(--ink-3); }
.meter--met .meter__fill { background: var(--ink); }
.meter--met .meter__value { color: var(--ink); font-weight: 500; }
/* At-risk is a conclusion, not a quantity — D3 permits the hue here for the same
   reason it forbids it on the bars beside it. */
.meter--risk .meter__fill { background: var(--stamp); }
.meter--risk .meter__value { color: var(--stamp-ink); }

/* ------------------------------------------------------------- Alarm ------- */
/* One instance only, above the nav, undismissable — never used for anything else.
   It carries the stamp: unattributed revenue means every decision below it is
   computed on a biased sample. */
.alarm {
  background: var(--paper-2);
  border-bottom: 1px solid var(--stamp);
  color: var(--stamp-ink);
  font-size: 13.5px;
}
.alarm__inner {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding-block: 11px;
  flex-wrap: wrap;
}
.alarm__figure {
  font-family: var(--font-mono);
  font-size: 17px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}
.alarm__text { flex: 1 1 320px; }
.alarm__remedy { display: block; color: var(--ink-3); margin-top: 2px; }
.alarm__action {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: var(--stamp-ink);
  border-bottom: 1px solid currentColor;
}
.alarm__action:hover { color: var(--ink); }

/* ------------------------------------------------------- Install check ----- */
/* Rows flip the instant the server first sees each call. A developer pasting code
   in one window and watching rows fill in the other is the entire first
   impression — it demonstrates the redirect works before asking anyone to trust a
   verdict. */
.install-row {
  display: grid;
  grid-template-columns: 20px 1fr auto;
  gap: 12px;
  align-items: start;
  padding: 14px 0;
  border-bottom: 1px solid var(--rule);
}
.install-row:last-child { border-bottom: 0; }
.install-row__mark {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-3);
  /* 200ms, no bounce (§17). */
  transition: color 200ms ease;
}
.install-row--done .install-row__mark { color: var(--ink); }
.install-row__label { font-family: var(--font-mono); font-size: 13px; color: var(--ink-2); }
.install-row__buys { font-size: 13px; color: var(--ink-3); margin-top: 2px; }
.install-row__state {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink-3);
  white-space: nowrap;
}
.install-row--done .install-row__state { color: var(--ink); font-weight: 500; }
.install-row__optional {
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ------------------------------------------------------------ Wizard ------- */
.wizard { max-width: 680px; margin: 48px auto; }
.wizard__progress {
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 8px;
}
.wizard__steps { display: flex; gap: 4px; margin-bottom: 32px; }
.wizard__step { height: 3px; flex: 1; background: var(--rule); }
.wizard__step--done { background: var(--ink-3); }
.wizard__step--current { background: var(--ink); }

/* --------------------------------------------------------- Readiness ------ */
/* DISCRETE NOTCHES — countable, about YOU (§7.2). Each notch is a real event you
   can point at. Deliberately a different shape from the gate meter's smooth bar,
   because they have different owners and different consequences: gates decide
   whether MADRUGA may speak, readiness decides whether YOU may post a link. */
.readiness__row {
  display: grid;
  grid-template-columns: 110px 1fr auto;
  gap: 12px;
  align-items: center;
  margin-bottom: 9px;
}
.readiness__label {
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3);
}
.readiness__notches { display: flex; gap: 3px; align-items: center; }
.readiness__notch {
  width: 10px; height: 10px;
  border: 1px solid var(--ink-4);
  background: transparent;
}
.readiness__notch--filled { background: var(--ink); border-color: var(--ink); }
.readiness__over { font-family: var(--font-mono); font-size: 11px; color: var(--ink-3); margin-left: 4px; }
.readiness__value {
  font-family: var(--font-mono); font-size: 12.5px;
  font-variant-numeric: tabular-nums; color: var(--ink-2);
}
.readiness__value--met { color: var(--ink); font-weight: 500; }
.readiness__cleared {
  font-size: 14px; color: var(--ink); margin: 12px 0 0; max-width: none;
}
.readiness__summary { font-size: 14px; color: var(--ink-2); margin: 12px 0 0; max-width: none; }

/* ------------------------------------------------------------- Funnel ----- */
/* Flex boxes with heights. No charting library — it would weigh more than the rest
   of the front end and invite chart types the small-numbers rule forbids. */
.funnel__label {
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3);
  margin-bottom: 14px;
}
.funnel__withheld { font-size: 14px; color: var(--ink-2); margin: 0; max-width: none; }
.funnel__bars { display: flex; gap: 24px; align-items: flex-end; }
.funnel__stage { flex: 1; }
/* A stage nothing can measure. Drawn, not omitted (design doc §2) — dimmed to read as absent
   rather than as a quantity, since a quantity is what it is specifically not. An outline instead of
   a fill: a filled bar of any height is a quantity, and there isn't one. */
.funnel__stage--unmeasured { opacity: 0.5; }
.funnel__stage--unmeasured .funnel__bar {
  background: none; border-bottom: 1px dashed var(--ink-3); min-height: 0; height: 0;
}
.funnel__count {
  font-family: var(--font-mono); font-size: 22px;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
/* Grey: a quantity is not a conclusion (D3). */
.funnel__bar { background: var(--ink-3); min-height: 2px; margin: 6px 0; }
.funnel__stage-name { font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-3); }
/* The denominator, always (D1). "4 of 61 clicks · 6.5%", never "4 signups". */
.funnel__rate {
  font-family: var(--font-mono); font-size: 11.5px;
  font-variant-numeric: tabular-nums; color: var(--ink-3);
}

/* -------------------------------------------------------- Empty state ----- */
/* Three sentences, in this order, no illustration — EVER (§15). A blank surface
   reads as broken software; one that says what it is waiting for reads as a
   machine counting on your behalf. */
.empty {
  border: 1px solid var(--rule);
  border-left: 2px solid var(--ink);
  padding: 20px 22px;
  background: var(--paper-2);
}
.empty__waiting { font-size: 17px; font-weight: 600; color: var(--ink); margin: 0 0 8px; max-width: none; }
.empty__expected { font-size: 14.5px; color: var(--ink-2); margin: 0 0 8px; max-width: none; }
.empty__instead { font-size: 14.5px; color: var(--ink-2); margin: 0; max-width: none; }

/* ---------------------------------------------------------- App shell ----- */
/* Four pillars, one per question the founder actually asks. Flat — a five-item
   rail that never grows is faster to use and faster to build than a tree (§3). */
.pillars { display: flex; gap: 24px; align-items: center; }
.pillars a {
  font-family: var(--font-sans); font-size: 11.5px; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-3);
  padding-bottom: 2px; border-bottom: 2px solid transparent;
}
.pillars a:hover { color: var(--ink); }
.pillars a[aria-current] { color: var(--ink); border-bottom-color: var(--ink); }

/* -------------------------------------------------------- Kill clock ------ */
/* The copy quotes the user's own rule back at them, so it has to read as their
   commitment rather than a threat from the software (§10) — which is why a
   running clock is ink, and only an OVERDUE one takes the stamp. */
.killclock { display: flex; gap: 4px; margin: 10px 0; }
.killclock__week {
  flex: 1; height: 8px; background: var(--paper-2); border: 1px solid var(--rule);
}
.killclock__week--running { background: var(--ink-3); border-color: var(--ink-3); }
.killclock__week--overdue { background: var(--stamp); border-color: var(--stamp); }
/* A satisfied clock has CONCLUDED, so it goes to solid ink and stops reading as pressure. */
.killclock__week--satisfied { background: var(--ink); border-color: var(--ink); }
.killclock__quote { font-size: 14px; color: var(--ink-2); margin: 0; max-width: none; }

/* ------------------------------------------------------------- Block ------ */
/* The only element reflecting wall-clock time. A companion, not a timer: it never
   counts down and never alarms. The QUOTA ends the block (§8). */
.block__row {
  display: grid; grid-template-columns: 52px 1fr auto; gap: 12px;
  padding: 11px 0; border-bottom: 1px solid var(--rule);
  font-size: 14.5px;
}
.block__row:last-child { border-bottom: 0; }
.block__row--current { border-left: 2px solid var(--ink); padding-left: 12px; margin-left: -14px; }
.block__time {
  font-family: var(--font-mono); font-size: 12.5px;
  font-variant-numeric: tabular-nums; color: var(--ink-3);
}
.block__dur {
  font-family: var(--font-mono); font-size: 12px;
  font-variant-numeric: tabular-nums; color: var(--ink-3);
}
.quota__item {
  display: flex; gap: 12px; align-items: baseline;
  padding: 11px 0; font-size: 14.5px;
  border-bottom: 1px solid var(--rule);
}
.quota__item:last-child { border-bottom: 0; }
.quota__mark { font-family: var(--font-mono); font-size: 13px; color: var(--ink-3); }
.quota__item--done .quota__mark { color: var(--ink); }
/* Struck-through completed items are the one text allowed below the contrast
   floor — they are, by definition, no longer the thing you are reading (§17). */
.quota__item--done .quota__label { color: var(--ink-3); text-decoration: line-through; }
.quota__derived {
  font-family: var(--font-mono); font-size: 11.5px;
  font-variant-numeric: tabular-nums; color: var(--ink-3);
}

/* ------------------------------------------------------------ Cohorts ----- */
/* Absolute counts, never percentages, until a cohort has 10+ members: a 50%
   retention rate on n=2 is a coin flip wearing a suit (§11). */
.cohorts {
  width: 100%; border-collapse: collapse;
  font-family: var(--font-mono); font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.cohorts td, .cohorts th { padding: 8px 10px 8px 0; text-align: left; color: var(--ink-2); border-bottom: 1px solid var(--rule); }
.cohorts th {
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3);
  border-bottom: 1px solid var(--ink);
}
.cohorts__young { color: var(--ink-3); font-style: normal; }

/* The three self-checked quota items are buttons (they write), but must read as
   part of the same list as the three derived ones — a row of form-styled buttons
   under a plain list looks like two unrelated things. */
/* A product's own row under the item it belongs to. Indented by a rule rather than
   by whitespace, so the grouping survives at any width and reads as a breakdown of
   the row above rather than as five more quota items. */
.quota__item--sub {
  padding-left: 18px;
  margin-left: 7px;
  border-left: 1px solid var(--rule);
  font-size: 14px;
}
.quota__item--sub .quota__label { color: var(--ink-2); }

.quota__form { margin: 0; }
.quota__form button {
  display: block; width: 100%; text-align: left;
  background: transparent; border: 0; padding: 0; margin: 0;
  color: inherit; font: inherit; cursor: pointer;
}
.quota__form button:hover .quota__label { color: var(--ink); }

/* ---------------------------------------------------------------- the report

   Plain text in a scroll box, because the destination is another program's paste buffer and a
   rendered table arrives in most of them as noise. Fixed height so a 200-line report does not push
   everything else off the page — it is there to be copied, not read on screen. */
.report {
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.5;
  white-space: pre;
  overflow: auto;
  max-height: 380px;
  margin: 12px 0;
  padding: 12px;
  border: 1px solid var(--rule);
  background: var(--surface);
  color: var(--ink-muted);
}

/* ---------------------------------------------------------------- the ritual card

   Real checkboxes, because the card is a checklist and the board it mirrors is written as one.
   Grey throughout — colour marks a state the engine concluded, and nothing on Today concludes
   anything.

   TICKING AN ITEM ASKS WHERE. Most rows are a <details> whose <summary> IS the checkbox, so
   clicking it opens the one question that makes the work countable: which community. No JS — the
   CSP is enforced and fails silently, and a checklist that needs a script to tick is a checklist
   that breaks quietly. */
.check { border-bottom: 1px solid var(--rule); }
.check:last-child { border-bottom: 0; }

.check__row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 9px 0;
  width: 100%;
  cursor: pointer;
  list-style: none;
  text-align: left;
}
.check__row::-webkit-details-marker { display: none; }

/* 0 radius, like every container. The box is a box. */
.check__box {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  margin-top: 2px;
  border: 1px solid var(--ink-4);
  background: transparent;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 12px;
  text-align: center;
  color: var(--ink);
}
.check__row:hover .check__box { border-color: var(--ink); }
.check--done .check__box { border-color: var(--ink); background: var(--paper-2); }

.check__label { flex: 1 1 auto; font-size: 14px; color: var(--ink-2); }
.check--done .check__label { color: var(--ink-3); text-decoration: line-through; }
.check__label strong { font-weight: 600; color: var(--ink); }
.check--done .check__label strong { font-weight: 500; color: var(--ink-3); }

/* Never a number without its denominator, so the count sits on the row rather than replacing it. */
.check__count {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-3);
}

.check__note {
  margin: 0 0 10px 24px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-3);
}

.check__body { padding: 2px 0 16px 24px; }

.check__form { margin: 0; }
.check__form button {
  all: unset;
  display: block;
  width: 100%;
  cursor: pointer;
}

.check--sub { border-bottom: 0; }
.check--sub .check__row { padding: 5px 0 5px 24px; }
.check--sub .check__label { font-size: 13px; }

/* The choose-one group under Build. */
.check__group { padding: 2px 0 8px; }

/* The move to the next product's card. Small on purpose: it is a step, not the subject — the
   product you are ON is the heading. */
.today__next {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin: 14px 0 0;
  font-size: 13px;
}
.today__next a { color: var(--ink); }
.today__next span { color: var(--ink-3); font-size: 12px; }

/* WHICH READING THIS IS, and when it is replaced. A property of the page rather
   than a finding on it, so it takes the same small grey the "next product" step
   takes and never earns a hue — colour marks a state, never a fact about the
   schedule (§2). Under the heading, because the eyebrow already carries a rule. */
.cadence {
  margin: 12px 0 0;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink-3);
  max-width: none;
}
