@import url("./fonts.css");

/* ============================================================================
   Lane A. Ledger.
   The product is the record of your agents' mail.
   Physical object: a registered-mail receipt and a customs declaration form.

   Rules this file enforces, from DESIGN.md:
   - Light only. No pure #000 or #fff. Every neutral tinted warm (hue 65-82).
   - Post-yellow is a large calm field, never a highlighter. It never encodes state.
   - Status is a struck mark first, colour second. Marks carry a glyph and a label.
   - Archivo for everything, Sometype Mono for every literal value. No serif.
   - No gradients except the inherited logo mark. No blue, no purple.
   ========================================================================== */

:root {
  /* Stock */
  --paper:      oklch(0.981 0.008 82);
  --paper-2:    oklch(0.964 0.010 82);
  --paper-3:    oklch(0.938 0.012 80);
  --paper-4:    oklch(0.912 0.013 78);

  /* Ink */
  --ink:        oklch(0.245 0.014 65);
  --ink-2:      oklch(0.430 0.012 70);
  /* 0.505 is the lightest this can be and still clear 4.5:1 on paper-4,
     the darkest ground it is used against. Measured, not guessed. */
  --ink-3:      oklch(0.505 0.010 72);
  --ink-inv:    oklch(0.975 0.008 82);

  /* Rules */
  --rule:       oklch(0.888 0.012 78);
  --rule-2:     oklch(0.808 0.014 76);
  --rule-3:     oklch(0.700 0.016 74);

  /* Brand field. Structure and identity only. Never a state.
     --yellow is the accent, used small: tags, marks, focus rings, rules.
     --field is the same hue held back, for areas large enough that full
     chroma would shout. A big flat plane and a 2px rule are not the same
     colour problem, so they are not the same token. */
  --field:       oklch(0.934 0.072 89);
  --field-2:     oklch(0.910 0.082 89);
  --field-rule:  oklch(0.790 0.118 88);
  --yellow:      oklch(0.868 0.158 92);
  --yellow-2:    oklch(0.822 0.156 91);
  --yellow-deep: oklch(0.740 0.145 88);

  /* Reserved status vocabulary. Used for nothing else. */
  --ok:         oklch(0.500 0.110 152);
  --ok-field:   oklch(0.955 0.028 152);
  --bad:        oklch(0.520 0.190 27);
  --bad-field:  oklch(0.960 0.028 27);
  --wait:       oklch(0.505 0.011 72);

  --sans: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --mono: "Sometype Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  /* Fixed rem scale, product register. Ratio 1.25 at the top, 1.2 below. */
  --t-mega:  clamp(2.55rem, 1.6rem + 3.6vw, 4.25rem);
  --t-h1:    clamp(2rem, 1.4rem + 2.2vw, 3rem);
  --t-h2:    1.625rem;
  --t-h3:    1.25rem;
  --t-h4:    1.0625rem;
  --t-body:  0.9375rem;
  --t-sm:    0.8125rem;
  --t-xs:    0.75rem;
  --t-mark:  0.6875rem;

  --gut: clamp(1.25rem, 0.6rem + 2.4vw, 3rem);
  --max: 1240px;
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { min-width: 0; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 5.5rem;
  /* The canvas colour lives here, not on body, so a page can lay a texture
     between the two. See .lattice. */
  background: var(--paper);
}
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 var(--t-body)/1.55 var(--sans);
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
h1, h2, h3, h4, p, ul, ol, dl, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; }
a { color: inherit; }
table { border-collapse: collapse; width: 100%; }
:where(input, textarea, select, button) { font-family: inherit; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

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

/* ---------- type primitives ---------- */
.mono, code, kbd, samp, pre { font-family: var(--mono); font-variant-ligatures: none; }
.num, td.num, .tabular { font-variant-numeric: tabular-nums; }

h1, .h1 { font-size: var(--t-h1); font-weight: 700; letter-spacing: -0.022em; line-height: 1.08; text-wrap: balance; }
.mega   { font-size: var(--t-mega); font-weight: 700; letter-spacing: -0.03em; line-height: 1.02; text-wrap: balance; }
h2, .h2 { font-size: var(--t-h2); font-weight: 650; letter-spacing: -0.018em; line-height: 1.15; text-wrap: balance; }
h3, .h3 { font-size: var(--t-h3); font-weight: 620; letter-spacing: -0.012em; line-height: 1.25; }
h4, .h4 { font-size: var(--t-h4); font-weight: 620; letter-spacing: -0.008em; line-height: 1.3; }
.lede   { font-size: 1.0625rem; line-height: 1.6; color: var(--ink-2); max-width: 66ch; }
.prose  { max-width: 68ch; }
.prose p + p { margin-top: 0.9em; }
.sm { font-size: var(--t-sm); }
.xs { font-size: var(--t-xs); }
.muted { color: var(--ink-2); }
.dim { color: var(--ink-3); }

/* Mono parenthesised section marker. Section headers only, never above an h1. */
.marker {
  display: flex; align-items: center; gap: 0.75rem;
  font: 500 var(--t-mark)/1 var(--mono);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-3);
}
.marker::after { content: ""; flex: 1; height: 1px; background: var(--rule); }
.marker--plain::after { display: none; }

/* The hidden attribute has to win over the layout classes below. The UA sheet's
   [hidden] { display: none } is an author-overridable default, so a .row or .panel that
   also carries hidden would otherwise still be laid out. */
[hidden] { display: none !important; }

/* ---------- layout ---------- */
.wrap { max-width: var(--max); margin-inline: auto; padding-inline: var(--gut); }
.wrap--wide { max-width: 1440px; }
.stack > * + * { margin-top: var(--s, 1rem); }
.row { display: flex; align-items: center; gap: 0.6rem; }
.row--wrap { flex-wrap: wrap; }
.spread { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.section { padding-block: clamp(3rem, 1.5rem + 4vw, 5.5rem); scroll-margin-top: 5.5rem; }
.section--tight { padding-block: clamp(2rem, 1rem + 2.6vw, 3.25rem); }
.rule-top { border-top: 1px solid var(--rule); }
.rule-bot { border-bottom: 1px solid var(--rule); }

/* ---------- the lattice ----------
   Stock texture, not decoration. Two 1px dot rasters, one at 24px for grain and
   a sparser one at 96px for a slower rhythm, so the eye reads texture rather
   than a grid. Hard edged on purpose: the dots are SVG rects, not soft radial
   stops, so a 1px dot is exactly 1px at any zoom.

   The images are attached to the viewport, so the raster never crawls while the
   page scrolls, but the element spans the document, so the mask can retire the
   texture toward the foot of the page where the content gets denser.

   Opacity is tuned per ground: the same dot on the yellow field has less
   contrast than on paper, so the field carries its own pair. */
/* Transparent, so the root's paper shows through and the texture can sit
   between the two. A negative z-index child paints after the root background
   but before any content. */
.lattice { position: relative; background: transparent; }
.lattice::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Crect width='1' height='1' fill='%233a322b' fill-opacity='.10'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96'%3E%3Crect width='1' height='1' fill='%233a322b' fill-opacity='.15'/%3E%3C/svg%3E");
  background-size: 24px 24px, 96px 96px;
  background-attachment: fixed, fixed;
  mask-image: linear-gradient(#000 0 34%, #000c 62%, #0000 92%);
}
/* ---------- the yellow field ----------
   Large flat area, near-black type, torn-stamp bottom edge.
   Not a gradient. Not a state. */
.field {
  background-color: var(--field);
  color: var(--ink);
  position: relative;
}
.lattice .field {
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Crect width='1' height='1' fill='%233a322b' fill-opacity='.115'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96'%3E%3Crect width='1' height='1' fill='%233a322b' fill-opacity='.17'/%3E%3C/svg%3E");
  background-size: 24px 24px, 96px 96px;
  background-attachment: fixed, fixed;
}
/* Neutral grey goes flat on a tinted ground, so the field redefines the two
   secondary inks toward its own hue. Redefining the tokens rather than the
   selectors means anything reading var(--ink-2) inside the field, including
   inline styles, gets the warm variant without knowing it exists. */
.field {
  --ink-2: oklch(0.440 0.045 82);
  --ink-3: oklch(0.505 0.038 82);
}
.field .marker::after { background: var(--field-rule); }
/* Perforated edge, the way a tear-off strip on a paper form is perforated: half
   circles bitten out of the bottom of the field. One mask layer, one circle per
   column, centred on the bottom edge so the cut is taken from the field itself.
   The two-layer composite this replaced left a full-width transparent band
   above the pattern, so the points read as a row of little tents floating below
   the edge rather than as anything removed from it. */
.field--notch {
  --notch: 11px;
  padding-bottom: calc(var(--notch) + 0.5rem);
  mask-image: radial-gradient(circle var(--notch) at 50% 100%, #0000 98%, #000 100%);
  mask-size: calc(var(--notch) * 2) 100%;
  mask-position: calc(var(--notch) * -0.5) 0;
  mask-repeat: repeat-x;
}

/* Ledger strip across the foot of the field. Ruled, tabular, no cards. */
.hero-facts {
  margin-top: clamp(2rem, 1.2rem + 2vw, 3rem);
  border-top: 1.5px solid var(--yellow-deep);
  display: grid; gap: 0;
  grid-template-columns: 1fr;
}
.hero-facts > div { padding: 0.7rem 0; border-bottom: 1px solid var(--field-rule); }
.hero-facts dt {
  font: 500 var(--t-mark)/1.4 var(--mono); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-2);
}
.hero-facts dd { margin: 0.2rem 0 0; font-weight: 620; font-size: var(--t-sm); }
@media (min-width: 720px) {
  /* Auto columns, not repeat(4): a fact can be gated off (the verification timing is hidden
     until it is measured) and auto-flow lays out only the facts that are actually shown, so
     the strip never leaves an empty track where the hidden one was. */
  .hero-facts { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
  .hero-facts > div { border-bottom: 0; padding: 0.85rem 1rem 0 0; }
  .hero-facts > div + div { border-left: 1px solid var(--field-rule); padding-left: 1.1rem; }
}
/* ---------- the hero cursor ----------
   Over the hero the pointer is the brand mark itself, floating on the river and
   pressing the white envelope well into the surface under it. Rasterised from
   the shipped mark (assets/brand/aiax-mark.svg) at cursor size, hotspot on the
   mark's point. The plain url() is the fallback for engines without image-set;
   the image-set below hands retina the 2x so the mark stays crisp; every value
   ends in auto, so a browser that refuses the image still shows a cursor. Hero
   only. Affordance is kept: links and buttons still read clickable, and the
   window cards keep the grab cursor where they are draggable. */
.hero-sec {
  cursor: url("./assets/brand/aiax-cursor-32.png") 16 3, auto;
  cursor: image-set(
      url("./assets/brand/aiax-cursor-32.png") 1x,
      url("./assets/brand/aiax-cursor-64.png") 2x
    ) 16 3, auto;
}
.hero-sec a[href], .hero-sec button { cursor: pointer; }
.field hr, .field .rule-top { border-color: var(--field-rule); }
/* A ghost control on the field needs 3:1 against it. The accent yellow reaches
   1.9, which is a border you can see only if you already know it is there. */
.field .btn--ghost { --bd: oklch(0.545 0.125 84); }
.field .btn--ghost:hover { --bd: var(--ink); --bg: color-mix(in oklch, var(--ink) 7%, transparent); }

/* ---------- buttons ---------- */
.btn {
  --bg: var(--ink); --fg: var(--ink-inv); --bd: var(--ink);
  display: inline-flex; align-items: center; gap: 0.5rem;
  background: var(--bg); color: var(--fg); border: 1.5px solid var(--bd);
  padding: 0.62rem 1.05rem; border-radius: 2px; cursor: pointer;
  font-weight: 600; font-size: var(--t-sm); letter-spacing: 0.005em;
  text-decoration: none; white-space: nowrap;
  transition: background 160ms cubic-bezier(.22,1,.36,1), color 160ms cubic-bezier(.22,1,.36,1);
}
.btn:hover { --bg: oklch(0.175 0.014 65); }
.btn--ghost { --bg: transparent; --fg: var(--ink); --bd: var(--rule-3); }
.btn--ghost:hover { --bg: var(--paper-3); --bd: var(--ink); }
.btn--sm { padding: 0.4rem 0.7rem; font-size: var(--t-xs); }
.btn[disabled], .btn[aria-disabled="true"] {
  --bg: var(--paper-3); --fg: var(--ink-3); --bd: var(--rule-2);
  cursor: not-allowed;
}
.btn--ghost[disabled] { --bg: transparent; }

/* Copy control. Always has an accessible name. */
.copy {
  display: inline-flex; align-items: center; gap: 0.35rem;
  background: transparent; border: 1px solid var(--rule-2); border-radius: 2px;
  padding: 0.2rem 0.4rem; cursor: pointer;
  font: 500 var(--t-xs)/1 var(--mono); color: var(--ink-2);
  transition: border-color 140ms cubic-bezier(.22,1,.36,1), color 140ms cubic-bezier(.22,1,.36,1);
}
.copy:hover { border-color: var(--ink); color: var(--ink); }
.copy[data-done="1"] { color: var(--ok); border-color: var(--ok); }
/* The clipboard was refused. Saying so in the failure colour is the whole
   point: a green "copied" over an empty clipboard is a small lie in a product
   whose argument is that it does not tell you small lies. */
.copy[data-done="2"] { color: var(--bad); border-color: var(--bad); }

/* ---------- the stamp: struck mark first, colour second ---------- */
.stamp {
  display: inline-flex; align-items: baseline; gap: 0.4em;
  font: 600 var(--t-mark)/1 var(--mono);
  letter-spacing: 0.1em; text-transform: uppercase;
  padding: 0.36em 0.5em 0.32em; border: 1.5px solid currentColor; border-radius: 2px;
  white-space: nowrap;
}
.stamp .gl { font-size: 1.05em; line-height: 1; }
.stamp--ok   { color: var(--ok);    background: var(--ok-field);  transform: rotate(-1.3deg); }
.stamp--bad  { color: var(--bad);   background: var(--bad-field);  transform: rotate(1.1deg); }
.stamp--wait { color: var(--wait);  border-style: dashed; background: transparent; }
.stamp--run  { color: var(--ink);   border-style: solid; background: var(--paper-3); }
.stamp--lg { font-size: var(--t-sm); padding: 0.5em 0.7em 0.44em; }

/* ---------- ledger table ---------- */
.ledger-wrap { overflow-x: auto; border: 1px solid var(--rule-2); border-radius: 2px; background: var(--paper); }
.ledger { min-width: 620px; font-size: var(--t-sm); }
.ledger caption { text-align: left; padding: 0.7rem 0.85rem; border-bottom: 1px solid var(--rule-2); background: var(--paper-2); }
.ledger th {
  text-align: left; font: 500 var(--t-mark)/1.2 var(--mono);
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3);
  padding: 0.55rem 0.7rem; border-bottom: 1px solid var(--rule-2); white-space: nowrap;
  background: var(--paper-2);
}
.ledger td { padding: 0.62rem 0.7rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
.ledger tbody tr:nth-child(even) td { background: color-mix(in oklch, var(--paper-2) 62%, transparent); }
.ledger tbody tr:last-child td { border-bottom: 0; }
.ledger .gutter { width: 1%; padding-right: 0; color: var(--ink-3); font: 500 var(--t-xs)/1.6 var(--mono); }
.ledger tr[data-state="bad"] td { background: color-mix(in oklch, var(--bad-field) 70%, transparent); }
.ledger tr[data-state="ok"] .val { color: var(--ink-2); }

/* Long literal values: wrap deliberately, stay fully copyable. */
.val { font-family: var(--mono); font-size: var(--t-xs); line-height: 1.5; overflow-wrap: anywhere; }
.val--clip { display: block; max-width: 34ch; }
.val--name { display: block; white-space: nowrap; }
/* A qualifier that belongs to the name, not to a column of its own. Six columns
   do not fit a console panel at 1440, and a clipped last column reads as broken
   even when the container scrolls. */
.val--sub { display: block; font: 400 var(--t-xs)/1.5 var(--sans); color: var(--ink-3); }
.ledger td.rec-state { white-space: nowrap; }
/* For a ledger in a rail rather than a column. Same rows, less gutter. */
.ledger--tight th, .ledger--tight td { padding-inline: 0.5rem; }
.expect-observe { display: grid; gap: 0.2rem; }
.expect-observe > div { display: grid; grid-template-columns: 4.6rem 1fr; gap: 0.5rem; align-items: baseline; }
.expect-observe .lbl { font: 500 var(--t-mark)/1.5 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3); }
.observed-bad { color: var(--bad); text-decoration: line-through 1px; text-decoration-color: color-mix(in oklch, var(--bad) 55%, transparent); }
.observed-none { color: var(--ink-3); font-style: normal; }

/* ---------- the record book: append-only evidence ---------- */
.book {
  border: 1px solid var(--rule-2); border-radius: 2px; background: var(--paper);
  display: grid; grid-template-rows: auto 1fr auto; min-height: 0;
}
.book__head { padding: 0.6rem 0.8rem; border-bottom: 1px solid var(--rule-2); background: var(--paper-2); }
.book__lines {
  margin: 0; padding: 0.5rem 0.2rem 0.6rem; list-style: none;
  font: 400 var(--t-xs)/1.65 var(--mono);
  max-height: 19rem; overflow-y: auto; scrollbar-width: thin;
}
.book__lines li {
  display: grid; grid-template-columns: 5.2rem 1.1rem 1fr; gap: 0.5rem;
  padding: 0.2rem 0.8rem; border-bottom: 1px dotted var(--rule);
  animation: line-in 420ms cubic-bezier(.16,1,.3,1) both;
}
.book__lines li:last-child { border-bottom: 0; }
.book__lines time { color: var(--ink-3); font-variant-numeric: tabular-nums; }
.book__lines .gl { text-align: center; }
.book__lines li[data-kind="ok"] .gl { color: var(--ok); }
.book__lines li[data-kind="bad"] .gl { color: var(--bad); }
.book__lines li[data-kind="run"] .gl { color: var(--ink-2); }
.book__lines li[data-kind="ok"] .txt { color: var(--ink); }
.book__lines .txt { overflow-wrap: anywhere; }
.book__foot { padding: 0.5rem 0.8rem; border-top: 1px solid var(--rule); background: var(--paper-2); }
@keyframes line-in { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }

/* ---------- code ---------- */
.code {
  border: 1px solid var(--rule-2); border-radius: 2px; background: var(--paper);
  overflow: hidden;
}
.code__bar {
  display: flex; align-items: center; gap: 0.15rem;
  padding: 0.3rem 0.4rem; border-bottom: 1px solid var(--rule-2); background: var(--paper-2);
}
.code__tab {
  background: transparent; border: 1px solid transparent; border-radius: 2px; cursor: pointer;
  padding: 0.3rem 0.55rem; font: 500 var(--t-xs)/1 var(--mono); color: var(--ink-3);
  letter-spacing: 0.04em;
}
.code__tab[aria-selected="true"] { color: var(--ink); background: var(--paper); border-color: var(--rule-2); font-weight: 600; }
.code__tab:hover { color: var(--ink); }
.code pre {
  margin: 0; padding: 0.85rem 0.95rem; overflow-x: auto;
  font: 400 var(--t-xs)/1.65 var(--mono); tab-size: 2;
}
.code--num pre { counter-reset: ln; }
.code--num .ln { display: block; padding-left: 2.6rem; text-indent: -2.6rem; }
.code--num .ln::before {
  counter-increment: ln; content: counter(ln);
  display: inline-block; width: 1.8rem; margin-right: 0.8rem;
  text-align: right; color: var(--ink-3); user-select: none;
}
/* Light syntax palette. Warm, low chroma, no dark block on a light page.
   No blue and no purple, which rules out the two hues every highlighter reaches
   for first. Keywords are set in weight rather than hue, which leaves three
   colours to carry three roles and keeps a code block from turning into a
   rainbow on a page whose whole argument is that colour means something. */
.k  { color: var(--ink); font-weight: 620; }  /* keyword */
.s  { color: oklch(0.44 0.11 152); }   /* string */
.n  { color: oklch(0.46 0.12 55); }    /* number, literal */
.c  { color: var(--ink-3); font-style: normal; }
.f  { color: oklch(0.40 0.06 108); }   /* function, property */
.p  { color: var(--ink-2); }

/* ---------- panels ---------- */
.panel { border: 1px solid var(--rule-2); border-radius: 2px; background: var(--paper); }
.panel__head { padding: 0.65rem 0.85rem; border-bottom: 1px solid var(--rule-2); background: var(--paper-2); }
/* Every working section of the console is titled here, so this is a heading and
   not a decorated span. As a span the screen-reader heading list stopped at the
   page h1 and Mailboxes, Quota, Deliverability, the record book and the records
   table were reachable only by reading the whole document in order. */
.panel__t {
  margin: 0; font-family: var(--mono); font-size: var(--t-xs); font-weight: 500;
  letter-spacing: 0.1em; text-transform: uppercase; color: inherit;
}
.panel__body { padding: 0.85rem; }
.inset { background: var(--paper-2); border: 1px solid var(--rule); border-radius: 2px; padding: 0.8rem 0.9rem; }
/* Inside a panel the panel is already the edge. A second border 12px in from
   the first draws the same boundary twice, which is the nested-card ban wearing
   a different name. The tint alone is enough to say "this is a block". */
.panel .inset, .panel .ledger-wrap { border-color: transparent; }

/* Definition rows. Used instead of repeated icon cards. */
.defs { border-top: 1px solid var(--rule); }
.defs > li { display: grid; grid-template-columns: 2.4rem 1fr; gap: 1rem; padding: 1.05rem 0; border-bottom: 1px solid var(--rule); }
.defs .no { font: 500 var(--t-sm)/1.5 var(--mono); color: var(--ink-3); font-variant-numeric: tabular-nums; }
/* Three children in a two-column grid puts the description in an implicit third
   row under the number, which is a 38px column of one word per line. It has to
   be told to stay beside the number, not under it. */
@media (max-width: 859px) { .defs > li > :last-child { grid-column: 2; } }
@media (min-width: 860px) {
  .defs > li { grid-template-columns: 2.4rem 15rem 1fr; }
}
.defs > li > :last-child { max-width: 72ch; }

/* ---------- forms ---------- */
.lbl-t { display: block; font: 500 var(--t-mark)/1.4 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 0.35rem; }
.input, .ta {
  width: 100%; background: var(--paper); color: var(--ink);
  border: 1.5px solid var(--rule-2); border-radius: 2px;
  padding: 0.55rem 0.65rem; font: 400 var(--t-sm)/1.4 var(--mono);
}
.input:hover, .ta:hover { border-color: var(--rule-3); }
.input:focus, .ta:focus { border-color: var(--ink); outline: none; box-shadow: 0 0 0 3px color-mix(in oklch, var(--yellow) 55%, transparent); }
.input[aria-invalid="true"] { border-color: var(--bad); }
.ta { font-family: var(--sans); font-size: var(--t-body); resize: vertical; }
.hint { font-size: var(--t-xs); color: var(--ink-2); margin-top: 0.3rem; }
.hint--bad { color: var(--bad); }

/* Pre-printed form choice. The three topologies. */
/* Three boxes inside a box is the nested-card ban, and it was the literal shape
   here: a bordered panel holding three bordered cards. Rules between the
   options do the separating, which is the vocabulary the rest of this system
   already speaks, and the border is spent once, on the option chosen. */
.choices { display: grid; }
.choice {
  display: grid; grid-template-columns: 1.15rem 1fr; gap: 0.7rem;
  border: 1.5px solid transparent; border-radius: 2px;
  /* Pulled out to just short of the panel's own padding: the rules between the
     options run wider than the text, and the outline on the chosen one still
     has air on both sides instead of landing on the panel border. */
  padding: 0.8rem 0.55rem; margin-inline: -0.55rem;
  cursor: pointer;
  transition: border-color 150ms cubic-bezier(.22,1,.36,1), background 150ms cubic-bezier(.22,1,.36,1);
}
.choice + .choice { border-top-color: var(--rule-2); }
.choice:hover { background: var(--paper-2); }
.choice input { margin: 0.25rem 0 0; accent-color: var(--ink); }
.choice__t { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; font-weight: 620; }
.choice__d { font-size: var(--t-sm); color: var(--ink-2); margin-top: 0.25rem; }
/* The chosen one is the only thing on the panel wearing an outline. */
.choice:has(input:checked) { border-color: var(--ink); background: var(--paper); }
.choice:has(input:checked) + .choice { border-top-color: transparent; }
.choice:has(input:checked) .choice__t { color: var(--ink); }
.tag {
  font: 600 var(--t-mark)/1 var(--mono); letter-spacing: 0.1em; text-transform: uppercase;
  border: 1px solid var(--rule-3); border-radius: 2px; padding: 0.25em 0.4em; color: var(--ink-2);
}
.tag--default { border-color: var(--ink); color: var(--ink); background: var(--yellow); }
.tag--adv { border-style: dashed; }

/* ---------- meters: quota beside the action that consumes it ---------- */
.meters { display: grid; gap: 0.75rem; }
.meter { display: grid; gap: 0.3rem; }
.meter__t { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; font-size: var(--t-xs); }
.meter__t b { font-variant-numeric: tabular-nums; font-weight: 600; }
.meter__bar { height: 6px; background: var(--paper-4); border-radius: 1px; overflow: hidden; }
.meter__fill { height: 100%; background: var(--ink-2); }
.meter[data-level="warn"] .meter__fill { background: var(--wait); }
.meter[data-level="over"] .meter__fill { background: var(--bad); }
.meter[data-level="warn"] .meter__bar { box-shadow: inset 0 0 0 1px var(--rule-3); }

/* ---------- checklist ---------- */
.check { border-top: 1px solid var(--rule); }
.check > li { border-bottom: 1px solid var(--rule); }
.check__row { display: grid; grid-template-columns: 1.5rem 1fr auto; gap: 0.7rem; align-items: start; padding: 0.75rem 0; }
.check__box { font: 600 var(--t-sm)/1.4 var(--mono); color: var(--ink-3); }
.check li[data-done="1"] .check__box { color: var(--ok); }
.check li[data-done="1"] .check__t { color: var(--ink-3); text-decoration: line-through 1px; text-decoration-color: var(--rule-3); }
.check__t { display: block; font-weight: 600; font-size: var(--t-sm); }
.check__d { font-size: var(--t-xs); color: var(--ink-2); margin-top: 0.15rem; }
.check li[data-current="1"] .check__row { background: color-mix(in oklch, var(--yellow) 24%, var(--paper)); padding-inline: 0.7rem; margin-inline: -0.7rem; }
.check__do { padding: 0 0 0.85rem; }

/* ---------- state machine (verification narration) ---------- */
.states { border-top: 1px solid var(--rule); list-style: none; margin: 0; padding: 0; }
.states > li {
  display: grid; grid-template-columns: 1.6rem 1fr; gap: 0.6rem;
  padding: 0.55rem 0; border-bottom: 1px solid var(--rule);
  font-size: var(--t-sm); color: var(--ink-3);
}
.states .gl { font: 500 var(--t-xs)/1.6 var(--mono); text-align: center; }
.states li[data-s="done"] { color: var(--ink-2); }
.states li[data-s="done"] .gl { color: var(--ok); }
.states li[data-s="now"] { color: var(--ink); font-weight: 620; }
.states li[data-s="now"] .gl { color: var(--ink); }
.states li[data-s="fail"] { color: var(--bad); font-weight: 600; }
.states li[data-s="fail"] .gl { color: var(--bad); }
.states small { display: block; font-weight: 400; font-size: var(--t-xs); color: var(--ink-3); }

/* ---------- alerts. Full border or a field tint, never a side stripe. ---------- */
.note { border: 1px solid var(--rule-2); border-radius: 2px; padding: 0.7rem 0.8rem; font-size: var(--t-sm); background: var(--paper-2); }
.note--bad { border-color: var(--bad); background: var(--bad-field); }
.note--ok  { border-color: var(--ok);  background: var(--ok-field); }
.note--keep { border-style: dashed; }
.note b { font-weight: 620; }

/* ---------- header / nav ---------- */
.masthead { position: sticky; top: 0; z-index: 40; background: var(--paper); border-bottom: 1px solid var(--rule-2); }
.masthead__in { display: flex; align-items: center; gap: 0.75rem 1.25rem; min-height: 3.5rem; flex-wrap: wrap; padding-block: 0.5rem; }
@media (min-width: 640px) { .masthead__in { flex-wrap: nowrap; padding-block: 0; } }
.brand { display: inline-flex; align-items: center; gap: 0.5rem; text-decoration: none; }
.brand svg, .brand img { width: 24px; height: 24px; }
.brand b { font-weight: 700; letter-spacing: -0.02em; font-size: 1.0625rem; }
.navlinks { display: none; gap: 1.1rem; margin-left: auto; }
.navlinks a { font-size: var(--t-sm); text-decoration: none; color: var(--ink-2); padding: 0.25rem 0; }
.navlinks a:hover { color: var(--ink); box-shadow: inset 0 -2px 0 0 var(--yellow-deep); }
@media (min-width: 900px) { .navlinks { display: flex; } }

/* ---------- footer ---------- */
.colophon { background: var(--paper-3); border-top: 1px solid var(--rule-2); font-size: var(--t-sm); }
.colophon a { color: var(--ink-2); text-decoration: none; }
.colophon a:hover { color: var(--ink); text-decoration: underline; }

/* ---------- portrait plate: full-bleed black and white ---------- */
.plate { position: relative; background: var(--paper-3); overflow: hidden; }
.plate img { width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) contrast(1.06); }
.plate__stamp {
  position: absolute; inset-inline: 0; bottom: 0; padding: 0.5rem 0.7rem;
  background: color-mix(in oklch, var(--paper) 88%, transparent);
  border-top: 1px solid var(--rule-2);
  font: 600 var(--t-mark)/1.4 var(--mono); letter-spacing: 0.1em; text-transform: uppercase;
  /* Not --bad. That red means "this DNS record has the wrong value", and a
     colour that also labels a stock photograph stops meaning anything. */
  color: var(--ink-2);
}

/* ---------- app shell (portal) ---------- */
.app { display: grid; min-height: 100vh; grid-template-columns: 1fr; }
.app__rail { border-bottom: 1px solid var(--rule-2); background: var(--paper-2); }
.app__main { min-width: 0; }
/* Under 1000px the rail is a full-width block, and twelve links plus their
   counts push the first thing anyone came for about 750px down the page. A
   disclosure is the native answer: closed by default at this width, one control
   to open, and no scroll to pay before the console starts. */
.railfold > summary {
  list-style: none; cursor: pointer; padding: 0.6rem 0.9rem;
  font: 500 var(--t-xs)/1 var(--mono); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-2);
}
.railfold > summary::-webkit-details-marker { display: none; }
.railfold > summary::after { content: " +"; color: var(--ink-3); }
.railfold[open] > summary::after { content: " \2212"; }
@media (min-width: 1000px) {
  .app { grid-template-columns: 15rem 1fr; }
  .app__rail { border-bottom: 0; border-right: 1px solid var(--rule-2); position: sticky; top: 0; height: 100vh; overflow-y: auto; }
  .railfold > summary { display: none; }
}
.railnav { padding: 0.35rem 0.5rem 1rem; }
.railnav a {
  display: flex; justify-content: space-between; gap: 0.5rem; align-items: center;
  padding: 0.4rem 0.6rem; border-radius: 2px; text-decoration: none;
  font-size: var(--t-sm); color: var(--ink-2);
}
.railnav a:hover { background: var(--paper-3); color: var(--ink); }
/* Named but not rendered in this lane. Not a link, so it cannot pretend to be
   one: no pointer, no focus stop, no jump to the top of the page. */
.railnav a[data-unbuilt] { color: var(--ink-3); }
.railnav a[data-unbuilt]:hover { background: transparent; color: var(--ink-3); }
.railnav a[aria-current="page"] { color: var(--ink); font-weight: 620; background: color-mix(in oklch, var(--yellow) 30%, var(--paper)); }
.railnav .ct { font: 500 var(--t-xs)/1 var(--mono); color: var(--ink-3); font-variant-numeric: tabular-nums; }
.railgroup { font: 500 var(--t-mark)/1 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-3); padding: 1rem 0.6rem 0.4rem; }

.topbar { border-bottom: 1px solid var(--rule-2); background: var(--paper); position: sticky; top: 0; z-index: 30; }
.topbar__in { display: flex; align-items: center; gap: 0.75rem; padding: 0.55rem var(--gut); flex-wrap: wrap; }
.pill {
  display: inline-flex; align-items: center; gap: 0.4rem;
  border: 1px solid var(--rule-2); border-radius: 2px; padding: 0.28rem 0.5rem;
  font-size: var(--t-xs); background: var(--paper);
}
/* Not .k: that is the syntax keyword token, and two meanings on one class name
   is a collision waiting for the first code sample inside a pill. */
.pill__k { color: var(--ink-3); font-family: var(--mono); font-size: var(--t-mark); letter-spacing: 0.08em; text-transform: uppercase; }
.pill b { font-weight: 620; }

/* ---------- docs ---------- */
.docs { display: grid; grid-template-columns: 1fr; gap: 0; }
@media (min-width: 1060px) {
  .docs { grid-template-columns: 16rem minmax(0,1fr) 15rem; }
  /* The rails keep their full-height hairline; the content inside them sticks
     under the 57px masthead. A scrollspy that scrolls out of view is a
     decoration, and a column rule that stops halfway is a defect, so the two
     jobs go to two elements. */
  .docs__stick {
    position: sticky; top: 4.8125rem;
    max-height: calc(100vh - 6rem); overflow-y: auto;
    overscroll-behavior: contain;
  }
}
.docs__idx { border-right: 1px solid var(--rule); padding: 1.25rem var(--gut) 3rem 0; }
.docs__idx ol { counter-reset: ch; }
.docs__body { padding: 1.25rem var(--gut) 4rem; min-width: 0; }
.docs__aside { border-left: 1px solid var(--rule); padding: 1.25rem 0 3rem var(--gut); }
@media (max-width: 1059px) {
  .docs__idx, .docs__aside { border: 0; border-top: 1px solid var(--rule); padding: 1.25rem 0 2rem; }
  .docs__body { padding-inline: 0; }
}
.idxlink { display: block; padding: 0.28rem 0; font-size: var(--t-sm); color: var(--ink-2); text-decoration: none; }
.idxlink:hover { color: var(--ink); }
.idxlink[aria-current="true"] { color: var(--ink); font-weight: 620; background: color-mix(in oklch, var(--yellow) 26%, var(--paper)); padding-inline: 0.4rem; margin-inline: -0.4rem; }
.docs__body h2 { margin-top: 2.25rem; }
.docs__body h3 { margin-top: 1.5rem; }
.docs__body p, .docs__body ul li { max-width: 70ch; }
.docs__body p { margin-top: 0.75rem; color: var(--ink-2); }
.docs__body ul.bul { margin-top: 0.6rem; }
.docs__body ul.bul li { position: relative; padding-left: 1.1rem; margin-top: 0.3rem; color: var(--ink-2); font-size: var(--t-sm); }
.docs__body ul.bul li::before { content: "\2013"; position: absolute; left: 0; color: var(--ink-3); }
.docs__body code:not(pre code) {
  font-size: 0.86em; background: var(--paper-2); border: 1px solid var(--rule);
  border-radius: 2px; padding: 0.08em 0.28em;
}

/* ---------- the board ----------
   Window cards laid on the field, the way papers land on a desk. Real markup and
   real type, not screenshots: it survives zoom, a screen reader gets the content
   in source order, and the drag is what makes a visitor believe the surfaces are
   real rather than pictures of surfaces.

   Below the wide breakpoint the cards are a plain stacked column and no drag
   handler is attached at all, because a pointer handler that swallows vertical
   scrolling on a phone is worse than no board. */
/* The headline and the argument for it, side by side, so the board below gets
   the whole width of the field rather than a column of it. Five windows need
   the room; three would not have needed the restructuring. */
.hero-lay { display: grid; gap: clamp(1.25rem, 0.6rem + 2vw, 2.5rem); align-items: end; }
@media (min-width: 900px) {
  .hero-lay { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); }
}
.board { margin-top: clamp(1.5rem, 0.8rem + 2vw, 2.5rem); }
.board__stage { display: grid; gap: 1rem; }
/* Hidden until the width where a drag handler is actually attached. An
   instruction the visitor cannot follow is worse than no instruction. */
.board__hint { display: none; }

.win {
  border: 1px solid var(--rule-2); border-radius: 6px; background: var(--paper);
  overflow: hidden; min-width: 0;
  box-shadow: 0 1px 0 color-mix(in oklch, var(--ink) 6%, transparent);
  /* A card is a surface, not the hero background, so it does not wear the logo
     cursor the hero sets. Equal specificity to the wide-screen grab rule below
     and declared first, so grab still wins where the card is draggable. */
  cursor: auto;
}
.win__bar {
  display: flex; align-items: center; gap: 0.55rem;
  padding: 0.5rem 0.6rem; border-bottom: 1px solid var(--rule-2); background: var(--paper-2);
}
/* Window controls, in the neutral. Red and green are reserved for delivery state
   on every other surface in this system, and a colour that also means "close"
   stops meaning "this record is wrong". */
.win__dots { display: inline-flex; gap: 0.3rem; flex: none; }
.win__dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--paper-4); box-shadow: inset 0 0 0 1px var(--rule-3); }
.win__t {
  font: 500 var(--t-mark)/1 var(--mono); letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.win__b { padding: 0.7rem 0.75rem; }
.win__foot {
  padding: 0.55rem 0.75rem; border-top: 1px solid var(--rule);
  background: var(--paper-2); font-size: var(--t-xs); color: var(--ink-2);
}
.win__foot b { color: var(--ink); font-weight: 620; }
.win__film { display: block; width: 100%; height: auto; aspect-ratio: 1000 / 600; background: var(--paper-2); }
/* The terminal render is 1000x640, and the blue field is its own resting
   colour, so the poster gap shows terminal blue rather than paper while it
   loads. Contained here: the blue is one card's background, nothing leaks. */
.win__film--term { aspect-ratio: 1000 / 640; background: oklch(0.255 0.072 256); }
/* The code component supplies its own frame everywhere else. Inside a window it
   would be a border inside a border, which this system does not do. */
.win .code { border: 0; border-radius: 0; }
.win .code pre { padding: 0.7rem 0.75rem; }

/* The board is wide AND fine-pointer, in one query, matching what ledger.js
   reads on every press. Width alone is not enough: a tablet in landscape is
   1194px with no mouse, and there touch-action:none swallows the vertical
   scroll the visitor actually wanted and the hint tells them to do something
   they cannot do. */
@media (min-width: 1180px) and (pointer: fine) {
  /* A sentence, so it is set as a sentence. The uppercase mono elsewhere in this
     system labels things; it is not a voice to write two clauses in. */
  .board__hint {
    display: block; margin-top: 1rem; font-size: var(--t-xs); color: var(--ink-2);
  }
  .board__stage { display: block; position: relative; height: 42rem; }
  .win {
    position: absolute; left: var(--x); top: var(--y); width: var(--w);
    rotate: var(--r, 0deg);
    translate: var(--dx, 0px) var(--dy, 0px);
    z-index: var(--z, 1);
    cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none;
    /* A card at rest is lying on the desk, so it casts a contact shadow: short
       throw, tight blur. The wide soft shadow is not the resting state here, it
       is what "in the air" looks like, and it is spent on the held card only. */
    box-shadow:
      0 1px 1px color-mix(in oklch, var(--ink) 9%, transparent),
      0 5px 10px -6px color-mix(in oklch, var(--ink) 30%, transparent);
    transition: box-shadow 180ms cubic-bezier(.16,1,.3,1), scale 180ms cubic-bezier(.16,1,.3,1);
  }
  /* Lifted, not launched. The card gains height above the desk and nothing else. */
  .win[data-held] {
    cursor: grabbing; z-index: 40; scale: 1.015;
    box-shadow:
      0 2px 4px color-mix(in oklch, var(--ink) 12%, transparent),
      0 26px 46px -18px color-mix(in oklch, var(--ink) 38%, transparent);
  }
}
@media (prefers-reduced-motion: reduce) {
  .win { transition: none; }
  .win[data-held] { scale: 1; }
}

/* ---------- demo inbox ---------- */
.inbox { display: grid; gap: 0; border: 1px solid var(--rule-2); border-radius: 2px; background: var(--paper); }
.inbox__head { padding: 0.7rem 0.85rem; border-bottom: 1px solid var(--rule-2); background: var(--paper-2); display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.inbox__addr { font: 600 var(--t-sm)/1.3 var(--mono); overflow-wrap: anywhere; }
.inbox__thread { padding: 0.5rem 0.85rem; display: grid; gap: 0.5rem; max-height: 20rem; overflow-y: auto; }
.msg { border-bottom: 1px dotted var(--rule); padding: 0.5rem 0; animation: line-in 380ms cubic-bezier(.16,1,.3,1) both; }
.msg:last-child { border-bottom: 0; }
.msg__meta { display: flex; gap: 0.5rem; align-items: baseline; font: 500 var(--t-mark)/1.4 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3); }
.msg__b { font-size: var(--t-sm); margin-top: 0.25rem; white-space: pre-wrap; overflow-wrap: anywhere; }
.msg[data-from="agent"] .msg__meta { color: var(--ok); }
.inbox__compose { padding: 0.7rem 0.85rem; border-top: 1px solid var(--rule-2); background: var(--paper-2); }
.typing span { display: inline-block; width: 4px; height: 4px; margin-right: 3px; background: var(--ink-3); border-radius: 50%; animation: blink 1.1s infinite; }
.typing span:nth-child(2) { animation-delay: .18s; }
.typing span:nth-child(3) { animation-delay: .36s; }
@keyframes blink { 0%,60%,100% { opacity: .25; } 30% { opacity: 1; } }

/* ---------- utility ---------- */
.grid2 { display: grid; gap: clamp(1.25rem, 0.5rem + 2vw, 2.5rem); }
@media (min-width: 900px) {
  .grid2 { grid-template-columns: minmax(0,1fr) minmax(0,1fr); }
  .grid2--wide-l { grid-template-columns: minmax(0,1.15fr) minmax(0,1fr); }
  /* The domain flow: the listing needs the room, the panel does not.
     The four blocks are placed explicitly rather than nested in two columns, so
     that the single-column order can be the one that matters: name, shape,
     verification, records, remove. Nested columns would put the verification
     panel below the seven-row record table on a tablet, which is the one thing
     "one screen until the domain works" cannot survive. */
  .grid2--flow { grid-template-columns: minmax(0,1.62fr) minmax(0,1fr); }
  .flow-a { grid-column: 1; grid-row: 1; }
  .flow-c { grid-column: 1; grid-row: 2; }
  .flow-d { grid-column: 1; grid-row: 3; }
  .flow-b { grid-column: 2; grid-row: 1 / span 3; align-self: start; position: sticky; top: 4.5rem; }
  /* The console overview: ledgers left, meters right. */
  .grid2--console { grid-template-columns: minmax(0,1.5fr) minmax(0,1fr); }
}
.grid3 { display: grid; gap: 1rem; }
@media (min-width: 760px) { .grid3 { grid-template-columns: repeat(3, minmax(0,1fr)); } }
.sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* The console rail is twelve links and a topbar. Without this, reaching the
   checklist by keyboard means tabbing past all of it on every page load, and
   narrow viewports put the same wall in front of everyone. Off-screen rather
   than hidden, so it is in the tab order from the first key press. */
.skip {
  position: absolute; left: 0.5rem; top: -3rem; z-index: 100;
  padding: 0.5rem 0.9rem; background: var(--ink); color: var(--paper);
  font-size: var(--t-sm); text-decoration: none; border-radius: 2px;
  transition: top 140ms cubic-bezier(.16,1,.3,1);
}
.skip:focus { top: 0.5rem; }
hr.hair { border: 0; border-top: 1px solid var(--rule); margin: 0; }
.pad { padding: var(--p, 1rem); }
