/* rotascale.com — the layout grammar.
 *
 * subhadipmitra@: Rules, alignment and tabular figures. No card borders, no
 * shadow, no radius except where a control genuinely needs one.
 *
 * The reason is not taste. The page is arguing that this product produces
 * records a stranger can check, and a record is set as ruled columns with
 * figures that line up — not as a soft-cornered tile floating over a
 * background. Every visual decision below is downstream of that.
 */

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.56;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img, svg { max-width: 100%; height: auto; }

/* subhadipmitra@: The browser default is a blue-grey that belongs to no design
   system on this page. Selection is one of the few surfaces a reader creates
   themselves, and it should look like the site did it. Tinted from the accent
   rather than set to it — a full-strength magenta wash makes the text under it
   harder to read, and the point of selecting text is to read it. */
/* subhadipmitra@: `background-color`, not the `background` shorthand, and no
   `color` at all.
   The first version set `background: …; color: …` and on some browsers only the
   colour landed — so selecting text changed the letters and painted nothing
   behind them, which reads as the text darkening rather than as a highlight.
   The shorthand is the unreliable half here. Leaving `color` unset also means
   the highlight cannot make text less readable than it was, which is the one
   thing a selection style must never do. */
::selection { background-color: var(--select); }
::-moz-selection { background-color: var(--select); }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gutter); }

/* Anything that can scroll sideways does so inside its own box. The page body
   never does. */
.scroll-x { overflow-x: auto; }
/* A scrolling block is a box; give it the space a box needs below it. */
.scroll-x + .note, .scroll-x + .limit { margin-top: 30px; }
.defs + .note, .defs + .limit, .claims + .note, .claims + .limit { margin-top: 30px; }

a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.skip {
  position: absolute; left: -9999px;
  background: var(--ink); color: var(--on-ink);
  padding: 10px 16px; z-index: 100;
}
.skip:focus { left: var(--gutter); top: 10px; }

/* --- type ---------------------------------------------------------------- */

h1, h2, h3 { text-wrap: balance; }

h1 {
  font-family: var(--serif);
  font-size: clamp(40px, 5.9vw, 72px);
  line-height: 1.03;
  font-weight: 600;
  letter-spacing: -0.021em;
  margin: 0 0 24px;
  max-width: 17ch;
}
h1 em { font-style: italic; font-weight: 500; color: var(--accent); }

h2 {
  font-family: var(--serif);
  font-size: clamp(29px, 3.6vw, 42px);
  line-height: 1.1;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 18px;
  max-width: 21ch;
}

h3 { font-size: 15.5px; font-weight: 600; margin: 0; letter-spacing: -0.004em; }

.lead {
  font-size: 18.5px;
  line-height: 1.6;
  color: var(--ink-2);
  max-width: 58ch;
  margin: 0;
}
.lead b, .lead strong { font-weight: 600; color: var(--ink); }

.kicker {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 24px;
}
.kicker b { color: var(--accent); font-weight: 500; }

.note { font-size: 13.5px; color: var(--muted); margin: 26px 0 0; max-width: 66ch; }

/* --- the claim mark ------------------------------------------------------
 *
 * The organising idea of this site: every claim carries the product's own
 * three-state discipline. `observed` links to the thing itself — the running
 * demo, the published specification, the source. `asserted` says plainly that
 * there is nothing here a reader can check from outside.
 *
 * It costs us something, which is exactly why it is worth having. A page that
 * marks its own unverifiable claims is not a device a competitor copies.
 */

.mark {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  padding: 2px 6px;
  white-space: nowrap;
  border: 1px solid currentColor;
  display: inline-block;
}
.mark--observed { color: var(--allow); }
.mark--asserted { color: var(--muted); }
a.mark { text-decoration: none; }
a.mark:hover { background: var(--allow); color: var(--paper); border-color: var(--allow); }

/* --- masthead ------------------------------------------------------------ */

.mast { border-bottom: 1px solid var(--rule-hard); }
/* subhadipmitra@: `>` matters. The panel reuses `.wrap` for its own gutter, so
   a descendant selector here reached inside it and applied `display:flex;
   align-items:center` to the panel's grid — which centred the columns and
   dropped the shorter one by half the height difference. Second time a
   contextual class rule has quietly reformatted a component on this site; the
   fix both times is to stop the ancestor rule travelling. */
.mast > .wrap { display: flex; align-items: center; gap: 18px; padding-top: 13px; padding-bottom: 11px; }

.brand { display: flex; align-items: center; gap: 9px; text-decoration: none; }
/* subhadipmitra@: No `display`, for the reason spelled out at `.shot__img` —
   `.brand img` scores (0,1,1) and would beat the unscoped `.m-dark`
   (0,1,0), so both cuts of the logo rendered. Unscoping the swap moved
   this fight here; removing the declaration ends it. */
.brand img { height: 20px; width: auto; vertical-align: middle; }
.brand .word { font-size: 17px; font-weight: 700; letter-spacing: -0.012em; align-self: baseline; }
/* subhadipmitra@: The descriptor is the first thing to go. Five menu buttons
   plus a call to action need the room more than a subtitle does, and a masthead
   that wraps to two lines looks broken in a way a missing tagline does not. */

/* Two cuts of one asset; each hides in the other's theme.
 *
 * subhadipmitra@: UNSCOPED. These rules lived under `.brand` and did nothing
 * anywhere else — so the console screenshots, which use the same two classes,
 * rendered both cuts stacked on every page. Reused mechanism, reused bug: the
 * class name promised a behaviour the selector only delivered in one place.
 * One rule for "this asset has a light and a dark cut", wherever it appears. */
.m-dark { display: none; }
:root[data-theme="dark"] .m-light { display: none; }
:root[data-theme="dark"] .m-dark { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .m-light { display: none; }
  :root:not([data-theme="light"]) .m-dark { display: block; }
}

/* --- the menu -----------------------------------------------------------
 *
 * subhadipmitra@: A ruled panel, not a card. Columns divided by hairlines, mono
 * labels, no icons, no shadow, no radius — because a mega menu that looked like
 * every other one would quietly undo the argument the rest of the page is
 * making about what this product's surfaces look like.
 */

.mast { position: relative; }
.mast__nav { margin-left: auto; display: flex; gap: 0; flex-shrink: 1; min-width: 0; }

.navitem { position: static; }

.navitem__btn {
  display: inline-flex; align-items: center; gap: 6px;
  /* subhadipmitra@: 14.5/500 rather than 14/400. At 400 the nav sat lighter
     than the body text beneath it, so the masthead read as a caption rather
     than as the site's controls — the one row on every page that has to look
     like it is in charge. */
  font: 500 14.5px/1 var(--sans); color: var(--ink-2);
  background: none; border: 0; padding: 9px 11px; cursor: pointer;
}
.navitem__btn svg { transition: transform 120ms ease; opacity: .55; }
.navitem__btn[aria-expanded="true"] { color: var(--ink); }
.navitem__btn[aria-expanded="true"] svg { transform: rotate(180deg); opacity: 1; }
.navitem__btn:hover { color: var(--ink); }
.navitem__btn[data-current="true"] { color: var(--ink); box-shadow: inset 0 -2px 0 var(--accent); }
@media (prefers-reduced-motion: reduce) { .navitem__btn svg { transition: none; } }

/* Full-bleed under the masthead: the panel belongs to the rule above it. */
.panel {
  position: absolute; left: 0; right: 0; top: 100%;
  background: var(--paper);
  border-bottom: 1px solid var(--rule-hard);
  z-index: 40;
}
.panel[hidden] { display: none; }

.panel__grid {
  display: grid;
  /* subhadipmitra@: A bounded track, not `1fr`. With `auto-fit` and a fraction
     the browser made six tracks for three columns of links and the panel
     emptied into the right half of the page. Columns are 200–250px and pack
     left, so two groups and five groups both look deliberate. */
  grid-template-columns: repeat(auto-fit, minmax(200px, 250px));
  justify-content: start; align-items: start;
  gap: 0; padding-top: 26px; padding-bottom: 30px;
}
.panel__col { padding-right: 28px; border-right: 1px solid var(--rule); }
.panel__col:last-child { border-right: 0; padding-right: 0; }
/* subhadipmitra@: …and the one before the feature column, which is no longer
   `:last-child` once a feature exists. Without this its rule hangs in the gap
   between the links and the feature's own border, dividing nothing from
   nothing. */
.panel__col:has(+ .panel__feature) { border-right: 0; padding-right: 0; }
.panel__col + .panel__col { padding-left: 28px; }

.panel__label {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 12px;
}
.panel ul, .mobilenav ul { list-style: none; margin: 0; padding: 0; }
.panel li { margin-bottom: 2px; }
.panel a {
  display: block; text-decoration: none; padding: 7px 0; font-size: 14.5px;
  border-bottom: 1px solid transparent;
}
.panel a:hover { color: var(--accent); }
.panel a[aria-current="page"] { color: var(--ink); font-weight: 600; }
.panel__note {
  display: block; font-size: 12.5px; color: var(--muted); margin-top: 2px;
  max-width: 30ch;
}

/* --- mobile -------------------------------------------------------------- */

.mast__burger {
  display: none; background: none; border: 1px solid var(--rule);
  color: var(--ink-2); width: 32px; height: 32px; cursor: pointer;
  place-items: center;
}
.mobilenav {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 40;
  background: var(--paper); border-bottom: 1px solid var(--rule-hard);
  max-height: 78vh; overflow-y: auto;
}
.mobilenav[hidden] { display: none; }
.mobilenav__group { padding: 20px 0 4px; border-bottom: 1px solid var(--rule); }
.mobilenav a { display: block; text-decoration: none; padding: 9px 0; font-size: 15.5px; }
.mobilenav a[aria-current="page"] { font-weight: 600; }

.mast .cta {
  font-size: 13px; font-weight: 500; padding: 9px 13px; white-space: nowrap;
  background: var(--ink); color: var(--on-ink); text-decoration: none; flex-shrink: 0;
}
.mast .cta:hover { background: var(--accent); color: #fff; border-color: var(--accent); }

/* The one a stranger can actually use. Quiet, because the live platform is the
   thing we want them to look at — but present, because they cannot get to it
   without this. */
.mast__acts { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.mast .cta--quiet {
  background: none;
  color: var(--ink-2);
  border: 1px solid var(--rule);
}
.mast .cta--quiet:hover { color: var(--ink); border-color: var(--ink); background: none; }

.theme-toggle {
  background: none; border: 1px solid var(--rule); color: var(--muted);
  width: 32px; height: 32px; cursor: pointer; display: grid; place-items: center;
  font-family: inherit;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--ink); }

/* --- buttons ------------------------------------------------------------- */

.btn {
  display: inline-block;
  font: 500 14.5px/1 var(--sans);
  padding: 13px 19px;
  text-decoration: none;
  background: var(--ink);
  color: var(--on-ink);
  border: 1px solid var(--ink);
  cursor: pointer;
}
.btn:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn--ghost { background: none; color: var(--ink); }

.acts { display: flex; gap: 12px; margin: 32px 0 0; align-items: center; flex-wrap: wrap; }

/* --- the record ----------------------------------------------------------
 * Set as a ruled document because it IS one. A card would make it a
 * screenshot of a UI, which is a weaker claim.
 */

.record { margin: 54px 0 0; border-top: 1px solid var(--rule-hard); }

.record__head {
  display: flex; align-items: baseline; gap: 13px; flex-wrap: wrap;
  padding: 13px 0 11px; border-bottom: 1px solid var(--rule);
  font-family: var(--mono); font-size: 12.5px;
}
.record__head .who { color: var(--muted); }
.record__state {
  margin-left: auto; font-size: 10.5px; letter-spacing: 0.09em;
  text-transform: uppercase; padding: 2px 8px; border: 1px solid currentColor;
}
.record__state--deny { color: var(--deny); }
.record__state--allow { color: var(--allow); }
.record__state--review { color: var(--review); }

.rows { display: grid; grid-template-columns: 1fr 1fr; }
.rows > div { padding: 14px 0; border-bottom: 1px solid var(--rule); }
.rows > div:nth-child(odd) { padding-right: 30px; border-right: 1px solid var(--rule); }
.rows > div:nth-child(even) { padding-left: 30px; }

.k {
  font-size: 11.5px; font-weight: 500; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 4px;
}
/* subhadipmitra@: A `<label>` is inline, and vertical margins do nothing on a
   non-replaced inline box — so `.k`'s `margin-bottom` was silently discarded on
   every form label on the site, and the only gap was the input's own
   `margin-top`. The label looked attached to the field above it rather than to
   the one below. */
label.k { display: block; margin-bottom: 7px; }
.v { font-family: var(--mono); font-size: 14px; }
.v--deny { color: var(--deny); font-weight: 500; }

/* --- the gate rail -------------------------------------------------------
 * Nine gates in a FIXED order is the one structural fact no competitor's
 * diagram has, so it is drawn as a sequence that visibly stops rather than as
 * nine equal boxes.
 */

.rail { padding: 20px 0 0; }
.rail__k {
  font-size: 11.5px; font-weight: 500; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 11px;
}
.rail__track { display: grid; grid-template-columns: repeat(9, 1fr); gap: 3px; }
.rail__track span { height: 6px; background: var(--allow); }
.rail__track span.is-stop { background: var(--deny); }
.rail__track span.is-off { background: var(--rule); }
.rail__names {
  display: grid; grid-template-columns: repeat(9, 1fr); gap: 3px; margin-top: 9px;
  font-family: var(--mono); font-size: 10.5px; color: var(--muted);
  letter-spacing: -0.01em; word-break: break-word; line-height: 1.3;
}
.rail__names b { color: var(--deny); font-weight: 600; }
.rail__names i { font-style: normal; opacity: 0.45; }

.hero { padding: 86px 0 76px; }
.hero .lead { margin-top: 4px; }

/* --- sections and claim lists -------------------------------------------- */

.sec { padding: 72px 0; border-top: 1px solid var(--rule); }
.sec > .wrap > h2::before {
  content: "";
  display: block;
  width: 26px; height: 3px;
  background: var(--accent);
  margin-bottom: 18px;
}
/* subhadipmitra@: `:not()` because this rule was silently winning against the
   utility classes. `.sec > .wrap > p` scores 0,2,1 and `.note` scores 0,1,0, so
   a `.note` or `.limit` sitting directly in a section lost its top margin and
   collided with whatever was above it — visible after a `.defs` list and after
   a `.scroll-x` table. A section-level type selector fighting a component class
   over spacing is the classic way a stylesheet develops mystery gaps. */
.sec > .wrap > p:not(.note):not(.limit):not(.lead):not(.legend):not(.sub__fine) {
  font-size: 17px; color: var(--ink-2); max-width: 63ch; margin: 0;
}

.claims { margin: 36px 0 0; border-top: 1px solid var(--rule-hard); }
.claim {
  display: grid; grid-template-columns: 230px 1fr 108px; gap: 32px;
  align-items: start; padding: 19px 0; border-bottom: 1px solid var(--rule);
}
.claim p { font-size: 14.8px; color: var(--ink-2); margin: 0; max-width: 60ch; }

.legend { margin: 30px 0 0; font-size: 13.5px; color: var(--muted); max-width: 66ch; }
.legend .mark { margin-right: 5px; }

/* --- footer -------------------------------------------------------------- */

.foot { border-top: 1px solid var(--rule-hard); padding: 54px 0 40px;  }
/* subhadipmitra@: FIVE link columns now, not four. The count was hardcoded and
   adding Solutions would have pushed it onto a second row of one — which reads
   as a mistake rather than as a group. */
.foot__grid { display: grid; grid-template-columns: 1.5fr repeat(5, 1fr); gap: 30px; }
.foot h4 {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--muted); margin: 0 0 12px; font-weight: 500;
}
.foot ul { list-style: none; margin: 0; padding: 0; font-size: 14px; }
.foot li { margin-bottom: 7px; }
.foot a { color: var(--ink-2); text-decoration: none; }
.foot a:hover { color: var(--ink); box-shadow: inset 0 -1px 0 var(--ink); }
.foot__legal {
  display: flex; gap: 20px; flex-wrap: wrap; align-items: baseline;
  margin-top: 40px; padding-top: 18px; border-top: 1px solid var(--rule);
  font-size: 13px; color: var(--muted);
}
.foot__legal .spacer { margin-left: auto; }

@media (max-width: 900px) {
  :root { --gutter: 20px; }
  .rows { grid-template-columns: 1fr; }
  .rows > div:nth-child(odd) { border-right: 0; padding-right: 0; }
  .rows > div:nth-child(even) { padding-left: 0; }
  .claim { grid-template-columns: 1fr; gap: 8px; }
  .foot__grid { grid-template-columns: 1fr 1fr; }
  .rail__names { font-size: 8px; }
  /* subhadipmitra@: This had collapsed into one selector list —
   *
   *     .mast__nav, .mast__acts,   .mast__burger { display: grid; }
   *
   * — where it must be two rules. The double space is the fingerprint of the
   * edit that ate `{ display: none; }` and the newline after it, welding the
   * things that must be HIDDEN on a phone onto the one thing that must be
   * SHOWN. So every page rendered the full desktop menu, both call-to-action
   * buttons and the burger simultaneously, stacked over the wordmark. On the
   * masthead. On a phone. On every page.
   *
   * It is invisible to every check this repo runs: the markup is balanced, the
   * CSS parses, the links resolve, and nothing overflows the viewport because
   * the pile is inside the header's own box. Only a rendered narrow viewport
   * shows it, which is why scripts/check-responsive.js now loads three pages at
   * 390px and asserts the desktop nav is not painted. */
  .mast__nav, .mast__acts { display: none; }
  .mast__burger { display: grid; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}


/* --- components the platform pages need ---------------------------------- */

/* A code sample. Ruled top and bottom rather than boxed, same as a record. */
.code {
  font-family: var(--mono); font-size: 13px; line-height: 1.7;
  background: var(--paper-2); padding: 18px 20px; margin: 26px 0 0;
  overflow-x: auto; border-left: 2px solid var(--rule-hard);
}
.code .c { color: var(--muted); }
.code .s { color: var(--accent); }

/* The gate table: nine rows, in order, with what each one asks. */
.gates { width: 100%; border-collapse: collapse; margin: 34px 0 0; }
.gates th {
  text-align: left; font-size: 11.5px; font-weight: 500; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); padding: 0 0 10px;
  border-bottom: 1px solid var(--rule-hard);
}
.gates td { padding: 14px 0; border-bottom: 1px solid var(--rule); vertical-align: top; font-size: 14.5px; }
.gates td:first-child { font-family: var(--mono); font-size: 13px; color: var(--muted); width: 56px; }
.gates td:nth-child(2) { font-family: var(--mono); font-size: 13.5px; width: 168px; }
.gates td:nth-child(2) b { font-weight: 500; }
.gates td:last-child { color: var(--ink-2); }

/* The enforcement ladder: four rungs, and only two of them refuse. */
.ladder { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; margin: 32px 0 0;
  background: var(--rule); border-top: 1px solid var(--rule-hard); }
.ladder > div { background: var(--paper); padding: 18px 20px 20px; }
.ladder h4 { font-family: var(--mono); font-size: 12.5px; font-weight: 500; margin: 0 0 8px; }
.ladder p { font-size: 13.5px; color: var(--ink-2); margin: 0; }
.ladder .is-enforcing h4 { color: var(--allow); }
.ladder .is-watching h4 { color: var(--review); }
.ladder__note { font-size: 13.5px; color: var(--muted); margin: 14px 0 0; }

/* Two-column definition rows: term on the left, what it means on the right. */
.defs { margin: 32px 0 0; border-top: 1px solid var(--rule-hard); }
.def { display: grid; grid-template-columns: 230px 1fr; gap: 32px; padding: 18px 0;
  border-bottom: 1px solid var(--rule); }
.def dt { font-weight: 600; font-size: 15px; }
.def dd { margin: 0; font-size: 14.8px; color: var(--ink-2); max-width: 64ch; }
.def dd code, .def dt code { font-family: var(--mono); font-size: 13px; }

/* A comparison: what this is not. */
.compare { width: 100%; border-collapse: collapse; margin: 32px 0 0; }
.compare th { text-align: left; font-size: 11.5px; font-weight: 500; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); padding: 0 16px 10px 0;
  border-bottom: 1px solid var(--rule-hard); }
.compare td { padding: 15px 16px 15px 0; border-bottom: 1px solid var(--rule);
  vertical-align: top; font-size: 14.5px; color: var(--ink-2); }
.compare td:first-child { color: var(--ink); font-weight: 500; width: 24%; }

/* A stated limit. Used where the honest answer is "we do not do that". */
.limit { border-left: 2px solid var(--review); padding: 6px 0 6px 20px;
  margin: 34px 0 0; font-size: 15px; color: var(--ink-2); max-width: 64ch; }
.limit b { color: var(--ink); }

/* Page navigation between siblings. */
.siblings { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1px; background: var(--rule); border-top: 1px solid var(--rule-hard);
  margin: 0; }
.siblings a { background: var(--paper); padding: 18px 20px; text-decoration: none; display: block; }
.siblings a:hover { background: var(--paper-2); }
.siblings .k { margin-bottom: 5px; }
.siblings span { font-size: 15px; font-weight: 600; }

@media (max-width: 900px) {
  .ladder { grid-template-columns: 1fr; }
  .def { grid-template-columns: 1fr; gap: 6px; }
  .gates td:nth-child(2) { width: auto; }
}


/* --- the verifier -------------------------------------------------------- */

.vinput {
  width: 100%; font-family: var(--mono); font-size: 13px; line-height: 1.6;
  background: var(--paper-2); color: var(--ink);
  border: 1px solid var(--rule); border-left: 2px solid var(--rule-hard);
  padding: 14px 16px; resize: vertical;
}
/* The gap belongs to the label, which is next to the thing it names. An input
   carrying its own top margin also pushed itself away from a preceding
   paragraph, which is not the same relationship. */
label.k + .vinput { margin-top: 0; }
.vinput:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.vinput::placeholder { color: var(--muted); }

.vout:not(:empty) { margin: 30px 0 0; border-top: 1px solid var(--rule-hard); }
.vrow { display: grid; grid-template-columns: 92px 1fr; gap: 20px; align-items: start;
  padding: 16px 0; border-bottom: 1px solid var(--rule); }
.vrow b { font-size: 15px; font-weight: 600; }
.vdetail { font-size: 14px; color: var(--ink-2); margin-top: 5px; max-width: 62ch; }
.vdetail code { font-family: var(--mono); font-size: 12px; word-break: break-all; }
.vstate { font-family: var(--mono); font-size: 10px; letter-spacing: 0.09em;
  text-transform: uppercase; padding: 2px 6px; border: 1px solid currentColor;
  display: inline-block; }
.vstate--passed { color: var(--allow); }
.vstate--failed { color: var(--deny); }
.vstate--unknown { color: var(--review); }

@media (max-width: 900px) { .vrow { grid-template-columns: 1fr; gap: 8px; } }


/* --- long-form ------------------------------------------------------------
 *
 * subhadipmitra@: Twenty-five posts were rendering with no prose styles at all,
 * because the stylesheet was rebuilt around the marketing pages and `.prose`
 * never came with it. They were readable only in the sense that a browser
 * default is readable.
 *
 * Measure first: the posts use h2/h3 heavily, fenced code, tables and long
 * lists, so those are what this has to get right. Body copy sits at 65-ish
 * characters, which is where reading stops being work.
 */

.prose { max-width: 68ch; font-size: 17px; line-height: 1.68; }

/* subhadipmitra@: SPECIFICITY, not order. This is worth spelling out because
 * the obvious reading is wrong.
 *
 * The flow rule `.prose > * + *` scores (0,1,0) — the universal selector and
 * the combinator both contribute nothing, so it is one class and no more. The
 * reset was written `.prose p`, which is (0,1,1) and therefore beats it
 * wherever both apply, at any position in the file. Every paragraph of all 25
 * blog posts ran into the next with nothing between them but the line-height.
 * The markdown has proper blank-line-separated paragraphs; the spacing was
 * being thrown away here.
 *
 * Moving the reset above the flow rule does NOT fix it, which is the trap: the
 * two rules look like an ordering problem and are not one.
 *
 * So the reset is scoped to direct children, `.prose > *` at (0,1,0) — same
 * weight as the flow rule, so the later one wins for every element that has a
 * preceding sibling, and the first child keeps zero. Paragraphs nested inside
 * list items and blockquotes still need the UA's `1em 0` removed, and they get
 * it explicitly below rather than by a blanket rule that reaches too far.
 *
 * Everything with its own spacing — h2, h3, h4, lists, tables, hr, img — is
 * (0,1,1) and continues to win over both. That is intended and is why those
 * rules did not have to change.
 *
 * Nothing errored and every markup, link and claim check in this repo passed
 * throughout. A lost margin does not break a page, it just makes it worse. */
.prose > * { margin: 0; }
.prose > * + * { margin-top: 1.1em; }

.prose h2 {
  font-family: var(--serif); font-size: 28px; line-height: 1.2; font-weight: 600;
  letter-spacing: -0.015em; margin: 2em 0 0; max-width: 24ch;
  padding-top: 0.7em; border-top: 1px solid var(--rule);
}
.prose h3 {
  font-size: 18px; font-weight: 600; letter-spacing: -0.005em; margin: 1.8em 0 0;
}
.prose h4 { font-size: 15.5px; font-weight: 600; margin: 1.5em 0 0; color: var(--ink-2); }

.prose a { color: var(--ink); box-shadow: inset 0 -1px 0 var(--rule-hard); text-decoration: none; }
.prose a:hover { color: var(--accent); box-shadow: inset 0 -1px 0 var(--accent); }

.prose ul, .prose ol { margin: 1.1em 0 0; padding-left: 1.3em; }
.prose li { margin-bottom: 0.45em; }
.prose li > ul, .prose li > ol { margin-top: 0.45em; }

.prose blockquote {
  margin: 1.6em 0 0; padding: 2px 0 2px 20px; border-left: 2px solid var(--accent);
  color: var(--ink-2);
}
.prose li p, .prose blockquote p { margin: 0; }
.prose blockquote p + p { margin-top: 0.8em; }

.prose code {
  font-family: var(--mono); font-size: 0.86em;
  background: var(--paper-2); padding: 1px 5px;
}
.prose pre {
  font-family: var(--mono); font-size: 13px; line-height: 1.7;
  background: var(--paper-2); border-left: 2px solid var(--rule-hard);
  padding: 16px 18px; overflow-x: auto; margin: 1.5em 0 0;
}
.prose pre code { background: none; padding: 0; font-size: inherit; }

/* A table in a post is data; it gets the same rules the rest of the site uses. */
.prose table { width: 100%; border-collapse: collapse; margin: 1.6em 0 0; font-size: 15px; }
.prose th {
  text-align: left; font-size: 11.5px; font-weight: 500; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); padding: 0 14px 9px 0;
  border-bottom: 1px solid var(--rule-hard);
}
.prose td { padding: 12px 14px 12px 0; border-bottom: 1px solid var(--rule); vertical-align: top; }

.prose hr { border: 0; border-top: 1px solid var(--rule); margin: 2.4em 0 0; }
.prose img { display: block; margin: 1.6em 0 0; }
.prose strong { font-weight: 600; }

/* --- post listing --------------------------------------------------------- */

.posts { margin: 40px 0 0; border-top: 1px solid var(--rule-hard); list-style: none; padding: 0; }
.posts li { border-bottom: 1px solid var(--rule); }
.posts a { display: grid; grid-template-columns: 118px 1fr; gap: 28px;
  padding: 20px 0; text-decoration: none; align-items: baseline; }
.posts a:hover h3 { color: var(--accent); }
.posts time { font-family: var(--mono); font-size: 12.5px; color: var(--muted); }
.posts h3 { font-size: 17px; font-weight: 600; margin: 0 0 5px; max-width: 46ch; }
.posts p { font-size: 14.5px; color: var(--ink-2); margin: 0; max-width: 62ch; }

.postmeta {
  font-family: var(--mono); font-size: 12px; color: var(--muted);
  letter-spacing: 0.03em; margin: 0 0 30px;
}

/* --- the section index on a post ----------------------------------------
 *
 * subhadipmitra@: `:has(.toc)` carries the grid, so a post with fewer than
 * three headings — where the include emits nothing — is not left with an empty
 * 212px column beside it. In a browser without :has() the rule never matches,
 * the grid never applies, and the index renders as a plain block above the
 * article. That is the correct thing to degrade to, so it is the fallback by
 * construction rather than by a second stylesheet.
 *
 * `align-self: start` is what makes the sticky work. A grid item stretches to
 * the row height by default, which for a single-row grid is the height of the
 * article — so `position: sticky` has nothing left to travel and silently does
 * nothing. It looks identical in a short viewport and wrong in a long one.
 *
 * The masthead is `position: relative`, not sticky, so there is no header to
 * clear; `top` is breathing room and not an offset.
 */
.post:has(.toc) { display: grid; grid-template-columns: 212px minmax(0, 1fr); gap: 0 56px; }
.post__body { min-width: 0; }

.toc {
  position: sticky; top: 28px; align-self: start;
  max-height: calc(100vh - 56px); overflow-y: auto;
  border-top: 1px solid var(--rule-hard); padding-top: 14px;
}
.toc__label {
  font-family: var(--mono); font-size: 11px; font-weight: 400;
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--muted);
  margin: 0 0 12px;
}
.toc__list { list-style: none; margin: 0; padding: 0; counter-reset: toc; }
.toc__list li { counter-increment: toc; }
.toc__list a {
  display: grid; grid-template-columns: 22px 1fr; gap: 8px;
  padding: 6px 0; font-size: 13.5px; line-height: 1.4;
  color: var(--ink-2); text-decoration: none; text-wrap: pretty;
}
/* The number is the section's position in the article, which is information the
   reader is using — how far in, how much left. It is not decoration, so it is
   generated from the list rather than typed. */
.toc__list a::before {
  content: counter(toc, decimal-leading-zero);
  font-family: var(--mono); font-size: 11px; color: var(--muted);
  padding-top: 2px; font-variant-numeric: tabular-nums;
}
.toc__list a:hover { color: var(--ink); }
.toc__list a:hover::before { color: var(--accent); }
.toc__list a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Set by the scrollspy below. Weight and a rule, not colour alone — this has to
   survive greyscale and the two themes without being restyled for either. */
.toc__list a[aria-current="true"] { color: var(--ink); font-weight: 600; }
.toc__list a[aria-current="true"]::before { color: var(--accent); }

/* An anchor jump must not land the heading hard against the top of the window,
   and `.prose h2` carries a rule above it that would otherwise sit off-screen. */
.prose h2 { scroll-margin-top: 24px; }

@media (max-width: 1080px) {
  /* Below the width where a rail fits, it becomes a block above the article —
     still useful on a phone, where a twelve-section post is a long scroll with
     no way to see its shape. Sticky is dropped: a sticky block in normal flow
     would pin the index over the text it is indexing. */
  .post:has(.toc) { display: block; }
  .toc {
    position: static; max-height: none; overflow: visible;
    border: 1px solid var(--rule); border-top-color: var(--rule-hard);
    padding: 16px 18px; margin-bottom: 40px;
  }
  .toc__list { columns: 2; column-gap: 32px; }
  .toc__list li { break-inside: avoid; }
}
@media (max-width: 560px) {
  .toc__list { columns: 1; }
}

@media (max-width: 900px) {
  .prose { font-size: 16.5px; }
  .posts a { grid-template-columns: 1fr; gap: 6px; }
}

/* A rendered diagram is a figure, not a code block. */
.prose .mermaid { margin: 1.8em 0 0; text-align: center; overflow-x: auto; }
.prose .mermaid svg { max-width: 100%; height: auto; }


/* --- the subscribe block -------------------------------------------------
 *
 * subhadipmitra@: It sits at the end of a post, where somebody has just
 * finished reading and is the most likely they will ever be to want the next
 * one. `--paper-2` rather than a card: a tint and two rules mark it as a
 * different KIND of thing from the article without borrowing the boxed-CTA
 * idiom the rest of the site avoids.
 *
 * `.sub__fine` is in the `:not()` chain of the section paragraph rule above,
 * because that selector scores 0,2,1 and would otherwise win the font-size
 * back — the same collision documented there, which is why it is worth
 * repeating the fix rather than discovering it a third time.
 */
/* subhadipmitra@: The last thing before the footer, so its bottom padding, the
   footer's 20px margin and the footer's own 54px of top padding stacked into
   130px of nothing between the last words and the first words of the footer.
   Three separate rules each being individually reasonable is the usual way a
   gap like that appears — none of them is wrong on its own. */
.sec--sub {
  background: var(--paper-2);
  border-top: 1px solid var(--rule-hard);
  border-bottom: 1px solid var(--rule);
  padding: 54px 0 46px;
}
.sec--sub h2 { margin: 10px 0 0; max-width: 22ch; }
.sub__lead {
  font-size: 16px; color: var(--ink-2); max-width: 60ch; margin: 14px 0 0;
}
.sub__form { align-items: flex-end; margin-top: 26px; }
.sub__field { flex: 1; max-width: 360px; }
.sub__fine {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: 0.02em;
  color: var(--muted); margin: 18px 0 0;
}
.sub__fine a { color: var(--muted); }
.sub__fine a:hover { color: var(--accent); }

@media (max-width: 620px) {
  .sub__form { align-items: stretch; }
  .sub__field { max-width: none; }
  .sub__form .btn { width: 100%; text-align: center; }
}

/* --- latest posts, on the home page --------------------------------------
 * The compact cut of `.posts`: no descriptions, so three of them cost the
 * height of one paragraph.
 */
.posts--tight a { grid-template-columns: 96px 1fr; gap: 20px; padding: 15px 0; }
.posts--tight h3 { margin: 0; font-size: 16.5px; }

/* A second line inside a `.rows` cell — where to go for the policy, not the
   policy itself. Muted so the address above it stays the thing you read. */
.muted-note { font-size: 13px; color: var(--muted); }
.rows p { margin: 6px 0 0; font-size: 15px; line-height: 1.6; }

/* Three fields rather than one, so the subscribe row's `flex:1` would squeeze
   them all onto one line at a width nobody can type into. */
/* subhadipmitra@: A GRID, not wrapped flex.
 *
 * Three fields and a button in a `flex-wrap` row is a layout that decides
 * itself: every item took a line of its own, and because `gap` only applies
 * BETWEEN items on the cross axis the button ended up flush against the email
 * input with no space at all. Reasoning about which line an item lands on is
 * not worth it for a form with four controls.
 *
 * One column, generous rows, and the button on its own row. Two columns once
 * there is room for two fields to sit side by side without either becoming a
 * slot too narrow to read what you typed. */
.demo__form { display: grid; grid-template-columns: 1fr; gap: 20px; align-items: end; }
.demo__form .sub__field { flex: none; max-width: none; }
.demo__form button[type="submit"] { justify-self: start; }

@media (min-width: 720px) {
  .demo__form { grid-template-columns: 1fr 1fr; column-gap: 24px; }
  /* The email is the field people mistype; it gets the full width. */
  .demo__form .sub__field:nth-of-type(3) { grid-column: 1 / -1; }
}

/* --- the mega menu's right-hand column -----------------------------------
 *
 * subhadipmitra@: Two groups of links left a wide empty half on four of the
 * five menus. The fix is not an illustration — this site has no ornament
 * anywhere, and adding some in the navigation would announce that the empty
 * space was a problem being covered up.
 *
 * It was briefly the nine gates as a numbered list. True, and nine rows tall: a
 * menu is scanned in a second, and a column that takes longer to read than the
 * links beside it has stopped helping. One statement and one sentence now, the
 * same shape in every menu, so the panel's height does not lurch as the reader
 * moves across the bar.
 *
 * `margin-left:auto` via `justify-self`, not a grid track, so it reaches the
 * far edge on a two-column menu and simply follows the last column on a
 * four-column one — `Solutions` never needed it and must not be pushed around
 * by it.
 */
.panel__feature {
  /* `span 2` because the grid's tracks are a bounded 200–250px, and a single
     track would squeeze the statement to three words a line. */
  grid-column: span 2;
  justify-self: end;
  width: 100%;
  max-width: 340px;
  padding-left: 30px;
  border-left: 1px solid var(--rule);
}

.panel__statement {
  margin: 12px 0 0;
  font-family: var(--serif);
  font-size: 19px;
  line-height: 1.3;
  color: var(--ink);
  text-wrap: balance;
}

/* subhadipmitra@: NOT `.panel__note`. That class already belongs to the small
   note under each nav link, and a second rule of the same name later in the
   file won the cascade on `margin` — every link note in every menu quietly
   gained ten pixels. The one-word-longer name is the whole fix. */
.panel__feature-note {
  margin: 11px 0 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted);
}

.panel__more {
  display: inline-block;
  margin-top: 14px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-hard);
}
.panel__more:hover { color: var(--accent); border-color: var(--accent); }

/* Below the mega-menu breakpoint the panel stacks; the feature column would be
   a screenful of scrolling before the reader reaches a link. */
@media (max-width: 900px) { .panel__feature { display: none; } }

/* --- the region map ------------------------------------------------------
 *
 * subhadipmitra@: The land is one `<path>` of small squares, coloured with
 * `currentColor` so it inherits the theme rather than carrying two hardcoded
 * fills. Deliberately faint: the map is context, and the five markers are the
 * content. If the land competes with the pins the whole thing has inverted.
 */
.map { margin: 40px 0 0; }
.map__frame {
  border: 1px solid var(--rule);
  background: var(--paper-2);
  padding: 22px 26px;
}
/* The pins' positioning context is the MAP, not the padded frame — see the
   note in `region-map.html`. */
.map__plot { position: relative; }
.map__land { display: block; width: 100%; height: auto; color: var(--rule-hard); opacity: 0.17; }
:root[data-theme="dark"] .map__land { opacity: 0.26; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .map__land { opacity: 0.26; }
}

.map__pin {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: 7px;
  text-decoration: none;
  white-space: nowrap;
}
/* subhadipmitra@: Sized to be READ, not found. These were 9px squares with a
   10.5px label — legible on a 2× screenshot and close to invisible on a
   1× laptop, which is where somebody will actually meet them. The map is
   context and the markers are the content; the content has to win. */
.map__dot {
  width: 14px; height: 14px;
  border: 2px solid var(--ink-2);
  background: var(--paper);
  flex: none;
}
.map__code {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--ink-2);
}
.map__pin:hover .map__dot { border-color: var(--ink); }
.map__pin:hover .map__code { color: var(--ink); }

/* The one you are on. Filled AND ringed AND bigger — on a mark this size a
   colour change alone is easy to miss, and telling you where you are is the
   whole job of the map. */
.map__pin.is-active { z-index: 1; }
.map__pin.is-active .map__dot {
  width: 17px; height: 17px;
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
}
.map__pin.is-active .map__code { color: var(--accent); font-weight: 600; font-size: 14px; }

.map figcaption {
  margin-top: 12px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--muted);
  max-width: 72ch;
}

@media (max-width: 620px) {
  .map__frame { padding: 14px 10px; }
  .map__code { font-size: 11px; }
  .map__dot { width: 11px; height: 11px; border-width: 1.5px; }
  .map__pin.is-active .map__dot { width: 13px; height: 13px; }
  .map__pin.is-active .map__code { font-size: 11.5px; }
}

/* --- the announcement strip ----------------------------------------------
 *
 * subhadipmitra@: Above the masthead, and it is the one place on the site that
 * uses the accent as a GROUND rather than as an inflection. That is deliberate
 * and it is the reason it works only here: this bar has to be noticed once and
 * then ignored, which is the opposite of everything below it.
 *
 * A small filled square as the marker, not an icon — the same square that
 * builds the region map and the same shape language as the record's state
 * chips. No gradient: fireworks.ai pulses two gradient blocks either side of
 * theirs, and a gradient is the element on any page that dates fastest.
 */
.announce {
  background: var(--ink);
  color: var(--on-ink);
  border-bottom: 1px solid var(--ink);
}
.announce__in {
  display: flex;
  align-items: center;
  gap: 16px;
  padding-top: 9px;
  padding-bottom: 9px;
}
.announce__text {
  margin: 0;
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 13.5px;
  line-height: 1.4;
}
.announce__mark {
  width: 9px; height: 9px;
  background: var(--accent);
  flex: none;
}
.announce__cta {
  color: var(--on-ink);
  text-decoration: none;
  border-bottom: 1px solid var(--accent);
  padding-bottom: 1px;
  white-space: nowrap;
}
.announce__cta:hover { color: var(--accent); }
.announce__x {
  appearance: none;
  background: none;
  border: 0;
  color: var(--on-ink);
  opacity: 0.55;
  font-size: 19px;
  line-height: 1;
  padding: 2px 4px;
  cursor: pointer;
  flex: none;
}
.announce__x:hover { opacity: 1; }

/* subhadipmitra@: DARK IN BOTH THEMES. The first version inverted to a
   near-white strip on a dark page, on the theory that it should contrast with
   what is under it. It read as a torch shone at the reader — the brightest
   thing on the screen, above the fold, saying something incidental. In dark it
   goes darker than the page instead, and a hairline does the separating. */
:root[data-theme="dark"] .announce {
  background: #0a0d10;
  color: var(--ink);
  border-bottom-color: var(--rule);
}
:root[data-theme="dark"] .announce__cta { color: var(--ink); }
:root[data-theme="dark"] .announce__x { color: var(--ink); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .announce {
    background: #0a0d10;
    color: var(--ink);
    border-bottom-color: var(--rule);
  }
  :root:not([data-theme="light"]) .announce__cta { color: var(--ink); }
  :root:not([data-theme="light"]) .announce__x { color: var(--ink); }
}

@media (max-width: 620px) {
  .announce__text { font-size: 12.5px; gap: 7px; }
  /* subhadipmitra@: The marker goes. It is a flex item, so once the message
     wrapped to two lines it sat alone on a line of its own above them — a
     magenta square floating over nothing, which reads as a rendering fault
     rather than as a mark. It is decoration, and this is the width where
     decoration is the first thing to lose.

     The link keeps its own width too: `width: 100%` ran its accent underline
     the full span of the bar, so the rule looked like a divider rather than
     like the underline of the words above it. */
  .announce__mark { display: none; }
  .announce__cta { width: auto; }
  .announce__in { align-items: flex-start; }
}

/* The group entities, under the brand block.
 *
 * subhadipmitra@: A one-word gloss after each, because "Rotalabs" and
 * "Rotavision" mean nothing to somebody meeting them for the first time, and a
 * bare list of sibling brands reads as a conglomerate's org chart rather than
 * as an answer to "who else is behind this".
 */
.foot__group { margin-top: 22px; }
.foot__group ul { list-style: none; margin: 8px 0 0; padding: 0; }
.foot__group li { margin-bottom: 5px; font-size: 13.5px; }
.foot__group a { color: var(--ink-2); text-decoration: none;
                 border-bottom: 1px solid var(--rule); }
.foot__group a:hover { color: var(--accent); border-color: var(--accent); }
.foot__group span {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--muted); margin-left: 7px;
}

/* --- the record, given presence ------------------------------------------
 *
 * Still a document: no shadow, no radius, no fake browser chrome. What it gains
 * is a surface it sits on, a state you can see from across the room, and colour
 * on the values that carry meaning — a refusal should look like one.
 */
.record--card {
  margin: 0;
  border: 1px solid var(--rule);
  border-top: 2px solid var(--rule-hard);
  background: var(--paper-2);
  padding: 16px 20px 20px;
}
.record--card .record__head {
  border-bottom: 0;
  padding: 0 0 2px;
  align-items: center;
}
.record__sub {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--muted);
  padding-bottom: 13px;
  border-bottom: 1px solid var(--rule);
}

.rec { margin: 0; }
.rec > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding: 10px 0;
  border-bottom: 1px dotted var(--rule);
}
.rec dt {
  font-size: 11.5px; font-weight: 500; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); white-space: nowrap;
}
.rec dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--ink);
  text-align: right;
  word-break: break-word;
}
.rec dd b { font-weight: 600; }
.rec dd.v--deny { color: var(--deny); font-weight: 500; }

.record--card .rail { padding-top: 16px; }
/* subhadipmitra@: Nine labels in a narrower column than the full-width version
   was designed for, and "authority" is the longest word in the set — it broke
   to "authorit / y", which on a diagram whose whole point is a fixed ORDER
   makes the first gate look like two. Smaller, tighter, and allowed to sit
   outside its track rather than wrap. */
.record--card .rail__names { font-size: 9px; letter-spacing: 0; }
.record--card .rail__names > * { white-space: nowrap; overflow: visible; }
.record__tags {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: 16px; padding-top: 14px;
  border-top: 1px solid var(--rule);
}
.record__tags span {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.07em;
  color: var(--muted);
  border: 1px solid var(--rule);
  padding: 3px 7px;
  background: var(--paper);
}
.record__cap {
  margin: 12px 0 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted);
}

@media (max-width: 1000px) {
  .hero--split .hero__grid { grid-template-columns: 1fr; gap: 40px; }
  .hero--split .lead { max-width: 60ch; }
}

/* --- the sequence, as the fold -------------------------------------------
 *
 * subhadipmitra@: Nine gates across the page, and it stops. A progress bar does
 * not stop; a stepper does not have a step that never runs. This is neither,
 * and that is the point — the shape belongs to a product with an ordered
 * pipeline that refuses, which is the one structural fact this category does
 * not otherwise have.
 *
 * It was already on this page as a nine-pixel strip at the bottom of a record
 * below the fold. Promoting it is the whole change.
 */
.hero--seq .lead { max-width: 58ch; }

.seq { margin: 46px 0 0; border-top: 2px solid var(--rule-hard); }

.seq__head {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  padding: 12px 0 14px;
  font-family: var(--mono); font-size: 12.5px;
}
.seq__id { color: var(--ink); }
.seq__who { color: var(--muted); }
.seq__state {
  margin-left: auto;
  font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--deny); border: 1px solid var(--deny); padding: 3px 9px;
}

.seq__gates {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(9, 1fr); gap: 4px;
  counter-reset: gate;
}
.seq__gates li {
  counter-increment: gate;
  padding: 13px 0 0;
  border-top: 4px solid var(--allow);
}
.seq__gates li::before {
  content: counter(gate);
  display: block;
  font-family: var(--mono); font-size: 10px; color: var(--muted);
  margin-bottom: 4px;
}
.seq__gates b {
  display: block;
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  color: var(--ink-2);
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.seq__gates span {
  display: block; margin-top: 3px;
  font-size: 10.5px; color: var(--muted);
}

/* The stop. Heavier rule, the name in the deny colour, and the word said out
   loud — this is the only cell that matters and it should not need hunting. */
.seq__gates li.is-stop { border-top-color: var(--deny); border-top-width: 7px; }
.seq__gates li.is-stop b { color: var(--deny); font-weight: 700; }
.seq__gates li.is-stop span { color: var(--deny); }
.seq__gates li.is-stop::before { color: var(--deny); }

/* Never ran. Not a failure and not a pass — the third state, drawn as absence
   rather than as grey success. */
.seq__gates li.is-off { border-top-color: var(--rule); border-top-style: dotted; }
.seq__gates li.is-off b,
.seq__gates li.is-off span,
.seq__gates li.is-off::before { color: var(--muted); opacity: 0.7; }

.seq__why {
  margin: 22px 0 0;
  font-size: 14.5px; line-height: 1.6; color: var(--ink-2);
  max-width: 74ch;
  padding-top: 16px;
  border-top: 1px solid var(--rule);
}

@media (max-width: 900px) {
  /* Nine columns become three rows of three: the order still reads left to
     right and top to bottom, which is the property that has to survive. */
  .seq__gates { grid-template-columns: repeat(3, 1fr); gap: 4px 10px; }
  .seq__gates li { padding-bottom: 12px; }
  .seq__state { margin-left: 0; }
}

/* Three claims under the fold, each checkable elsewhere on the site.
 * (Lost its rules when the copied two-column block was removed wholesale —
 * it had been defined inside it, which is what happens when a component is
 * written into a layout instead of beside it.) */
.trust {
  list-style: none;
  margin: 30px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0 30px;
  border-top: 1px solid var(--rule);
}
.trust li {
  display: flex; align-items: baseline; gap: 10px;
  padding: 13px 0 0;
  font-size: 13.5px; line-height: 1.45; color: var(--ink-2);
}
.trust__m { width: 7px; height: 7px; background: var(--accent); flex: none; transform: translateY(-1px); }
@media (max-width: 780px) { .trust { grid-template-columns: 1fr; gap: 0; } }

/* --- works with ----------------------------------------------------------
 *
 * subhadipmitra@: A grid of ruled cells, not a row of floating logos. Every
 * mark is monochrome and inherits `currentColor`, so eleven brands in eleven
 * different colours do not turn one section into the loudest thing on a site
 * that uses its accent about four times a page.
 *
 * The four without an official mark carry a monogram in the site's own square
 * instead. That keeps the grid even without inventing somebody's logo, and the
 * square is the shape already doing this job in the strip and the map.
 */
.works {
  list-style: none; margin: 34px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--rule-hard);
  border-left: 1px solid var(--rule);
}
.works__i {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 18px;
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  min-width: 0;
}
.works__m {
  width: 22px; height: 22px; flex: none;
  display: grid; place-items: center;
  color: var(--ink);
}
.works__m svg { width: 100%; height: 100%; fill: currentColor; display: block; }
/* The monogram: the site's square, with the initial knocked out of it. */
.works__m b {
  width: 20px; height: 20px;
  display: grid; place-items: center;
  background: var(--ink);
  color: var(--paper);
  font: 600 11px/1 var(--sans);
}
.works__n {
  font-size: 14px; font-weight: 500; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.works__f {
  margin-left: auto;
  font-family: var(--mono); font-size: 10.5px; color: var(--muted);
  white-space: nowrap;
}

@media (max-width: 1000px) { .works { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 720px)  { .works { grid-template-columns: repeat(2, 1fr); }
                             .works__f { display: none; } }
@media (max-width: 460px)  { .works { grid-template-columns: 1fr; } }

/* The compact cut, for the home page: one ruled band, no cells, no function
 * names. Same marks, a quarter of the weight — a logo wall on a home page is
 * the fastest way for this site to look like every other developer tool. */
.works--band {
  display: flex; flex-wrap: wrap;
  gap: 0 34px;
  border-left: 0;
  border-bottom: 1px solid var(--rule);
}
.works--band .works__i {
  border: 0;
  padding: 14px 0;
  gap: 10px;
}
.works--band .works__m { width: 19px; height: 19px; }
.works--band .works__m b { width: 17px; height: 17px; font-size: 10px; }
.works--band .works__n { font-size: 13.5px; font-weight: 500; color: var(--ink-2); }

/* The instrument table. Numbers align, so they can be compared without reading. */
.instruments td, .instruments th { vertical-align: top; }
.instruments .num { text-align: right; font-variant-numeric: tabular-nums;
                    font-family: var(--mono); font-size: 13.5px; }
.instruments .ref { font-size: 12px; color: var(--muted); margin-top: 3px; }
.instruments .st {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em;
  text-transform: uppercase; border: 1px solid currentColor; padding: 2px 6px;
  white-space: nowrap; color: var(--muted);
}
.instruments .st--in-force { color: var(--allow); }
.instruments .st--guidance, .instruments .st--voluntary { color: var(--review); }

.foot__say {
  font-size: 14px; line-height: 1.5; color: var(--ink-2);
  max-width: 26ch; margin: 14px 0 0;
}

/* --- the architecture diagram --------------------------------------------
 *
 * subhadipmitra@: HTML and CSS, not SVG and not mermaid. It inherits the site's
 * type and tokens, it reflows on a phone rather than scrolling sideways, a
 * screen reader reads it as the ordered list of steps it actually is, and
 * mermaid's defaults are rounded — which this site is not.
 */
.arch { margin: 34px 0 0; }
.arch__box { border: 2px solid var(--rule-hard); padding: 0 0 18px; }
.arch__label {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--muted);
  padding: 11px 18px; border-bottom: 1px solid var(--rule);
  background: var(--paper-2);
}
.arch__flow { list-style: none; margin: 0; padding: 0; }
.arch__n {
  display: grid; grid-template-columns: 210px 1fr; gap: 24px;
  padding: 14px 18px; border-bottom: 1px solid var(--rule);
}
.arch__k { font-family: var(--mono); font-size: 12.5px; color: var(--ink); }
.arch__d { font-size: 14px; color: var(--ink-2); }
/* The two halves the page argues must not be one. */
.arch__n.is-decide  { border-left: 4px solid var(--deny); }
.arch__n.is-evidence { border-left: 4px solid var(--allow); }

.arch__store {
  display: grid; gap: 6px;
  padding: 16px 18px 0;
  font-family: var(--mono); font-size: 11.5px; color: var(--muted);
}

.arch__out {
  display: grid; gap: 6px;
  margin-top: 0; padding: 16px 18px 18px;
  border: 1px dashed var(--rule); border-top: 0;
}
.arch__out b { font-size: 14px; }
.arch__cross {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted);
}
.arch figcaption {
  margin-top: 12px; font-size: 13px; line-height: 1.55; color: var(--muted);
  max-width: 74ch;
}
@media (max-width: 700px) {
  .arch__n { grid-template-columns: 1fr; gap: 4px; }
}

/* --- the enforcement ladder, as rungs -------------------------------------
 *
 * subhadipmitra@: `.rungs`, not `.ladder`. `.ladder` already existed for the
 * four-column diagram on /platform/, and a second rule of that name later in
 * the file inherited its `display: grid` for every property the new one did not
 * redeclare — so four stacked rungs rendered as four columns squeezed side by
 * side. Fifth collision in this stylesheet, and the fifth to be a name generic
 * enough that reusing it felt obvious.
 *
 * Original note follows.
 * --- the enforcement ladder ----------------------------------------------
 *
 * subhadipmitra@: Four rungs, and the two that bite are marked. The rule down
 * the left grows heavier at `canary` because that is the rung where an agent is
 * actually stopped for the first time — the page's whole argument is that the
 * step from `shadow` to `canary` is the real one, and the drawing should agree
 * with the words.
 */
.rungs { list-style: none; margin: 34px 0 0; padding: 0; counter-reset: rung; }
.rungs > li {
  counter-increment: rung;
  border-top: 1px solid var(--rule);
  border-left: 3px solid var(--rule);
  padding: 18px 0 20px 22px;
}
.rungs > li:first-child { border-top: 2px solid var(--rule-hard); }
.rungs > li.is-enforcing { border-left-color: var(--deny); border-left-width: 5px; }

.rungs__head { display: flex; align-items: baseline; gap: 14px; }
.rungs__head::before {
  content: counter(rung);
  font-family: var(--mono); font-size: 11px; color: var(--muted);
}
.rungs__head code {
  font-family: var(--mono); font-size: 17px; font-weight: 600; color: var(--ink);
}
.rungs__price {
  margin-left: auto;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--rule); padding: 3px 8px;
}
.is-enforcing .rungs__price { color: var(--ink); border-color: var(--ink-2); }

.rungs__what { margin: 11px 0 0; font-size: 15.5px; line-height: 1.6; color: var(--ink); max-width: 68ch; }
.rungs__us   { margin: 9px 0 0;  font-size: 14px;  line-height: 1.6; color: var(--muted); max-width: 68ch; }

@media (max-width: 620px) {
  .rungs > li { padding-left: 16px; }
  .rungs__price { margin-left: 0; }
  .rungs__head { flex-wrap: wrap; }
}

/* The index collapses. subhadipmitra@: On a phone it sits above the article and
   a twelve-item list is a screen of scrolling before the first sentence; on a
   wide screen it is a rail somebody may simply not want. Same control both
   ways, and the state is remembered across posts — a reader who closed it once
   meant it. */
.toc__head { display: flex; align-items: baseline; gap: 10px; }
.toc__head .toc__label { flex: 1 1 auto; }
.toc__toggle {
  flex: 0 0 auto; appearance: none; background: none; cursor: pointer;
  border: 1px solid var(--rule); padding: 0; width: 22px; height: 22px;
  display: grid; place-items: center; color: var(--muted); line-height: 0;
}
.toc__toggle:hover { border-color: var(--rule-hard); color: var(--ink); }
.toc__toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A minus when open, a plus when closed — the control shows what it will do
   next, which is the only thing a reader needs from it. */
.toc__toggle span { position: relative; width: 9px; height: 9px; display: block; }
.toc__toggle span::before, .toc__toggle span::after {
  content: ""; position: absolute; background: currentColor;
}
.toc__toggle span::before { left: 0; right: 0; top: 4px; height: 1px; }
.toc__toggle span::after {
  top: 0; bottom: 0; left: 4px; width: 1px;
  transform: scaleY(0); transition: transform 0.12s ease;
}
.toc__toggle[aria-expanded="false"] span::after { transform: scaleY(1); }
.toc.is-collapsed .toc__list { display: none; }
.toc.is-collapsed { padding-bottom: 2px; }
@media (prefers-reduced-motion: reduce) {
  .toc__toggle span::after { transition: none; }
}

/* Topic and sector chips. subhadipmitra@: Square, ruled, and carrying a count —
   the count is the point. A tag list without one asks the reader to click to
   find out whether there is anything behind it. */
.chips { list-style: none; margin: 22px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.chips a {
  display: inline-flex; align-items: baseline; gap: 7px;
  border: 1px solid var(--rule); padding: 6px 11px;
  font-size: 13.5px; color: var(--ink-2); text-decoration: none;
}
.chips a span {
  font-family: var(--mono); font-size: 11px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.chips a:hover { border-color: var(--rule-hard); color: var(--ink); }
.chips a:hover span { color: var(--accent); }
.chips a[aria-current="page"] { border-color: var(--ink); color: var(--ink); font-weight: 550; }
.chips a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --- sector use cases -----------------------------------------------------
 *
 * subhadipmitra@: A card per agent, and the gate name sits in the header beside
 * the agent's name rather than buried in the body. That pairing IS the argument
 * these pages make — an ambitious agent on the left, the specific thing that
 * stops it on the right — and a reader scanning four of them should be able to
 * take that in without reading a word of the prose. */
.cases { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; margin: 34px 0 0;
         background: var(--rule); border: 1px solid var(--rule); }
.case { background: var(--paper); padding: 22px 24px 24px; }
.case__head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 10px; }
.case__name { font-weight: 600; font-size: 16px; letter-spacing: -0.005em; flex: 1 1 auto; }
.case__gate {
  flex: 0 0 auto; font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em;
  color: var(--accent); border: 1px solid var(--rule-hard); padding: 2px 7px;
}
.case__does { font-size: 15px; color: var(--ink-2); margin: 0 0 16px; line-height: 1.6; }
.case__rows { margin: 0; display: grid; gap: 10px; }
.case__rows div { display: grid; grid-template-columns: 1fr; gap: 2px; }
.case__rows dt {
  font-family: var(--mono); font-size: 10.5px; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--muted);
}
.case__rows dd { margin: 0; font-size: 14px; line-height: 1.55; }
@media (max-width: 860px) { .cases { grid-template-columns: 1fr; } }

.post__tags { margin-top: 52px; padding-top: 26px; border-top: 1px solid var(--rule-hard); }
.post__tags .k {
  font-family: var(--mono); font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--muted);
}
.post__tags .k + .chips { margin-top: 10px; }
.post__tags .chips + .k { margin-top: 22px; }

/* --- the sector call to action ------------------------------------------- */
.cta-block {
  display: grid; grid-template-columns: 1fr 300px; gap: 44px;
  border-top: 2px solid var(--ink); padding-top: 30px;
}
.cta-block h2 { margin-top: 0; }
.cta-block__acts { display: flex; flex-direction: column; align-items: stretch; gap: 10px; }
/* subhadipmitra@: `.btn`, not `.cta`. `.cta` is defined ONLY as `.mast .cta` —
   it is masthead chrome, and used anywhere else it renders as an unstyled link.
   Which is exactly what happened here: two dead-looking underlined links where
   the page's only two actions should be. Third time on this site a class has
   been reused outside the scope that defines it; the tell is always a rule with
   an ancestor in front of it. */
.cta-block__acts .btn { text-align: center; justify-content: center; }
.cta-block__acts .mark { margin-top: 4px; align-self: center; }
@media (max-width: 860px) {
  .cta-block { grid-template-columns: 1fr; gap: 26px; }
}

/* The third option under a sector CTA. Reads as a link, at reading size. */
.cta-block__sales {
  align-self: center; margin-top: 6px;
  font-size: 15px; color: var(--ink-2); text-decoration: none;
  box-shadow: inset 0 -1px 0 var(--rule-hard);
}
.cta-block__sales:hover { color: var(--accent); box-shadow: inset 0 -1px 0 var(--accent); }
.cta-block__sales:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* --- the nine gates, with this sector's lit ------------------------------- */
.gaterow { margin: 36px 0 0; }
.gaterow__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(9, 1fr); gap: 1px;
  background: var(--rule); border: 1px solid var(--rule);
}
.gaterow__gate {
  background: var(--paper); padding: 12px 10px 14px; min-width: 0;
  display: flex; flex-direction: column; gap: 3px;
}
.gaterow__n {
  font-family: var(--mono); font-size: 10px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.gaterow__name {
  font-family: var(--mono); font-size: 11.5px; color: var(--muted);
  overflow-wrap: anywhere; line-height: 1.35;
}
/* The question is the payload; it appears only on the gates that matter here,
   because nine of them at once is a wall nobody reads. */
.gaterow__q { display: none; font-size: 12px; line-height: 1.45; color: var(--ink-2); margin-top: 4px; }
.gaterow__gate.is-hit { background: var(--paper-2); }
.gaterow__gate.is-hit .gaterow__name { color: var(--ink); font-weight: 600; }
.gaterow__gate.is-hit .gaterow__n { color: var(--accent); }
.gaterow__gate.is-hit .gaterow__q { display: block; }
.gaterow figcaption {
  margin-top: 12px; font-size: 13.5px; color: var(--muted);
  line-height: 1.6; max-width: 74ch;
}
@media (max-width: 1000px) {
  .gaterow__list { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 560px) {
  .gaterow__list { grid-template-columns: repeat(2, 1fr); }
  .gaterow__q { display: none !important; }
}

.rec-lead { font-size: 18px; font-weight: 600; margin: 44px 0 0; letter-spacing: -0.005em; }
.seq--sector { margin-top: 16px; }
.seq__state--ok { color: var(--allow, #177445); border-color: var(--allow, #177445); }

/* --- the ways into the blog ----------------------------------------------
 *
 * subhadipmitra@: Label left, chips right. These are labels for the row beside
 * them, not section headings, and marking them up as h2 for structure while
 * styling them as h2 put three 42px display headings on a page whose own title
 * is 52px. The label carries no weight of its own; the chips are the content.
 *
 * `.filters__k` deliberately does not reuse `.k`, which is defined inside
 * several components on this site and would drag their spacing in with it.
 * Fourth time a shared name has caused that, so this one is its own. */
.filters { display: grid; gap: 26px; }
.filters__row { display: grid; grid-template-columns: 132px minmax(0, 1fr); gap: 0 24px; align-items: start; }
.filters__k {
  font-family: var(--mono); font-size: 11px; font-weight: 400;
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--muted);
  margin: 8px 0 0; padding: 0; border: 0;
  /* subhadipmitra@: the base h2 caps at 21ch, which at 11px mono is about
     139px and wrapped "Everything, newest first" onto two lines. */
  max-width: none;
}
.filters__row .chips { margin: 0; }
/* Standing alone above the list rather than beside a row. */
.filters__k--solo { margin: 0 0 18px; }
@media (max-width: 700px) {
  .filters__row { grid-template-columns: 1fr; gap: 10px; }
  .filters__k { margin-top: 0; }
}

/* --- the enquiry forms ----------------------------------------------------
 * subhadipmitra@: `.sub__field` caps at 360px, which is right for a single
 * address on the newsletter strip and too narrow for a message. The pair row
 * puts name and organisation side by side on a wide screen and stacks them on
 * a phone; everything else runs the full column. */
.eform { display: block; max-width: 620px; }
.eform .sub__field { max-width: none; margin-top: 18px; }
.eform__pair { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.eform__pair .sub__field { margin-top: 0; }
.eform__msg { min-height: 128px; resize: vertical; line-height: 1.6; font-family: inherit; }
.eform select.vinput { appearance: none; background-image: none; }
.eform button[type="submit"] { margin-top: 22px; }
.eform button[disabled] { opacity: 0.55; cursor: default; }
.vout--ok  { color: var(--ink); padding-top: 16px; }
.vout--bad { color: var(--deny, #b3261e); padding-top: 16px; }
@media (max-width: 620px) {
  .eform__pair { grid-template-columns: 1fr; }
  .eform__pair .sub__field + .sub__field { margin-top: 18px; }
}

/* --- the cookie banner ----------------------------------------------------
 * subhadipmitra@: Bottom of the viewport, ruled, not a modal and not a
 * scrim — analytics storage is denied until asked, so there is nothing to
 * withhold the page for. Both buttons carry the same visual weight; the
 * accept-is-loud-reject-is-invisible pattern is a dark pattern and a
 * self-defeating one on a site selling evidence to people who spot them. */
.consent {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  background: var(--paper); border-top: 2px solid var(--ink);
}
.consent__in {
  max-width: var(--wrap); margin: 0 auto; padding: 18px var(--gutter);
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 20px 32px; align-items: center;
}
.consent__t { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--ink-2); max-width: 78ch; }
.consent__t b { color: var(--ink); }
.consent__acts { display: flex; gap: 10px; flex-shrink: 0; }
.consent__acts .btn { white-space: nowrap; }
@media (max-width: 760px) {
  .consent__in { grid-template-columns: 1fr; gap: 14px; }
  .consent__acts .btn { flex: 1; text-align: center; }
}
