/* ============================================================
   MERIDIAN — styles (v2)
   One family. Monochrome. Structure from fills, not borders.
   Every value resolves to a token in tokens.css.
   ============================================================ */

/* ---- reset ------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

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

body {
  background: var(--color-canvas);
  color: var(--color-ink);
  font-family: var(--font-sans);
  font-size: var(--text-body);
  font-weight: var(--weight-regular);
  line-height: var(--leading-body);
  letter-spacing: var(--track-body);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
  font-variant-numeric: tabular-nums;
}

/* Display type never breaks mid-word: a headline too wide for its
   column must fail visibly so the copy gets fixed, not the wrapping. */
:where(h1, h2, h3, .statement, .absent__what) { overflow-wrap: normal; }
@media (max-width: 479px) {
  :where(h1, h2, h3, .statement, .absent__what) { overflow-wrap: break-word; }
}

img, svg { display: block; max-width: 100%; }
ul, ol { list-style: none; padding: 0; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: none; }

section[id] { scroll-margin-top: var(--space-9); }

/* ---- focus ------------------------------------------------ */
:focus-visible {
  outline: 2px solid var(--color-ink);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

.skip {
  position: absolute;
  left: var(--space-4);
  top: var(--space-4);
  z-index: 100;
  transform: translateY(-200%);
  padding: var(--space-3) var(--space-5);
  background: var(--color-accent);
  color: var(--color-accent-ink);
  border-radius: var(--radius-full);
  font-size: var(--text-ui);
  font-weight: var(--weight-medium);
  transition: transform var(--dur-fast) var(--ease);
}
.skip:focus { transform: translateY(0); }

/* ---- layout ----------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section-y); }

/* No max-width here. A ch value on this wrapper resolves against its own
   16px font-size, not the 61px heading inside it, which clamped every
   section head to ~300px and ran the H2s to six lines. Each child carries
   its own measure instead, resolved at the size it is actually set in. */
.section-head { margin-bottom: var(--space-8); }

/* ---- type roles ------------------------------------------- */
.eyebrow,
.label {
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  line-height: var(--leading-flat);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-ink-dim);
}
.label--sm {
  font-size: var(--text-micro);
  letter-spacing: var(--track-label-sm);
}
.eyebrow { margin-bottom: var(--space-6); }

h1, .h1 {
  font-size: var(--text-h1);
  font-weight: var(--weight-medium);
  line-height: var(--leading-flat);
  letter-spacing: var(--track-h1);
  text-wrap: balance;
}

.h2 {
  font-size: var(--text-h2);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--track-h2);
  text-wrap: balance;
  /* resolved at 61px, so this is ~790px — 2 to 3 lines in both locales */
  max-width: 20ch;
}

.h3 {
  font-size: var(--text-h3);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--track-h3);
}

.lede {
  font-size: var(--text-lead);
  font-weight: var(--weight-regular);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-lead);
  color: var(--color-ink-muted);
  /* not --measure: that is 62ch for 16px body and would run ~740px here */
  max-width: 42ch;
  margin-top: var(--space-5);
}

.statement {
  max-width: 22ch;
  font-size: var(--text-mega);
  font-weight: var(--weight-medium);
  line-height: var(--leading-flat);
  letter-spacing: var(--track-h1);
}

.note {
  max-width: var(--measure);
  margin-top: var(--space-7);
  font-size: var(--text-body);
  color: var(--color-ink-muted);
}

.caption {
  margin-top: var(--space-4);
  font-size: var(--text-caption);
  line-height: var(--leading-body);
  color: var(--color-ink-dim);
  max-width: 46ch;
}

/* ---- brand ------------------------------------------------ */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-1);
}
.brand__mark {
  width: 20px;
  height: 20px;
  flex: none;
  fill: none;
  stroke: var(--color-ink);
  stroke-width: 1.25;
}
.brand__word {
  font-size: var(--text-ui);
  font-weight: var(--weight-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

/* ---- buttons ---------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-full);
  font-family: var(--font-ui);
  font-size: var(--text-ui);
  line-height: 1.43;
  letter-spacing: 0;
  text-align: center;
  transition: background-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.btn--primary {
  background: var(--color-accent);
  color: var(--color-accent-ink);
}
.btn--primary:hover { background: var(--color-ember-hover); }
.btn--primary:active { transform: scale(0.98); }

/* the reference's secondary pill: transparent, 1.5px SOLID obsidian.
   Solid here, not dotted - dotted is reserved for separators. */
.btn--ghost {
  background: transparent;
  border: 1.5px solid var(--color-line-strong);
  color: var(--color-ink);
}
.btn--ghost:hover { background: var(--color-ink); color: var(--color-raised); }

.btn--lg {
  min-height: 52px;
  padding: var(--space-4) var(--space-7);
  font-size: var(--text-body);
}

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 40px;
  font-family: var(--font-ui);
  font-size: var(--text-ui);
  color: var(--color-ink-muted);
  transition: color var(--dur-fast) var(--ease);
}
.link-arrow::after {
  content: "";
  width: 22px;
  height: 22px;
  border-radius: var(--radius-full);
  background: var(--fill-2) no-repeat center / 9px 9px
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2 10 L10 2 M4.5 2 H10 V7.5' fill='none' stroke='%23070607' stroke-width='1.4'/%3E%3C/svg%3E");
  transition: transform var(--dur-base) var(--ease), background-color var(--dur-fast) var(--ease);
}
.link-arrow:hover { color: var(--color-ink); }
.link-arrow:hover::after { transform: translate(2px, -2px); background-color: var(--fill-3); }

/* ---- nav --------------------------------------------------- */
.masthead {
  position: sticky;
  top: 0;
  z-index: 50;
  padding: var(--space-4) var(--gutter);
}

.island {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-5);
  border-radius: var(--radius-full);
  background: rgba(247, 246, 242, 0.82);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}

.island__links {
  display: none;
  gap: var(--space-1);
  margin-inline: auto;
}
.island__links a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-full);
  font-family: var(--font-ui);
  font-size: var(--text-ui);
  color: var(--color-ink-muted);
  transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.island__links a:hover,
.island__links a[aria-current="true"] {
  color: var(--color-ink);
  background: var(--fill-2);
}

.island__end {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-left: auto;
}

.locale {
  display: flex;
  gap: 2px;
  padding: 2px;
  border-radius: var(--radius-full);
  background: var(--fill-1);
}
.locale button {
  min-width: 38px;
  min-height: 28px;
  padding-inline: var(--space-2);
  border-radius: var(--radius-full);
  font-family: var(--font-ui);
  font-size: var(--text-label);
  letter-spacing: var(--track-label);
  color: var(--color-ink-dim);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.locale button:hover { color: var(--color-ink); }
.locale button[aria-pressed="true"] {
  background: var(--color-ink);
  color: var(--color-canvas);
}

.island__cta { display: none; }

/* ---- 01 hero ---------------------------------------------- */
/* top padding is the cheapest 16px: the masthead already separates the hero
   from the top of the page, so the hero does not need to repeat it */
.hero { padding-block: var(--space-7) var(--section-y); }
.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-9);
  align-items: end;
}
.hero__h1 { max-width: 14ch; }
.hero__sub {
  margin-top: var(--space-6);
  max-width: 52ch;
  color: var(--color-ink-muted);
  font-size: var(--text-body-lg);
  line-height: var(--leading-body);
}
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4) var(--space-6);
  margin-top: var(--space-8);
}

/* lifecycle panel */
/* ---- block entrance ----------------------------------------
   Fade from 0.001 and rise, as the reference does. Both classes are
   applied by graphics.js, never authored in the markup, so with JS
   off nothing is hidden. 0.001 rather than 0 is the reference's own
   value — it keeps the element rendered rather than skipped. */
.rise {
  opacity: 0.001;
  transform: translateY(var(--rise-lg));
  transition:
    opacity var(--dur-base) var(--ease),
    transform var(--dur-slow) var(--ease);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}
.rise.is-in { opacity: 1; transform: none; }

/* the nav alone enters downward */
.island-enter {
  opacity: 0.001;
  transform: translateY(var(--rise-nav));
  transition:
    opacity var(--dur-base) var(--ease),
    transform var(--dur-slow) var(--ease);
}
.island-enter.is-in { opacity: 1; transform: none; }

/* the reference's icon hover: scale 1.15 over 0.3s */
.brand__mark { transition: transform var(--dur-base) var(--ease); }
.brand:hover .brand__mark { transform: scale(1.15); }

/* ---- text motion -------------------------------------------
   The reference's line reveal: a mask with the line rising into it.
   Both classes are added by graphics.js and never present in the
   markup, so with no JS — or with motion reduced, where the script
   returns early — the text is plain and already correct.

   The mask is padded and pulled back by the same amount: at
   --leading-flat the content box is exactly the font size, so
   `overflow: hidden` would otherwise shave the descenders off a
   61px figure. The padding buys that room without moving anything.
   Travel is 120%, not 100%, so the line clears the padded box too. */
.tm-mask {
  display: block;
  overflow: hidden;
  padding-block: 0.14em;
  margin-block: -0.14em;
}
.tm-line {
  display: block;
  transform: translateY(120%);
  opacity: 0;
  transition:
    transform var(--dur-slow) var(--ease),
    opacity var(--dur-base) var(--ease);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}
.tm-mask.is-in .tm-line {
  transform: none;
  opacity: 1;
}

/* ---- halftone ----------------------------------------------
   The reference's signature motif, rebuilt in CSS. Theirs is a
   proprietary raster; this is three layers — ember dots on a
   ramp that burns to solid ember at the top right, over violet.

   It appears EXACTLY ONCE, as DESIGN.md requires of it and as the
   instruction to go easy on the orange and violet requires again.
   Nothing is set on it directly: it is a plate, and content sits
   on an opaque card on top, so no text is ever measured against a
   gradient. */
.halftone {
  background-color: var(--color-violet);
  background-image:
    radial-gradient(circle at center, var(--color-ember) 1.6px, transparent 1.7px),
    linear-gradient(215deg, var(--color-ember) 0%, rgba(252, 80, 0, 0) 58%);
  background-size: 10px 10px, 100% 100%;
}

.hero__plate {
  padding: var(--space-5);
  border-radius: var(--radius-lg);
}

/* nested inside the plate, so it takes the smaller radius — 40 inside 40
   reads as a mistake, the reference nests 24 inside 40 */
.lifecycle {
  padding: var(--space-5);
  border-radius: var(--radius-nested);
  background: var(--color-surface);
}
.lifecycle__legend { display: block; margin-bottom: var(--space-4); }

.lifecycle__row {
  display: grid;
  grid-template-columns: 10px 1fr;
  grid-template-areas: "dot name" ".  where" ".  means";
  column-gap: var(--space-4);
  padding-block: var(--space-3);
  position: relative;
}
.lifecycle__row + .lifecycle__row { border-top: var(--rule-solid); }

.lifecycle__dot {
  grid-area: dot;
  width: 8px;
  height: 8px;
  margin-top: 0.5em;
  border-radius: var(--radius-full);
  background: var(--color-ink-dim);
}
.lifecycle__row[data-state="issued"] .lifecycle__dot { background: var(--metric-warn); }
.lifecycle__row[data-state="pool"]   .lifecycle__dot { background: var(--metric-info); }
.lifecycle__row[data-state="locked"] .lifecycle__dot { background: var(--metric-neg); }
.lifecycle__row[data-state="burned"] .lifecycle__dot { background: var(--metric-pos); }

.lifecycle__row:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 3.5px;
  top: calc(0.5em + var(--space-4) + 12px);
  bottom: -2px;
  width: 1px;
  background: var(--color-line);
}

.lifecycle__name { grid-area: name; font-weight: var(--weight-medium); font-size: var(--text-body); }
.lifecycle__where {
  grid-area: where;
  font-size: var(--text-micro);
  letter-spacing: var(--track-label-sm);
  text-transform: uppercase;
  color: var(--color-ink-dim);
  margin-top: var(--space-2);
}
.lifecycle__means {
  grid-area: means;
  font-size: var(--text-caption);
  line-height: var(--leading-body);
  color: var(--color-ink-muted);
  margin-top: var(--space-2);
}
.lifecycle__rule {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: var(--rule-solid);
  font-size: var(--text-caption);
  line-height: var(--leading-body);
  color: var(--color-ink-muted);
}

/* The hero has to hold the actions AND the whole lifecycle panel above the
   fold, and the panel is fixed text that cannot reflow its way smaller. At
   1440x900 the tuned values clear it with 34px to spare; at 1440x800 - an
   ordinary 13" laptop - they were 66px short. So the panel gets a second,
   tighter tier keyed to viewport HEIGHT rather than width.

   Scoped to >=1152px because that is where the hero is two columns; below it
   the copy and figure stack and no amount of tightening fits both anyway. */
@media (min-width: 1152px) and (max-height: 880px) {
  .hero { padding-block: var(--space-6) var(--section-y); }
  .hero__plate { padding: var(--space-4); }
  .lifecycle { padding: var(--space-4); }
  .lifecycle__legend { margin-bottom: var(--space-3); }
  .lifecycle__row { padding-block: var(--space-2); }
  .lifecycle__rule { margin-top: var(--space-4); padding-top: var(--space-4); }
}

/* ---- 02 metrics band --------------------------------------- */
/* the section label sat flush on top of the 61px figures - measured 0px
   between them - so it read as part of the first number */
.metrics h2.label { margin-bottom: var(--space-6); }

.metrics {
  padding-block: var(--space-9);
  border-top: var(--rule-dotted-soft);
  border-bottom: var(--rule-dotted-soft);
}
.metrics__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-7) var(--space-6);
}
.metric__value {
  font-size: var(--text-h2);
  font-weight: var(--weight-medium);
  line-height: var(--leading-flat);
  letter-spacing: -0.052em;
}
.metric__label {
  margin-top: var(--space-4);
  font-size: var(--text-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-ink-dim);
}
.metric__note {
  margin-top: var(--space-2);
  font-size: var(--text-caption);
  color: var(--color-ink-muted);
  line-height: var(--leading-body);
}

/* ---- 03 three objects -------------------------------------- */
.objects {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-5);
}
.object {
  display: grid;
  gap: var(--space-4);
  align-content: start;
  padding: var(--space-6);
  border-radius: var(--radius-lg);
  background: var(--fill-1);
  transition: background-color var(--dur-base) var(--ease);
}
.object:hover { background: var(--fill-2); }
.object__index { font-size: var(--text-micro); letter-spacing: var(--track-label-sm);
  text-transform: uppercase; color: var(--color-ink-dim); }
.object__name {
  font-size: var(--text-h3);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--track-h3);
}
.object__meta { display: grid; gap: var(--space-2); margin-top: var(--space-2); }
.object__meta dt { margin-top: var(--space-4); }
.object__meta dt:first-child { margin-top: 0; }
.object__meta dd { margin-left: 0; font-size: var(--text-body); }

.principles { margin-top: var(--space-9); }
.principles__title { display: block; margin-bottom: var(--space-5); }
.principles__list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
}
.principles__list li {
  display: block;
  padding: var(--space-6);
  border: var(--rule-solid);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  transition: border-color var(--dur-base) var(--ease);
}
.principles__list li:hover { border-color: rgba(7, 6, 7, 0.28); }
/* Cards. The numbers are gone: seven counters said nothing the order did
   not already say, and each principle now leads with an icon of its own
   subject, drawn inline at the page's 1.5px stroke — the project has no
   icon dependency and adding one for seven glyphs is not worth a build
   step.

   Ember here, not the neutral ink of the first pass: on a tinted chip the
   colour is a surface, not type, so the brand orange can be used at full
   strength. The GLYPH is --color-ember-ink (#b33800), because a stroke is
   a non-text graphic and #fc5000 measures 2.57 against this chip. */
.prin__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-bottom: var(--space-5);
  border-radius: 14px;
  background: rgba(252, 80, 0, 0.12);
  color: var(--color-ember-ink);
}
.prin__chip svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.prin__title {
  margin-bottom: var(--space-2);
  font-size: var(--text-body-lg);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: -0.01em;
}
.prin__text {
  font-size: var(--text-caption);
  line-height: var(--leading-body);
  color: var(--color-ink-muted);
}

/* ---- 04 settlement flow ------------------------------------ */
.flow {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-7);
  align-items: start;
}

.flow__panel {
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--fill-1);
}
/* ---- lane diagram (in the flow rail) ------------------------
   Six participants; an arc joins the two the active step moves
   value between. Static and fully legible without JS — the arcs
   simply stay empty. */
.lanes {
  width: 100%;
  margin-bottom: var(--space-5);
  overflow: visible;
}
.lanes text {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: var(--weight-regular);
  letter-spacing: 0.04em;
  fill: var(--color-ink-dim);
  transition: fill var(--dur-base) var(--ease);
}
.lanes circle {
  fill: var(--color-canvas);
  stroke: var(--color-line-strong);
  stroke-width: 1.5;
  transition: fill var(--dur-base) var(--ease), stroke var(--dur-base) var(--ease);
}
.lane.is-on text { fill: var(--color-ink); }
.lane.is-on circle { fill: var(--color-ink); stroke: var(--color-ink); }
.lanes__arc {
  fill: none;
  stroke: var(--color-ink);
  stroke-width: 1.5;
  stroke-linecap: round;
}

.flow__counter {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding-bottom: var(--space-4);
  border-bottom: var(--rule-solid);
  line-height: var(--leading-flat);
}
.flow__counter-now {
  font-size: var(--text-h3);
  font-weight: var(--weight-medium);
  letter-spacing: var(--track-h2);
}
.flow__counter-of,
.flow__counter-total {
  font-size: var(--text-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-ink-dim);
}
.flow__units-label { display: block; margin-top: var(--space-4); }
.flow__units {
  margin-top: var(--space-2);
  min-height: 3em;
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

.chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-4); }
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 28px;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-full);
  background: var(--fill-1);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label-sm);
  text-transform: uppercase;
  color: var(--color-ink-dim);
  transition: color var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease);
}
.chip__dot {
  width: 6px; height: 6px;
  border-radius: var(--radius-full);
  background: var(--color-ink-dim);
  transition: background-color var(--dur-base) var(--ease);
}
.chip.is-on { color: var(--color-ink); background: var(--fill-3); }
.chip.is-on[data-chip="issued"] .chip__dot { background: var(--metric-warn); }
.chip.is-on[data-chip="pool"]   .chip__dot { background: var(--metric-info); }
.chip.is-on[data-chip="locked"] .chip__dot { background: var(--metric-neg); }
.chip.is-on[data-chip="burned"] .chip__dot { background: var(--metric-pos); }

.flow__steps { display: grid; }
.step {
  display: grid;
  grid-template-columns: 3rem 1fr;
  gap: var(--space-3) var(--space-5);
  padding-block: var(--space-6);
  border-top: var(--rule-solid);
  position: relative;
}
.step__num {
  font-size: var(--text-micro);
  letter-spacing: var(--track-label-sm);
  text-transform: uppercase;
  color: var(--color-ink-dim);
  padding-top: 0.6em;
}
.step__text {
  font-size: var(--text-lead);
  font-weight: var(--weight-regular);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-lead);
}
.step__fixed {
  grid-column: 2;
  display: flex;
  flex-wrap: wrap;
  /* Baseline, not the default stretch. The label is 12px on a 12px line
     box and the value 13px on a 19.5px one, so stretching them to equal
     height left the label's text riding above the value's. */
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  font-size: var(--text-caption);
  color: var(--color-ink-muted);
}

/* ---- text reveal (after magicui.design/docs/components/text-reveal) ----
   Each word is drawn twice: a dim ghost that holds the layout, and a live
   copy whose opacity is scrubbed 0 to 1.

   Their component pins a 200vh track and maps progress across it. This one
   is a static block, because the statement is set at 96px and at that size
   it runs 1373px in English and 1901px in Russian — taller than a viewport,
   so a pinned box would clip it. Words reveal as they pass a line in the
   viewport instead.

   Only `opacity` is scrubbed. An earlier version animated `color`, which
   repaints every word every frame and was one of the causes of the scroll
   stutter; opacity composites. */
/* Full bleed. The statement now sits OUTSIDE .container, as a direct child
   of the section, so it spans the whole section width — no 100vw anywhere.
   That matters: this document reserves 15px for a scrollbar, so a 100vw
   breakout would have run 15px past the visible area and been clipped.
   Its own gutter matches the masthead's, so the statement starts on the
   same vertical as the nav island rather than on the narrower text column. */
.textreveal {
  margin-block: var(--space-10);
  padding-inline: var(--gutter);
}
/* Exactly the nav island's box: the island is max-width --container inside
   the masthead's --gutter padding, and this repeats both. The statement
   therefore starts and ends on the island's own verticals at every width,
   which is what "touching the lines" meant. Without the max-width it ran
   the full section and sat ~9px proud of the island on each side. */
.textreveal__inner {
  max-width: var(--container);
  margin-inline: auto;
  /* so the type below can size itself from THIS box rather than the window */
  container-type: inline-size;
}
/* Justified, full bleed, capped at 92px — and the 92 is measured, not
   chosen. This is the fourth pass at justification and the first where
   every line actually reaches both edges.

   Two things changed to make it work. The statement left .container, so
   the measure went from 1152px to 1297px; and the size came down four
   pixels from 96.

   Justified worst gap and how far the furthest line ends from the right
   edge, at the 1297px measure:
        72px    50px gap,  0 short,  0 single-word lines
        84px   107px gap,  0 short,  0
        88px    82px gap,  0 short,  0
        92px    56px gap,  0 short,  0   <- here
        96px   165px gap, 628px short, 1 single-word line
   At 96px the break pattern isolates one long word on its own line. A
   line holding one word cannot be justified — there is no space in it to
   stretch — so it hangs 628px short of the right edge no matter what.
   92px is the largest size at which no line is left holding a single
   word.

   For the record, at the old 1152px measure 96px justified gave a 539px
   worst gap. The width mattered as much as the size did.

   Below 1024 this is ragged: at a 327px column half the lines carry one
   word and nothing can stretch them. Measured every size from 44px down
   to 20px there — none is clean. */
.textreveal__text {
  display: block;
  text-align: left;
  /* Sized from the COLUMN, not the window. The viewport curve is kept
     first as the fallback for anything without container units; the cqw
     line below wins where they are supported.

     7.2cqw is measured. Above roughly 7.3% of the measure the line
     breaking isolates one long Russian word on a line of its own, and a
     line holding one word cannot be justified — nothing in it to stretch
     — so it hangs hundreds of pixels short. The threshold held at both
     widths tested: 1280px column breaks at 96px (7.50%) and holds at
     92px (7.19%); 1137px column breaks at 84px (7.39%) and holds at
     82px (7.21%). A fixed px cap could only be right at one width; this
     keeps the ratio at every width.

     The floor is 18px, low enough never to fight the ratio. On a phone
     the ratio yields ~23px, which is what it costs to keep at least two
     words on every line: the pair "выпущенными, обязательство" needs
     23.5px or less in a 327px column. Bigger type there means lone words
     — at 64px, 17 of 18 lines carried one.

     The soft hyphens in the copy stay. They no longer fire at this size,
     but they are what stops "обязательство" being clipped rather than
     wrapped if the column ever gets very narrow. */
  font-size: clamp(1.125rem, 1.157rem + 7.08vw, 5.75rem);
  font-size: clamp(1.125rem, 7.2cqw, 5.75rem);
  font-weight: var(--weight-medium);
  line-height: var(--leading-flat);
  letter-spacing: var(--track-h1);
  /* The un-revealed ghost, deliberately dimmer than body text so the reveal
     reads as an arrival. 3.17 on canvas: below the 4.5 body floor, but this
     is set at 44px and the large-text floor of 3.0 is what applies. */
  color: #807d75;
}

/* inline-block, and the gaps are now REAL spaces in the markup rather
   than a flex column-gap — justify stretches text spaces, and has no
   effect whatever on flex items. That was the first failed attempt. */
.tr-word { position: relative; display: inline-block; }

/* Justify only where the column can absorb it — see the sizes above. */
@media (min-width: 1024px) {
  .textreveal__text { text-align: justify; }
}

/* No separate phone tier. 7.2cqw turns out to satisfy the phone for the
   same reason it satisfies the desktop: both constraints are really "two
   long Russian words have to share a line".

   On a 327px column the tightest adjacent pair, "выпущенными,
   обязательство" — 26 characters — fits only at 23.5px or below, and
   23.5/327 is 7.19%. The desktop threshold measured 7.19% too. One ratio,
   two different-looking problems. */

/* the ghost holds the layout; the live copy sits exactly on top of it */
.tr-live {
  position: absolute;
  inset: 0;
  opacity: 0;
  color: var(--color-ink);
}

/* Inverted variant. It used to mean "a light panel on a dark page"; the page
   is now light, so the same idea means an obsidian panel. The class name is
   kept so the switch in index.html still reads the same.
   Ghost #615f5a is 3.17 on obsidian, matching the light side; chalk is 20.23. */
.textreveal--light .textreveal__inner {
  background: var(--color-ink);
  border-radius: var(--radius-lg);
  padding: var(--space-9) var(--space-8);
}
.textreveal--light .textreveal__text { color: #615f5a; }
.textreveal--light .tr-live { color: var(--color-raised); }

/* ---- constant-obligation bar -------------------------------
   The document's hardest claim as a quantity rather than a graph:
   the holder above changes three times while the bar underneath
   does not move, and only a confirmed payout takes it to zero.
   Every step is present as text, so nothing depends on the motion. */
.obligation { margin-top: var(--space-10); }
.obl__card { padding: var(--space-7) var(--space-6); border-radius: var(--radius-lg); background: var(--color-surface); }

.obl__steps { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-7); }
/* No rule above the holder: the rail above the row is the connector now,
   and the active holder is marked by its own weight plus the station the
   token is sitting on. */
/* The active holder is marked on its left edge and the WHOLE block lifts -
   the name and the sentence under it together. Lighting only the name left
   the reader with a bright label above dim text that still described the
   current state.

   The border is always present and only changes colour, so nothing shifts
   horizontally when the step becomes active. Ember here is the same ember as
   the travelling token and the bar: it marks where the unit is now. */
.obl__step {
  position: relative;
  padding-top: var(--space-4);
  padding-left: var(--space-4);
  border-left: 2px solid transparent;
  transition: border-color var(--dur-base) var(--ease);
}
.obl__step.is-on { border-left-color: var(--color-ember); }

.obl__who {
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  color: var(--color-ink-dim);
  transition: color var(--dur-base) var(--ease);
}
.obl__note {
  margin-top: var(--space-2);
  font-size: var(--text-caption);
  line-height: var(--leading-body);
  color: var(--color-ink-muted);
  transition: color var(--dur-base) var(--ease);
}
.obl__step.is-on .obl__who,
.obl__step.is-on .obl__note { color: var(--color-ink); }

/* The track the unit travels between holders. Hidden while the steps are
   stacked — the path is built from the four columns' x positions, which
   only exist once they sit side by side. */
.obl__track { display: none; }
.obl__rail {
  fill: none;
  stroke: var(--color-line);
  stroke-width: 1.5;
}
.obl__hop {
  fill: none;
  stroke: var(--color-ink-muted);
  stroke-width: 1.5;
  stroke-linecap: round;
}
.obl__stations circle {
  fill: var(--color-canvas);
  stroke: var(--color-line-strong);
  stroke-width: 1.5;
  transition: stroke var(--dur-base) var(--ease), fill var(--dur-base) var(--ease);
}
.obl__stations circle.is-on { stroke: var(--color-ember); }
.obl__token {
  fill: var(--color-ember);
  stroke: var(--color-canvas);
  stroke-width: 2;
}

.obl__meter { margin-top: var(--space-8); }
.obl__legend { display: block; margin-bottom: var(--space-4); }
/* The track is chalk and the card is limestone, and both are set by contrast
   rather than taste. Ember is a light colour: against the canvas track this
   used to sit at 2.57 and against an --fill-1 card at 2.83, under the 3.0 that
   WCAG 1.4.11 asks of a non-text graphic. White takes the bar to 3.34 and
   limestone takes the travelling token to 3.09. */
.obl__bar {
  height: 14px;
  border-radius: var(--radius-full);
  background: var(--fill-3);
  overflow: hidden;
}
.obl__fill {
  height: 100%;
  width: 100%;
  border-radius: var(--radius-full);
  background: var(--color-ember);
  transform-origin: left center;
}
.obl__amount {
  margin-top: var(--space-4);
  font-size: var(--text-h3);
  font-weight: var(--weight-medium);
  line-height: 1;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
}

/* ---- 05 what it does not have ------------------------------
   Pills. Each item is a capsule rather than a row between rules:
   the thing that is absent, then why it never arises.

   The radius is --radius-full (800px), which the browser clamps to
   half the box height — so a two-line pill stays a true capsule
   instead of a rectangle with rounded corners. That clamp is also
   why the horizontal padding has to clear the curve: at these
   heights the left arc reaches ~50px in, and anything less would
   crowd the text against it. */
.absent {
  display: grid;
  gap: var(--space-4);
}
.absent__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-2);
  padding: var(--space-6) var(--space-8);
  border-radius: var(--radius-full);
  background: var(--fill-1);
  transition: background-color var(--dur-base) var(--ease);
}
.absent__row:hover { background: var(--fill-2); }

.absent__what {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  /* down from --text-h3: at h3 the longest item ran three lines inside
     the capsule and the pill stopped reading as one */
  font-size: var(--text-lead);
  font-weight: var(--weight-medium);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-lead);
}
.absent__slash {
  flex: none;
  width: 12px; height: 12px;
  align-self: center;
  background: no-repeat center / contain
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M1 11 L11 1' stroke='%23070607' stroke-width='1.5' fill='none'/%3E%3C/svg%3E");
}
.absent__why {
  margin-left: 0;
  /* aligns under the title text, past the slash and its gap */
  padding-left: calc(12px + var(--space-4));
  font-size: var(--text-body);
  color: var(--color-ink-muted);
  max-width: 62ch;
}

/* ---- 06 pool bento ----------------------------------------- */
.bento {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  grid-auto-flow: dense;
}
.card {
  display: flex;
  flex-direction: column;
  padding: var(--space-6);
  border-radius: var(--radius-lg);
  background: var(--fill-1);
}
.card__title {
  font-size: var(--text-h3);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--track-h3);
  margin-bottom: var(--space-5);
}
.card__body { flex: 1; }

.ruleset { display: grid; gap: 0; }
.ruleset > div {
  display: grid;
  gap: var(--space-1);
  padding-block: var(--space-3);
  border-top: var(--rule-solid);
}
.ruleset dt {
  font-size: var(--text-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-ink-dim);
}
.ruleset dd { margin-left: 0; font-size: var(--text-body); }

.round { display: grid; counter-reset: rnd; }
.round li {
  counter-increment: rnd;
  display: grid;
  gap: var(--space-1);
  padding-block: var(--space-3);
  border-top: var(--rule-solid);
}
.round__name { font-weight: var(--weight-medium); font-size: var(--text-body); }
.round__name::before {
  content: counter(rnd, decimal-leading-zero);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label-sm);
  color: var(--color-ink-dim);
  margin-right: var(--space-3);
}
.round__text { font-size: var(--text-caption); color: var(--color-ink-muted); line-height: var(--leading-body); }
.round__guard {
  display: flex; flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-2);
  font-size: var(--text-caption);
  color: var(--color-ink-dim);
}

.exec { display: grid; gap: var(--space-4); }
.exec li {
  display: grid;
  grid-template-columns: 1.5rem 1fr;
  gap: var(--space-3);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}
.exec li::before {
  content: "";
  width: 6px; height: 6px;
  margin-top: 0.6em;
  border-radius: var(--radius-full);
  background: var(--color-ink-dim);
}

.quorum__stat {
  font-size: var(--text-h2);
  font-weight: var(--weight-medium);
  line-height: var(--leading-flat);
  letter-spacing: -0.052em;
}
.quorum__text {
  margin-top: var(--space-5);
  font-size: var(--text-caption);
  line-height: var(--leading-body);
  color: var(--color-ink-muted);
}

/* ---- 07 netting -------------------------------------------- */
.netting { margin-bottom: var(--space-9); }
.netting__panels {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
}
.netting__panel {
  padding: var(--space-7) var(--space-6);
  border-radius: var(--radius-lg);
  background: var(--fill-1);
  text-align: center;
}
.netting__panel .label { display: block; margin-bottom: var(--space-6); }

.graph { width: 100%; max-width: 240px; margin-inline: auto; }
.graph__edges path { fill: none; stroke: var(--color-line-strong); stroke-width: 1; }
.graph__edges--net path { stroke: var(--metric-pos); stroke-width: 1.5; }
/* the gross edges shown inside the net panel, retracted on scroll */
.graph__ghost path { stroke: var(--color-line-strong); stroke-width: 1; opacity: 0.55; }
.graph__nodes circle { fill: var(--color-canvas); stroke: var(--color-ink-muted); stroke-width: 1.5; }
.graph__hub {
  fill: var(--metric-pos);
  stroke: var(--color-canvas);
  stroke-width: 2;
  /* scale must pivot on the node itself, not the SVG's user-space origin */
  transform-box: fill-box;
  transform-origin: center;
}

.netting__count {
  margin-top: var(--space-6);
  font-size: var(--text-kinetic);
  font-weight: var(--weight-medium);
  line-height: var(--leading-flat);
  letter-spacing: var(--track-kinetic);
}
.netting__panel:first-child .netting__count { color: var(--color-ink-muted); }
.netting__panel:last-child .netting__count { color: var(--metric-pos); }
.netting__unit {
  display: block;
  margin-top: var(--space-3);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label-sm);
  text-transform: uppercase;
  color: var(--color-ink-dim);
}

.netting__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-8);
  align-items: start;
}
.ordered { display: grid; counter-reset: ord; margin-top: var(--space-5); }
.ordered li {
  counter-increment: ord;
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  gap: var(--space-3);
  padding-block: var(--space-4);
  border-top: var(--rule-solid);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}
.ordered li::before {
  content: counter(ord, decimal-leading-zero);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label-sm);
  color: var(--color-ink-dim);
  padding-top: 0.45em;
}

.caveat {
  padding: var(--space-7) var(--space-6);
  border-radius: var(--radius-lg);
  background: var(--fill-2);
}
.caveat__title {
  font-size: var(--text-h3);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--track-h3);
  margin-bottom: var(--space-5);
}
.caveat p { font-size: var(--text-body); line-height: var(--leading-body); color: var(--color-ink-muted); }

/* ---- 08 invariants ----------------------------------------- */
.invariants {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  column-gap: var(--space-8);
  counter-reset: inv;
}
.invariants li {
  counter-increment: inv;
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  gap: var(--space-4);
  padding-block: var(--space-4);
  border-top: var(--rule-solid);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}
.invariants li::before {
  content: counter(inv, decimal-leading-zero);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label-sm);
  color: var(--color-ink-dim);
  padding-top: 0.45em;
}

.stack { margin-top: var(--space-9); }
.stack__title { display: block; margin-bottom: var(--space-5); }
/* A cell grid rather than a list of ruled rows: each entry is its own
   card, divided by hairlines, with an orange marker riding the left
   edge on hover.

   The marker grows from the centre out, which is why it is a height
   transition on a centred bar rather than an opacity fade — it should
   read as the edge being picked out, not as something switching on.
   Ember is decoration here: every cell already carries its label and
   value at full contrast, so nothing depends on the bar being seen. */
.stack__list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
}
.stack__list > div {
  position: relative;
  display: grid;
  gap: var(--space-2);
  padding: var(--space-5) var(--space-6);
  border-top: var(--rule-solid);
  transition: background-color var(--dur-base) var(--ease);
}
.stack__list > div::before {
  content: "";
  position: absolute;
  left: -1px;
  top: 50%;
  width: 2px;
  height: 0;
  border-radius: var(--radius-full);
  background: var(--color-ember);
  transform: translateY(-50%);
  transition: height var(--dur-base) var(--ease);
}
.stack__list > div:hover { background: var(--fill-1); }
.stack__list > div:hover::before { height: 56%; }

.stack__list dt {
  font-size: var(--text-micro);
  letter-spacing: var(--track-label-sm);
  text-transform: uppercase;
  color: var(--color-ink-dim);
}
.stack__list dd {
  margin-left: 0;
  font-size: var(--text-body-lg);
  font-weight: var(--weight-medium);
  letter-spacing: -0.01em;
}

/* ---- access ------------------------------------------------ */
.access { padding-block: var(--section-y); }
.access__inner {
  padding: var(--space-8) var(--space-6);
  border-radius: var(--radius-lg);
  background: var(--fill-1);
}
.access__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-8);
  align-items: start;
}
.access__h2 {
  font-size: var(--text-h2);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--track-h2);
  text-wrap: balance;
}
.access .btn { margin-top: var(--space-7); }
.access__note {
  margin-top: var(--space-6);
  max-width: 48ch;
  font-size: var(--text-caption);
  line-height: var(--leading-body);
  color: var(--color-ink-dim);
}
.access__list { display: grid; }
.access__list li {
  display: grid;
  grid-template-columns: 1.5rem 1fr;
  gap: var(--space-4);
  padding-block: var(--space-5);
  border-top: var(--rule-solid);
  font-size: var(--text-lead);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-lead);
}
.access__list li::before {
  content: "";
  width: 6px; height: 6px;
  margin-top: 0.55em;
  border-radius: var(--radius-full);
  background: var(--color-ink-muted);
}

/* ---- access panel: the drifting wash ---------------------------------
   Mechanics taken from a neumorphic "gradient blob card": a blurred disc
   that walks the four quadrants behind a frosted pane. Rebound to
   Caldera — the reference's pink/red/yellow becomes ember/violet/sulfur,
   and both of its neumorphic drop shadows are dropped outright, because
   this direction ships zero box-shadow.

   The pane is deliberately opaque. At the blob's worst stop (violet) the
   note line measures 4.97 against the 4.5 body floor; on the clear part
   of the panel it measures 5.50, against 5.11 for the flat fill this
   replaces. The contrast walker cannot see any of that — the pane is a
   pseudo-element, not an ancestor of the text — so those three numbers
   are computed by hand. Do not thin the pane without redoing them.

   Only transform animates, so the blurred disc is rasterised once and
   moved on the compositor. Nothing here reads layout per frame, which is
   the cost that got the earlier scroll libraries removed. */
.access__inner { position: relative; overflow: hidden; }

.access__fx {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  overflow: hidden;
  pointer-events: none;
}
.access__fx::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 55%;
  aspect-ratio: 1;
  border-radius: var(--radius-full);
  background: conic-gradient(from 210deg,
    var(--color-ember), var(--color-violet), var(--color-sulfur), var(--color-ember));
  filter: blur(48px);
  opacity: 0.38;
  will-change: transform;
  animation: access-blob 18s linear infinite;
}
/* the frosted pane the copy actually sits on */
.access__fx::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(247, 246, 242, 0.82);
}
/* the two real columns ride above the wash; the wash and the ring keep
   their own positioning, so both are excluded by name rather than by
   source order — a blanket `position: relative` here would knock the
   absolutely positioned ring back into the grid flow */
.access__grid > *:not(.access__fx):not(.access__ring) { position: relative; z-index: 1; }

/* ---- the gradient contour ------------------------------------
   The colour along the block's edge. Until now the section had only
   the blurred blob behind a frosted pane, which reads as a smudge in
   one corner — there was no line tracing the contour at all.

   The ring is one element painted in the 2px band between its border
   box and its padding box: two masks, the inner one composited OUT,
   leave only the rim. Nothing is clipped by .access__inner's
   overflow because the whole band sits inside the element's bounds. */
@property --ring-angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}
.access__ring {
  position: absolute;
  inset: 0;
  z-index: 2;              /* above .access__fx::after, the frosted pane */
  border-radius: inherit;
  padding: 2px;            /* = the ring's thickness */
  pointer-events: none;
  /* also declared plainly, so a browser without @property still paints a
     static ring instead of dropping the whole background as invalid */
  --ring-angle: 0deg;
  background: conic-gradient(from var(--ring-angle),
    var(--color-ember), var(--color-violet), var(--color-sulfur), var(--color-ember));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  animation: access-ring 14s linear infinite;
}
/* the angle turns, never the element: rotating a rounded rectangle would
   swing the whole shape off its own corners */
@keyframes access-ring {
  to { --ring-angle: 360deg; }
}

@keyframes access-blob {
  0%   { transform: translate(-100%, -100%); }
  25%  { transform: translate(0%, -100%); }
  50%  { transform: translate(0%, 0%); }
  75%  { transform: translate(-100%, 0%); }
  100% { transform: translate(-100%, -100%); }
}

/* ---- footer ------------------------------------------------ */
.footer {
  padding-block: var(--space-9) var(--space-7);
  background: var(--color-surface);
}
.footer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-8);
  padding-bottom: var(--space-8);
}
.footer__tagline {
  margin-top: var(--space-4);
  max-width: 34ch;
  font-size: var(--text-caption);
  line-height: var(--leading-body);
  color: var(--color-ink-muted);
}
.footer__col h2 { display: block; margin-bottom: var(--space-4); }
.footer__col ul { display: grid; gap: var(--space-1); }
.footer__col a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  font-family: var(--font-ui);
  font-size: var(--text-ui);
  color: var(--color-ink-muted);
  transition: color var(--dur-fast) var(--ease);
}
.footer__col a:hover { color: var(--color-ink); }

.footer__base {
  display: grid;
  gap: var(--space-4);
  padding-top: var(--space-6);
  border-top: var(--rule-solid);
}
.footer__disclaimer {
  max-width: 68ch;
  font-size: var(--text-caption);
  line-height: var(--leading-body);
  color: var(--color-ink-dim);
}
.footer__rights {
  font-size: var(--text-micro);
  letter-spacing: var(--track-label-sm);
  text-transform: uppercase;
  color: var(--color-ink-dim);
}

/* ============================================================
   RESPONSIVE
   ============================================================ */

@media (max-width: 479px) {
  .hero__h1 { font-size: var(--text-h2); }
}

/* ---- mobile settlement flow ---------------------------------
   The readout has to stay on screen or the step counter, units and
   chips change where nobody can see them — which is what "not working
   on mobile" meant. It cannot stick inside a single-column grid,
   because each child gets its own row and sticky has no room to
   travel, so the flow becomes block layout here and the rail sticks
   against the whole section. The lane diagram is dropped: it is the
   tallest part and the least readable at this width. */
/* Pills on a phone: the capsule's own arc would eat the measure at 375,
   so the radius steps down and the padding comes in. Still a pill, just
   not a 50px arc on a 327px line. */
@media (max-width: 639px) {
  .absent__row {
    padding: var(--space-5) var(--space-6);
    border-radius: var(--radius-lg);
  }
  .absent__what { font-size: var(--text-body-lg); gap: var(--space-3); }
  .absent__why { padding-left: calc(12px + var(--space-3)); }
}

@media (max-width: 1023px) {
  .flow { display: block; }

  /* MOBILE ONLY. Desktop above 1023 keeps the lane diagram, the sticky
     rail, the units line and the chips exactly as they were — none of
     the rules below reach it.

     Here the rail stops being a pinned panel. It was costing 271px, a
     third of a 812px viewport, held for the whole 1816px scroll; and
     what it pinned was a counter duplicating .step__num plus a
     four-chip legend that wrapped three ragged rows. What is left is
     the stage counter alone, unpinned, and the numbered list carries
     the section — the born-architects idiom: the ACTIVE step's number
     takes the orange.

     Nothing is removed from the DOM. motion.js bails out of step
     tracking entirely if [data-flow-index] or [data-flow-units] is
     missing, which would kill the active-step class this depends on,
     so the units line is hidden rather than deleted. */
  .flow__rail {
    position: static;
    margin-bottom: var(--space-7);
  }
  .flow__panel {
    padding: 0;
    background: none;
    border-bottom: 0;
  }

  .lanes { display: none; }

  /* Nothing from the old rail is shown here any more, the counter
     included: .step__num already prints 01-10 beside every step, so a
     stage line repeated the number that is on screen anyway.

     The rail is hidden, NOT removed. motion.js drops step tracking
     entirely if [data-flow-index] or [data-flow-units] is missing, and
     the active-step class the orange number depends on goes with it. */
  .flow__rail { display: none; }

  /* The active step has to carry itself on a phone: there is no rail
     beside it any more, so the state is the only thing telling you
     where you are. It gets an ember bar, a fill, and a wider gap from
     its neighbours than a colour change alone could give.

     The bar is a ::before rather than a border-left so nothing shifts
     horizontally when a step activates — .step is a 3rem/1fr grid and
     a real border would move every line of text by 2px. */
  .step {
    padding: var(--space-5) var(--space-4);
    margin-inline: calc(var(--space-4) * -1);
    border-radius: var(--radius-md);
    transition: background-color var(--dur-base) var(--ease);
  }
  .step::before {
    content: "";
    position: absolute;
    left: 0;
    top: var(--space-4);
    bottom: var(--space-4);
    width: 3px;
    border-radius: var(--radius-full);
    background: var(--color-ember);
    opacity: 0;
    transition: opacity var(--dur-base) var(--ease);
  }
  .step.is-active::before { opacity: 1; }
  .step.is-active { background: var(--fill-1); }

  .step__text {
    font-size: var(--text-body);
    /* dim, not muted: the gap between the resting and active states is
       what makes the active one read, and ink-muted at 7.05 sat too
       close to full ink to register on a small screen */
    color: var(--color-ink-dim);
    transition: color var(--dur-base) var(--ease);
  }
  .step.is-active .step__num { font-weight: var(--weight-medium); }
  .step__num { transition: color var(--dur-base) var(--ease); }

  /* the orange is --color-ember-ink (#b33800, 4.65 on canvas), never
     --color-ember (#fc5000): ember is a SURFACE colour and measures
     2.57 as type here. Same orange to read, no contrast failure. */
  .step.is-active .step__num { color: var(--color-ember-ink); }
  .step.is-active .step__text { color: var(--color-ink); }
}

@media (min-width: 640px) {
  .metrics__grid { grid-template-columns: repeat(2, 1fr); }
  .objects { grid-template-columns: repeat(2, 1fr); }
  .principles__list { grid-template-columns: repeat(2, 1fr); }
  .netting__panels { grid-template-columns: repeat(2, 1fr); }
  .stack__list { grid-template-columns: repeat(2, 1fr); }
  /* hairlines between cells: left border on every cell, removed on
     the first of each row so the grid does not gain an outer edge */
  .stack__list > div { border-left: var(--rule-solid); }
  .stack__list > div:nth-child(2n + 1) { border-left: 0; }
  .access__list { grid-template-columns: repeat(2, 1fr); column-gap: var(--space-7); }
  .footer__base { grid-template-columns: 1fr auto; align-items: end; }
}

@media (min-width: 768px) {
  .bento { grid-template-columns: repeat(2, 1fr); }
  .card--closed { grid-column: span 2; }
  .footer__grid { grid-template-columns: 2fr 1fr 1fr; }
  .access__inner { padding: var(--space-9) var(--space-8); }
}

@media (min-width: 1024px) {
  .stack__list { grid-template-columns: repeat(4, 1fr); }
  .stack__list > div { border-left: var(--rule-solid); }
  .stack__list > div:nth-child(2n + 1) { border-left: var(--rule-solid); }
  .stack__list > div:nth-child(4n + 1) { border-left: 0; }

  .island__links { display: flex; }
  .island__cta { display: inline-flex; }

  .metrics__grid { grid-template-columns: repeat(4, 1fr); }
  .obl__steps { grid-template-columns: repeat(4, 1fr); gap: var(--space-8); }
  .obl__track { display: block; width: 100%; height: 34px; margin-bottom: var(--space-2); overflow: visible; }
  .objects { grid-template-columns: repeat(3, 1fr); }
  .principles__list { grid-template-columns: repeat(3, 1fr); }

  .flow { grid-template-columns: minmax(0, 340px) minmax(0, 1fr); gap: var(--space-8); }
  /* The rail unsticks the moment its grid area ends, which without this
     happens while the last step is still being read. Padding on the grid
     container does not help — sticky is bound by the content box — so the
     extra height goes on the steps column, making the row taller. */
  /* Enough that when the last step is snapped to the top the rail is still
     fully pinned rather than sliding under the nav. Measured, not guessed:
     it needs to clear the panel height (444px) less the last step's own. */
  .flow__steps { padding-bottom: var(--space-11); }


  .flow__rail {
    position: sticky;
    top: var(--space-9);
    /* the panel is sized to fit; this only ever engages on a window far
       shorter than the design targets, and clips nothing when it does */
    max-height: calc(100dvh - var(--space-9) - var(--space-5));
    overflow-y: auto;
  }

  .step { transition: opacity var(--dur-base) var(--ease); }
  .step .step__text { color: var(--color-ink-muted); transition: color var(--dur-base) var(--ease); }
  .step::before {
    content: "";
    position: absolute;
    left: calc(var(--space-5) * -1);
    top: var(--space-6);
    bottom: var(--space-6);
    width: 2px;
    border-radius: var(--radius-full);
    background: var(--color-ink);
    opacity: 0;
    transition: opacity var(--dur-base) var(--ease);
  }
  .step.is-active .step__text { color: var(--color-ink); }
  .step.is-active .step__num { color: var(--color-ink); }
  .step.is-active::before { opacity: 1; }

  .bento { grid-template-columns: repeat(3, 1fr); }
  .card--closed { grid-column: span 2; grid-row: span 2; }
  .card--round  { grid-column: 3; grid-row: span 2; }
  .card--exec   { grid-column: span 2; }
  .card--quorum { grid-column: 3; }

  .netting__body { grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); gap: var(--space-9); }
  .invariants { grid-template-columns: repeat(2, 1fr); }
  .stack__list { grid-template-columns: repeat(4, 1fr); }

  .access__grid { grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); gap: var(--space-9); }
  .access__list { grid-template-columns: 1fr; }
}

@media (min-width: 1152px) {
  .hero__grid { grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.85fr); gap: var(--space-8); }
}

/* ============================================================
   REDUCED MOTION
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .flow__rail { position: static; }
  .step .step__text { color: var(--color-ink); }
  .step::before { display: none; }
  .chip { color: var(--color-ink); }
  .link-arrow:hover::after { transform: none; }

  /* Graphics keep their information, lose their movement: the state
     machine shows every edge at rest, the netting panel shows the net
     result, and the figures are plain text. */
  .graph__ghost { display: none; }
  /* every step is already text; show them all lit and the bar at full */
  .obl__step .obl__who,
  .obl__step .obl__note { color: var(--color-ink); }
  .obl__track { display: none; }
  .obl__fill { transform: none !important; }
}


/* ============================================================
   HERO PROTOTYPE (merged from index2.html, 2026-08-25)

   The hero is a client-instruction prototype inside a browser
   frame, plus two decorative layers behind it. Its markup carries
   inline styles by design — that is how it was authored — so only
   what cannot be inline lives here: keyframes, hover, pseudo
   elements and media queries.

   Colour literals were rebound to tokens on merge. The three that
   remain in the markup (#ff5f57 / #febc2e / #28c840) are the macOS
   traffic lights: they depict a real window and are not ours to
   restyle.
   ============================================================ */
@keyframes om-grid { 0% { background-position: 0 0; } 100% { background-position: 0 50px; } }
  @keyframes om-live { 0%, 100% { opacity: 1; } 50% { opacity: 0.28; } }
  @keyframes om-caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

  /* the only rules that cannot be inline: hover, pseudo-element, media */
  .nav-link { color: var(--color-ink-muted); transition: color 200ms var(--ease), background-color 200ms var(--ease); }
  .nav-link:hover { color: var(--color-ink); background: var(--color-surface); }
  .badge:hover { color: var(--color-ink); }
  .cta-primary:hover { background: var(--color-ember-hover); }
  .cta-quiet { color: var(--color-ink-muted); transition: color 200ms var(--ease); }
  .cta-quiet:hover { color: var(--color-ink); }
  .cta-quiet::after {
    content: ""; width: 22px; height: 22px; border-radius: 800px;
    background: var(--color-surface) no-repeat center / 9px 9px
      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2 10 L10 2 M4.5 2 H10 V7.5' fill='none' stroke='%23070607' stroke-width='1.4'/%3E%3C/svg%3E");
    transition: transform 300ms var(--ease), background-color 200ms var(--ease);
  }
  .cta-quiet:hover::after { transform: translate(2px, -2px); background-color: var(--color-raised); }
  .btn-ghost { transition: background-color 200ms var(--ease), color 200ms var(--ease); }
  .btn-ghost:hover { background: var(--color-ink); color: var(--color-raised); }
  @media (max-width: 900px) { .island-links { display: none !important; } }
  /* The pane divider is a BOTTOM border, with no width condition on it.
     The frame's grid is `repeat(auto-fit, minmax(min(100%, 620px), 1fr))`
     inside a figure capped at max-width 1120px, so two 620px columns —
     which would need 1240px — never fit: the grid is one column at every
     viewport. The old rule assumed it split above 1264px and drew a
     right border there, leaving a stray vertical line down the frame
     with no second column beside it. */
  /* mobile: Safari chrome keeps only the traffic lights and the address strip */
  @media (max-width: 720px) {
    /* below 720 the frame stops being a desktop window and becomes a
       phone: the whole chrome bar is replaced by an iOS status bar and
       a bottom bar, both of which are display:none by default. */
    .om-chrome-bar { display: none !important; }
    .om-ios-status { display: flex !important; }
    .om-ios-bottom { display: flex !important; }
    .om-figure { margin-top: 56px !important; }
    .om-pane { padding: 18px !important; }
    .om-viewsub { margin-left: 0 !important; flex-basis: 100%; }
    .om-record-row { flex-wrap: wrap; gap: 4px 16px; }
  }
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  }

/* ============================================================
   HERO PROTOTYPE — execution timeline

   The dots are joined by a line that draws out of one and into the
   next as each step completes, and the meta line under each name
   swaps with a quick fade rather than blinking.

   None of this was possible before: paint() rebuilt the whole list
   with innerHTML on every 100ms tick, so the transitions the markup
   already declared never fired once — the elements were new on every
   frame and had no previous value to animate from. The list is now
   built once and only its classes and text change.
   ============================================================ */
.om-tl__row {
  position: relative;
  display: grid;
  grid-template-columns: 14px 1fr;
  column-gap: 16px;
  padding-block: 14px;
  border-bottom: 1px solid var(--color-line);
}
.om-tl__row:last-child { border-bottom: 0; }

.om-tl__dot {
  width: 8px;
  height: 8px;
  margin-top: 6px;
  border-radius: var(--radius-full);
  background: var(--color-raised);
  box-shadow: 0 0 0 1.5px rgba(7, 6, 7, 0.28);
  transition: background-color 300ms linear, box-shadow 300ms linear;
}
.om-tl__row.is-done .om-tl__dot { background: var(--metric-pos); }
.om-tl__row.is-now  .om-tl__dot { background: var(--color-ink); box-shadow: 0 0 0 4px rgba(7, 6, 7, 0.10); }

/* The connector. Absolutely positioned so it can cross the row's own
   bottom edge and reach the next dot: top clears this dot by 4px,
   bottom overshoots the row by 16px to stop 4px short of the next
   one. It scales from the top, so it reads as the line growing out
   of the completed dot toward the one below. */
.om-tl__line {
  position: absolute;
  /* Whole pixels, both of them. At width 1.5px sitting at x=3.25 the line
     straddles two device pixels, so the browser antialiases it — and it
     re-rasterises as the element is scaled, which is why the width looked
     like it changed mid-animation. 2px starting at x=3 is still centred on
     the 8px dot (centre x=4) and renders identically at every scale. */
  left: 3px;
  top: 32px;
  bottom: -16px;
  width: 2px;
  background: var(--metric-pos);
  transform: scaleY(0);
  /* Resting origin is the BOTTOM, so when the line leaves it collapses
     downward into the next dot rather than retracting back up into the
     one it came from. Only the origin flips between the two states —
     transform-origin does not transition, it just switches, which is
     what makes the line read as travelling down the list. */
  transform-origin: bottom center;
  /* Leaving: the top edge is drawn DOWN into the dot below, so the line
     is swallowed by the step it was pointing at. Eased in, because a
     retraction should accelerate away rather than glide. */
  transition: transform 320ms cubic-bezier(0.4, 0, 1, 1);
}
.om-tl__row:last-child .om-tl__line { display: none; }

/* Exactly one line is drawn at a time: the one leaving the step that
   just finished, pointing at the step now running.

   The delay is the whole point of the effect. Without it the outgoing
   line and the incoming one animate together and you see two lines at
   once, which read as the connector stretching rather than moving. At
   340ms the retraction (320ms) has finished first, so the sequence is
   strictly: pull in from the far side, vanish, then emerge from the
   same dot heading for the next one. 780ms end to end, well inside
   the 2000ms a phase lasts. */
.om-tl__row.is-linking .om-tl__line {
  transform: scaleY(1);
  transform-origin: top center;
  transition: transform 460ms cubic-bezier(0.16, 1, 0.3, 1) 340ms;
}

.om-tl__name {
  font-size: 14px;
  color: var(--color-ink-dim);
  transition: color 300ms linear, opacity 140ms linear;
}
.om-tl__row.is-done .om-tl__name,
.om-tl__row.is-now  .om-tl__name { color: var(--color-ink); }

.om-tl__meta {
  margin-top: 2px;
  font-size: 10px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--color-ink-dim);
  transition: opacity 140ms linear;
}

/* the fast out/in: the text is only invisible for the 140ms between
   the two halves of the swap */
.om-tl__name.is-swap,
.om-tl__meta.is-swap { opacity: 0; }

/* ============================================================
   MASTHEAD TUCK + ACTIVE TAB INDICATOR
   ============================================================ */

/* Tucks up out of the way on the way down, comes back on the way up.
   The transform is on the masthead, not the island, so the blur panel
   and its shadow-free edge travel together. */
.masthead {
  transition: transform var(--dur-base) var(--ease);
  will-change: transform;
}
.masthead.is-tucked { transform: translateY(-140%); }

/* the reader asked for no motion — then the nav simply never moves */
@media (prefers-reduced-motion: reduce) {
  .masthead.is-tucked { transform: none; }
}

/* The active tab marker: a short orange bar riding the island's top
   edge above the current section's link.

   It is ember (#fc5000), the brand orange, and that is safe here
   because it is REINFORCEMENT, not the signal: the active link
   already changes both its fill and its ink, so the state survives
   without the bar. Ember measures ~2.5 on the island and could not
   carry 1.4.11 alone. */
.island__links a { position: relative; }
.island__links a::before {
  content: "";
  position: absolute;
  /* straddles the island's top edge rather than sitting inside it:
     measured, -10px put the bar 2px in, so it read as a stripe on the
     pill instead of a tab hanging over its rim */
  top: -14px;
  left: 50%;
  width: 30px;
  height: 5px;
  border-radius: var(--radius-full);
  background: var(--color-ember);
  transform: translateX(-50%) scaleX(0);
  transform-origin: center;
  opacity: 0;
  transition: transform var(--dur-base) var(--ease), opacity var(--dur-fast) var(--ease);
}
.island__links a[aria-current="true"]::before {
  transform: translateX(-50%) scaleX(1);
  opacity: 1;
}
