/* ==========================================================================
   BRAND FACE. Schibsted Grotesk, variable, Latin subset, self-hosted.
   SIL Open Font License. 46KB, one file, no third-party request.

   ONE FAMILY for display and UI. A museum that has to frame ninety-five
   different visual languages should not also be performing typographically —
   a single well-made grotesque carries the institution and then gets out of
   the way. The expression comes from scale, spacing and composition instead.

   Alternatives render side by side at /lab/type.html.

   IT IS THE PRODUCT'S FACE AND NEVER A SPECIMEN'S. Swiss's `typeface-class`
   names a CLASS and lets the machine supply a member; if this face satisfied
   "grotesque" the specimen system would start lying about what the corpus
   licenses. Specimens use their own stacks in specimen.css, and the two Annie
   routes use their own in annies.css. Tests assert both separations.
   ========================================================================== */
@font-face {
  /* APPARATUS. Fragment Mono is built on a grotesque skeleton, which is why it
     was chosen from eight candidates: it is the closest relative to AI Sans in
     the set, so the apparatus reads as the same institution speaking in a
     different register rather than as a second brand.

     It is a single-weight face. `font-synthesis: none` below means the browser
     will not fake a bold, so any rule asking mono for weight is a rule to fix
     rather than a weight to invent. */
  font-family: "Fragment Mono";
  src: url("fonts/fragment-mono.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "AI Sans";
  src: url("fonts/schibsted.woff2") format("woff2-variations");
  font-weight: 400 900;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==========================================================================
   AESTHETIC INTELLIGENCE — PRODUCT ALPHA
   The apparatus: a wide text column, a narrow marginal column, one hairline
   between them. The margin records how we know. Where the margin is empty,
   that emptiness is the finding.

   The shell carries no colour of its own beyond ink, paper and one editorial
   red. Every other colour on this site belongs to a visual language.
   ========================================================================== */

:root {
  /* ======================================================================
     AESTHETIC INTELLIGENCE — HOUSE STYLE
     The museum, not the art. This palette exists to FRAME radically
     different visual worlds, so it is cool, architectural and quiet, with
     two accents that make the institution recognisable when no specimen is
     on screen.

     White means white. Not ivory, not parchment, not cream — the previous
     house style drifted warm and editorial, which put the product in
     competition with Swiss instead of containing it.
     ====================================================================== */

  /* architecture ------------------------------------------------------- */
  --paper:      #FFFFFF;   /* white-white */
  --paper-deep: #F2F5F8;   /* cool field, faint blue cast */
  --paper-3:    #E6EBF1;   /* edges, wells */
  --ink:        #0B0D12;   /* cool near-black */
  --ink-2:      #454C57;   /*  8.9:1 on white */
  --ink-3:      #6B7280;   /*  5.0:1 on white */
  --rule:       #DFE4EA;
  --rule-firm:  #B6BEC9;
  --plate:      #0B0D12;   /* the structural slab */
  --plate-ink:  #FFFFFF;
  --plate-ink-2:#A8B0BD;
  --plate-accent:#B8A0FF;

  /* brand -------------------------------------------------------------- */
  /* Violet, because the jewel register is the honest read of the taste
     brief and because it is not the indigo every AI product reaches for.
     Acid is the interruption: rare, deliberate, never decorative. */
  --brand:      #5B21D6;   /*  8.6:1 on white */
  --brand-lift: #7C3AED;
  --brand-wash: #F4F0FF;
  --brand-edge: #DDD3FF;
  /* ON THE SLAB. A lighter tint of the brand violet rather than an acid
     green: acid is the single most over-used colour in AI product design at
     the moment, and it made the footer read as a developer tool. This is the
     same brand hue, lifted to 8.8:1 on the dark slab. The product has one
     accent, not two. */
  --on-plate-strong: #B8A0FF;
  --accent:     var(--brand);      /* editorial mark, kept for continuity */
  --accent-soft:var(--brand-wash);

  /* type ---------------------------------------------------------------
     Display is a grotesque, not a traditional serif: the serif made the
     product read as an academic journal. Roles are explicit so a specimen
     can take over without the shell wobbling. */
  /* --display and --ui are THE SAME TYPEFACE. Two tokens exist because their
     fallback stacks differ: display falls back to grotesques that hold up at
     large sizes, UI falls back to the system face so small interface text
     matches the platform. One family, two fallback intentions — not two
     typefaces, and not a licence to set them differently. */
  /* ======================================================================
     THREE ROLES. Not three typefaces — three jobs.

       --display   AIQ speaks          AI Sans, heavy, tight, large
       --ui        AIQ explains        AI Sans, text weights
       --mono      AIQ shows its work  Fragment Mono

     Display and UI are ONE typeface with two fallback intentions: display
     falls back to grotesques that hold at size, UI to the system face so
     small interface text matches the platform. One family, two stacks — not
     a licence to set them differently.

     THE SERIF IS GONE. It was --serif, and across twelve pages it reached
     thirteen elements: three links, four table headers, six device names.
     Three unrelated things, no shared semantic purpose, and — because it was
     never a loaded font — Iowan on a Mac and Georgia on Windows. A brand
     element that changes per visitor is not a brand element. Its thirteen
     rules have been retargeted by information role, not replaced.

     SYSTEM STACKS MAY BE APPROPRIATE FOR THE ARTWORK; THEY ARE NOT
     APPROPRIATE FOR THE MUSEUM. The ten specimen type classes deliberately
     resolve to system stacks: there, variety is the point and the exact face
     is not a claim. The institutional shell gets controlled typography, so
     that aesthetic-specific type visibly reads as the subject matter.
     ====================================================================== */
  --display: "AI Sans", "Helvetica Neue", Helvetica, Arial, "Liberation Sans", sans-serif;
  --ui:      "AI Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono:    "Fragment Mono", ui-monospace, "SF Mono", Menlo, "DejaVu Sans Mono", monospace;

  --t-xs:   0.78125rem;
  --t-sm:   0.875rem;
  --t-base: 1rem;
  --t-md:   1.25rem;
  --t-lg:   1.75rem;
  --t-xl:   2.5rem;
  --t-2xl:  3.75rem;
  /* DISPLAY SCALE — three steps, separated enough that size reads as rank.
     There was one display size and two unreconciled clamps: the homepage hero
     resolved to 71.7px and every interior h1 to 102.4px, so the word "Explore"
     was set 43% larger than "Every visual world has a language." Wayfinding
     outranked the thesis. Rank now runs downward from the homepage.
       xl  the product's own statement — homepage hero only
       lg  a page announcing itself — every interior h1
       md  a section inside a page */
  /* ======================================================================
     THE SCALE. Every step exists because it carries a different kind of
     information, not because a section wanted to look different.
     ====================================================================== */
  /* ONE LINE-HEIGHT PER JOB. The audit found AI Sans 16/400 set at five
     different line-heights and 14/400 at another five — no page had a reason,
     it was accumulation. Prose reads at --lh-body; anything set as a label,
     count or single line uses --lh-tight; apparatus gets its own slightly
     looser figure because monospace needs the room. */
  --lh-body: 1.58;
  --lh-tight: 1.3;
  --lh-meta: 1.55;

  /* SECTION HEADING. One clamp for every section title on a page. There were
     two — 4vw/3rem and 4.5vw/3.25rem — with the same floor and the same
     tracking, 4px apart at desktop and IDENTICAL below 700px. Seven headings
     were split across them and the split was never visible. `.wallsec__h`
     stays outside this on purpose: the wall is the page's centrepiece and its
     larger size is a compositional decision, not drift. */
  --t-section: clamp(1.75rem, 4vw, 3rem);

  --t-display-xl: clamp(2.75rem, 7vw, 5.5rem);
  --t-display-lg: clamp(2.25rem, 4.6vw, 3.5rem);
  --t-display-md: clamp(1.75rem, 3.2vw, 2.75rem);
  /* Retained: other rules still reference it and they move in the system pass. */
  --t-display: var(--t-display-lg);

  /* space --------------------------------------------------------------- */
  --s1: 0.25rem; --s2: 0.5rem;  --s3: 0.75rem; --s4: 1rem;
  --s5: 1.5rem;  --s6: 2rem;    --s7: 3rem;    --s8: 4rem;
  --s9: 6rem;    --s10: 8rem;

  --measure: 38rem;
  --margin-col: 15rem;
  --page: 80rem;

  /* shape: hard edges. The museum has corners. */
  --r: 0px;
  --r-soft: 3px;
  --lift: 0 1px 0 var(--rule), 0 12px 32px -18px rgba(11,13,18,.28);
}

/* NO DARK INVERSION FOR THE PRODUCT.
   The site used to flip the whole palette under prefers-color-scheme: dark,
   which turned the museum into a dark grotesque documentation theme — the
   exact register the brand brief ruled out. White means white: the
   architecture is a white-walled room so the collection can perform in it.

   This is a deliberate brand decision rather than an oversight, and it is
   scoped: the two Annie routes are night-native by design and keep their own
   dark palette in annies.css, and a record specimen may still assert a dark
   ground where its corpus licenses one. Contrast is measured on the light
   ground in both cases, and prefers-reduced-motion is still honoured. */
:root { color-scheme: light; }

/* ==========================================================================
   THE TWO PALETTES.

   This reverses a decision written into this file: "NO DARK INVERSION FOR THE
   PRODUCT … White means white." That decision was right when the whole product
   was a white-walled room. It stopped being right when Version C won and the
   collection hall went dark — from that point the product HAD two grounds and
   only one of them was chosen by the reader.

   So the inversion comes back, under three constraints that the earlier
   attempt did not have:

   1. IT IS A CHOICE, NOT A MEDIA QUERY. `prefers-color-scheme` is the
      default, not the rule, and the reader's choice persists.
   2. IT DIMS THE GALLERY, NEVER THE PAINTINGS. Specimens assert grounds
      derived from the corpus — Swiss light, Manga off-white, Terminal
      phosphor-on-black — and an exhibition room is art direction. Neither
      inverts. A museum turning its lights down does not repaint the work.
   3. IT IS ONE SET OF TOKENS. No component knows which palette is active;
      every surface already resolves from these, which is how Version C
      restyled the masthead, the menus and the footer without touching them.
   ========================================================================== */

/* Company chrome stays black on a room with a fixed ground (founder,
   2026-09-26): the rail and the action cluster take the night tokens even
   where the page itself does not. */
.night, [data-fixed-theme]:not(.chrome-day) .rail, [data-fixed-theme]:not(.chrome-day) .ia-act {
  --paper:      #0B0D12;   /* the house plate, promoted to ground */
  --paper-deep: #10131B;
  --paper-3:    #161A24;
  --ink:        #F2F3F7;
  --ink-2:      #A8B0BD;   /* the plate's own secondary */
  --ink-3:      #78808E;
  --rule:       #222735;
  --rule-firm:  #39415A;
  --brand:      #B8A0FF;   /* the same violet, at its on-plate strength */
  --brand-lift: #CBB8FF;
  --brand-wash: #1A1530;
  --brand-edge: #3A2E66;
  --accent:     var(--brand);
  --accent-soft:var(--brand-wash);
  --lift: 0 1px 0 var(--rule), 0 14px 36px -16px rgba(0, 0, 0, 0.85);
}

.night, [data-fixed-theme]:not(.chrome-day) .rail, [data-fixed-theme]:not(.chrome-day) .ia-act { color-scheme: dark; }
/* A room's own sheet may restyle every link on its page (".sw-page a
   { color: inherit }"); the rail keeps the house colours regardless. Loaded
   before rail.css, so rail.css still wins its own ties (aria-current). */
[data-fixed-theme] .rail { color: var(--ink); background: var(--paper); }
[data-fixed-theme] .rail a { color: var(--ink-2); }
/* The wordmark artwork is near-black ink; in the dark it flips to white.
   Same file, no second asset. */
.night .masthead__mark img, .night .rail__mark img, [data-fixed-theme]:not(.chrome-day) .rail__mark img { filter: invert(1); }
.night .foot__mark img { filter: none; }

/* ART DIRECTION DOES NOT HAVE A DARK MODE. An exhibition room and the two
   Annie routes are the work, not the wall: they declare their own ground and
   opt out of the switch entirely. */
/* On a fixed-ground page the switch sets the house chrome (rail, action
   cluster) only; the page keeps its own ground (2026-09-26). */


*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: var(--t-base)/var(--lh-body) var(--ui);
  /* No faked weights or italics: Fragment Mono ships one weight, and a
     synthesised bold would be the browser inventing a typeface. */
  font-synthesis: none;
  text-rendering: optimizeLegibility;
}

/* long aesthetic names must not push the page sideways. No auto-hyphenation:
   it breaks words in the lead and reads as a justified column, which this is not. */
h1, h2, h3, h4, .entry__name, .lead { overflow-wrap: break-word; }

a { color: inherit; text-underline-offset: 0.18em; text-decoration-thickness: from-font; }
a:hover { text-decoration-color: var(--accent); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.skip {
  position: absolute; top: 0; left: 0; z-index: 99;
  width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  background: var(--ink); color: var(--paper); padding: var(--s3) var(--s4); font: var(--t-sm)/1 var(--ui);
}
.skip:focus {
  width: auto; height: auto; overflow: visible; clip-path: none;
}

/* ---------------------------------------------------------------- shell -- */

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

.masthead {
  border-bottom: 1px solid var(--rule);
  padding: var(--s4) 0;
  margin-bottom: var(--s7);
}
/* CENTRE, NOT BASELINE. Baseline was right when the wordmark was text: two
   pieces of type sitting on one line. The mark is now an image, and an
   image's baseline is its bottom edge — so the artwork was being lifted until
   its bottom sat on the nav's baseline, opening a gap under the wordmark and
   dropping the links relative to it. Optical centring is what a mark and a
   row of links actually want. */
.masthead__row { display: flex; flex-wrap: wrap; gap: var(--s4) var(--s6); align-items: center; }
.masthead__mark {
  /* Type is set in the wordmark block below, which overrode the family,
     weight and tracking from here. What is still live is the hit area:
     WCAG 2.5.8, navigation links are not inline prose, so they carry a real
     target rather than relying on the inline exception. */
  text-decoration: none; white-space: nowrap;
  display: inline-block; padding: var(--s2) 0; min-height: 1.5rem;
}
.masthead__mark b { font-weight: 600; }
.masthead nav { margin-left: auto; display: flex; gap: var(--s5); flex-wrap: wrap; }
.masthead nav a {
  /* Font and colour are set in the type-roles block below, which overrode both
     from here; what is left is the hit area. WCAG 2.5.8: navigation links are
     not inline prose, so they carry a real target rather than relying on the
     inline exception. */
  text-decoration: none;
  display: inline-block; padding: var(--s2) 0; min-height: 1.5rem;
}
.masthead nav a:hover { color: var(--ink); }
.masthead nav a[aria-current="page"] { color: var(--accent); }

/* WAS `footer.foot`, WHICH IS WHY THE FOOTER STAYED MONO.
   The slab block further down sets the footer in the UI face and carries a
   comment saying "the old footer was set wholesale in mono, which is why it
   read as terminal output". That rule is `.foot`, specificity (0,1,0). This
   one was `footer.foot`, (0,1,1), so it won on every page regardless of
   order and the intended fix never took effect: 17 of the footer's 21 text
   elements were still monospace while the rest of the site was not.

   Reduced to the same specificity as the rule that supersedes it, and the
   type declarations removed, leaving only what the slab block does not set. */
.foot {
  margin-top: var(--s10); border-top: 1px solid var(--rule);
  padding: var(--s5) 0 var(--s8);
}
/* WAS `.foot p`, (0,1,1), which silently beat every single-class rule that
   tried to space a specific footer paragraph — `.foot__alpha` asked for a
   32px top margin and got 0, which is why the identity column read as one
   crowded block. Reduced to a class so the rules below can actually do their
   job. The stack owns its own rhythm now; see .foot__identity. */
.foot .wrap p { max-width: 42rem; }

/* ------------------------------------------------------- the apparatus --- */

.leaf {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--margin-col);
  gap: 0 var(--s7);
  align-items: start;
  padding: var(--s6) 0;
  border-top: 1px solid var(--rule);
}
.leaf:first-of-type { border-top: 0; }
.leaf__body > :first-child { margin-top: 0; }
/* CORPUS PROSE CONTAINS SCHEMA PATHS. Open questions quotes the research's own
   notes, which carry tokens like `works/captures/regions/collections/sources`
   and `implementationConstraints.requires.` — unbreakable runs of 35-40
   characters that pushed the page sideways by 9px below 336px. `break-word`
   rather than `anywhere`: it only breaks a word that would otherwise overflow,
   so ordinary prose is unaffected. This is a property of the content, not of
   this page, so it belongs on the body column everywhere. */
.leaf__body { min-width: 0; max-width: var(--measure); overflow-wrap: break-word; }
.leaf__body--wide { max-width: none; }

.margin {
  min-width: 0;
  border-left: 1px solid var(--rule);
  padding-left: var(--s4);
  font: var(--t-xs)/var(--lh-meta) var(--mono);
  color: var(--ink-2);
}
.margin > * + * { margin-top: var(--s4); }
/* Apparatus labels are not document structure - they title two or three lines,
   not a section - so they are paragraphs. Using headings here put an h3
   immediately after the page h1 on every route. */
.margin__head {
  font: var(--t-xs)/var(--lh-meta) var(--mono); font-weight: 400;
  color: var(--ink-3); margin: 0 0 var(--s1);
}
.margin p { margin: 0; }
/* An apparatus with more than one label ran its blocks together: every child
   had zero margin, so a second label sat flush against the paragraph above it.
   Spacing belongs to the stack, not to each block — so it is applied between
   siblings rather than by nudging individual elements. */
.margin > p + p { margin-block-start: var(--s2); }
.margin > * + .margin__head { margin-block-start: var(--s5); }
.margin dl { margin: 0 0 var(--s2); display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr); gap: var(--s1) var(--s3); }
.margin dt { color: var(--ink-3); }
.margin dd { margin: 0; }
.margin ul { margin: 0; padding-left: 1.1em; }
.margin--empty { color: var(--ink-3); }

/* the empty margin says so, once, quietly */
.margin__silence { font-style: normal; color: var(--ink-3); }

/* --------------------------------------------------------- typography ---- */

h1, h2, h3, h4 { font-weight: 600; letter-spacing: -0.014em; }
h2 { font-size: var(--t-lg); line-height: var(--lh-tight); margin: 0 0 var(--s4); }
h3 { font-size: var(--t-md); line-height: var(--lh-tight); margin: var(--s6) 0 var(--s3); }
h4 { font-size: var(--t-base); margin: var(--s5) 0 var(--s2); }
p { margin: 0 0 var(--s4); }
p:last-child { margin-bottom: 0; }

.display {
  /* line-height, letter-spacing and font-weight used to be set here as well as
     in the type-roles block below, which won all three. Only the properties
     that are actually live remain. */
  font-size: var(--t-display);
  margin: 0 0 var(--s5);
  /* A measure in the heading's own characters, so it scales with the type.
     See the note at the foot of this file on why the column must not cap it. */
  max-width: 16ch;
}
/* .lead is defined once, further down with the rest of the type roles. It
   used to be declared here too — same four properties, different values —
   and this copy lost all four, so editing it did nothing. */
/* .sub: only its line-height was still live here; family, size and colour
   were all overridden by the type-roles block below. Moved there whole. */
.quiet { color: var(--ink-2); }

.label {
  font: var(--t-xs)/1 var(--ui);
  color: var(--ink-3);
  display: block; margin-bottom: var(--s3);
}

/* the bracket: everything Aesthetary supplied rather than found */
.ours { color: var(--accent); }
.ours::before { content: "["; }
.ours::after  { content: "]"; }
.bracket { font-family: var(--mono); font-size: 0.92em; color: var(--accent); }

/* ------------------------------------------------------------- records --- */

.record { margin: 0 0 var(--s5); }
.record:last-child { margin-bottom: 0; }
.record__rule { font-size: var(--t-base); line-height: var(--lh-body); margin: 0; }
.record__basis {
  font: var(--t-xs)/var(--lh-meta) var(--mono);
  color: var(--ink-3); margin: var(--s2) 0 0;
  display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3);
}
.record__basis .ours { color: var(--accent); }
.record__note { font: italic var(--t-sm)/var(--lh-body) var(--ui); color: var(--ink-2); margin: var(--s2) 0 0; }

.scoped {
  border-left: 2px solid var(--accent);
  padding-left: var(--s4); margin: var(--s3) 0 0;
}
.scoped__head { font: var(--t-xs)/var(--lh-meta) var(--mono); color: var(--accent); margin: 0 0 var(--s2); }
.scoped p { font-size: var(--t-sm); line-height: var(--lh-body); color: var(--ink-2); margin: 0; }

/* ------------------------------------------------------------- catalogue - */

.entries { margin: 0; padding: 0; list-style: none; }
.entry {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--margin-col);
  gap: 0 var(--s7);
  padding: var(--s5) 0;
  border-top: 1px solid var(--rule);
}
.entry__name {
  font: 650 var(--t-lg)/1.12 var(--display);
  letter-spacing: -0.02em; margin: 0 0 var(--s2);
}
.entry__name a { text-decoration: none; }
.entry__name a:hover { text-decoration: underline; text-decoration-color: var(--accent); }
.entry__line { margin: 0; max-width: 40rem; color: var(--ink-2); }
.entry__line--legacy { color: var(--ink-3); }
.entry__meta { font: var(--t-xs)/var(--lh-meta) var(--mono); color: var(--ink-3); margin: var(--s3) 0 0; }

/* depth is shown by what the margin holds, never by a badge or a number */
.holdings { font: var(--t-xs)/var(--lh-body) var(--mono); color: var(--ink-2); margin: 0; }
.holdings span { white-space: nowrap; }
.holdings { overflow-wrap: break-word; }
.holdings span + span::before { content: " · "; color: var(--rule-firm); }
.holdings--none { color: var(--ink-3); font-style: normal; }

.entry[data-depth="researched"] .entry__name { font-size: var(--t-xl); }
.entry[data-depth="catalogued"] .entry__name { font-size: var(--t-md); font-weight: 400; }

/* ------------------------------------------------------------ dark plate - */

.plate {
  background: var(--plate); color: var(--plate-ink);
  margin: var(--s9) 0;
  padding: var(--s8) 0;
}
.plate h2 { color: var(--plate-ink); }
.plate a { color: var(--plate-ink); }
.bases { display: grid; gap: var(--s5); grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); margin: var(--s6) 0 0; }
.basis__name { font: var(--t-xs)/var(--lh-meta) var(--mono); color: var(--plate-ink-2); margin: 0 0 var(--s2); }
.basis__label { font: 650 var(--t-md)/var(--lh-tight) var(--display); margin: 0 0 var(--s2); }
.basis__answer { font-size: var(--t-sm); line-height: var(--lh-body); color: var(--plate-ink-2); margin: 0; max-width: 24rem; }
.basis--ours .basis__label { color: var(--plate-accent); }
/* the rubrication mark follows the plate rather than the page */
.plate .ours { color: var(--plate-accent); }
.plate__standfirst { font-size: var(--t-md); line-height: var(--lh-body); color: var(--plate-ink); margin: 0; max-width: 42rem; }

/* ------------------------------------------------------- reserved plate -- */

.reserved {
  border: 1px solid var(--rule); border-left-width: 2px; border-left-color: var(--rule-firm);
  padding: var(--s5) var(--s5) var(--s4);
  margin: 0 0 var(--s6);
  min-height: 7rem;
  display: flex; flex-direction: column; justify-content: flex-end;
}
.reserved figcaption { font: var(--t-xs)/var(--lh-meta) var(--mono); color: var(--ink-3); margin: 0; }
.reserved b { font-weight: 400; color: var(--ink-2); }

/* ------------------------------------------------------------- listings -- */

.stack { list-style: none; margin: 0; padding: 0; }
.stack li + li { margin-top: var(--s4); }
.stack--tight li + li { margin-top: var(--s2); }

.deflist { margin: 0; display: grid; grid-template-columns: minmax(0, max-content) minmax(0, 1fr); gap: var(--s2) var(--s5); align-items: baseline; }
.deflist dt { font: var(--t-xs)/var(--lh-body) var(--ui); color: var(--ink-3); }
.deflist dd { margin: 0; }

.slot { margin: 0 0 var(--s6); }
.slot__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s2) var(--s4); margin: 0 0 var(--s2); }
.slot__name { font: 650 var(--t-md)/var(--lh-tight) var(--display); margin: 0; }
.device__name { font: 650 var(--t-base)/var(--lh-tight) var(--display); margin: var(--s5) 0 var(--s2); }
.slot__posture { font: var(--t-xs)/var(--lh-meta) var(--mono); }
.slot__posture[data-tone="specified"] { color: var(--ink); }
.slot__posture[data-tone="free"]      { color: var(--ink-2); }
.slot__posture[data-tone="excluded"]  { color: var(--accent); }
.slot__posture[data-tone="open"]      { color: var(--ink-3); }
.slot__posture[data-tone="outside"]   { color: var(--ink-3); }
.slot__sentence { color: var(--ink-2); margin: 0 0 var(--s3); }
.slot__rationale { font: italic var(--t-sm)/var(--lh-body) var(--ui); color: var(--ink-2); margin: 0 0 var(--s3); }

.unlisted { font: var(--t-sm)/var(--lh-body) var(--mono); color: var(--ink-3); margin: 0; }
.unlisted b { color: var(--ink-2); font-weight: 400; }

/* ------------------------------------------------------------ build view - */

.build { border-top: 2px solid var(--ink); padding-top: var(--s5); }
.bucket { margin: 0 0 var(--s7); }
.bucket__head { display: flex; align-items: baseline; gap: var(--s3); margin: 0 0 var(--s2); }
.bucket__name { font: 650 var(--t-md)/var(--lh-tight) var(--display); margin: 0; }
.bucket__count { font: var(--t-xs)/1 var(--ui); color: var(--ink-3); }
.bucket__gloss { font: var(--t-sm)/var(--lh-body) var(--ui); color: var(--ink-2); margin: 0 0 var(--s4); max-width: 34rem; }
.bucket--unresolved .bucket__name { color: var(--accent); }
.item { margin: 0 0 var(--s4); }
.item__text { margin: 0; }
.item__source { font: var(--t-xs)/var(--lh-meta) var(--mono); color: var(--ink-3); margin: var(--s1) 0 0; }
.item__detail { font: italic var(--t-sm)/var(--lh-body) var(--ui); color: var(--ink-2); margin: var(--s1) 0 0; }

.withheld { border-left: 2px solid var(--accent); padding: var(--s3) 0 var(--s3) var(--s4); margin: 0 0 var(--s5); }
.withheld h3 { margin: 0 0 var(--s2); font-size: var(--t-base); }
.withheld code { font: var(--t-sm)/var(--lh-meta) var(--mono); background: var(--accent-soft); padding: 0.1em 0.35em; }
.withheld p { font-size: var(--t-sm); line-height: var(--lh-body); color: var(--ink-2); margin: var(--s2) 0 0; }

/* -------------------------------------------------------------- compare -- */

.matrix-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 0 0 var(--s6); }
table.matrix { border-collapse: collapse; width: 100%; min-width: 46rem; font-size: var(--t-sm); }
table.matrix caption { text-align: left; font: var(--t-sm)/var(--lh-body) var(--ui); color: var(--ink-2); padding: 0 0 var(--s4); max-width: 46rem; }
.matrix th, .matrix td { text-align: left; vertical-align: top; padding: var(--s3) var(--s4) var(--s3) 0; border-top: 1px solid var(--rule); }
.matrix thead th { border-top: 0; border-bottom: 1px solid var(--rule-firm); font: 600 var(--t-sm)/var(--lh-tight) var(--ui); position: sticky; top: 0; background: var(--paper); z-index: 2; }
.matrix thead th span { display: block; font: var(--t-xs)/var(--lh-tight) var(--ui); color: var(--ink-3); font-weight: 400; margin-top: var(--s1); }
.matrix tbody th { font: var(--t-sm)/var(--lh-meta) var(--mono); font-weight: 400; color: var(--ink-2); width: 12rem; padding-right: var(--s5); }
.matrix tbody th span { display: block; color: var(--ink-3); font-size: var(--t-xs); margin-top: var(--s1); }
.matrix tr[data-comparable="false"] td { color: var(--ink-3); }
.matrix td[data-kind="unknown"], .matrix td[data-kind="undocumented"] { color: var(--ink-3); font-family: var(--mono); font-size: var(--t-xs); }
.matrix tr[data-differs="true"] th { color: var(--ink); }
.matrix .rowgroup th { border-top: 2px solid var(--ink); font: 600 var(--t-sm)/var(--lh-tight) var(--ui); color: var(--ink); padding-top: var(--s5); }
.matrix .nocompare td { font: var(--t-xs)/var(--lh-meta) var(--mono); color: var(--ink-3); border-top: 0; padding-top: 0; }

/* ---------------------------------------------------------- disclosure --- */

details.disclose { border-top: 1px solid var(--rule); padding: var(--s4) 0 0; margin: var(--s5) 0 0; }
details.disclose > summary {
  cursor: pointer; list-style: none; padding: var(--s2) 0; min-height: 1.5rem;
  font: var(--t-sm)/var(--lh-tight) var(--ui); color: var(--ink-2);
  display: flex; gap: var(--s2); align-items: baseline;
}
details.disclose > summary::-webkit-details-marker { display: none; }
details.disclose > summary::before { content: "+"; color: var(--accent); }
details.disclose[open] > summary::before { content: "–"; }
details.disclose > summary:hover { color: var(--ink); }
.disclose__body { padding: var(--s4) 0 var(--s2); }

/* ------------------------------------------------------------- controls -- */

.controls { border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); padding: var(--s4) 0; margin: 0 0 var(--s2); }
.controls__row { display: flex; flex-wrap: wrap; gap: var(--s4) var(--s6); align-items: flex-start; }
.field { display: flex; flex-direction: column; gap: var(--s2); }
.field > label { font: var(--t-xs)/1 var(--ui); color: var(--ink-3); }
.field__note {
  display: block; max-width: 15rem;
  font: var(--t-xs)/var(--lh-body) var(--ui); color: var(--ink-3);
}
.field input[type="search"], .field select {
  font: var(--t-sm)/var(--lh-tight) var(--ui); color: var(--ink);
  background: transparent; border: 0; border-bottom: 1px solid var(--rule-firm);
  padding: var(--s2) 0; min-width: 13rem; min-height: 1.75rem;
}
.field input[type="search"]:focus, .field select:focus { border-bottom-color: var(--accent); }
.result-count { font: var(--t-xs)/1 var(--ui); color: var(--ink-3); margin: var(--s4) 0 0; }

/* -------------------------------------------------------------- utility -- */

.hr { border: 0; border-top: 1px solid var(--rule); margin: var(--s7) 0; }
.hr--firm { border-top-color: var(--ink); }
.pair { display: grid; gap: var(--s6); grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.note-block { border-left: 2px solid var(--rule-firm); padding-left: var(--s4); color: var(--ink-2); font-size: var(--t-sm); line-height: var(--lh-body); }

/* ------------------------------------------------------------ responsive - */

@media (max-width: 62rem) {
  :root { --margin-col: 12rem; }
  .leaf, .entry { gap: 0 var(--s5); }
}

@media (max-width: 48rem) {
  :root { --t-base: 1rem; --measure: none; }
  .wrap { padding: 0 var(--s4); }

  /* the apparatus stays WITH its section rather than collapsing to the end of
     the page: each leaf stacks, so text and margin remain a pair */
  .leaf, .entry { grid-template-columns: minmax(0, 1fr); gap: var(--s4) 0; }
  .margin { border-left: 2px solid var(--rule); padding-left: var(--s3); }
  .entry[data-depth="researched"] .entry__name { font-size: var(--t-lg); }

  .display { max-width: none; letter-spacing: -0.028em; }
  .plate { padding: var(--s7) 0; }

  .deflist { grid-template-columns: minmax(0, 1fr); gap: var(--s1) 0; }
  .deflist dd { margin-bottom: var(--s3); }

  .matrix tbody th { width: 9rem; }
  .masthead__row { gap: var(--s3); }
  .masthead nav { margin-left: 0; width: 100%; gap: var(--s4); }
}

@media (max-width: 22rem) {
  .wrap { padding: 0 var(--s3); }
  .controls__row { gap: var(--s4); }
}

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

@media print {
  .masthead nav, .controls { display: none; }
  body { background: white; color: black; }
  .leaf, .entry { break-inside: avoid; }
}

/* --------------------------------------------------------- visual evidence */

.backing { color: var(--accent); }
.backing[data-supported="false"] { color: var(--ink-3); }
.record__backing { font: var(--t-xs)/var(--lh-meta) var(--mono); color: var(--ink-3); margin: var(--s1) 0 0; }

.work { border-top: 1px solid var(--rule); padding: var(--s5) 0 0; margin: 0 0 var(--s5); }
.work__title { font: 650 var(--t-md)/var(--lh-tight) var(--display); margin: 0 0 var(--s1); }
.work__meta { font: var(--t-sm)/var(--lh-meta) var(--mono); color: var(--ink-2); margin: 0 0 var(--s2); }
.work__rights { font: var(--t-xs)/var(--lh-meta) var(--mono); margin: 0 0 var(--s3); }
.work__rights[data-decision="display_image"] { color: var(--ink-2); }
.work__rights[data-decision="link_externally"],
.work__rights[data-decision="display_thumbnail"],
.work__rights[data-decision="metadata_only"] { color: var(--accent); }

/* ==========================================================================
   IA PASS — planned areas, and the four layers.

   One rule governs everything here: the interface distinguishes what works
   from what does not, in the shell's own register, without mock data. A
   planned area gets a marker and a page that explains itself. It never gets
   a count, an empty state dressed as a populated one, or a disabled control.
   ========================================================================== */

/* The nav marker. Mono, because it is the product talking about itself. */
.masthead nav a[data-state] { color: var(--ink-3); }
.nav__state, .nav__planned {
  font: 500 0.6875rem/1 var(--ui);
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-inline-start: 0.35em;
  vertical-align: 0.1em;
}
@media (max-width: 34rem) { .nav__state, .nav__planned { display: none; } }

/* The status line at the head of a scaffold route. */
.status {
  font: var(--t-xs)/var(--lh-tight) var(--ui);
  letter-spacing: 0.04em;
  margin-block: 0 var(--s4);
  padding-block-end: var(--s3);
  border-block-end: 1px solid var(--rule);
}
.status--planned { color: var(--accent); }

/* The four layers on the homepage. A list, not four cards: three of them are
   not built, and cards would give them the same weight as the one that is. */
.loop { list-style: none; counter-reset: loop; margin: var(--s5) 0 var(--s5); padding: 0; }
.loop__step {
  display: grid; grid-template-columns: 2rem minmax(0, 1fr) auto; gap: var(--s1) var(--s3);
  padding-block: var(--s4); border-block-start: 1px solid var(--rule); counter-increment: loop;
}
.loop__step::before {
  content: counter(loop); font: var(--t-xs)/1.8 var(--ui); color: var(--ink-3); grid-row: 1 / span 2;
}
.loop__name { font-size: var(--t-md); text-decoration-thickness: 1px; }
.loop__state { font: var(--t-xs)/1.8 var(--ui); color: var(--ink-3); letter-spacing: 0.04em; text-align: right; }
.loop__step[data-state="working"] .loop__state { color: var(--accent); }
.loop__step[data-state="not-built"] .loop__name { color: var(--ink-2); }
.loop__step p { grid-column: 2 / -1; margin: 0; color: var(--ink-2); max-width: var(--measure); }
@media (max-width: 34rem) {
  .loop__step { grid-template-columns: 1.5rem minmax(0, 1fr); }
  .loop__state { grid-column: 2; text-align: left; }
  .loop__step p { grid-column: 2; }
}

.layers { list-style: none; margin: var(--s5) 0 0; padding: 0; }
.layer {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s1) var(--s4);
  padding-block: var(--s4);
  border-block-start: 1px solid var(--rule);
}
.layer__name { font-size: var(--t-md); }
.layer__state {
  font: var(--t-xs)/var(--lh-body) var(--ui);
  color: var(--ink-3);
  letter-spacing: 0.04em;
  text-align: right;
}
.layer--built .layer__state { color: var(--accent); }
.layer p { grid-column: 1 / -1; margin: 0; color: var(--ink-2); max-width: var(--measure); }

/* --- tap targets on standalone links -------------------------------------
   Pre-existing across every route, found by a responsive sweep during the IA
   pass: standalone link rows rendered at 14–21px tall, under the 24px minimum.

   WCAG 2.5.8 exempts a link inside a sentence, and those are left alone —
   padding them out would wreck the line rhythm of the prose columns, which is
   most of this site. What is fixed here is the links that are NOT in a
   sentence: masthead navigation, the footer, and the `.sub` rows that act as
   navigation between sections.

   This is also the first product-side example of the invariant argument in
   docs/PRESENTATION-ARCHITECTURE-FOLLOW-UP.md §3: a floor has to be enforced
   in the stylesheet, not declared somewhere upstream. */
.masthead nav a,
.foot a,
.sub a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}
/* The primary nav anchors were exactly as wide as their text, so the shortest
   label — "Mix", 24px — was a 24x30 target on every route at every width.
   The padding is paid for out of the list gap rather than added to it, so the
   row does not get wider and start spilling past the masthead. */
.masthead nav .nav__item > a { min-height: 44px; padding-inline: var(--s3); }
.nav__list { gap: var(--s3); }

/* ==========================================================================
   MIXER. Two panes: ingredients, and what the corpus resolves from them.
   ========================================================================== */
.status--partial { color: var(--ink-2); }
.status--inline {
  font: var(--t-xs)/1 var(--ui); color: var(--accent);
  letter-spacing: 0.04em; margin-inline-start: 0.4em;
}
.count { font: var(--t-sm)/1 var(--ui); color: var(--ink-3); margin-inline-start: 0.4em; }

.mix-grid { display: grid; grid-template-columns: minmax(0, 20rem) minmax(0, 1fr); gap: var(--s7); align-items: start; }
@media (max-width: 54rem) { .mix-grid { grid-template-columns: minmax(0, 1fr); gap: var(--s6); } }
.mix-pane h2 { font-size: var(--t-md); margin-block: 0 var(--s3); }

.mix-chips { list-style: none; margin: 0 0 var(--s4); padding: 0; display: flex; flex-wrap: wrap; gap: var(--s2); }
.mix-chip {
  display: inline-flex; align-items: center; gap: var(--s2); min-height: 28px;
  padding: 2px var(--s2) 2px var(--s3); border: 1px solid var(--rule-firm);
  font: var(--t-sm)/var(--lh-tight) var(--ui);
}
.mix-chip--empty { border-style: dashed; color: var(--ink-3); }
.mix-chip button {
  border: 0; background: none; cursor: pointer; color: var(--ink-2);
  font: 1rem/1 var(--ui); min-width: 24px; min-height: 24px;
}
.mix-chip button:hover { color: var(--accent); }

.mix-options { list-style: none; margin: var(--s3) 0 0; padding: 0; max-height: 26rem; overflow-y: auto; border-block-start: 1px solid var(--rule); }
.mix-options li { border-block-end: 1px solid var(--rule); }
.mix-options button {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--s3);
  width: 100%; min-height: 32px; padding: var(--s2) 0; cursor: pointer;
  background: none; border: 0; text-align: left; color: inherit; font: inherit;
}
.mix-options button:hover .mix-opt__name { color: var(--accent); }
.mix-opt__name { font-size: var(--t-sm); }
.mix-opt__state { font: var(--t-xs)/1 var(--ui); color: var(--ink-3); white-space: nowrap; }
.mix-opt__state[data-depth="none"] { opacity: .6; }

.mix-summary { font: var(--t-xs)/var(--lh-body) var(--ui); color: var(--ink-2); padding-block-end: var(--s3); border-block-end: 1px solid var(--rule); }
.mix-block { margin-block: var(--s5); }
.mix-block h3 { font-size: var(--t-base); margin-block: 0 var(--s2); }
.mix-block--contested h3 { color: var(--accent); }
.mix-block--silent { color: var(--ink-3); }
.mix-area { margin-block: var(--s3); }
.mix-area h4 { font: var(--t-xs)/var(--lh-tight) var(--ui); letter-spacing: 0.04em; color: var(--ink-3); margin-block: 0 var(--s1); }
.mix-foot { font-size: var(--t-sm); color: var(--ink-2); border-block-start: 1px solid var(--rule); padding-block-start: var(--s3); margin-block-start: var(--s5); }

/* ---------------------------------------------------- related languages --- */
.related { list-style: none; margin: var(--s3) 0 var(--s5); padding: 0; display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fill, minmax(min(13rem, 100%), 1fr)); }
.related__item { display: flex; flex-direction: column; gap: var(--s1); }
.related__spec { display: block; padding: var(--s3); border: 1px solid var(--rule); text-decoration: none; min-height: 44px; }
.related__spec:hover { border-color: var(--rule-firm); }
.related__name { font-size: var(--t-base); }
.related__note { margin: 0; font-size: var(--t-xs); color: var(--ink-3); }

/* ------------------------------------------------- sources + changelog --- */
.sources li, .changelog li { margin-block-end: var(--s3); }
.sources .sub, .changelog .sub { display: block; }
.changelog__head { margin: 0 0 var(--s1); }
.changelog li p:last-child { margin: 0; color: var(--ink-2); max-width: var(--measure); }

.status--demo { color: var(--ink-2); }
.empty-state { font: var(--t-sm)/var(--lh-body) var(--ui); color: var(--ink-3); padding-block: var(--s4); border-block: 1px dashed var(--rule-firm); }
.recipe { border: 1px solid var(--rule); padding: var(--s5); }
.recipe__name { margin-block: 0 var(--s2); font-size: var(--t-md); }
.recipe__by { margin-block: 0 var(--s4); color: var(--ink-2); font-size: var(--t-sm); }
.related__spec--unrouted { opacity: .8; border-style: dashed; }
.related__kind { display: block; font: var(--t-xs)/var(--lh-tight) var(--ui); color: var(--ink-3); }

/* ==========================================================================
   PRIMARY NAV WITH SECTIONS.

   Children live in the DOM on every page and are revealed on hover and on
   :focus-within. Nothing uses `display: none`, because an unfocusable child
   cannot trigger :focus-within and the keyboard path would dead-end.

   Below 48rem the dropdown is suppressed entirely — a hover menu on a touch
   screen is a trap — and the same links render as a section strip instead.
   ========================================================================== */
.nav__list { display: flex; gap: var(--s5); list-style: none; margin: 0; padding: 0; }
.nav__item { position: relative; }


.nav__sub {
  /* A DRAWER OUT OF THE MASTHEAD, NOT A CARD OVER THE PAGE.
     The panel is pulled up one pixel so its top border lands exactly on the
     masthead's bottom rule: the menu reads as the header continuing downward.
     That is why it is square, flush to the right edge and full-bleed inside —
     a rounded floating card would be a component borrowed from somewhere else.

     Right-aligned, because the nav sits at the right edge of the masthead and
     a left-anchored menu on the last item pushed the page's scroll width out
     by a few pixels between 776 and 1464px. A `visibility: hidden` element
     still contributes to scrollable area, so this affected every route even
     with every menu closed. */
  position: absolute; inset-inline-start: auto; inset-inline-end: 0; top: 100%; z-index: 20;
  min-width: 15rem; margin: -1px 0 0; padding: var(--s2) 0;
  list-style: none;
  background: var(--paper); border: 1px solid var(--rule-firm);
  box-shadow: 0 14px 30px -14px rgb(11 13 18 / .24);
  opacity: 0; visibility: hidden; transform: translateY(-2px);
  transition: opacity .12s ease, transform .12s ease, visibility 0s .12s;
}
.nav__item:hover > .nav__sub,
.nav__item:focus-within > .nav__sub {
  opacity: 1; visibility: visible; transform: none; transition-delay: 0s;
}
.nav__sub a {
  /* WAS `--t-xs` MONO IN GREY, which is the apparatus voice this product uses
     for evidence and metadata. A menu is navigation, so it takes the
     navigation face at the navigation size. The same mistake was already
     fixed once in `.masthead__aside` and had been left standing here. */
  display: flex; align-items: center; min-height: 40px;
  padding: var(--s2) var(--s4); white-space: nowrap;
  font: 500 var(--t-sm)/var(--lh-tight) var(--ui); letter-spacing: -0.005em; color: var(--ink-2);
  border-inline-start: 2px solid transparent;
}
/* The row fills rather than the text merely recolouring: at this size a colour
   change alone is not a strong enough hit target signal. Wash and edge are the
   existing brand tints — no new colour enters the palette for a hover state. */
.nav__sub a:hover, .nav__sub a:focus-visible {
  color: var(--brand); background: var(--brand-wash); border-inline-start-color: var(--brand);
}
/* Section label. Only used where the menu is long enough to need a heading. */
.nav__sub__lbl {
  /* Matches the 2px leading border the links carry, so the label's left edge
     sits on the same axis as the link text rather than 2px off it. */
  padding: var(--s2) var(--s4) var(--s1);
  border-inline-start: 2px solid transparent;
  /* Was uppercase at .08em — one of two survivors of the retired micro-label.
     Mapped onto the same `label` treatment every other interface label uses. */
  font: 500 var(--t-xs)/var(--lh-tight) var(--ui); color: var(--ink-3);
}

/* The section strip. Carries the same children where a dropdown cannot. */
.sectionbar { border-block-end: 1px solid var(--rule); background: var(--paper-deep); }
.sectionbar ul {
  display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5);
  list-style: none; margin: 0; padding-block: var(--s2);
}
.sectionbar a {
  display: inline-flex; align-items: center; min-height: 28px;
  font: var(--t-xs)/var(--lh-tight) var(--ui); color: var(--ink-2);
}
.sectionbar a:hover { color: var(--accent); }

/* --- mobile disclosure ---------------------------------------------------- */
.navtoggle { position: absolute; opacity: 0; width: 0; height: 0; }
.navburger { display: none; }

@media (max-width: 48rem) {
  /* MOBILE NAVIGATION.
     Wrapping five items produced two ragged rows with uneven gaps — the
     padding each link carries for its tap target fought the flex gap, so
     nothing lined up. One row that scrolls instead: the order is preserved,
     the rhythm is even, and the section strip below already carries the
     children that the hover menus cannot show on a touch screen. */
  /* A SCROLLING ROW HID ITEMS. The panel shows all five at once, plus the
     section children the hover menus cannot reach on a touch screen. */
  .navburger {
    /* The word "Menu" is gone from view but NOT from the document — three bars
       with no accessible name announce as an unlabelled control. It is clipped
       below rather than removed, so the name still comes from real text that
       translates with the page, instead of an aria-label that does not.

       Square, because with the label gone the padding that used to sit beside
       the text was the only thing holding the target open. 44px both ways is
       the hit area WCAG 2.5.5 asks for, and the icon centres inside it. */
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    min-height: 44px; min-width: 44px;
    /* Pushed to the right edge. With the word beside it the control had enough
       width to read as deliberate sitting next to the wordmark; as a bare
       44px square it just looked adrift, with a hundred pixels of nothing to
       its right. The negative end margin puts the ICON on the page's right
       margin — the 44px target still overhangs it, which is what you want on
       a touch target and invisible either way. */
    margin-inline-start: auto;
    margin-inline-end: calc(var(--s2) * -1);
    color: var(--ink);
  }
  .navburger__txt {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .navburger__box { display: inline-flex; flex-direction: column; justify-content: center; gap: 4px; width: 20px; }
  .navburger__box i { display: block; height: 2px; background: currentColor; transition: transform .18s ease, opacity .18s ease; }
  .navtoggle:checked ~ .navburger .navburger__box i:nth-child(1) { transform: translateY(6px) rotate(45deg); }
  .navtoggle:checked ~ .navburger .navburger__box i:nth-child(2) { opacity: 0; }
  .navtoggle:checked ~ .navburger .navburger__box i:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
  .navtoggle:focus-visible ~ .navburger { outline: 2px solid var(--brand); outline-offset: 2px; }

  .masthead__row { flex-wrap: wrap; gap: var(--s2); }
  .masthead nav {
    flex-basis: 100%; order: 3;
    display: grid; grid-template-rows: 0fr;
    transition: grid-template-rows .22s ease;
  }
  .masthead nav > .nav__list { overflow: hidden; }
  .navtoggle:checked ~ nav { grid-template-rows: 1fr; }

  .nav__list { flex-direction: column; gap: 0; padding: 0; }
  .nav__item { border-block-start: 1px solid var(--rule); }
  .masthead nav .nav__item > a { min-height: 52px; padding-inline: 0; width: 100%; }

  /* The children come with it — they are unreachable by hover on touch. */
  .nav__sub, .nav__sub--res {
    position: static; opacity: 1; visibility: visible; transform: none;
    border: 0; background: none; box-shadow: none; margin: 0;
    padding: 0 0 var(--s4) var(--s4);
    min-width: 0; display: block;
  }
  .nav__sub a { min-height: 40px; padding-inline: 0; border-inline-start: 0; }
  .nav__sub a:hover, .nav__sub a:focus-visible { background: none; }
  .nav__sub__lbl { padding-inline: 0; }
}
@media (min-width: 48.0625rem) {
  /* On desktop the dropdown already carries the children. */
  .sectionbar { display: none; }
}

/* ------------------------------------------------------------- footer map */
.foot__map {
  display: grid; gap: var(--s5) var(--s6);
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  padding-block-end: var(--s6); margin-block-end: var(--s5);
  border-block-end: 1px solid var(--rule);
}
.foot__head { margin: 0 0 var(--s2); font: 500 var(--t-sm)/var(--lh-tight) var(--ui); }
.foot__head a { text-decoration: none; }
.foot__head a:hover { text-decoration: underline; }
.foot__col ul { list-style: none; margin: 0; padding: 0; }
.foot__col li { margin-block-end: 1px; }
.foot__col li a {
  /* Stale: this rule predates the footer becoming a dark slab, and the slab
     block further down overrides display, align-items, min-height, font-size
     and colour. Only the family and line-height still come from here — and
     min-width, which is the one thing the slab block does not set.

     WCAG 2.5.8 wants 24x24 on targets that are not inline in prose, and these
     are a link list. The height was already handled; the WIDTH was not, so the
     two shortest labels in the footer — "3D" and "FAQ" — were 18px and 23px
     wide and failed on every route. */
  min-width: 24px;
  line-height: var(--lh-tight);
}
.foot__col li a:hover { color: var(--accent); }
.foot__note p { max-width: 42rem; }

/* ==========================================================================
   ABOUT — founder block.
   A change of rhythm from the reference pages: portrait beside text rather
   than another marginal apparatus column. Uses the existing scale and colour;
   nothing decorative is introduced.
   ========================================================================== */
.founder {
  display: grid; gap: var(--s5) var(--s7);
  grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  align-items: start;
  padding-block: var(--s7);
  border-block: 1px solid var(--rule);
  margin-block: var(--s7);
}
.founder__media { margin: 0; }
.founder__img { display: block; width: 100%; height: auto; }
.founder__slot {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  aspect-ratio: 4 / 5; background: var(--paper-deep);
  border: 1px dashed var(--rule-firm);
  font: var(--t-sm)/var(--lh-body) var(--ui); color: var(--ink-3); text-align: center;
}
.founder__slotnote { font-size: var(--t-xs); }
.founder__media figcaption {
  margin-block-start: var(--s2);
  font: var(--t-xs)/var(--lh-tight) var(--ui); color: var(--ink-3);
}
.founder__body h2 { margin-block: 0 var(--s3); }
.founder__body p { max-width: var(--measure); }
@media (max-width: 40rem) {
  .founder { grid-template-columns: minmax(0, 1fr); }
  .founder__media { max-width: 13rem; }
}

.cta-stack { margin-block: var(--s4) 0; }
.cta-stack li { margin-block-end: var(--s4); }
.cta {
  display: inline-flex; align-items: center; min-height: 28px;
  font-size: var(--t-md); text-decoration-thickness: 1px; text-underline-offset: 3px;
}
.cta-stack li span { display: block; color: var(--ink-2); font-size: var(--t-sm); max-width: var(--measure); }

/* ------------------------------------------------- masthead utility + CTA --
   The CTA points at Explore because that is the strongest action this product
   actually supports. There is no sign-up, no account and no mailing list, so
   a "Get started" button would be a control with nothing behind it. */
.masthead__row { gap: var(--s5); }
.masthead__utility { display: flex; align-items: center; gap: var(--s4); }
.masthead__contact { font: var(--t-xs)/1 var(--ui); color: var(--ink-3); display: inline-flex; align-items: center; min-height: 44px; }
.masthead__contact:hover { color: var(--accent); }
/* Resources sits in the utility tier, beside Contact — deliberately not a
   fifth pillar. It reads as a reference layer people go to on purpose. */
/* The utility tier gets the same hover/focus-within menu as the pillars, so
   the fifteen resource categories are reachable without a second page load.
   Right-anchored for the same reason the pillar menus are: a left-anchored
   menu on the last item pushes the page's scroll width out. */
/* Secondary tier: same face and size as the pillars, held apart by a rule and
   a quieter ink. Difference in weight of attention, not in kind. */
.masthead__aside { display: flex; align-items: center; gap: var(--s4); padding-inline-start: var(--s5); margin-inline-start: var(--s2); border-inline-start: 1px solid var(--rule); }
.masthead__about { position: relative; display: inline-flex; }
.nav__sub--about { min-width: 16rem; }
.masthead__aside a { font: 500 var(--t-sm)/1 var(--ui); letter-spacing: -0.005em; color: var(--ink-3); display: inline-flex; align-items: center; min-height: 44px; }
.masthead__aside a:hover { color: var(--ink); }
/* The one solid control in the shell. It is the primary action, so it is the
   only filled element up there — and it points at a page that tells the truth
   about what works rather than at a signup that would collect nothing. */
/* Specificity, not intent: `.masthead__aside a` is (0,1,1) and `.masthead__cta`
   was (0,1,0), so the quiet grey ink won and put --ink-3 on a near-black
   ground at about 2.6:1. The selector is qualified so the declaration that
   was always meant to apply actually does. White on --ink is 19:1. */
.masthead__aside a.masthead__cta { font: 500 var(--t-sm)/1 var(--ui); background: var(--ink); color: var(--paper); padding-inline: var(--s4); margin-inline-start: var(--s3); min-height: 44px; }
.masthead__aside a.masthead__cta:hover { background: var(--brand); color: var(--paper); }
@media (max-width: 60rem) { .masthead__aside { display: none; } }
/* Thirteen categories is too tall for one column and too wide for two narrow
   ones: at 26rem the longest names ("Images & Visual Assets", "Research &
   Inspiration") ran straight into the second column. Width is set by the
   longest label, not by a round number. */
.nav__sub--res { inset-inline-end: 0; min-width: 30rem; display: grid; grid-template-columns: 1fr 1fr; align-content: start; }
.nav__sub--res .nav__sub__lbl { grid-column: 1 / -1; }
@media (max-width: 44rem) { .nav__sub--res { grid-template-columns: 1fr; min-width: 0; } }
.masthead__util { font: var(--t-xs)/1 var(--ui); color: var(--ink-3); display: inline-flex; align-items: center; min-height: 44px; }
.masthead__util:hover { color: var(--accent); }
.masthead__cta {
  display: inline-flex; align-items: center; min-height: 32px;
  padding-inline: var(--s4);
  border: 1px solid var(--ink); background: var(--ink); color: var(--paper);
  font: var(--t-xs)/1 var(--ui); letter-spacing: 0.04em; text-decoration: none;
  white-space: nowrap;
}
.masthead__cta:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
@media (max-width: 60rem) { .masthead__contact, .masthead__util { display: none; } }
@media (max-width: 34rem) { .masthead__cta { display: none; } }

.contact__address { font-size: var(--t-md); margin-block: var(--s4) 0; }
.contact__address--pending span {
  display: inline-block; padding: var(--s2) var(--s3);
  border: 1px dashed var(--rule-firm); background: var(--paper-deep);
  font: var(--t-sm)/var(--lh-tight) var(--ui); color: var(--ink-3);
}
.contact__address--placeholder a { color: var(--ink-3); text-decoration-style: dashed; }
.contact__flag {
  display: inline-block; margin-inline-start: var(--s3);
  padding: 1px var(--s2); border: 1px dashed var(--rule-firm);
  font: var(--t-xs)/var(--lh-body) var(--ui); color: var(--accent);
  letter-spacing: 0.03em; vertical-align: 2px;
}
.classification__head {
  margin-block: 0 var(--s2);
  font: var(--t-xs)/var(--lh-tight) var(--ui); letter-spacing: 0.04em; color: var(--ink-3);
}
.classification { margin-block-end: var(--s5); }

/* ------------------------------------------------------------------ search */
.field--big input { font-size: var(--t-md); padding-block: var(--s2); width: 100%; }
.s-results { list-style: none; margin: var(--s5) 0 0; padding: 0; }
.s-hit { border-block-start: 1px solid var(--rule); }
.s-hit a { display: block; padding-block: var(--s3); text-decoration: none; }
.s-hit a:hover .s-hit__name { color: var(--accent); }
.s-hit__name { display: block; font-size: var(--t-md); }
.s-hit__line { display: block; color: var(--ink-2); font-size: var(--t-sm); max-width: var(--measure); }
.s-hit__aka { margin: 0 0 var(--s3); font: var(--t-xs)/var(--lh-tight) var(--ui); color: var(--ink-3); }
.s-empty { padding-block: var(--s5); color: var(--ink-2); }
.compare__comparability[data-mixed="true"] { border-inline-start: 2px solid var(--accent); padding-inline-start: var(--s3); }
.crumb { margin: 0 0 var(--s2); font: var(--t-xs)/var(--lh-tight) var(--ui); color: var(--ink-3); }
.pipeline {
  font: var(--t-xs)/1.9 var(--ui); color: var(--ink-2);
  padding: var(--s3) var(--s4); border: 1px solid var(--rule);
  overflow-x: auto; white-space: nowrap;
}

/* The single non-canonical row in Explore. Marked, never counted. */
.entry[data-outside-corpus] .entry__spec {
  border-inline-start: 2px solid #E0409B;
  padding-inline-start: var(--s4);
}
.entry__flag {
  font: var(--t-xs)/1 var(--ui); letter-spacing: .06em;
  color: #A32E14; vertical-align: .25em; margin-inline-start: var(--s2);
  white-space: nowrap;
}


/* ==========================================================================
   HOUSE STYLE — components.
   Order, interruption, order. The shell is quiet so the collection performs,
   with a small number of deliberate moments that are not quiet at all.
   ========================================================================== */

body { font-family: var(--ui); }
.wrap { max-width: var(--page); }

/* --- masthead: a thin architectural rail -------------------------------- */
.masthead {
  position: sticky; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-block-end: 1px solid var(--rule);
}
/* ==========================================================================
   THE WORDMARK — TEXT STANDING IN FOR THE REAL MARK.

   This is NOT the logo. The supplied Aesthetary mark is hairline geometric
   capitals on very wide tracking, and it has not been delivered as a vector
   yet. What this does is stop the name being set in the old lockup's clothes:
   the previous rule was 650 weight on NEGATIVE tracking, which is the exact
   opposite of the mark's character, so "Aesthetary" was reading as the old
   two-word logo with a new spelling.

   Caps, the lightest weight the face has, and open tracking. 400 is the floor
   of the variable font's range, so the true hairline is not reachable in type
   and will arrive with the SVG.

   The trailing letter-space after the last Y is real space that pushes the
   optical left edge of everything after it; the negative end margin takes it
   back so the mark sits flush to the page grid.
   ========================================================================== */
/* THE WORDMARK, AS ARTWORK.
   Measured before sizing: the stems are 18px in a 180px-tall mark, a ratio of
   1:9.9. That is far heavier than it looks at full size, and it is the reason
   this can sit at navigation scale at all — at 20px tall the stems land at
   2.0px, well clear of the sub-pixel shimmer a true hairline would give.

   Height drives it and width follows the aspect, so the mark can never be
   stretched. One asset at 1100px wide covers both placements at 3x. */
.masthead__mark { line-height: 0; }
.masthead__mark img { display: block; height: 20px; width: auto; }
@media (max-width: 30rem) { .masthead__mark img { height: 17px; } }
.masthead nav a { font: 500 var(--t-sm)/1 var(--ui); letter-spacing: -0.005em; color: var(--ink-2); }
.masthead nav a:hover { color: var(--ink); }
.masthead nav a[aria-current="page"] { color: var(--brand); }
.masthead__cta {
  background: var(--ink); border-color: var(--ink); color: var(--paper);
  font: 500 var(--t-xs)/1 var(--ui); letter-spacing: 0; border-radius: var(--r);
}
.masthead__cta:hover { background: var(--brand); border-color: var(--brand); color: #fff; }
.nav__state { font-family: var(--ui); }

/* --- display type ------------------------------------------------------- */
.display {
  font-family: var(--display); font-weight: 700;
  /* Schibsted takes real negative tracking at display size without closing
     its counters, which is where the confidence comes from. */
  letter-spacing: -0.03em; line-height: .95; text-wrap: balance;
}
h2, h3 { font-family: var(--display); font-weight: 650; letter-spacing: -0.02em; }
.lead { font-size: var(--t-md); line-height: var(--lh-body); color: var(--ink-2); max-width: 44rem; }
.leaf__body p, .leaf__body li { font-family: var(--ui); }

/* Long-form reading keeps the serif; product chrome does not. */
.record__rule, .deflist dd, .stack li { font-family: var(--ui); }

/* --- the slab: the structural interruption ------------------------------ */
.plate { background: var(--plate); color: var(--plate-ink); }
.plate h2 { color: var(--plate-ink); }
.plate .basis__answer, .plate p { color: var(--plate-ink-2); }

/* --- catalogue entries become openable worlds --------------------------- */
.entries { border-block-start: 1px solid var(--rule); }
.entry {
  position: relative;
  transition: background .18s ease;
}
.entry:hover { background: var(--paper-deep); }
.entry__name { font-family: var(--display); letter-spacing: -0.025em; }
.entry__name a { text-decoration: none; }
.entry:hover .entry__name a { color: var(--brand); }
/* a quiet marker that a row is a door */
.entry__spec::after {
  content: "↗"; position: absolute; inset-block-start: var(--s4); inset-inline-end: 0;
  font: var(--t-sm)/1 var(--ui); color: var(--rule-firm);
  opacity: 0; transform: translate(-4px, 4px);
  transition: opacity .18s ease, transform .18s ease;
}
.entry:hover .entry__spec::after { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .entry, .entry__spec::after { transition: none; }
}

/* --- controls ------------------------------------------------------------ */
.field input, .field select {
  border: 1px solid var(--rule-firm); border-radius: var(--r);
  background: var(--paper); color: var(--ink);
  font-family: var(--ui);
}
.field input:focus-visible, .field select:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 1px; border-color: var(--brand);
}
.field label, .field__note { font-family: var(--ui); color: var(--ink-3); }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* --- the editorial mark keeps working, in the brand colour -------------- */
.ours { color: var(--brand); }

/* --- section rhythm ------------------------------------------------------ */
.leaf { border-block-start: 1px solid var(--rule); }
.margin { border-inline-start: 1px solid var(--rule); }
.margin__head { font-family: var(--mono); color: var(--ink-2); letter-spacing: .04em; }

/* --- FAQ + Dreams -------------------------------------------------------- */
.faq dt { font: 650 var(--t-md)/1.3 var(--display); letter-spacing: -0.02em; margin-block: var(--s6) var(--s2); color: var(--ink); }
.faq dt:first-of-type { margin-block-start: 0; }
.faq dd { margin: 0; max-width: var(--measure); }
.faq dd p { margin-block: 0 var(--s3); color: var(--ink-2); }
.dreams { list-style: none; counter-reset: dream; margin: var(--s6) 0 0; padding: 0; }
.dreams li { counter-increment: dream; border-block-start: 1px solid var(--rule); padding-block: var(--s5); display: grid; grid-template-columns: 3rem minmax(0,1fr); gap: var(--s4); }
.dreams li::before { content: counter(dream, decimal-leading-zero); font: var(--t-xs)/1.9 var(--ui); color: var(--brand); }
/* The row has THREE children — the counter, the heading and the paragraph —
   against two explicit columns, so the paragraph wrapped onto an implicit
   second row and landed in the 3rem counter column, setting at roughly two
   words a line. Both content blocks are pinned to the content column. */
.dreams h3 { grid-column: 2; font-size: var(--t-lg); margin: 0 0 var(--s2); letter-spacing: -0.025em; }
.dreams p { grid-column: 2; margin: 0; color: var(--ink-2); max-width: var(--measure); }
@media (max-width: 34rem) {
  .dreams li { grid-template-columns: minmax(0,1fr); gap: var(--s2); }
  .dreams h3, .dreams p { grid-column: 1; }
}

/* ==========================================================================
   HOME — the museum entrance.
   White architecture, one big statement, then the collection immediately.
   ========================================================================== */
.hero { padding-block: clamp(3rem, 8vw, 7rem) clamp(2.5rem, 6vw, 5rem); }
.hero__grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 17rem); gap: var(--s7); align-items: start; }
.hero__title { font-size: var(--t-display-lg); margin: 0 0 var(--s5); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--s3); margin-block-start: var(--s6); }
.hero__stats { margin: 0; display: grid; gap: 0; border-block-start: 2px solid var(--ink); }
.hero__stats > div { display: flex; justify-content: space-between; gap: var(--s4); padding-block: var(--s3); border-block-end: 1px solid var(--rule); }
.hero__stats dt { font: var(--t-xs)/var(--lh-body) var(--ui); color: var(--ink-3); letter-spacing: .02em; }
.hero__stats dd { margin: 0; font: 650 var(--t-md)/1.2 var(--display); }
@media (max-width: 60rem) { .foot .wrap { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 52rem) { .hero__grid { grid-template-columns: minmax(0, 1fr); gap: var(--s6); } }

.btn {
  display: inline-flex; align-items: center; min-height: 44px;
  padding-inline: var(--s5); border: 1px solid var(--ink); border-radius: var(--r);
  font: 500 var(--t-sm)/1 var(--ui); text-decoration: none; color: var(--ink);
  transition: background .16s ease, color .16s ease, border-color .16s ease;
}
.btn:hover { background: var(--brand-wash); border-color: var(--brand); color: var(--brand); }
.btn--primary { background: var(--ink); color: var(--paper); }
.btn--primary:hover { background: var(--brand); border-color: var(--brand); color: #fff; }
@media (prefers-reduced-motion: reduce) { .btn { transition: none; } }

/* --- the collection: worlds, not rows ----------------------------------- */
.collection { padding-block: clamp(2rem, 5vw, 4rem) clamp(3rem, 7vw, 6rem); }
.collection__head { max-width: 46rem; margin-block-end: var(--s6); }
.collection__head h2 { font-size: var(--t-xl); margin: 0 0 var(--s3); }
.worlds { display: grid; gap: clamp(1rem, 2vw, 1.5rem); grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr)); align-items: start; }
.world {
  /* Bounded by the viewport, not only by the rail. The rail scrolls its own
     overflow, but a tile wider than the screen still pushed the document's
     scroll width out by a couple of pixels below 330px. */
  max-width: calc(100vw - var(--s6));
  display: flex; flex-direction: column; gap: var(--s3);
  text-decoration: none; color: inherit;
  transition: transform .2s cubic-bezier(.2,.7,.2,1);
}
.world:hover { transform: translateY(-3px); }
.world__spec {
  display: flex; flex-direction: column; gap: var(--s3);
  /* Fixed, not minimum. Content varies per record, so a minimum produced three
     different shapes side by side — the specimen inside should differ, the
     frame holding it should not. */
  height: 17rem; padding: var(--s5);
  border: 1px solid var(--rule-firm);
  background: var(--paper-deep);
  min-width: 0; overflow: hidden;
}
.world__line { min-width: 0; overflow-wrap: anywhere; }
.world:hover .world__spec { border-color: var(--ink); }
.world__name {
  font: 650 var(--t-md)/1.15 var(--display); letter-spacing: -0.01em;
  /* The names must never leave the tile. An extended face at display size in a
     16rem box will break a long word otherwise. */
  min-width: 0; overflow-wrap: anywhere; hyphens: auto;
}
.world__line { font-size: var(--t-sm); line-height: var(--lh-body); opacity: .82; }
.world__field { display: flex; flex-wrap: wrap; gap: var(--s2); margin-block-start: auto; overflow: hidden; max-height: 4.4rem; }
.world__field span { font: var(--t-xs)/var(--lh-body) var(--ui); padding: 2px var(--s2); border: 1px solid currentColor; opacity: .55; }
.world__meta { font: var(--t-xs)/var(--lh-body) var(--ui); color: var(--ink-3); }
.collection__more { margin-block-start: var(--s6); }
@media (prefers-reduced-motion: reduce) { .world { transition: none; } }

/* --- the loop, on the slab ---------------------------------------------- */
.loop-plate { padding-block: clamp(3rem, 7vw, 6rem); }
.loop-plate h2 { font-size: var(--t-xl); margin: 0 0 var(--s3); letter-spacing: -0.03em; }
.plate__standfirst { max-width: 44rem; }
.steps { list-style: none; margin: var(--s7) 0 0; padding: 0; display: grid; gap: 1px; grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr)); background: rgba(255,255,255,.14); }
.step { background: var(--plate); padding: var(--s5) var(--s4); }
.step__name { display: block; font: 650 var(--t-md)/1.2 var(--display); color: var(--plate-ink); text-decoration: none; letter-spacing: -0.02em; }
.step__name:hover { color: var(--plate-accent); }
.step__state { display: block; font: var(--t-xs)/1.8 var(--ui); color: var(--plate-ink-2); }

.step p { margin: var(--s3) 0 0; font-size: var(--t-sm); color: var(--plate-ink-2); }
/* `.sub` was mono, which made standfirst copy read as code in the new house
   style. Mono is now reserved for data, metadata and the epistemic apparatus. */
.sub { font-family: var(--ui); font-size: var(--t-sm); line-height: var(--lh-body); color: var(--ink-3); }
.collection__head .sub { font-size: var(--t-base); line-height: var(--lh-body); color: var(--ink-2); }
.world__field span { overflow-wrap: anywhere; }

/* ==========================================================================
   BROWSE — 95 worlds, on shelves.
   Grouping is lawful (origin_kind). Tile size follows derived richness, so the
   depth gradient is the composition rather than a badge.
   ========================================================================== */
.browse { padding-block: clamp(2rem, 5vw, 4rem); min-width: 0; max-width: 100%; }
.browse__head { max-width: 46rem; margin-block-end: var(--s6); }
.browse__head h2 { font-size: var(--t-xl); margin: 0 0 var(--s2); }
.shelf { margin-block-end: clamp(2rem, 4vw, 3.5rem); min-width: 0; max-width: 100%; }
.shelf__head { display: flex; align-items: baseline; gap: var(--s3); font-size: var(--t-md); margin: 0 0 var(--s4); }
.shelf__n { font: var(--t-xs)/1 var(--ui); color: var(--ink-3); }
.shelf__rail {
  display: flex; gap: var(--s4); overflow-x: auto; scroll-snap-type: x proximity;
  /* No negative-margin bleed: it made the rail wider than its parent and the
     page scrolled sideways. The rail scrolls inside its own box instead. */
  padding-block-end: var(--s3);
  scrollbar-width: thin;
  overscroll-behavior-x: contain;
  /* Fixed-width tiles must scroll INSIDE the rail. Without an explicit
     min-width of zero the rail grows to fit them and the page scrolls. */
  min-width: 0; max-width: 100%;
}
.shelf__rail > * { scroll-snap-align: start; flex: 0 0 auto; }
/* UNIFORM TILES.
   These varied by derived richness, so the masonry would be the depth
   gradient. In practice a one-versus-zero difference produced different
   widths, which read as noise rather than signal — and the extended display
   face burst the names out of the narrow ones. The depth signal stays where
   it is legible: the meta line, and the specimen itself. */
.shelf .world { width: 16.5rem; }
/* FIXED height, not minimum. With a minimum the tiles stretched to their
   content and the shelf became a ragged row of different shapes. The specimen
   inside still differs; the frame holding it does not. */
.shelf .world__spec { height: 13rem; overflow: hidden; }
.shelf .world__field { overflow: hidden; max-height: 4.2rem; }
.shelf__rail { align-items: start; }

.index-head { padding-block-start: clamp(2rem, 4vw, 3rem); border-block-start: 2px solid var(--ink); }
.index-head h2 { font-size: var(--t-lg); margin: var(--s4) 0 var(--s2); }

/* --- the void: where the system refuses ----------------------------------
   The strangest content in this product is its honesty. It was set in polite
   grey and is now allowed to be the confrontation. Acid on ink, never on white. */
.void {
  display: inline-block; position: relative;
  color: var(--ink-3); font-family: var(--ui);
}
.void::after {
  content: ""; position: absolute; inset-inline: -2px; inset-block-start: 52%;
  height: 1px; background: currentColor; opacity: .55;
}
.spec__why { font-family: var(--mono); }
/* Mix: you should see what you are combining, not read its name. */
.mix-opt__spec, .mix-chip__spec { display: block; padding: var(--s2) var(--s3); min-width: 0; }
.mix-opt__spec .spec__name { font-size: var(--t-sm); }
.mix-chip { padding: 0 var(--s2) 0 0; }
.mix-chip__spec { font-size: var(--t-sm); }
.mix-options button { align-items: center; gap: var(--s3); padding: var(--s1) 0; }
/* The footer note was carrying three jobs across 111 pages. The alpha status
   and the no-generation claim stay, because both are load-bearing; the `ours`
   convention lives on Learn and the privacy facts live on Using, each linked
   rather than restated everywhere. */
.foot__note p { margin-block: 0 var(--s2); font-size: var(--t-sm); }
.foot__legal { font: var(--t-xs)/var(--lh-body) var(--ui); color: var(--ink-3); }
.foot__legal a { color: var(--ink-3); }
.foot__legal a:hover { color: var(--brand); }

/* --- footer, reduced ------------------------------------------------------
   The mega menu carries the full hierarchy on every page. A five-column link
   farm underneath was a second copy of the same navigation, and link farms are
   the most reliable SaaS tell there is. What is left is the five destinations
   a reader might want from the bottom of a page, plus the legal row. */






/* ==========================================================================
   LINKS: underline where it aids comprehension, nowhere else.

   Everything was underlined by default, which made navigation, lists and
   metadata read as one continuous ruled surface — and underlined mono in a
   footer is about as documentation-site as a page can look.

   An underline earns its place inside running prose, where a reader needs to
   tell a link from the sentence around it. In a navigation list, a footer, a
   definition list or a catalogue row, the context already does that job and
   the rule is just noise. WCAG's concern is links in BODY TEXT being
   distinguishable by more than colour, and those keep their underline.
   ========================================================================== */
.masthead a, .foot a, .foot__links a, .sectionbar a, .nav__sub a,
.stack a, .deflist a, .entries a, .worlds a, .shelf a,
.margin a, .faq a, .dreams a, .s-hit a, .mix-options a, .crumb a,
.hero__stats a, .foot__legal a {
  text-decoration: none;
}
.masthead a:hover, .foot a:hover, .foot__links a:hover, .sectionbar a:hover,
.stack a:hover, .deflist a:hover, .margin a:hover,
.faq a:hover, .dreams a:hover, .foot__legal a:hover {
  text-decoration: underline;
  text-decoration-color: var(--brand);
  text-underline-offset: 0.2em;
}
/* Prose keeps its underline, in a lighter weight so a paragraph does not
   turn into a ruled page. */
.leaf__body p a, .lead a, .n-p a, .body a {
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--brand) 55%, transparent);
  text-underline-offset: 0.18em;
}
.leaf__body p a:hover, .lead a:hover { text-decoration-color: var(--brand); }


/* ==========================================================================
   FOOTER — the floor of the room.

   First it was a five-column link farm, which is a SaaS tell. Then it was a
   bare strip, which is a receipt. This is the version in between: a dark slab
   that ends the white room definitively, an oversized wordmark, two short
   grouped lists instead of the whole sitemap, and the one genuinely useful
   thing the menu buries — the machine layer.

   White on the slab measures 19.4:1, secondary 8.9:1.
   ========================================================================== */
.foot {
  background: var(--plate); color: var(--plate-ink);
  border-block-start: 0; padding-block: clamp(4rem, 8vw, 7rem) var(--s7);
  /* The old footer was set wholesale in mono, which is why it read as terminal
     output. Mono is reserved for the labels and the legal row. */
  font: var(--t-sm)/var(--lh-body) var(--ui);
}
/* The wordmark and the social marks are ONE block, so the grid has three
   children rather than four. Pinning them individually with explicit rows
   worked at desktop and collapsed the first track to 80px at 832px, taking
   the full-width base row down with it. A wrapper is the sturdier answer:
   auto-placement then does the right thing at every width. */
.foot .wrap { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2.1fr); gap: var(--s6) var(--s8); align-items: start; }
/* Three unlike things stacked — a mark, a row of inert marks, a paragraph —
   and they were sitting 12px and 0px apart while the rest of the footer used
   32px and 64px. Spacing belongs to the stack rather than to each block, so it
   is set once here, on the scale, rather than nudged per element. */
.foot__identity { min-width: 0; display: grid; gap: var(--s5); justify-items: start; }
.foot__mark { margin: 0; line-height: 0; }
/* Larger than the masthead because the footer is where the institution signs
   its name. Capped at the column so it cannot outgrow its track, and the
   height clamp keeps the stems above 2px at every width. */
.foot__mark img { display: block; width: 100%; max-width: 20rem; height: auto; }
/* Social marks, not links — see layout.ts. Rendered at the ink of disabled
   text so they read as placeholders rather than as controls that ignore you. */
.foot__social { display: flex; gap: var(--s4); }
.foot__soc { display: inline-flex; width: 22px; height: 22px; color: #6E7684; }
.foot__soc svg { width: 100%; height: 100%; fill: currentColor; }
a.foot__soc { color: #9AA2B1; }  /* live accounts read brighter than the inert mark */
a.foot__soc:hover, a.foot__soc:focus-visible { color: #FFFFFF; }
.foot__socnote { margin: var(--s3) 0 0; font: var(--t-xs)/var(--lh-body) var(--ui); color: #9AA2B1; max-width: 22rem; }  /* 6.1:1 — was #6E7684 at 4.25 */

/* Three explicit columns — collection, project, machines — rather than
   auto-fit. Auto-fit resolved to two at this width and pushed "For machines"
   onto a full-width row of its own, which read as an afterthought rather than
   as the third of three. */
.foot__cols { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s6) var(--s7); }
/* Declared AFTER the base rule, because an earlier revision put these first
   and the three-column rule silently overrode both. */
@media (max-width: 60rem) { .foot__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 34rem) { .foot__cols { grid-template-columns: minmax(0, 1fr); } }
/* No span. The machine column is the third column, not a row beneath. */
.foot__col { min-width: 0; }
.foot__h {
  margin: 0 0 var(--s3); font: var(--t-xs)/1 var(--ui);
  letter-spacing: .1em; text-transform: uppercase; color: var(--on-plate-strong);
}
.foot__col ul { list-style: none; margin: 0; padding: 0; }
.foot__col li { margin-block-end: var(--s1); }
.foot__col li a {
  display: inline-flex; align-items: center; min-height: 26px;
  font-size: var(--t-sm); color: var(--plate-ink); text-decoration: none;
}
.foot__col li a:hover { color: var(--on-plate-strong); }
.foot__mach { margin: 0 0 var(--s3); font-size: var(--t-sm); line-height: var(--lh-body); color: var(--plate-ink-2); }
.foot__mach-link { font-size: var(--t-sm); color: var(--plate-ink); text-decoration: underline; text-decoration-color: var(--on-plate-strong); text-underline-offset: .2em; }
/* The link sat at 3.52:1 on the slab because --plate-ink is tuned for body
   copy at a larger size. An interactive element needs the full floor. */
.foot__mach-link { color: #E4E8EE; }
.foot__alpha { margin: 0; font-size: var(--t-sm); line-height: var(--lh-body); color: var(--plate-ink-2); max-width: 32rem; }
.foot__base {
  grid-column: 1 / -1; display: flex; justify-content: flex-end; gap: var(--s5) var(--s7);
  flex-wrap: wrap; align-items: baseline;
  border-block-start: 1px solid rgba(255,255,255,.16); padding-block-start: var(--s5); margin-block-start: var(--s4);
}
.foot .foot__legal { margin: 0; font: var(--t-xs)/var(--lh-body) var(--ui); color: var(--plate-ink-2); }
.foot .foot__legal a { color: var(--plate-ink-2); text-decoration: none; }
.foot .foot__legal a:hover { color: var(--on-plate-strong); }
@media (max-width: 52rem) {
  .foot .wrap { grid-template-columns: minmax(0, 1fr); gap: var(--s6); }
  .foot__col--wide { grid-column: span 1; }
}

/* ==========================================================================
   ONE FAMILY MEANS ONE FAMILY.

   Schibsted was chosen for display AND UI, then mono kept carrying every label,
   count and piece of metadata — which is a third voice nobody picked, and the
   single biggest reason the stats rail and footer labels read as developer
   documentation.

   Metadata is now Schibsted: small, uppercase, tracked. Mono survives only
   where the content is genuinely machine text — claim addresses, file paths,
   JSON — because there it is semantic rather than decorative.
   ========================================================================== */
.hero__stats dt, .foot__h, .margin__head, .field label, .field__note,
.shelf__n, .count, .world__meta, .spec__why, .result-count, .lab,
.n, .crumb, .nav__state, .foot__legal, .status, .record__basis,
.mix-opt__state, .mix-summary, .mix-area h4, .s-hit__aka, .n-ev__kind,
.pipeline, .lab-reason, .entry__flag, .index-head .sub, .dreams li::before {
  /* THE `label` TOKEN.
     This rule used to add `text-transform: uppercase` and 0.06em tracking to
     twenty-seven selectors at once — the single largest source of the
     uppercase micro-label treatment. Caps were doing no semantic work here:
     they were making the interface look technical. Aesthetary IS technical;
     it does not need to cosplay as a developer tool, and a label earns its
     rank from weight, size and colour instead.

     No `meta-caps` token was created. Every candidate for one turned out to
     be a label that simply wanted to be noticed, which is not a defensible
     semantic function for capitals. */
  font-family: var(--ui);
  font-size: var(--t-xs);
  font-weight: 500;
  line-height: var(--lh-tight);
  letter-spacing: 0;
  text-transform: none;
}
/* Sentence-case metadata that would read as shouting in caps. */
.spec__why, .mix-summary, .index-head .sub, .field__note, .margin__head,
.result-count, .status, .s-hit__aka {
  text-transform: none;
  letter-spacing: 0.01em;
  font-weight: 400;
}
.hero__stats dt { font-size: var(--t-xs); color: var(--ink-3); }
.dreams li::before { text-transform: none; letter-spacing: .08em; }
/* Genuinely machine text keeps the mono. */
code, pre, .claim-address { font-family: var(--mono); text-transform: none; letter-spacing: 0; }


/* ==========================================================================
   THE APPARATUS BELONGS TO THE REFERENCE, NOT TO THE PRODUCT.

   The marginal column is a reference-page device: on a record it carries the
   epistemic apparatus — what backs a claim, what is unrecorded, why a slot is
   silent. On Contact, FAQ, Dreams, Mix and Recipes it became a running
   commentary in a second column, which is clutter with a scholarly costume.

   `.plain` routes drop the margin entirely and give the text its full measure.
   Record pages, Learn and Method keep it, because there it is load-bearing.
   ========================================================================== */
.plain .leaf { display: block; }
.plain .margin { display: none; }
.plain .leaf__body { max-width: 52rem; }
.plain .leaf__body--wide { max-width: none; }

/* The margin itself was still set in mono, which is what made it read as a
   terminal note rather than an annotation. */
.margin { font-family: var(--ui); font-size: var(--t-sm); line-height: var(--lh-body); }
.margin p { font-family: var(--ui); }

/* ==========================================================================
   THE SHELL HAS FOUR TYPE TREATMENTS. That is the whole system.

     DISPLAY    Schibsted 650–700, tight tracking — page and section titles
     BODY       Schibsted 400 — everything a person reads
     META       Schibsted 500, small, tracked, uppercase — labels and counts
     CODE       IBM Plex Mono — claim addresses, paths, JSON, and nothing else

   A visual-language specimen may differ, because its presentation is derived
   from its own record. Nothing else on a shell page may introduce a fifth.
   ========================================================================== */
.leaf__body h2, .leaf__body h3, .display, .hero__title,
.collection__head h2, .browse__head h2, .shelf__head, .faq dt, .dreams h3,
.foot__mark, .world__name, .entry__name { font-family: var(--display); }
.leaf__body p, .leaf__body li, .leaf__body dd, .lead, .sub, .body,
.stack li, .deflist dd, .n-p { font-family: var(--ui); }

/* --- media placeholders ---------------------------------------------------
   Visibly reservations, never broken images. Dashed, labelled with what goes
   there and at what ratio, and dimensioned so the composition around them is
   correct before the asset exists. */
.ph { margin: var(--s6) 0 0; aspect-ratio: var(--ph-ratio, 16 / 9); border: 1px dashed var(--rule-firm);
  background: repeating-linear-gradient(135deg, transparent, transparent 9px, rgba(11,13,18,.028) 9px, rgba(11,13,18,.028) 18px);
  display: flex; align-items: flex-end; min-width: 0; }
.ph figcaption { padding: var(--s4); max-width: 34rem; }
.ph b { display: block; font: 500 var(--t-xs)/var(--lh-tight) var(--ui); color: var(--brand); margin-block-end: var(--s2); }
.ph span { display: block; font-size: var(--t-sm); line-height: var(--lh-body); color: var(--ink-2); }
.ph i { display: block; font: var(--t-xs)/1 var(--ui); color: var(--ink-3); font-style: normal; margin-block-start: var(--s2); }
.ph--dark { border-color: #4A515E; background: repeating-linear-gradient(135deg, transparent, transparent 9px, rgba(255,255,255,.035) 9px, rgba(255,255,255,.035) 18px); }
.ph--dark span { color: #B9C0CC; }
.ph--dark b { color: var(--on-plate-strong); }  /* 8.8:1; --brand-lift is 3.41 on this ground */
.ph--dark i { color: #6E7684; }


/* --- testimonial reservations ---------------------------------------------
   Visibly empty on purpose. No quote, no name, no avatar — the lines are
   blank rules, so nobody can mistake this for an endorsement that exists.
   Same register as the media placeholders: dashed, labelled, dimensioned. */
.tst { border: 1px dashed var(--rule-firm); padding: var(--s5); margin-block: var(--s6) 0; }
.tst__head { margin: 0 0 var(--s3); display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4); flex-wrap: wrap; }
/* The second survivor. Same mapping: weight and colour carry the rank. */
.tst__head b { font: 500 var(--t-xs)/var(--lh-tight) var(--ui); color: var(--brand); }
.tst__head span { font: var(--t-xs)/1 var(--ui); color: var(--ink-3); }
.tst__claim { margin: 0 0 var(--s5); font-size: var(--t-sm); line-height: var(--lh-body); color: var(--ink-2); max-width: 46rem; }
.tst__slots { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); gap: var(--s5); }
.tst__slot { min-width: 0; border-inline-start: 2px solid var(--rule); padding-inline-start: var(--s4); }
.tst__mark { display: block; font: 650 var(--t-lg)/1 var(--display); color: var(--rule-firm); }
.tst__line { display: block; height: 1px; background: var(--rule-firm); margin-block: var(--s4); }
.tst__line--short { width: 62%; }
.tst__attr { display: block; font: var(--t-xs)/var(--lh-tight) var(--ui); color: var(--ink-3); margin-block-start: var(--s4); }
.tst__ask { margin: var(--s5) 0 0; font-size: var(--t-xs); line-height: var(--lh-body); color: var(--ink-3); max-width: 46rem; }
.tst__ask b { color: var(--ink-2); font-weight: 600; }


/* ==========================================================================
   EXPLORE — two modes, one route.

   Discover is the default: horizontal rails of peer items, which is the one
   carousel pattern that earns its place, because browsing is the purpose and
   the continuation is visible at the edge. The A–Z index is not deleted — it
   answers a different question (you already know the name) and it is what
   researchers, search engines and power users need.

   The toggle is a radio pair, not JavaScript: it works before any script runs
   and both views are keyboard-reachable immediately.
   ========================================================================== */
.modes { display: inline-flex; border: 1px solid var(--rule-firm); margin-block: var(--s6) var(--s5); }
.modes__in { position: absolute; opacity: 0; pointer-events: none; }
.modes__btn { font: 500 var(--t-sm)/1 var(--ui); display: inline-flex; align-items: center; min-height: 44px;
  padding-inline: var(--s5); cursor: pointer; color: var(--ink-3); }
.modes__btn:hover { color: var(--ink); }
.modes__in:focus-visible ~ .modes .modes__btn { outline: 2px solid var(--brand); outline-offset: 2px; }

.xview--index { display: none; }
#xm-index:checked ~ .xview--index { display: block; }
#xm-index:checked ~ .xview--discover { display: none; }
#xm-discover:checked ~ .modes label[for="xm-discover"],
#xm-index:checked ~ .modes label[for="xm-index"] { background: var(--ink); color: var(--paper); }

/* --- rails ----------------------------------------------------------------- */
.rails { margin-block: var(--s6) var(--s8); }
.rail { margin-block-end: var(--s8); }
.rail__head { margin-block-end: var(--s4); }
.rail__h { margin: 0; font-size: var(--t-md); display: flex; align-items: baseline; gap: var(--s3); }
.rail__n { font: var(--t-xs)/1 var(--ui); color: var(--ink-3); }
.rail__basis { margin: var(--s2) 0 0; font-size: var(--t-xs); line-height: var(--lh-body); color: var(--ink-3); max-width: 44rem; }
/* Peer items in a row that scrolls. The next card is always partly visible,
   which is what makes it browsing rather than a slideshow. */
.rail__track {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(min(16rem, 78vw), 16rem);
  gap: var(--s4); overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x proximity; padding-block-end: var(--s3);
  scrollbar-width: thin;
}
.rail__track > * { scroll-snap-align: start; min-width: 0; }
.rail__track .world { max-width: none; }

/* ==========================================================================
   SCROLL REVEAL.

   Every rule here is gated on `.js-reveal` on <html>, which reveal.js adds
   only after it has confirmed IntersectionObserver and checked the motion
   preference. Without the script the selectors never match and the document
   renders exactly as it does today — the reveal cannot hide the site.

   12px and 460ms. Small enough to read as the page settling rather than as
   an effect, and one gesture for every section so scrolling has a rhythm
   instead of a sequence of entrances.
   ========================================================================== */
.js-reveal :is(main > section, main > .rec, main > .wrap > section, .docs__main > section, main > .ia > .hq-sec, .foot__cols) {
  opacity: 0; transform: translateY(12px);
  transition: opacity .46s cubic-bezier(.22,.61,.36,1), transform .46s cubic-bezier(.22,.61,.36,1);
  /* The transform creates a containing block; without this a position:fixed
     or sticky child inside a section would anchor to the section instead of
     the viewport for the life of the transition. */
  will-change: opacity, transform;
}
.js-reveal :is(main > section, main > .rec, main > .wrap > section, .docs__main > section, main > .ia > .hq-sec, .foot__cols).is-in {
  opacity: 1; transform: none; will-change: auto;
}
/* Above the fold at load: present, not arriving. */
.js-reveal :is(main > section, main > .rec, main > .wrap > section, .docs__main > section, main > .ia > .hq-sec, .foot__cols).is-instant {
  transition: none;
}
@media (prefers-reduced-motion: reduce) {
  /* Belt and braces: reveal.js already returns before adding the class, but if
     the preference changes after load the page must not be left blank. */
  .js-reveal :is(main > section, main > .rec, main > .wrap > section, .docs__main > section, main > .ia > .hq-sec, .foot__cols) {
    opacity: 1; transform: none; transition: none;
  }
}

/* The mixer's option groups. Named in the apparatus voice, because they are a
   statement about the state of the corpus rather than a control. */
.mix-opt__group {
  /* Was uppercase at .08em — one of two survivors of the retired micro-label.
     Mapped onto the same `label` treatment every other interface label uses. */
  font: 500 var(--t-xs)/var(--lh-tight) var(--ui); color: var(--ink-3);
  padding: var(--s4) 0 var(--s1);
}
.mix-opt__group:first-child { padding-block-start: 0; }

/* ==========================================================================
   A DISPLAY HEADING DOES NOT TAKE THE READING MEASURE.

   `--measure` is 38rem because that is roughly 70 characters of 17px body
   text. Applied to a 96px headline it is eight characters a line, which is
   how "Making visual language legible." — four words — ended up set on four
   lines with 256px of its own column sitting empty beside it.

   The system already had the right answer: `.display` carries `max-width:
   16ch`, a measure expressed in the heading's own characters, which is about
   845px at 96px and about 300px on a phone. It scales with the type because
   it is stated in the type's units. It simply never got a chance — the body
   column capped the heading at 608px before its own rule could apply.

   So the measure moves off the column and onto the prose, for the one case
   where a column opens with a display heading. The heading takes 16ch; the
   paragraphs under it keep the reading measure they already had. `.lead` is
   excluded because it carries its own, wider, measure on purpose.

   If :has() is unsupported the selector never matches and the page renders
   exactly as it does today, which is the correct way for this to fail.
   ========================================================================== */
.leaf__body:has(> .display) { max-width: none; }
.leaf__body:has(> .display) > :is(p, ul, ol, dl, blockquote):not(.lead) {
  max-width: var(--measure);
}

/* ---------------------------------------------------- the theme switch ---- */

/* NO ICON. The product shows behaviour instead of symbols everywhere else and
   a sun/moon pair would be the one exception. The control says what it will
   do — "Dark" on a light page, "Light" on a dark one — which is also the only
   labelling that survives a screen reader without a second announcement. */
/* `inherit`, not a token. This control appears on routes with bespoke
   palettes — the Annie pages, the Swiss reading view — and no token is safe
   across all of them: --ink-2 dropped the label to 1.95:1, and --ink landed
   on 1.01:1 where a page darkens its chrome without changing --ink. The
   surrounding text colour is the one value already contrast-checked against
   whatever is behind it, so the control takes that and nothing else. */
.themeswitch {
  font: var(--t-xs)/1 var(--mono);
  color: inherit; background: transparent;
  border: 1px solid color-mix(in srgb, currentColor 35%, transparent); border-radius: 0;
  padding: var(--s2) var(--s3); cursor: pointer;
  min-height: 32px;
}
.themeswitch:hover { border-color: currentColor; opacity: 0.75; }
.themeswitch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The classic switch (founder, 2026-09-24): the icon is the theme you'll
   GET — a moon in daylight, a sun in the dark. The text label stays for
   screen readers; the square keeps the 32px target. */
.themeswitch { min-width: 32px; padding: var(--s2); }
.themeswitch svg { display: block; margin: auto; }
.themeswitch .themeswitch__sun { display: none; }
.night .themeswitch .themeswitch__moon { display: none; }
.night .themeswitch .themeswitch__sun { display: block; }
.themeswitch [data-theme-label] {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.rail .themeswitch { justify-self: start; margin-block-end: var(--s3); }
.masthead__aside .themeswitch { align-self: center; }

/* FEEDBACK, FLOATING (founder, 2026-09-29): bottom right on every page, above room content and below open
   drawers. Solid ink so it reads on any room's palette; the First Impressionist violet (research.js) still wins. */
a.rail__feedback.site-feedback {
  position: fixed; z-index: 58; order: 0;
  right: max(var(--s4, 1rem), env(safe-area-inset-right));
  bottom: max(var(--s4, 1rem), env(safe-area-inset-bottom));
  display: inline-flex; align-items: center; gap: 0.45rem;
  height: 2.75rem; padding: 0 1.05rem;
  background: var(--ink); color: var(--paper); border: 1px solid var(--ink);
  font: 600 var(--t-sm, 0.875rem)/1 var(--ui, system-ui, sans-serif); letter-spacing: 0.01em; text-decoration: none;
}
a.rail__feedback.site-feedback:hover { background: var(--brand, #5B21D6); border-color: var(--brand, #5B21D6); color: var(--paper); }
a.rail__feedback.site-feedback:focus-visible { outline: 2px solid var(--brand, #5B21D6); outline-offset: 3px; }
@media print { a.rail__feedback.site-feedback { display: none; } }
/* On a phone the page's action bar (Save · Vote · Contribute) runs along the bottom edge; Feedback sits above it. */
@media (max-width: 48rem) {
  body:has(.ia-act) a.rail__feedback.site-feedback { bottom: calc(max(var(--s3, 0.75rem), env(safe-area-inset-bottom)) + 2.25rem + 0.6rem); right: var(--s2, 0.5rem); }
}
@media (max-width: 21.25rem) {
  body:has(.ia-act) a.rail__feedback.site-feedback { bottom: calc(max(var(--s3, 0.75rem), env(safe-area-inset-bottom)) + 4.75rem + 0.6rem); }
}

/* PRODUCT UPDATES — the email signup (2026-09-29). One field and one button
   in the house square; the status line takes the place of any toast. */
.subscribe { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s2); }
.subscribe[hidden] { display: none; }
.subscribe__label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.subscribe__row { display: flex; }
.subscribe__input { flex: 1 1 0; width: 0; min-width: 0; box-sizing: border-box; height: 36px; padding: 0 var(--s3); border: 1px solid var(--rule-firm); border-right: 0; border-radius: 0; background: var(--paper); color: var(--ink); font: var(--t-sm)/1 var(--ui); }
.subscribe__input:focus-visible { outline: 2px solid var(--brand, #5B21D6); outline-offset: -2px; }
.subscribe__input[aria-invalid="true"] { border-color: currentColor; }
.subscribe__go { flex: none; height: 36px; padding: 0 var(--s3); border: 1px solid var(--brand, #5B21D6); border-radius: 0; background: var(--brand, #5B21D6); color: var(--paper, #fff); font: 500 var(--t-sm)/1 var(--ui); cursor: pointer; }
.subscribe__go:hover { background: var(--brand-lift, #7C3AED); border-color: var(--brand-lift, #7C3AED); }
.subscribe__go:disabled { opacity: 0.6; cursor: default; }
.subscribe__fine { margin: 0; font: var(--t-xs)/1.4 var(--ui); color: var(--ink-3); }
.subscribe__status { margin: 0; font: 500 var(--t-sm)/1.3 var(--ui); color: var(--ink); }
.subscribe__status:empty { display: none; }
.subscribe[data-state="done"] .subscribe__row, .subscribe[data-state="done"] .subscribe__fine { display: none; }
