/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════
 * THE PORTAL THEME
 *
 * FOUR SETTINGS, AND EVERYTHING ELSE DERIVED FROM THEM. `--ink`, `--paper`, `--accent`, `--measure` and
 * `--logo-w` are written onto `:root` by `layout/theme.liquid`, from the editor. Nothing below introduces a
 * fifth colour: every tone here is a `color-mix` of those, so a client who changes one accent changes the
 * buttons, the rules, the diagram and the state cells in one move.
 *
 * ── THE ONE IDEA THIS PAGE IS DESIGNED AROUND ──────────────────────────────────────────────────────────
 *
 * This system's whole argument is that TWO THINGS WHICH LOOK THE SAME ARE NOT STORED THE SAME. A price that
 * is not set is not a price of zero. «Nobody sponsored them» is not «this shop does not track sponsors». A
 * verb an adapter refuses is not a verb that is missing. The estate says it about `Catalogue.offers`, about
 * `OrderMoney.tax`, about `Volume`, about `Placement` — four packages, one distinction.
 *
 * So the page's signature is `.state`: three cells showing one field in three states, set in the monospace
 * face, with PRESENCE OF COLOUR encoding presence of value. It is the hero's thesis, the diagram's legend
 * and the comparison's vocabulary, and it is the reason there is no fifth hue — «not set» has to read as an
 * absence, and an absence cannot be its own colour.
 *
 * ── AND WHY THERE IS NO WEB FONT ───────────────────────────────────────────────────────────────────────
 *
 * This theme is imported into somebody else's box and served from their domain. A mandatory request to a
 * third party for a typeface is a dependency its owner never chose, on a product whose argument is that it
 * does not decide things on the operator's behalf. So the personality is carried by the TREATMENT — a tight
 * display size with negative tracking, and a monospace utility face promoted to a structural voice, because
 * this system's vernacular really is field names, port names and states.
 * ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */

:root {
  /* Derived tones. No fifth colour — see the header. */
  --ink-soft: color-mix(in oklab, var(--ink) 64%, var(--paper));
  --ink-faint: color-mix(in oklab, var(--ink) 34%, var(--paper));
  --ink-ghost: color-mix(in oklab, var(--ink) 16%, var(--paper));
  --rule: color-mix(in oklab, var(--ink) 13%, var(--paper));
  --rule-soft: color-mix(in oklab, var(--ink) 7%, var(--paper));
  --wash: color-mix(in oklab, var(--ink) 4%, var(--paper));
  --wash-deep: color-mix(in oklab, var(--ink) 92%, var(--paper));
  --accent-soft: color-mix(in oklab, var(--accent) 60%, var(--paper));
  --accent-wash: color-mix(in oklab, var(--accent) 9%, var(--paper));
  --accent-line: color-mix(in oklab, var(--accent) 30%, var(--paper));

  --gap: clamp(1.15rem, 0.8rem + 1.5vw, 2rem);
  --band-y: clamp(3.75rem, 2.2rem + 6vw, 7.5rem);
  --page: 78rem;
  --radius: 12px;

  --step--1: clamp(0.8rem, 0.78rem + 0.1vw, 0.85rem);
  --step-0: clamp(1rem, 0.97rem + 0.16vw, 1.075rem);
  --step-1: clamp(1.15rem, 1.06rem + 0.42vw, 1.4rem);
  --step-2: clamp(1.45rem, 1.25rem + 0.95vw, 2.05rem);
  --step-3: clamp(1.95rem, 1.55rem + 1.95vw, 3.1rem);
  --step-4: clamp(2.5rem, 1.7rem + 3.7vw, 4.75rem);

  --mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
  --sans: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}

/* ── DARK, AND WHY THE SELECTOR IS DOUBLED ───────────────────────────────────────────────────────────────
 *
 * `layout/theme.liquid` writes the settings colours in a `<style>` block in the head, AFTER the `<link>` to
 * this file. Same origin, same `:root`, same specificity — so the later one wins, and a plain
 * `@media (prefers-color-scheme: dark) { :root { --ink: … } }` here LOSES to it on every page.
 *
 * MEASURED IN theme-standard, WHICH HAS EXACTLY THAT RULE AT assets/theme.css:421. Its dark variant is
 * written, shipped and inert, and nothing says so: the page draws, in light, for ever. This is the estate's
 * «a seam wired and inert» one artefact over, and it is invisible because a light page is a correct page.
 *
 * `:root:not([data-theme='light'])` is (0,2,0) against the settings block's (0,1,0), so it wins on
 * SPECIFICITY rather than on order — and the attribute gives an operator a way to pin the site to light. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --ink: #e8ecf4;
    --paper: #0a0c11;
  }
}
:root[data-theme='dark'] {
  --ink: #e8ecf4;
  --paper: #0a0c11;
}

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: var(--step-0)/1.62 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ── THE BAND SYSTEM ─────────────────────────────────────────────────────────────────────────────────────
 *
 * A BAND OWNS ITS OWN PADDING AND ITS OWN CLASS, and nothing selects through the chain above it. The editor
 * wraps every section in `<div data-cms-section style="display:contents">` so a click can name what it hit;
 * `display: contents` keeps that div out of the layout but a SELECTOR still reads the DOM, so `main > section`
 * becomes `main > div > section` and matches nothing. Right on the live site, silently wrong in the thing you
 * build the site in. `scripts/check.mjs` fails any selector that starts at main, body, html, :root, section,
 * header or footer and reaches with `>`. */
.band { padding-block: var(--band-y); position: relative; }
.band-in { width: min(var(--page), 100% - 2 * var(--gap)); margin-inline: auto; }
.band-narrow { width: min(var(--measure), 100% - 2 * var(--gap)); margin-inline: auto; }
.band-wash { background: var(--wash); }
.band-tight { padding-block: calc(var(--band-y) * 0.55); }
.band-rule { border-top: 1px solid var(--rule-soft); }

/* THE GUTTER LABEL — a margin note, the way a field name sits apart from a value.
 *
 * It is a STRUCTURAL DEVICE THAT CARRIES INFORMATION and not an ornament: the label says what KIND of thing
 * the band is (a claim, a mechanism, a scenario), which is metadata about the band rather than part of it.
 * At phone width there is no margin, so it becomes a line above the heading — same information, no gutter. */
.band-grid {
  display: grid;
  gap: var(--gap);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 62rem) {
  .band-grid { grid-template-columns: 11rem minmax(0, 1fr); gap: clamp(2rem, 4vw, 4rem); }
}
.band-label {
  font: 600 var(--step--1)/1.4 var(--mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
}
@media (min-width: 62rem) {
  .band-label { padding-top: 0.65rem; border-top: 2px solid var(--accent-line); }
}

.band-h {
  font-size: var(--step-3);
  font-weight: 640;
  letter-spacing: -0.024em;
  line-height: 1.06;
  text-wrap: balance;
  margin: 0 0 0.6em;
}
.band-lead {
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: var(--measure);
  text-wrap: pretty;
  margin: 0 0 1.2em;
}
.band-body { max-width: var(--measure); color: var(--ink-soft); }
.band-body p { margin: 0 0 1em; text-wrap: pretty; }
.band-body strong, .band-body b { color: var(--ink); font-weight: 650; }
.band-body code { font: var(--step--1)/1.5 var(--mono); background: var(--wash); padding: 0.15em 0.4em; border-radius: 4px; }

:where(h1, h2, h3, h4) { line-height: 1.1; margin: 0 0 0.5em; letter-spacing: -0.02em; text-wrap: balance; }
:where(p) { text-wrap: pretty; }
:where(a) { color: inherit; }
:where(img, svg) { max-width: 100%; height: auto; }
:where(:focus-visible) { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 3px; }

.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: var(--gap); top: var(--gap); z-index: 40; padding: 0.7rem 1.1rem;
  background: var(--paper); color: var(--ink); border: 1px solid var(--rule); border-radius: var(--radius);
}

/* ── BUTTONS ─────────────────────────────────────────────────────────────────────────────────────────── */
.btn {
  display: inline-block; padding: 0.82rem 1.5rem; border-radius: 999px;
  background: var(--accent); color: #fff; text-decoration: none;
  font-weight: 600; font-size: var(--step-0); letter-spacing: -0.01em;
  border: 1px solid transparent;
  transition: transform 0.16s ease, filter 0.16s ease;
}
.btn:hover { transform: translateY(-1px); filter: brightness(1.07); }
.btn:active { transform: translateY(0); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--rule); }
.btn-ghost:hover { border-color: var(--ink-faint); filter: none; }
.btn-row { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }

/* A LINK THAT IS A LINK AND NOT A BUTTON — used where the action is «read on», not «do the thing». */
.link-on {
  font-weight: 600; text-decoration: none; color: var(--accent);
  border-bottom: 1px solid var(--accent-line); padding-bottom: 1px;
}
.link-on:hover { border-bottom-color: var(--accent); }

/* ═══ THE SIGNATURE: THREE STATES OF ONE FIELD ═══════════════════════════════════════════════════════════
 *
 * `—` is «nobody has set this». `0` is a real answer somebody chose. A value is a value. Three cells that a
 * lesser model of the same data would store identically, and which mean three different things to whoever
 * gets paid from them.
 *
 * PRESENCE OF COLOUR ENCODES PRESENCE OF VALUE, which is why there is no fourth hue: an absence that had its
 * own colour would be a kind of presence. */
/* THREE COLUMNS OR ONE, AND NEVER auto-fit.
 *
 * `repeat(auto-fit, minmax(9rem, 1fr))` fitted TWO columns at phone width and left the third cell alone on
 * its own row — with an empty fourth grid area beside it showing the container's own background as a grey
 * block. Measured at 430px. The triple is exactly three things or exactly one thing stacked; there is no
 * reading of it in which two-and-a-half is right, so the breakpoint is explicit. */
.state {
  display: grid; gap: 1px; background: var(--rule);
  border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden;
  grid-template-columns: minmax(0, 1fr);
  font-family: var(--mono);
}
@media (min-width: 34rem) {
  .state { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.state-cell { background: var(--paper); padding: 1rem 1.1rem 1.05rem; }
.state-k {
  display: block; font-size: var(--step--1); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-faint); margin-bottom: 0.45rem;
}
.state-v { display: block; font-size: var(--step-2); font-weight: 600; letter-spacing: -0.02em; line-height: 1; }
.state-note { display: block; font-size: var(--step--1); color: var(--ink-soft); margin-top: 0.5rem; line-height: 1.45; font-family: var(--sans); }
.state-cell[data-state='unset'] .state-v { color: var(--ink-ghost); }
.state-cell[data-state='zero'] .state-v { color: var(--ink-soft); }
.state-cell[data-state='value'] .state-v { color: var(--accent); }

/* ── REVEAL ──────────────────────────────────────────────────────────────────────────────────────────────
 *
 * The default is VISIBLE. `theme.js` adds `.js` to the document element, and only then does a reveal have a
 * hidden state — so a page with no script, or a script that throws before it runs, is a page you can read.
 * Under reduced motion the observer is never installed AND the hidden state never applies, so there is no
 * second door for the motion to arrive through. */
@media (prefers-reduced-motion: no-preference) {
  .js [data-enhance='reveal'] {
    opacity: 0; transform: translateY(12px);
    transition: opacity 0.55s cubic-bezier(0.2, 0.7, 0.3, 1), transform 0.55s cubic-bezier(0.2, 0.7, 0.3, 1);
  }
  .js [data-enhance='reveal'].is-in { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ═══ THE CHROME ═════════════════════════════════════════════════════════════════════════════════════════ */
.site-hd {
  position: sticky; top: 0; z-index: 30;
  background: color-mix(in oklab, var(--paper) 88%, transparent);
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s ease, background 0.2s ease;
}
@supports (backdrop-filter: blur(1px)) {
  .site-hd { backdrop-filter: saturate(1.6) blur(12px); }
}
.site-hd.is-stuck, .site-hd.is-open { border-bottom-color: var(--rule); }
.site-hd-in {
  width: min(var(--page), 100% - 2 * var(--gap)); margin-inline: auto;
  display: flex; align-items: center; gap: clamp(1rem, 2.4vw, 2.25rem);
  min-height: 4.4rem; transition: min-height 0.2s ease;
}
.site-hd.is-stuck .site-hd-in { min-height: 3.6rem; }
.site-hd-brand {
  display: inline-flex; align-items: center; gap: 0.6rem; flex: none;
  font-weight: 680; font-size: var(--step-1); letter-spacing: -0.03em;
  text-decoration: none;
}
.site-hd-brand img { width: var(--logo-w); height: auto; display: block; }
.site-hd-mark, .site-ft-mark {
  width: 0.85em; height: 0.85em; border-radius: 3px; background: var(--accent);
  display: inline-block; flex: none;
}

/* ON A WIDE SCREEN THE PANEL IS NOT A BOX. `display: contents` lets the menu, the language switcher and the
   quiet button sit in the header's own row, so one piece of markup serves both widths. */
.site-hd-panel { display: contents; }
.site-hd-nav { display: flex; gap: clamp(0.9rem, 1.8vw, 1.6rem); margin-right: auto; }
.site-hd-nav a {
  text-decoration: none; font-weight: 550; font-size: var(--step-0); color: var(--ink-soft); white-space: nowrap;
  padding-block: 0.3rem; border-bottom: 1.5px solid transparent; transition: color 0.15s ease, border-color 0.15s ease;
}
.site-hd-nav a:hover { color: var(--ink); border-bottom-color: var(--accent); }
.site-hd-nav a[aria-current='page'] { color: var(--ink); border-bottom-color: var(--accent); }
.flag {
  width: 1.3rem; height: 0.95rem; flex: none; display: block; border-radius: 2px;
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--ink) 12%, transparent);
}
.site-hd-lang { position: relative; }
.site-hd-lang summary {
  display: flex; align-items: center; gap: 0.45rem; cursor: pointer; list-style: none;
  padding: 0.45rem 0.6rem; border: 1px solid var(--rule); border-radius: 999px;
  font: 600 0.75rem/1 var(--mono); color: var(--ink-soft);
}
.site-hd-lang summary::-webkit-details-marker { display: none; }
.site-hd-lang summary:hover, .site-hd-lang[open] summary { border-color: var(--ink-faint); color: var(--ink); }
.site-hd-lang summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.site-hd-lang-chev {
  width: 0.4rem; height: 0.4rem; border: solid currentColor; border-width: 0 1.5px 1.5px 0;
  transform: translateY(-0.12rem) rotate(45deg); transition: transform 0.15s ease;
}
.site-hd-lang[open] .site-hd-lang-chev { transform: translateY(0.08rem) rotate(-135deg); }
.site-hd-lang-list {
  position: absolute; top: calc(100% + 0.4rem); right: 0; z-index: 5; min-width: 11rem;
  display: flex; flex-direction: column; padding: 0.35rem;
  background: var(--paper); border: 1px solid var(--rule); border-radius: var(--radius);
  box-shadow: 0 14px 30px -12px color-mix(in oklab, var(--ink) 28%, transparent);
}
.site-hd-lang-list a {
  display: flex; align-items: center; gap: 0.65rem; padding: 0.55rem 0.65rem; border-radius: 8px;
  text-decoration: none; color: var(--ink-soft); font-size: var(--step--1); font-weight: 550;
}
.site-hd-lang-list a:hover { background: var(--wash); color: var(--ink); }
.site-hd-lang-list a[aria-current='true'] { color: var(--ink); font-weight: 650; }
.site-hd-lang-list a[aria-current='true']::after {
  content: ''; margin-left: auto; width: 0.3rem; height: 0.6rem;
  border: solid var(--accent); border-width: 0 2px 2px 0; transform: rotate(45deg) translateY(-0.1rem);
}

/* BOTH HEADER BUTTONS ARE THE SAME SIZE. Until 25 September 2026 the quiet one kept `.btn`'s full size and
   the primary one was made smaller, so the button asking for less stood taller than the one asking for more. */
.site-hd-cta, .site-hd-alt { padding: 0.62rem 1.15rem; font-size: var(--step--1); white-space: nowrap; flex: none; }

.site-hd-toggle {
  display: none; flex: none; align-items: center; justify-content: center;
  width: 2.6rem; height: 2.6rem; margin-right: -0.4rem; padding: 0;
  background: none; border: 0; border-radius: var(--radius); color: var(--ink); cursor: pointer;
}
.site-hd-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.site-hd-toggle-label {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.site-hd-toggle-bars, .site-hd-toggle-bars::before, .site-hd-toggle-bars::after {
  display: block; width: 1.2rem; height: 1.5px; background: currentColor; border-radius: 1px;
  transition: transform 0.2s ease, background-color 0.2s ease;
}
.site-hd-toggle-bars { position: relative; }
.site-hd-toggle-bars::before, .site-hd-toggle-bars::after { content: ''; position: absolute; left: 0; }
.site-hd-toggle-bars::before { top: -0.38rem; }
.site-hd-toggle-bars::after { top: 0.38rem; }
.site-hd.is-open .site-hd-toggle-bars { background-color: transparent; }
.site-hd.is-open .site-hd-toggle-bars::before { transform: translateY(0.38rem) rotate(45deg); }
.site-hd.is-open .site-hd-toggle-bars::after { transform: translateY(-0.38rem) rotate(-45deg); }

/* ══ THE HEADER BELOW 64rem ══════════════════════════════════════════════════════════════════════════════

   ONE ROW: the brand, the primary button, the menu button. The menu, the language switcher and the quiet
   button go into a panel under it that the menu button opens.

   This replaces a header that wrapped its links onto a second and third row under the brand, on the reasoning
   that «a disclosure nobody opens is a menu nobody reads». That was never measured, and it was reported from
   the seat on 25 September 2026 as a header that is not made for a phone.

   WITHOUT THE SCRIPT there is no button (`hidden` in the markup) and no `has-menu` on the header, so the
   panel is simply shown: every link reachable, just not folded away. */
@media (max-width: 64rem) {
  .site-hd-in { flex-wrap: wrap; gap: 0.75rem; min-height: 3.75rem; padding-block: 0.6rem; }
  .site-hd.is-stuck .site-hd-in { min-height: 3.75rem; }
  .site-hd-brand { font-size: var(--step-0); margin-right: auto; }
  .site-hd-cta { padding: 0.55rem 0.95rem; }
  .site-hd-toggle:not([hidden]) { display: inline-flex; order: 2; }

  .site-hd-panel {
    order: 3; width: 100%;
    display: flex; flex-direction: column; align-items: stretch; gap: 1rem;
    padding: 0.4rem 0 1.1rem;
  }
  .site-hd.has-menu:not(.is-open) .site-hd-panel { display: none; }
  /* WITH THE SCRIPT, the panel hangs under the header instead of wrapping inside it, so the first row never
     breaks: at 320px the brand, the button and the menu button did not fit on one row with wrapping allowed. */
  .site-hd.has-menu .site-hd-in { flex-wrap: nowrap; }
  .site-hd.has-menu .site-hd-brand { flex: 0 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; display: block; }
  .site-hd.has-menu .site-hd-mark { vertical-align: -0.05em; margin-right: 0.5rem; }
  .site-hd.has-menu .site-hd-panel {
    position: absolute; top: 100%; left: 0; right: 0;
    padding-inline: max(var(--gap), (100% - var(--page)) / 2);
    background: var(--paper); border-bottom: 1px solid var(--rule);
    box-shadow: 0 18px 30px -18px color-mix(in oklab, var(--ink) 30%, transparent);
    max-height: calc(100vh - 100%); overflow-y: auto;
  }

  .site-hd-nav { flex-direction: column; gap: 0; margin: 0; }
  .site-hd-nav a {
    font-size: var(--step-1); font-weight: 600; color: var(--ink);
    padding-block: 0.8rem; border-bottom: 1px solid var(--rule);
  }
  .site-hd-nav a:hover { border-bottom-color: var(--rule); color: var(--accent); }
  /* IN THE PHONE PANEL the list opens in place, pushing what is under it down, instead of floating over it. */
  .site-hd-lang { align-self: stretch; }
  .site-hd-lang summary { display: inline-flex; padding: 0.6rem 0.8rem; font-size: 0.8rem; }
  .site-hd-lang-list { position: static; margin-top: 0.5rem; box-shadow: none; min-width: 0; }
  .site-hd-lang-list a { font-size: var(--step-0); padding-block: 0.7rem; }
  .site-hd-alt { text-align: center; padding-block: 0.8rem; font-size: var(--step-0); }
}

.site-ft { border-top: 1px solid var(--rule); padding-block: clamp(2.5rem, 5vw, 4rem); margin-top: auto; }
.site-ft-in {
  width: min(var(--page), 100% - 2 * var(--gap)); margin-inline: auto;
  display: grid; gap: 1.6rem;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 52rem) {
  .site-ft-in { grid-template-columns: auto minmax(0, 1fr) auto; align-items: start; gap: 2.5rem; }
}
.site-ft-brand { display: inline-flex; align-items: center; gap: 0.55rem; font-weight: 680; letter-spacing: -0.03em; }
.site-ft-nav { display: flex; flex-wrap: wrap; gap: 0.6rem 1.4rem; }
.site-ft-nav a { text-decoration: none; color: var(--ink-soft); font-size: var(--step-0); }
.site-ft-nav a:hover { color: var(--ink); text-decoration: underline; }
.site-ft-end { display: grid; gap: 0.7rem; justify-items: start; }
@media (min-width: 52rem) { .site-ft-end { justify-items: end; text-align: right; } }
.site-ft-lang { display: flex; gap: 0.9rem; font-size: var(--step--1); }
.site-ft-lang a { text-decoration: none; color: var(--ink-faint); }
.site-ft-lang a[aria-current='true'] { color: var(--ink); }
.site-ft-line { margin: 0; font-size: var(--step--1); color: var(--ink-faint); max-width: 34ch; }

/* ═══ THE HERO ═══════════════════════════════════════════════════════════════════════════════════════════
 *
 * ASYMMETRIC AND LEFT-SET. A centred stack would leave the width empty and make the state table an
 * afterthought under the fold; side by side, the argument and its evidence are one glance. */
.hero { padding-block: clamp(3.25rem, 2rem + 6vw, 7rem) clamp(2.5rem, 1.5rem + 3.5vw, 4.25rem); overflow: hidden; }
.hero-grid { display: grid; gap: clamp(2rem, 5vw, 3.5rem); align-items: end; }
@media (min-width: 64rem) {
  .hero-grid { grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); }
}
.hero-eyebrow {
  font: 600 var(--step--1)/1.4 var(--mono); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--accent); margin: 0 0 1.1rem;
  display: inline-flex; align-items: center; gap: 0.6rem;
}
.hero-eyebrow::before { content: ''; width: 1.6rem; height: 2px; background: var(--accent); display: inline-block; }
.hero-h {
  font-size: var(--step-4); font-weight: 680; letter-spacing: -0.038em; line-height: 0.98;
  margin: 0 0 0.45em; max-width: 16ch;
}
.hero-lead { font-size: var(--step-1); line-height: 1.5; color: var(--ink-soft); max-width: 46ch; margin: 0 0 1.8rem; }
.hero-cta { margin: 0; }
.hero-fig { margin: 0; }
.hero-fig-cap {
  font: 600 var(--step--1)/1.5 var(--mono); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-faint); margin-bottom: 0.75rem;
}
.hero--has-image { color: #fff; }
.hero-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -1; }

/* ═══ CARDS ON A ROW ═════════════════════════════════════════════════════════════════════════════════════ */
.fg-list { list-style: none; margin: 0; padding: 0; display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); }
@media (min-width: 46rem) { .fg-list[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 46rem) { .fg-list[data-cols="3"] { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 64rem) { .fg-list[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 46rem) { .fg-list[data-cols="4"] { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 70rem) { .fg-list[data-cols="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.fg-item { padding-top: 1.35rem; border-top: 2px solid var(--accent-line); }
.fg-icon { display: block; width: 1.65rem; height: 1.65rem; color: var(--accent); margin-bottom: 0.9rem; }
.fg-icon svg { width: 100%; height: 100%; display: block; stroke-linecap: round; stroke-linejoin: round; }
.fg-h { font-size: var(--step-1); font-weight: 650; letter-spacing: -0.022em; margin: 0 0 0.55em; }
.fg-b { color: var(--ink-soft); font-size: var(--step-0); }
.fg-b p { margin: 0 0 0.85em; text-wrap: pretty; }
.fg-b p:last-child { margin-bottom: 0; }
.fg-b b, .fg-b strong { color: var(--ink); font-weight: 650; }

/* ═══ THE ARCHITECTURE DIAGRAM ═══════════════════════════════════════════════════════════════════════════
 *
 * Three columns with a rule between them, reading left to right: who asks, through what, answered by what.
 * The direction IS the information, so at phone width the columns stack and each keeps its own heading — a
 * stacked three-column diagram with no headings is three lists nobody can tell apart. */
.arch-wrap { margin-top: 0.5rem; }
.arch-cols { display: grid; gap: 1.5rem; }
@media (min-width: 58rem) {
  .arch-cols { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0; }
  .arch-col { padding-inline: clamp(1rem, 2.2vw, 1.75rem); border-left: 1px solid var(--rule); }
  .arch-col:first-child { padding-left: 0; border-left: 0; }
  .arch-col:last-child { padding-right: 0; }
}
.arch-col-h {
  font: 600 var(--step--1)/1.4 var(--mono); letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--ink-faint); margin: 0 0 0.9rem;
}
.arch-nodes { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
.arch-node {
  display: block; width: 100%; text-align: left; cursor: pointer;
  font: 550 var(--step-0)/1.35 var(--mono);
  padding: 0.75rem 0.9rem; border-radius: 9px;
  background: var(--paper); color: var(--ink);
  border: 1px solid var(--rule);
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}
.arch-node:hover { border-color: var(--accent); transform: translateX(2px); }
.arch-node.is-on { border-color: var(--accent); background: var(--accent-wash); }
.arch-col[data-kind="adapter"] .arch-node { border-style: dashed; }

/* THE DESCRIPTIONS. A definition list with no script, one panel with one. BOTH STATES ARE STYLED, so the
   page is finished either way rather than merely acceptable without JavaScript. */
.arch-desc { margin: 1.75rem 0 0; display: grid; gap: 1rem; }
.arch-desc-row { display: grid; gap: 0.2rem; padding-top: 0.9rem; border-top: 1px solid var(--rule-soft); }
.arch-desc dt { font: 600 var(--step--1)/1.4 var(--mono); letter-spacing: 0.04em; color: var(--ink); }
.arch-desc dd { margin: 0; color: var(--ink-soft); max-width: var(--measure); font-size: var(--step-0); }
.js .arch-desc {
  margin-top: 1.5rem; min-height: 6.5rem; padding: 1.1rem 1.25rem; gap: 0;
  border: 1px solid var(--rule); border-radius: var(--radius); background: var(--paper);
}
.js .arch-desc-row { display: none; border-top: 0; padding-top: 0; }
.js .arch-desc-row.is-on { display: grid; }

/* ═══ TABS ═══════════════════════════════════════════════════════════════════════════════════════════════ */
.tabs-strip { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.75rem; }
.tabs-tab {
  display: inline-flex; align-items: baseline; gap: 0.55rem; cursor: pointer;
  font: 550 var(--step-0)/1 var(--sans); letter-spacing: -0.01em;
  padding: 0.7rem 1.05rem; border-radius: 999px;
  background: transparent; color: var(--ink-soft);
  border: 1px solid var(--rule);
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.tabs-tab:hover { color: var(--ink); border-color: var(--ink-faint); }
.tabs-tab[aria-selected="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.tabs-tab-n { font: 600 var(--step--1)/1 var(--mono); opacity: 0.55; }

/* WITHOUT JAVASCRIPT every panel is shown and the band reads top to bottom. The script is what turns it
   into one at a time — it rearranges what is already on the page and adds nothing to it. */
.tabs-panels { display: grid; gap: 2.5rem; }
.js .tabs-panels { gap: 0; }
.tabs-panel { max-width: 54ch; }
.tabs-panel-h { font-size: var(--step-2); font-weight: 650; letter-spacing: -0.026em; margin: 0 0 0.6em; }
.tabs-panel-b p { margin: 0 0 0.9em; }
.tabs-panel-note {
  margin: 1.4rem 0 0; padding-top: 0.85rem; border-top: 1px solid var(--rule);
  font: var(--step--1)/1.5 var(--mono); color: var(--ink-faint);
}

/* ═══ SCENARIOS ══════════════════════════════════════════════════════════════════════════════════════════ */
.cases-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.25rem; }
@media (min-width: 62rem) { .cases-list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.case {
  background: var(--paper); border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 1.5rem 1.4rem 1.4rem; display: flex; flex-direction: column;
}
.case-who { font-size: var(--step-1); font-weight: 650; letter-spacing: -0.022em; margin: 0 0 1.1rem; }
.case-rows { margin: 0; display: grid; gap: 1rem; }
.case-row { display: grid; gap: 0.3rem; }
.case-row dt {
  font: 600 var(--step--1)/1.3 var(--mono); letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--ink-faint);
}
.case-row dd { margin: 0; color: var(--ink-soft); font-size: var(--step-0); }
.case-row--here dt { color: var(--accent); }
.case-row--here dd { color: var(--ink); }
.case-more { margin: 1.35rem 0 0; padding-top: 1rem; border-top: 1px solid var(--rule-soft); }

/* ═══ THE CODE PANEL ═════════════════════════════════════════════════════════════════════════════════════ */
.peek-fig {
  margin: 0; border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden;
  background: var(--wash);
}
.peek-cap {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 0.7rem 1rem; border-bottom: 1px solid var(--rule);
  font: 550 var(--step--1)/1 var(--mono); color: var(--ink-faint);
}
.peek-pre { margin: 0; padding: 1.15rem 1rem 1.3rem; overflow-x: auto; }
.peek-pre code { font: var(--step--1)/1.75 var(--mono); color: var(--ink); white-space: pre; }
.peek-note { margin: 1.1rem 0 0; color: var(--ink-soft); max-width: var(--measure); font-size: var(--step-0); }
.peek-note code { font: 0.95em/1 var(--mono); background: var(--wash); padding: 0.15em 0.4em; border-radius: 4px; color: var(--ink); }
.peek-copy {
  font: 550 var(--step--1)/1 var(--sans); cursor: pointer;
  background: transparent; border: 1px solid var(--rule); border-radius: 6px;
  padding: 0.35rem 0.6rem; color: var(--ink-soft);
}
.peek-copy:hover { color: var(--ink); border-color: var(--ink-faint); }

/* ═══ TEXT AND FIGURE ════════════════════════════════════════════════════════════════════════════════════ */
.split-grid { display: grid; gap: clamp(1.75rem, 4vw, 3.5rem); align-items: center; }
@media (min-width: 62rem) {
  .split-grid--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .split-grid--two[data-side="left"] .split-say { order: 2; }
}
/* A SPLIT WITH NO IMAGE IS ONE COLUMN, AND THE HEADING SHARES IT.
 *
 * Without this the grid is a single full-width track, so the h2 ran the whole 78rem while `.band-body` was
 * capped at the measure underneath it — a very wide heading over a half-width paragraph, with the right half
 * of the band empty. Measured at 1440. The two-column variant is unaffected: there the track is already
 * narrow and the heading is meant to fill it. */
.split-grid:not(.split-grid--two) .split-say { max-width: calc(var(--measure) + 6ch); }
.split-label { margin-bottom: 1rem; }
@media (min-width: 62rem) { .split-label { border-top: 2px solid var(--accent-line); padding-top: 0.65rem; display: inline-block; } }
.split-fig { margin: 0; }
.split-fig img { display: block; width: 100%; border-radius: var(--radius); }
.split-more { margin: 1.4rem 0 0; }

/* ═══ PROSE ══════════════════════════════════════════════════════════════════════════════════════════════ */
.prose-main { max-width: calc(var(--measure) + 6ch); }
.prose-h { font-size: var(--step-2); }

/* ═══ CALL TO ACTION ═════════════════════════════════════════════════════════════════════════════════════ */
.cta-in { display: grid; gap: 1.5rem; align-items: center; }
@media (min-width: 54rem) { .cta-in { grid-template-columns: minmax(0, 1fr) auto; gap: 2.5rem; } }
.cta-h { font-size: var(--step-2); font-weight: 650; letter-spacing: -0.026em; margin: 0 0 0.4em; }
.cta-b { margin: 0; color: var(--ink-soft); max-width: 52ch; }
.cta-act { margin: 0; }

/* ═══ QUESTIONS ══════════════════════════════════════════════════════════════════════════════════════════ */
.faq-main { max-width: 62ch; }
.faq-list { border-top: 1px solid var(--rule); }
.faq-item { border-bottom: 1px solid var(--rule); }
.faq-q {
  cursor: pointer; list-style: none; padding: 1.15rem 2rem 1.15rem 0; position: relative;
  font-size: var(--step-1); font-weight: 600; letter-spacing: -0.018em;
}
.faq-q::-webkit-details-marker { display: none; }
.faq-q::after {
  content: ""; position: absolute; right: 0.25rem; top: 50%; width: 0.7rem; height: 0.7rem;
  border-right: 2px solid var(--ink-faint); border-bottom: 2px solid var(--ink-faint);
  transform: translateY(-70%) rotate(45deg); transition: transform 0.2s ease;
}
.faq-item[open] .faq-q::after { transform: translateY(-20%) rotate(-135deg); }
.faq-a { padding: 0 0 1.35rem; }
.faq-a p { margin: 0 0 0.8em; }
.faq-a p:last-child { margin-bottom: 0; }

/* ═══ STEPS ══════════════════════════════════════════════════════════════════════════════════════════════
 *
 * NUMBERED BECAUSE THE ORDER IS REAL. Step 2 cannot happen before step 1, which is the only reason to number
 * anything. The rail fills as the band passes; with no script it is simply drawn, which is a rail rather
 * than a progress bar and is correct either way. */
.steps-list { list-style: none; margin: 0; padding: 0 0 0 3.1rem; position: relative; counter-reset: step; }
.steps-list::before {
  content: ""; position: absolute; left: 1.05rem; top: 0.6rem; bottom: 0.6rem; width: 2px;
  background: var(--rule);
}
.js .steps-list::after {
  content: ""; position: absolute; left: 1.05rem; top: 0.6rem; width: 2px;
  height: calc(var(--progress, 0) * (100% - 1.2rem)); background: var(--accent);
  transition: height 0.25s linear;
}
.step { position: relative; padding-bottom: 2rem; counter-increment: step; }
.step:last-child { padding-bottom: 0; }
.step::before {
  content: counter(step); position: absolute; left: -3.1rem; top: 0;
  width: 2.1rem; height: 2.1rem; border-radius: 50%;
  display: grid; place-items: center; z-index: 1;
  font: 600 var(--step--1)/1 var(--mono);
  background: var(--paper); color: var(--accent); border: 2px solid var(--accent-line);
}
.step-h { font-size: var(--step-1); font-weight: 640; letter-spacing: -0.02em; margin: 0.25em 0 0.4em; }
.step-b { margin: 0; color: var(--ink-soft); max-width: var(--measure); }

/* ═══ FIGURES ════════════════════════════════════════════════════════════════════════════════════════════ */
.stats-h { font-size: var(--step-2); }
.stats-list { margin: 0; display: grid; gap: 1px; background: var(--rule); border-block: 1px solid var(--rule); }
@media (min-width: 46rem) { .stats-list { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); } }
.stat { background: var(--paper); padding: 1.5rem 1.25rem; }
.stat-v { font: 650 var(--step-3)/1 var(--mono); letter-spacing: -0.03em; color: var(--accent); }
.stat-u { font-size: 0.5em; margin-left: 0.2em; color: var(--ink-faint); }
.stat-c { margin: 0.6rem 0 0; color: var(--ink-soft); font-size: var(--step--1); line-height: 1.5; }

/* ═══ THE DEMO ═══════════════════════════════════════════════════════════════════════════════════════════ */
.demo-main { max-width: 56ch; }
.demo-b { margin-bottom: 1.75rem; }
.demo-form { display: grid; gap: 1rem; }
@media (min-width: 40rem) { .demo-form { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.demo-field { margin: 0; display: grid; gap: 0.4rem; }
.demo-field--wide { grid-column: 1 / -1; }
.demo-field label { font: 600 var(--step--1)/1.3 var(--mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); }
.demo-field input, .demo-field textarea {
  font: var(--step-0)/1.5 var(--sans); color: var(--ink);
  background: var(--paper); border: 1px solid var(--rule); border-radius: 9px;
  padding: 0.7rem 0.85rem; width: 100%;
}
.demo-field input:focus, .demo-field textarea:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 0 3px var(--accent-wash); }
.demo-send { margin: 0.25rem 0 0; grid-column: 1 / -1; }
.demo-alt { margin: 0; }
.demo-alt-note { margin: 0.9rem 0 0; color: var(--ink-soft); font-size: var(--step--1); max-width: 46ch; }

/* ═══ THE COMPARISON ═════════════════════════════════════════════════════════════════════════════════════
 *
 * A real table, with real header cells, because a grid of divs looks identical and says nothing to a screen
 * reader. Below the table breakpoint each COLUMN becomes a card — done in CSS alone, reading the column's
 * name off `data-col` on the cell, so there is no second markup for the narrow view to fall out of step
 * with. */
.compare-scroll { overflow-x: auto; }
.compare-t { width: 100%; border-collapse: collapse; font-size: var(--step-0); }
.compare-t th, .compare-t td { text-align: left; vertical-align: top; padding: 0.9rem 1rem; }
.compare-t thead th {
  font: 600 var(--step--1)/1.3 var(--mono); letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--ink-faint); border-bottom: 1px solid var(--rule); white-space: nowrap;
}
.compare-t thead th:first-child { color: var(--ink-ghost); }
.compare-t tbody th { font-weight: 620; color: var(--ink); width: 1%; white-space: nowrap; }
.compare-t tbody td { color: var(--ink-soft); }
.compare-t tbody tr { border-bottom: 1px solid var(--rule-soft); }
.compare-t tbody tr:last-child { border-bottom: 0; }
.compare-t tbody td:last-child { color: var(--ink); }
.compare-note { margin: 1.2rem 0 0; color: var(--ink-faint); font-size: var(--step--1); max-width: var(--measure); }

@media (max-width: 52rem) {
  .compare-scroll { overflow-x: visible; }
  .compare-t thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .compare-t, .compare-t tbody, .compare-t tr, .compare-t th, .compare-t td { display: block; }
  .compare-t tbody tr {
    border: 1px solid var(--rule); border-radius: var(--radius); margin-bottom: 0.9rem; padding: 0.35rem 0;
  }
  .compare-t tbody th { width: auto; white-space: normal; padding-bottom: 0.35rem; }
  .compare-t tbody td { display: flex; gap: 0.75rem; padding-block: 0.35rem; }
  .compare-t tbody td::before {
    content: attr(data-col);
    flex: 0 0 8.5rem;
    font: 600 var(--step--1)/1.5 var(--mono); letter-spacing: 0.05em; text-transform: uppercase;
    color: var(--ink-faint);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════
 * THE VISUAL LAYER
 *
 * The first version of this site had no picture on it anywhere. Every band was a heading, a paragraph and a
 * rule, which is a document. What follows is the part that makes it a site: a drawn product screen, a price
 * matrix you can count, and enough atmosphere behind the hero that the page has a top rather than a start.
 *
 * NOTHING HERE IS A FILE. No image, no font, no icon set — every shape is markup and tokens, so it is sharp
 * at any size, follows the four colours the operator picks, survives dark mode, and costs one request.
 * ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */

/* ── ATMOSPHERE BEHIND THE HERO ─────────────────────────────────────────────────────────────────────────
 *
 * Two soft radial washes and a fine grid that fades out. The grid is the subject's own vernacular — a
 * catalogue is a grid — rather than the gradient blob every SaaS page opens with.
 *
 * `position: absolute` INSIDE THE BAND, never fixed and never on the body: the editor wraps every section in
 * a div, so anything anchored above the band is anchored to something that is not there in the preview. */
.hero { isolation: isolate; }
.hero::before {
  content: '';
  position: absolute; inset: -20% -10% 0 -10%; z-index: -2; pointer-events: none;
  background:
    radial-gradient(38rem 26rem at 18% 8%, color-mix(in oklab, var(--accent) 16%, transparent), transparent 70%),
    radial-gradient(30rem 22rem at 88% 0%, color-mix(in oklab, var(--accent) 9%, transparent), transparent 68%);
}
.hero::after {
  content: '';
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    linear-gradient(to right, color-mix(in oklab, var(--ink) 6%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in oklab, var(--ink) 6%, transparent) 1px, transparent 1px);
  background-size: 4.5rem 4.5rem;
  -webkit-mask-image: radial-gradient(60% 70% at 30% 20%, #000 0%, transparent 75%);
  mask-image: radial-gradient(60% 70% at 30% 20%, #000 0%, transparent 75%);
}

/* ── THE DRAWN PRODUCT SCREEN ───────────────────────────────────────────────────────────────────────────
 *
 * A window frame, a rail, a toolbar and a table. It is meant to read as the product at a glance and to
 * survive being looked at closely, which is why the numbers in it are real prices and one row is empty. */
.shot-band { overflow: hidden; }
.shot-band--pull { padding-top: 0; margin-top: calc(-1 * clamp(1.5rem, 4vw, 4rem)); }
.shot-say { max-width: calc(var(--measure) + 4ch); margin-bottom: clamp(1.75rem, 3vw, 2.75rem); }
.shot-label { margin-bottom: 1rem; }
@media (min-width: 62rem) { .shot-label { border-top: 2px solid var(--accent-line); padding-top: 0.65rem; display: inline-block; } }

.shot-stage { perspective: 2000px; }
.shot {
  border: 1px solid var(--rule);
  border-radius: 14px;
  background: var(--paper);
  overflow: hidden;
  box-shadow:
    0 1px 2px color-mix(in oklab, var(--ink) 6%, transparent),
    0 12px 28px -12px color-mix(in oklab, var(--ink) 18%, transparent),
    0 40px 80px -40px color-mix(in oklab, var(--ink) 30%, transparent);
  transition: transform 0.6s cubic-bezier(0.2, 0.7, 0.3, 1);
}
/* THE TILT IS OFF UNTIL THE SCRIPT ASKS FOR IT. A page with no javascript gets the screen square on, which
   is the readable state; the tilt is decoration and decoration is the enhancement. */
.js .shot--tilt { transform: perspective(2000px) rotateX(7deg) rotateY(-3deg) scale(0.985); }
.js .shot--tilt.is-level { transform: none; }
@media (prefers-reduced-motion: reduce) { .js .shot--tilt { transform: none; } }

.shot-chrome {
  display: flex; align-items: center; gap: 0.9rem;
  padding: 0.65rem 0.9rem;
  border-bottom: 1px solid var(--rule);
  background: var(--wash);
}
.shot-dots { display: inline-flex; gap: 0.35rem; }
.shot-dots i { width: 0.55rem; height: 0.55rem; border-radius: 50%; background: var(--ink-ghost); }
.shot-addr {
  font: 500 var(--step--1)/1 var(--mono); color: var(--ink-faint);
  background: var(--paper); border: 1px solid var(--rule); border-radius: 6px;
  padding: 0.35rem 0.7rem; flex: 1; max-width: 22rem;
}
.shot-body { display: grid; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 46rem) { .shot-body { grid-template-columns: 10.5rem minmax(0, 1fr); } }
.shot-rail {
  display: none; flex-direction: column; gap: 0.15rem;
  padding: 0.9rem 0.6rem; border-right: 1px solid var(--rule); background: var(--wash);
}
@media (min-width: 46rem) { .shot-rail { display: flex; } }
.shot-rail-item {
  font-size: var(--step--1); color: var(--ink-faint);
  padding: 0.45rem 0.6rem; border-radius: 6px;
}
.shot-rail-item.is-on { background: var(--accent-wash); color: var(--accent); font-weight: 600; }
.shot-main { padding: 1.1rem 1.15rem 1.35rem; min-width: 0; }
.shot-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1rem; }
.shot-title { font-size: var(--step-1); font-weight: 650; letter-spacing: -0.022em; }
.shot-btn {
  font: 600 var(--step--1)/1 var(--sans); color: #fff; background: var(--accent);
  padding: 0.5rem 0.85rem; border-radius: 7px; white-space: nowrap;
}
.shot-table { width: 100%; border-collapse: collapse; font-size: var(--step--1); }
.shot-table th, .shot-table td { text-align: left; padding: 0.68rem 0.55rem; }
.shot-table thead th {
  font: 600 0.92em/1 var(--mono); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-faint); border-bottom: 1px solid var(--rule); padding-bottom: 0.5rem;
}
.shot-table tbody tr { border-bottom: 1px solid var(--rule-soft); }
.shot-table tbody tr:last-child { border-bottom: 0; }
.shot-table tbody th { font-weight: 600; color: var(--ink); }
.shot-table tbody td { color: var(--ink-soft); }
.shot-table .shot-num { font-family: var(--mono); font-weight: 600; text-align: right; white-space: nowrap; color: var(--ink); }
.shot-table thead th.is-num { text-align: right; }
/* A `felt` cell is an input box, the way a price list draws pris, CV and PV. An empty box is a value
   nobody has set, and it is drawn as the admin draws it: a box with nothing in it, not a dash and not 0. */
.shot-table .shot-cell-field { text-align: right; }
.shot-field {
  display: inline-block; min-width: 4.5rem; min-height: 1.9em; box-sizing: border-box;
  padding: 0.32rem 0.6rem; border: 1px solid var(--rule); border-radius: 7px; background: var(--paper);
  font: 500 1em/1.2 var(--mono); color: var(--ink); text-align: left;
}
.shot-field.is-empty { color: var(--ink-faint); font-family: var(--sans); font-weight: 400; }
.shot-dash { color: var(--ink-ghost); font-family: var(--mono); }
.shot-sub { display: block; margin-top: 0.2rem; font: 500 0.88em/1 var(--mono); color: var(--ink-faint); letter-spacing: 0.04em; }
.shot-back { margin: 0 0 0.35rem; font-size: 0.8em; color: var(--ink-faint); }
.shot-rail-item.is-sub { padding-left: 1.35rem; }
.shot-table .shot-flag { text-align: left; }
.shot-flag span {
  display: inline-block; font: 600 0.9em/1 var(--sans);
  padding: 0.3rem 0.55rem; border-radius: 999px; white-space: nowrap;
}
.shot-flag span[data-state='sett'] { background: var(--accent-wash); color: var(--accent); }
.shot-flag span[data-state='null'] { background: var(--wash); color: var(--ink-soft); }
.shot-flag span[data-state='ingen'] { background: transparent; color: var(--ink-faint); border: 1px dashed var(--rule); }
.shot-foot { margin: 1.35rem 0 0; font-size: var(--step--1); color: var(--ink-faint); max-width: 56ch; }

/* ── THE PRICE MATRIX ───────────────────────────────────────────────────────────────────────────────────
 *
 * The cells arrive one after another when the band is scrolled past, left to right and top to bottom, so the
 * eye reads it as a grid being filled rather than a table that was already there. Off with no script and off
 * under reduced motion, where it is simply a finished grid. */
.matrix-grid { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
@media (min-width: 64rem) { .matrix-grid { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); } }
.matrix-say { max-width: calc(var(--measure) + 2ch); }
.matrix-label { margin-bottom: 1rem; }
@media (min-width: 62rem) { .matrix-label { border-top: 2px solid var(--accent-line); padding-top: 0.65rem; display: inline-block; } }
.matrix-more { margin: 1.4rem 0 0; }
.matrix-fig { margin: 0; }
.matrix-t { width: 100%; border-collapse: separate; border-spacing: 6px; }
.matrix-cap {
  caption-side: top; text-align: left;
  font: 600 var(--step--1)/1.4 var(--mono); letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--ink-faint); padding-bottom: 0.75rem;
}
.matrix-t thead th {
  text-align: center; padding: 0 0 0.4rem;
  font: 600 var(--step--1)/1.3 var(--sans); color: var(--ink);
}
.matrix-m { display: block; }
.matrix-cur { display: block; font: 500 0.85em/1.4 var(--mono); color: var(--ink-faint); letter-spacing: 0.05em; }
.matrix-t tbody th {
  text-align: left; padding-right: 0.75rem; white-space: nowrap;
  font: 600 var(--step--1)/1.3 var(--sans); color: var(--ink);
}
.matrix-t tbody td { padding: 0; }
.matrix-c {
  display: grid; place-items: center; min-height: 3.6rem;
  border-radius: 10px; border: 1px solid var(--rule); background: var(--paper);
  font: 650 var(--step-0)/1 var(--mono); letter-spacing: -0.02em; color: var(--ink);
}
.matrix-c[data-state='ingen'] {
  color: var(--ink-ghost); border-style: dashed; background: transparent;
}
.matrix-c[data-state='sett'] {
  color: var(--accent); border-color: var(--accent-line); background: var(--accent-wash);
}
.matrix-legend { margin-top: 1rem; font-size: var(--step--1); color: var(--ink-faint); }

@media (prefers-reduced-motion: no-preference) {
  .js [data-enhance='matrix'] .matrix-c { opacity: 0; transform: scale(0.94); transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.2, 0.7, 0.3, 1); }
  .js [data-enhance='matrix'].is-in .matrix-c { opacity: 1; transform: none; }
}

@media (max-width: 40rem) {
  .matrix-t { border-spacing: 4px; }
  .matrix-c { min-height: 2.9rem; font-size: var(--step--1); }
  .matrix-t tbody th { font-size: 0.8rem; }
}

/* ═══ SECOND PASS, ALL FOUR FOUND BY LOOKING ═════════════════════════════════════════════════════════════ */

/* 1. THE SCREEN'S FOOTNOTE WAS A NARROW COLUMN UNDER A FULL-WIDTH PANEL, with a dead half-band under it.
 *    It is a caption: it belongs with the thing it captions, at its width, close to it. */
.shot-band { padding-bottom: clamp(2rem, 1rem + 3vw, 3.5rem); }
.shot-foot {
  margin: 1.1rem auto 0; max-width: none; text-align: center;
  font-size: var(--step-0); color: var(--ink-soft);
}
@media (min-width: 52rem) { .shot-foot { max-width: 62ch; } }
.shot-foot b { color: var(--ink); font-weight: 650; }

/* 2. THE TAB PANEL LEFT HALF THE BAND EMPTY. A strip across the top and a 54ch panel under it is a column of
 *    text with a row of pills over it — the width was doing nothing. At desk widths the strip goes down the
 *    left instead, which is what the band is actually shaped like. */
@media (min-width: 64rem) {
  .tabs-wrap { display: grid; grid-template-columns: 15rem minmax(0, 1fr); gap: clamp(2rem, 4vw, 3.5rem); align-items: start; }
  .tabs-strip { flex-direction: column; margin-bottom: 0; gap: 0.35rem; position: sticky; top: 6rem; }
  .tabs-tab { width: 100%; justify-content: flex-start; border-radius: 10px; padding: 0.8rem 1rem; }
  .tabs-panel { max-width: 60ch; }
  .tabs-panel-h { font-size: var(--step-2); }
}

/* 3. THE EMPTY CELL IN THE MATRIX WAS ALMOST INVISIBLE, which is the opposite of its job: the whole caption
 *    under it points at that one cell. A dashed border on a white cell reads as a rendering artefact. */
.matrix-c[data-state='ingen'] {
  color: var(--ink-faint);
  border-style: dashed;
  border-color: color-mix(in oklab, var(--ink) 22%, var(--paper));
  background: repeating-linear-gradient(
    -45deg,
    transparent 0 6px,
    color-mix(in oklab, var(--ink) 4%, transparent) 6px 12px
  );
  font-size: var(--step-2);
}

/* 4. AND THE BANDS SAT TOO FAR APART for a page that is now carrying pictures. The rhythm was tuned when
 *    every band was a paragraph. */
:root { --band-y: clamp(3.25rem, 2rem + 4.6vw, 5.75rem); }

/* ═══ THE ORGANISATION TREE ══════════════════════════════════════════════════════════════════════════════
 *
 * Three rows of nodes with a connector drawn between them. Markup and borders only — no SVG file, no image —
 * so it follows the operator's colours and stays sharp at any size.
 *
 * THE CONNECTORS ARE PSEUDO-ELEMENTS ON THE LEVEL, not lines between specific pairs. A real genealogy is a
 * different picture for every shop, and a marketing page drawing one exact tree would be claiming a shape
 * nobody has. What this shows is the SHAPE of a network — widening downward — which is true of all of them. */
.tree-grid { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
@media (min-width: 64rem) { .tree-grid { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); } }
.tree-say { max-width: calc(var(--measure) + 2ch); }
.tree-label { margin-bottom: 1rem; }
@media (min-width: 62rem) { .tree-label { border-top: 2px solid var(--accent-line); padding-top: 0.65rem; display: inline-block; } }
.tree-more { margin: 1.4rem 0 0; }
.tree-fig { margin: 0; }

.tree-levels { display: grid; gap: 2.4rem; }
.tree-level {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0.6rem;
  position: relative;
}
/* The rail above each level below the first, and the stub that drops from the one above it. */
.tree-level[data-level='2']::before,
.tree-level[data-level='3']::before {
  content: '';
  position: absolute; left: 12%; right: 12%; top: -1.25rem; height: 1px;
  background: var(--rule);
}
.tree-level[data-level='2']::after,
.tree-level[data-level='3']::after {
  content: '';
  position: absolute; left: 50%; top: -2.4rem; width: 1px; height: 1.15rem;
  background: var(--rule);
}
.tree-node {
  display: grid; gap: 0.15rem; justify-items: center; text-align: center;
  padding: 0.6rem 0.85rem; border-radius: 10px;
  border: 1px solid var(--rule); background: var(--paper);
  position: relative;
}
/* Each node grows a short stem up to the rail, so the connection reads without drawing every edge. */
.tree-level[data-level='2'] .tree-node::before,
.tree-level[data-level='3'] .tree-node::before {
  content: '';
  position: absolute; left: 50%; top: -1.25rem; width: 1px; height: 1.25rem;
  background: var(--rule);
}
.tree-name { font: 600 var(--step--1)/1.3 var(--sans); color: var(--ink); white-space: nowrap; }
.tree-note { font: 500 0.78rem/1.3 var(--mono); color: var(--ink-faint); letter-spacing: 0.04em; }
.tree-node.is-mark {
  border-color: var(--accent); background: var(--accent-wash);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 12%, transparent);
}
.tree-node.is-mark .tree-name { color: var(--accent); }
.tree-node.is-mark .tree-note { color: var(--accent-ink); }
.tree-level[data-level='1'] .tree-node { padding-inline: 1.2rem; }
.tree-legend { margin-top: 1.6rem; font-size: var(--step--1); color: var(--ink-faint); text-align: center; }

@media (prefers-reduced-motion: no-preference) {
  .js [data-enhance='tree'] .tree-node { opacity: 0; transform: translateY(8px); transition: opacity 0.45s ease, transform 0.45s cubic-bezier(0.2, 0.7, 0.3, 1); }
  .js [data-enhance='tree'].is-in .tree-node { opacity: 1; transform: none; }
}
@media (max-width: 40rem) {
  .tree-node { padding: 0.45rem 0.6rem; }
  .tree-name { font-size: 0.78rem; }
  .tree-note { display: none; }
}

/* A SPLIT WITH NO IMAGE NOW USES `band-grid`, so its own max-width rule has to follow the same column. */
.band-grid > .split-say { max-width: calc(var(--measure) + 6ch); }

/* ═══ THE SIGNUP WIZARD ═══════════════════════════════════════════════════════════════════════════════════
 *
 * FOUR FIELDSETS, AND WITHOUT SCRIPT THEY ARE FOUR SECTIONS OF ONE LONG FORM. Nothing here hides a step:
 * the stepping rules are all under `.js .signup-form.is-stepped`, which only `theme.js` ever sets. A reader
 * with no script sees every field, fills them in, and presses one button — which is the whole form working.
 *
 * SO THE HIDING CANNOT BE IN THE PLAIN SELECTOR. Written as `.signup-step[hidden]` alone it would depend on
 * the script having run to un-hide the first one, and a page whose script failed to load would show a form
 * with nothing in it. `check.mjs` renders with no browser at all and would have said so — which is the
 * reason this stylesheet keeps that class-gated shape everywhere else too. */
.signup-head { max-width: var(--measure); margin-bottom: 2rem; }
.signup-form { display: grid; gap: 1.6rem; max-width: 42rem; }
.signup-step { margin: 0; padding: 0; border: 0; display: grid; gap: 1rem; }
.signup-legend {
  padding: 0; margin-bottom: 0.2rem; display: flex; align-items: center; gap: 0.6rem;
  font: 600 var(--step-0)/1.3 var(--sans); color: var(--ink);
}
.signup-num {
  display: grid; place-items: center; width: 1.6rem; height: 1.6rem; border-radius: 50%;
  background: var(--accent-wash); color: var(--accent-ink);
  font: 600 var(--step--1)/1 var(--mono);
}
.signup-row { margin: 0; display: grid; gap: 0.4rem; }
.signup-row label { font: 600 var(--step--1)/1.3 var(--mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); }
.signup-row input, .signup-row select, .signup-row textarea {
  font: var(--step-0)/1.5 var(--sans); color: var(--ink);
  background: var(--paper); border: 1px solid var(--rule); border-radius: 9px;
  padding: 0.7rem 0.85rem; width: 100%;
}
.signup-row input:focus, .signup-row select:focus, .signup-row textarea:focus {
  border-color: var(--accent); outline: none; box-shadow: 0 0 0 3px var(--accent-wash);
}
.signup-help { font-size: var(--step--1); color: var(--ink-faint); }
/* THE REFUSAL IS THE HOST'S SENTENCE AND IT SITS UNDER THE CONTROL IT IS ABOUT — `client-signup.ts` files
 * each one under a field name for exactly this, so «that address is taken» draws at the address box rather
 * than at the top of a twelve-field form. */
.signup-bad { font-size: var(--step--1); color: var(--accent-ink); font-weight: 600; }
.signup-err {
  margin: 0; padding: 0.8rem 1rem; border-radius: 9px;
  background: var(--accent-wash); color: var(--accent-ink); font-size: var(--step--1);
}
.signup-free { font-size: var(--step--1); color: var(--ink-faint); min-height: 1.2em; }
.signup-free[data-state='free'] { color: var(--accent-ink); font-weight: 600; }
.signup-act { margin: 0; display: flex; gap: 0.7rem; align-items: center; flex-wrap: wrap; }
.signup-count { font-size: var(--step--1); color: var(--ink-faint); font-variant-numeric: tabular-nums; }

.js .signup-form.is-stepped .signup-step { display: none; }
.js .signup-form.is-stepped .signup-step.is-on { display: grid; }

/* ── AND `hidden` HAS TO BEAT `.btn`, WHICH IT DOES NOT ON ITS OWN ───────────────────────────────────────
 *
 * MEASURED BY LOOKING, 25 September 2026. The wizard's script sets `back.hidden = true` on the first step
 * and `submit.hidden = true` on every step but the last, and the screenshot showed all three buttons on
 * step 1: «Tilbake», «Neste» and «Opprett butikk» in a row, on a step where two of them mean nothing.
 *
 * THE UA RULE IS `[hidden] { display: none }` AND `.btn` SETS `display`. Same origin, and `.btn` is a class
 * — so the author rule wins and the attribute does nothing at all. Nothing in the markup is wrong, the
 * script is doing what it says, and no assertion about either could see it: `button.hidden === true` is
 * true the whole time. Only the picture disagreed.
 *
 * SCOPED RATHER THAN GLOBAL `[hidden] { display: none !important }`, because that would quietly change
 * every other `hidden` on the site to settle one row's problem. */
.signup-act [hidden] { display: none; }

/* ═══ THE SIGNUP DIALOG ═══════════════════════════════════════════════════════════════════════════════════
 *
 * NATIVE `<dialog>`, SO CLOSED IS THE DEFAULT AND NOT SOMETHING THIS FILE HAS TO SAY. Without `open` the
 * element and everything in it are already off the render tree — no `display: none` to remember, and no
 * page this section renders on ever shows it before `theme.js` calls `showModal()`.
 *
 * NO NEW COLOUR. `--radius` and the same border/shadow the language panel already uses above — a second
 * floating panel is not a reason for a second idea about what a floating panel looks like. */
.signup-dialog {
  max-width: 40rem; width: min(40rem, 100% - 2rem); max-height: min(88vh, 100% - 2rem);
  margin: auto; padding: 1.6rem clamp(1.2rem, 2vw, 2rem) 1.8rem;
  border: 0; border-radius: var(--radius);
  background: var(--paper); color: var(--ink);
  box-shadow: 0 24px 60px -20px color-mix(in oklab, var(--ink) 34%, transparent);
  overflow: auto;
}
.signup-dialog::backdrop { background: color-mix(in oklab, var(--wash-deep) 55%, transparent); }
.signup-dialog-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  margin-bottom: 1.2rem;
}
.signup-dialog-head h2 { margin: 0; font: 650 var(--step-1)/1.25 var(--sans); }
.signup-dialog-close {
  display: grid; place-items: center; width: 2rem; height: 2rem; flex: none;
  background: none; border: 0; border-radius: 50%; color: var(--ink-faint);
  font-size: 1rem; line-height: 1; cursor: pointer;
}
.signup-dialog-close:hover { background: var(--wash); color: var(--ink); }
.signup-dialog .signup-form { max-width: none; }

/* ═══ THE PRICE LIST ══════════════════════════════════════════════════════════════════════════════════════
 *
 * AS MANY COLUMNS AS THERE ARE PLANS, WITHOUT THE STYLESHEET KNOWING HOW MANY. `auto-fit` with a `minmax`
 * floor: two plans are two wide cards, four are four narrower ones, and a box that prices a fifth needs no
 * edit here. A fixed three-column grid would have been right for exactly as long as there were three plans
 * — and the plans come from a table an operator writes, not from this file.
 *
 * AND THE FACTS ARE A `<dl>` RATHER THAN A TABLE. Six label/value pairs about ONE plan are a description
 * list; a table would claim a relationship between the columns that does not exist, because each card is
 * its own subject. The comparison section one screen over is the opposite case and is a real table. */
.pricing-grid { display: grid; gap: 1.2rem; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.pricing-card {
  display: flex; flex-direction: column; gap: 0.9rem;
  padding: 1.4rem 1.5rem; border: 1px solid var(--rule); border-radius: 14px; background: var(--paper);
}
.pricing-name { margin: 0; font: 620 var(--step-1)/1.2 var(--sans); color: var(--ink); }
.pricing-price { margin: 0; display: flex; align-items: baseline; gap: 0.4rem; flex-wrap: wrap; }
.pricing-sum { font: 700 var(--step-2)/1.1 var(--sans); color: var(--ink); font-variant-numeric: tabular-nums; }
.pricing-per { font-size: var(--step--1); color: var(--ink-faint); }
.pricing-trial { margin: 0; font-size: var(--step--1); color: var(--accent-ink); font-weight: 600; }
.pricing-has { margin: 0; padding: 0; list-style: none; display: grid; gap: 0.35rem; }
/* A MARKER DRAWN RATHER THAN TYPED — `house-style`'s own rule about `content:`, one repository over: never
 * type a glyph into a content rule. A bullet is one of the few Unicode has an honest character for, so this
 * is a shape instead, and the sweep for control bytes has nothing to find. */
.pricing-has li { position: relative; padding-left: 1.1rem; font-size: var(--step--1); color: var(--ink-soft); }
.pricing-has li::before {
  content: ''; position: absolute; left: 0; top: 0.45em;
  width: 0.4rem; height: 0.4rem; border-radius: 50%; background: var(--accent);
}
.pricing-facts { margin: 0; display: grid; gap: 0.3rem; border-top: 1px solid var(--rule-soft); padding-top: 0.9rem; }
.pricing-facts > div { display: flex; justify-content: space-between; gap: 1rem; align-items: baseline; }
.pricing-facts dt { font-size: var(--step--1); color: var(--ink-faint); }
.pricing-facts dd { margin: 0; font-size: var(--step--1); color: var(--ink); font-variant-numeric: tabular-nums; }
/* THE BUTTON SITS AT THE BOTTOM WHATEVER IS ABOVE IT. Plans include different numbers of packages, so the
 * cards are different heights and a button that followed the content would step down the row. */
.pricing-act { margin: auto 0 0; }
.pricing-shut { margin: auto 0 0; font-size: var(--step--1); color: var(--ink-faint); }
.pricing-none { margin: 0; color: var(--ink-faint); }
.pricing-foot { margin: 1.4rem 0 0; font-size: var(--step--1); color: var(--ink-faint); max-width: var(--measure); }
