
:root {
  --s1: 2px;
  --s2: 4px;
  --s3: 6px;
  --s4: 8px;
  --s5: 10px;
  --s6: 12px;
  --s7: 14px;
  --s8: 16px;
  --s9: 20px;
  --s10: 24px;
  --s11: 32px;
  --s12: 40px;
  --s13: 48px;

  --t1: 9px;
  --t2: 10px;
  --t3: 11px;
  --t4: 12.5px;
  --t5: 13.5px;
  --t6: 15px;
  --t7: 17px;
  --t8: 20px;
  --t9: 24px;
  --t10: 30px;
  --t11: 40px;

  /* Small text is read in bulk and wants air; a heading is one line and a
     loose one reads as two. */
  --lh1: 1.4;
  --lh2: 1.4;
  --lh3: 1.45;
  --lh4: 1.5;
  --lh5: 1.55;
  --lh6: 1.6;
  --lh7: 1.5;
  --lh8: 1.35;
  --lh9: 1.25;
  --lh10: 1.2;
  --lh11: 1.1;
  /* The interface's running text (UIC-42): `--t5` on this, every door. */
  --lh-body: 1.5;
  /* The least a field's type is on a phone, or iOS zooms into it (PBOX-13). */
  --t-field: 16px;
  /* A field's type: the running text's, and `--t-field` on a phone (`controls.css`), whoever draws the box. */
  --t-input: var(--t5);

  --r1: 2px;
  --r2: 4px;
  --r3: 6px;
  --r4: 8px;
  --r5: 10px;
  --r6: 12px;
  --r7: 16px;
  --r-pill: 999px;

  /* How long a state takes to arrive. Fifty-three rules across the sheets
     spelled twenty-two different durations for the one thing a hover does,
     and a screen where two neighbours settle at different moments reads as
     two screens. Long enough to be seen, short enough not to be waited for. */
  --quick: .15s ease;
  /* What unfolds — a label, a fold, a list closing to its row — and a mark that lands (UIC-42). */
  --unfold: .35s cubic-bezier(.4, 0, .2, 1);
  --spring: .18s cubic-bezier(.34, 1.56, .64, 1);
  /* A notice coming up from the foot, and going (UIC-42). */
  --arrive: .22s ease-out;
  --leave: .2s ease-in;
  /* A drawer sliding in from the right, and out again — the script waits out the second (UI-8). */
  --enter: .22s cubic-bezier(.4, 0, .2, 1);
  --exit: .18s cubic-bezier(.4, 0, .2, 1);
  /* The pulse of something at work — the assistant's three dots (UIC-42). */
  --beat: 1.2s;

  /* The three measures every product has and each had named for itself —
     `--k-page`/`--k-prose`/`--k-gutter` in the portal, a literal 1240 in nine
     of the portal's rules and in the front page's wrapper, `container` and
     `container-narrow` in the console's. They are *measures* and not steps
     (UI-42), which is why they are lengths here and not rungs; what they are
     not is one product's, so they are named once and a product that wants a
     different page retunes the name rather than inventing one.

     `--measure` and not `--page`: `--page` is the ground a page is painted
     on, and a width called the same thing as a colour is an hour lost the
     first time somebody reaches for the wrong one.

     `--read` is the fourth (UIC-41): the one centred column a page that is
     read or filled in stands in — a manual, the account, the settings — its
     title and its body on the same two edges. */
  --measure: 1240px;
  --read: 880px;
  --prose: 62ch;
  --gutter: clamp(16px, 2.4vw, 44px);

  /* The way back's ring, and the least a finger can press (UIC-43). */
  --back: 34px;
  --finger: 44px;

  /* The card a question or a small job is asked in (UIC-44): the mailbox's
     delete-task card, and its wide twin for a form that needs the room. */
  --dialog: 430px;
  --dialog-wide: 640px;
  /* The column a sheet's job stands in, its head on the same edges (UIC-43). */
  --sheet-col: 660px;
  /* And a writing surface's: the composer, its head, its message and its foot on one wide column. */
  --sheet-wide: 1420px;
  /* One turn of a conversation: a message, a tool's call, the cards it points at (UIC-42). */
  --bubble: 720px;
  /* A phone's width, for a page drawn as a phone would draw it (the console's canvas). */
  --phone: 402px;
  /* The least a field in a table's cell is drawn at: a few words (`Field.cell`). */
  --cell-least: 128px;
  /* A numbered part's name, down the left of its controls where the column holds both (KODU-110). */
  --part-label: 180px;

  /* The heights a press stands at, the mailbox's (UIC-42, P8); a field's box is the tall one. */
  --press: 30px;
  --press-small: 27px;
  --press-tall: 38px;

  /* The controls' own measures (UIC-42): the switch's track, a paint, a menu, a card, a scrolling list. */
  --switch-w: 44px;
  --switch-h: 26px;
  --swatch-size: 28px;
  --menu: 210px;
  --tile: 104px;
  --list-max: 272px;
  /* A list's search pill at its widest, on a board with the room to stretch it (UIC-43). */
  --find: 420px;
  /* The strip a notice is said in, a run of a form's controls, the report box (UIC-42). */
  --notice: 560px;
  --group: 560px;
  --report: 340px;
  /* A drawer's panel: a detail beside what is open, or a record's form (UI-8, UIC-43). */
  --drawer: 544px;

  /* A working surface's columns, the mailbox's, on every door (UIC-42, UIC-38): the rail, the list beside it, the list of what its chosen row holds (the mailbox's threads), and a side column for a part of the thing open (the console's properties, a record's facts). */
  --rail-col: 84px;
  --list-col: clamp(264px, 22vw, 296px);
  --sublist-col: 420px;
  /* The sublist in the list's place below the wide desk: its head holds the way back, a face, a name and two rounds. */
  --sublist-narrow: 300px;
  --side-col: 300px;
  /* How far in from a column's edges, and a pane's, what they hold stands: a phone's, the frame widening both on a desk (`layouts.css`). */
  --list-inset: var(--s7);
  --pane-inset: var(--s7);
  /* The least the thing keeps beside its side column before the side stands under it (`layouts::sided`). */
  --main-least: 420px;
  /* How far a sheet from the foot rises over the screen: the part chosen inside a pane below the wide desk. */
  --sheet-rise: 62dvh;
  /* How far what hangs on a face's edge stands in: nothing on a phone, where the face stands down; a face and its gap on a desk (`lists.css`, UIC-51). */
  --hang: 0px;
  /* The room left after a page's last thing on a phone, so it is never read against the foot (`layouts.css`, UIC-51). */
  --page-end: calc(var(--s13) * 2.5);
}
/* A phone's field at the size iOS will not zoom into (PBOX-13), so the device's width asks. In the
   scale and not in `controls.css`: every sheet starts with this, and a page's second sheet put the
   desk's `--t-input` back after the tier's phone had set it — the front page's form and Miiu's
   prompt zoomed on every tap. */
@media (max-width: 640px) {
  :root { --t-input: var(--t-field); }
}

/* comm.ee's own look (SITE-8, UIC-17): the ground, the ink, the gold and the
   two faces the front page is set in — kept in Platform so the front page
   and the assistant's door (`ai.<base>`) read one file and agree.

   Three states, not two: the reader's system setting, and the reader
   overruling it either way (`data-theme="light"` / `"dark"` on the root).
   The tier's own pattern in the tier's own order, so nothing needs
   `!important`: the media block is `:root:not([data-theme="light"])` so a
   reader who chose light on a dark machine keeps light, and the explicit
   dark block follows it so a reader who chose dark on a light machine
   gets dark.

   The palette is written onto the *shared* token names as well as its own:
   every `ui-*` control reads --surface, --ink, --line and their kin, so
   setting those from the `--hp-*` values is what makes the controls wear
   this look without a second stylesheet knowing about it (UIC-10: a look
   is modifications on the one set, not a palette of its own). */

:root {
  --hp-bg: #F1F1F0;
  --hp-surface: #FFFFFF;
  --hp-line: #E3E3E1;
  --hp-line2: #CFCFCB;
  --hp-ink: #111111;
  --hp-ink2: #45443F;
  --hp-mute: #6B6A64;
  --hp-on-ink: #F1F1F0;
  --hp-nav: rgba(241, 241, 240, 0.8);
  --hp-link: #7A5B1C;
  --hp-link-hover: #5E4512;
  --hp-gold: #8A6A2A;
  /* How deep a system's tone sinks into the ground for its band: enough to
     tell the bands apart, not enough to stop the page being the ground. */
  --hp-tint: 8%;
  /* The four tones are the same on either ground. */
  --hp-indigo: #5B5BF0;
  --hp-teal: #10C8B4;
  --hp-coral: #FF6B54;
  --hp-amber: #FFB93D;

  /* The faces: a serif with character for the headlines, a plain sans for
     the rest. Loaded by the page's head; these are the names the tier's
     controls set their type in. */
  --sans: 'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --serif: 'Fraunces', 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
}

/* The shared names, at the weight of the tier's own scheme blocks: the
   tier writes its dark under `:root:not([data-theme="light"])`, which
   outweighs a plain `:root` whatever the order, so a mapping written there
   held in light and lost in dark — the assistant's ground came out the
   tier's warm grey on a dark machine, not this black. Every root matches
   one of these two, and both weigh what the tier's blocks weigh. */
:root:not([data-theme="light"]),
:root[data-theme="light"] {
  --bg: var(--hp-bg);
  --page: var(--hp-bg);
  --surface: var(--hp-surface);
  --mid: color-mix(in oklab, var(--hp-bg) 50%, var(--hp-surface));
  --rail: var(--hp-bg);
  --field: var(--hp-surface);
  --hover: color-mix(in oklab, var(--hp-surface) 92%, var(--hp-ink));
  --chip: var(--hp-line);
  --line: var(--hp-line2);
  --line2: var(--hp-line2);
  --hair: var(--hp-line);
  --edge: var(--hp-line2);
  --ring: var(--hp-gold);
  --ink: var(--hp-ink);
  --ink2: var(--hp-ink2);
  --ink3: var(--hp-ink2);
  --mute: var(--hp-mute);
  --faint: var(--hp-mute);
  --rail-ink: var(--hp-mute);
  /* The page's own gold, which is what it spends attention with everywhere
     else — the links, the hover, the mark. */
  --accent: var(--hp-gold);
  --accent-down: var(--hp-link-hover);
  --accent-idle: color-mix(in oklab, var(--hp-gold) 35%, var(--hp-surface));
  --accent-line: var(--hp-gold);
  --accent-ink: var(--hp-on-ink);
  --accent-deep: var(--hp-link-hover);
  --accent-text: var(--hp-link);
  --chip-ink: var(--hp-link);
  --accent-soft: color-mix(in oklab, var(--hp-gold) 14%, transparent);
  --wash: color-mix(in oklab, var(--hp-gold) 9%, transparent);
  --solid: var(--hp-ink);
  --solid-ink: var(--hp-on-ink);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --hp-bg: #050505;
    --hp-surface: #121212;
    --hp-line: #1E1E1E;
    --hp-line2: #343434;
    --hp-ink: #F3EFE8;
    --hp-ink2: #CFCAC0;
    --hp-mute: #9A958B;
    --hp-on-ink: #050505;
    --hp-nav: rgba(5, 5, 5, 0.72);
    --hp-link: #E2C27A;
    --hp-link-hover: #F1D79A;
    --hp-gold: #D9B36A;
    --hp-tint: 9%;
  }
}
:root[data-theme="dark"] {
  --hp-bg: #050505;
  --hp-surface: #121212;
  --hp-line: #1E1E1E;
  --hp-line2: #343434;
  --hp-ink: #F3EFE8;
  --hp-ink2: #CFCAC0;
  --hp-mute: #9A958B;
  --hp-on-ink: #050505;
  --hp-nav: rgba(5, 5, 5, 0.72);
  --hp-link: #E2C27A;
  --hp-link-hover: #F1D79A;
  --hp-gold: #D9B36A;
  --hp-tint: 9%;
}

/* The account-settings sections (UI-40): their presses and fields are the tier's (UIC-43); what is left is what no tier piece says. */

/* The disc a person is, wherever they appear (UI-19), is the tier's face at the picker's size (`FaceSize::Portrait`, UIC-43). */

/* Dashed, and there is no dashed utility: this box is the one thing on the
   page that is not finished yet. */
.acct-totp { border: 1px dashed var(--line2, var(--line, oklch(0.88 0.006 85))); }
/* A camera reads the code, not a person: black modules on white paper, the
   standard's assumption, whatever either palette is doing around it. The
   quiet zone is the standard's too, not a step. */
.acct-qr { width: 168px; height: 168px; background: oklch(1 0 0); border: 8px solid oklch(1 0 0); border-radius: var(--r3); }
.acct-qr path { fill: oklch(0 0 0); }

/* The rows are a `stack line r-5 bg-surface` in the markup; this is the one
   thing a row cannot say about itself — that it has a neighbour above it. */
.acct-login + .acct-login { border-top: 1px solid var(--hair, var(--line, oklch(0.93 0.006 85))); }

/* Two pills and a panel, and each is here for its colour pair alone: there is
   no utility for a tag's accent text, and none for the deep red a warning
   is read in. Everything measured about them is a step in the markup, and so
   is a signed-in pill's green, which is `text-ok`. */
.acct-tag { color: var(--accent-text, var(--accent, oklch(0.38 0.1 265))); }
.acct-tag.is-flag {
  background: var(--danger-soft, oklch(0.96 0.03 25));
  color: var(--danger, oklch(0.5 0.16 25));
}
.acct-login-pill.is-failed {
  background: var(--danger-soft, oklch(0.96 0.03 25)); color: var(--danger, oklch(0.5 0.16 25));
}
.acct-flag-said { color: var(--danger-deep, var(--danger, oklch(0.45 0.13 25))); }

/* The chat robot's pane (UI-39) — one look on every surface, the mailbox's.
   Written on the token names both palettes define; the two the console does
   not have fall back. Self-contained on purpose: the mailbox does not load
   theme.css, so the pane brings its own buttons rather than leaning on `rb`.
   Lengths are steps on the ladder (UI-42). */

/* The disc a message hangs off, and with the gap beside it the column its text
   starts in. Everything that is *about* a message rather than in one lines up
   with the text, so the two are one decision rather than two numbers to keep
   in step. */
.ai-pane {
  --ai-face: var(--s10);
  --ai-hang: var(--hang);
  display: flex; flex-direction: column; height: 100%; min-height: 0; background: var(--page);
}
.ai-face {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: var(--press); height: var(--press); border-radius: 50%; font-size: var(--t4);
  background: var(--solid); color: var(--solid-ink);
  font-family: var(--serif); letter-spacing: 0.02em;
}
.ai-msg .ai-face { width: var(--ai-face); height: var(--ai-face); font-size: var(--t1); }
/* A surface with a face of its own (KODU-121): the picture fills the disc. */
img.ai-face { object-fit: cover; background: var(--surface); }

.ai-msgs {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: clamp(var(--s5), 2vw, var(--s8)) clamp(var(--s5), 2.5vw, var(--s9)); display: flex; flex-direction: column; gap: var(--s6);
}
.ai-msg { display: flex; gap: var(--s4); align-items: flex-end; max-width: min(var(--bubble), 92%); }
.ai-msg.is-you { align-self: flex-end; flex-direction: row-reverse; }
.ai-bubble {
  padding: var(--s4) var(--s6); border-radius: var(--r6); min-width: 0; overflow-wrap: anywhere;
  background: var(--surface); border: 1px solid var(--line);
}
.ai-msg.is-you .ai-bubble { background: var(--accent-soft); border-color: transparent; }
.ai-bubble p { margin: 0 0 var(--s4); }
.ai-bubble p:last-child { margin-bottom: 0; }
.ai-bubble code { font-family: var(--mono); font-size: var(--t4); }
/* The robot answers with documents: its tables, lists and code arrive as
   such, sized for a bubble rather than for the manual. */
.ai-bubble h1, .ai-bubble h2, .ai-bubble h3, .ai-bubble h4 {
  font-family: var(--serif); font-size: var(--t5); margin: var(--s5) 0 var(--s3);
}
.ai-bubble ul, .ai-bubble ol { margin: 0 0 var(--s4); padding-left: var(--s9); }
.ai-bubble li { margin: var(--s1) 0; }
/* A place the robot named (LLM-31): a link in the reply, to the mail beside
   the chat — drawn as one, in the accent, so the reader knows it opens. */
.ai-bubble a { color: var(--accent); text-decoration: underline; text-underline-offset: 0.15em; }
.ai-bubble a:hover { color: var(--ink); }
.ai-bubble .md-table {
  border-collapse: collapse; margin: var(--s3) 0 var(--s4); font-size: var(--t4);
  display: block; overflow-x: auto; max-width: 100%;
}
.ai-bubble .md-table th, .ai-bubble .md-table td {
  border: 1px solid var(--line); padding: var(--s2) var(--s4);
  text-align: left; vertical-align: top;
}
.ai-bubble .md-table th { background: var(--page); font-weight: 600; }
.ai-bubble .md-code {
  background: var(--page); border: 1px solid var(--line); border-radius: var(--r3);
  padding: var(--s4) var(--s5); overflow-x: auto; font-size: var(--t3); margin: var(--s3) 0 var(--s4);
}

.ai-call { max-width: min(var(--bubble), 92%); margin-left: var(--ai-hang); }
.ai-call summary { display: inline-flex; cursor: pointer; list-style: none; }
.ai-call summary::-webkit-details-marker { display: none; }
.ai-call[open] summary .ai-tool {
  background: var(--accent-soft); color: var(--accent-down, var(--accent)); border-color: transparent;
}
/* The fold's own gap: what a `<summary>` and the box under it are apart, and
   the one thing neither of them can say with a container's gap. */
.ai-call-io { margin-top: var(--s3); }
.ai-call-io pre {
  margin: 0; font-family: var(--mono); font-size: var(--t3); line-height: var(--lh3);
  white-space: pre-wrap; overflow-wrap: anywhere; max-height: calc(6 * var(--s12)); overflow: auto;
  color: var(--ink);
}

/* A finished turn's working, folded behind the answer (UI-44). The pill hangs
   where the tool chips hang; everything inside keeps the place it always had,
   so opening the fold draws the thread that used to be drawn outright. */
.ai-steps > summary {
  display: inline-flex; align-items: center; gap: var(--s3);
  margin-left: var(--ai-hang); cursor: pointer; list-style: none;
}
.ai-steps > summary::-webkit-details-marker { display: none; }
.ai-steps > summary:hover .ai-steps-count { color: var(--mute); border-color: var(--accent); }
.ai-steps[open] > summary .ai-steps-count {
  background: var(--accent-soft); color: var(--accent-down, var(--accent)); border-color: transparent;
}
.ai-steps[open] > summary .ai-steps-turn { transform: rotate(90deg); }

/* What a tool answer pointed at (LLM-21), drawn the way the service's own
   grid draws it: picture on top, the title over its line, the page in a
   tab. Inline, the words place the grid inside their own bubble. */
/* The track's floor is a floor and not an absolute (UIC-26): at 184 the pane
   leaves a card 112 wide, and a hard 150 put its right edge 11px past the
   viewport with the message list scrolling sideways to reach it. `min()`
   collapses the floor to the container's own width in the one case it is
   narrower, and changes nothing at any width above it. */
.ai-cards {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(calc(3 * var(--s13)), 100%), 1fr));
  gap: var(--s4); margin-left: var(--ai-hang); max-width: min(var(--bubble), 92%);
}
.ai-cards.is-inline { margin: var(--s4) 0; max-width: none; }
/* A card is a link and reads as a card, not as a link. */
.ai-card { text-decoration: none; }
/* The frame a picture is cut to — the same three-by-two the portal's own
   cards wear, so a drawing keeps its roof and its ground rather than the
   strip through its middle — and the wash where there is no picture. */
.ai-card-pic { aspect-ratio: 3 / 2; height: auto; background: var(--accent-soft); }
.ai-card-pic.is-blank { min-height: calc(2 * var(--s13)); }

/* A form the words placed (LLM-22): the tool's own schema as the tier's
   fields, inside the bubble, required first. A block in the middle of prose,
   so it keeps a margin from the words above and below it. */
.ai-form { margin: var(--s4) 0; }

/* The step being worked right now: the tool's name where a finished step's
   chip will stand, with the same dots the thinking bubble beats (UI-44). */
.ai-step-now { margin-left: var(--ai-hang); }
/* Dashed while it is running, solid once it is a step that happened. */
.ai-step-now .ai-tool {
  font-size: var(--t2); font-weight: 600; color: var(--faint);
  text-transform: uppercase; letter-spacing: 0.06em;
  border-style: dashed;
}

.ai-confirm {
  display: flex; flex-direction: column; gap: var(--s5);
  max-width: min(var(--bubble), 92%); margin-left: var(--ai-hang); padding: var(--s6) var(--s7);
  border-radius: var(--r5); background: var(--warn-bg);
  border: 1px solid var(--warn-line); color: var(--warn-ink); font-size: var(--t4);
}
.ai-confirm-said {
  margin: 0; padding: var(--s3) var(--s5); border-left: var(--s1) solid var(--warn-line);
  color: var(--ink); background: var(--surface); border-radius: 0 var(--r3) var(--r3) 0;
  overflow-wrap: anywhere;
}
.ai-confirm-args {
  margin: 0; padding: var(--s3) var(--s5); font-family: var(--mono); font-size: var(--t3);
  line-height: var(--lh3); white-space: pre-wrap; overflow-wrap: anywhere;
  max-height: calc(5 * var(--s12)); overflow: auto; color: var(--ink);
  background: var(--surface); border-left: var(--s1) solid var(--warn-line);
  border-radius: 0 var(--r3) var(--r3) 0;
}
.ai-told { margin: 0 0 0 var(--ai-hang); color: var(--mute); font-size: var(--t4); }
.ai-told.is-done { color: var(--ok-ink, var(--ink)); }
.ai-typing { white-space: pre-wrap; }
.ai-msgs .ai-error { padding: 0 0 0 var(--ai-hang); }

.ai-composer {
  display: flex; flex-direction: column; gap: var(--s4); flex: none;
  /* The narrowest screens give up padding and nothing else, fluidly (UIC-42): the press keeps its whole size. */
  padding: clamp(var(--s4), 1.5vw, var(--s6)) clamp(var(--s5), 2.5vw, var(--s9)) calc(clamp(var(--s5), 2vw, var(--s7)) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line); background: var(--surface);
}
.ai-composer > .row { align-items: flex-end; }

/* The pictures chosen for the next message (LLM-37), and what was refused
   choosing one: a row of utilities, hidden by the tier's `[hidden]`. */
.ai-chose { position: relative; display: inline-flex; }
/* The × is the tier's small round (`chosen`), placed on the picture's corner. */
.ai-chose-off { position: absolute; top: var(--s2); right: var(--s2); }
/* Everything let go over the conversation is a picture for it. */
.ai-pane.is-dropping .ai-composer { border-top-color: var(--accent); }
/* One picture, wherever it is shown: on the strip before it is sent, and on
   the message once it has ridden. */
.ai-shot {
  display: block; width: calc(2 * var(--s13)); height: calc(2 * var(--s13)); object-fit: cover;
  border-radius: var(--r4); border: 1px solid var(--line); background: var(--page);
}
/* The box is the tier's field (P11); the composer lets it take the row, and `grow` measures it to its words up to this cap. */
.ai-ask { flex: 1; max-height: calc(4 * var(--s12)); }

.ai-dots { display: inline-flex; gap: var(--s2); padding: var(--s2) var(--s1) var(--s1); }
.ai-dots i {
  width: var(--s3); height: var(--s3); border-radius: 50%; background: var(--mute);
  animation: ai-think var(--beat) infinite;
}
.ai-dots i:nth-child(2) { animation-delay: calc(var(--beat) / 6); }
.ai-dots i:nth-child(3) { animation-delay: calc(var(--beat) / 3); }
@keyframes ai-think { 0%, 60%, 100% { opacity: 0.25; } 30% { opacity: 1; } }

/* ── the pane in a dock (UIC-13) ─────────────────────────────────────────── */

/* The panel says what this is in its own head and scrolls its own body, so in
   here the pane drops its head and fills the height rather than scrolling
   twice. Narrower than a page column: bubbles take the width they are given
   and the cards stack. */
.ai-dock .ai-head { display: none; }
.ai-dock .ai-pane { background: var(--surface); }
.ai-dock .ai-msgs { padding: var(--s6); gap: var(--s6); }
.ai-dock .ai-composer { padding: var(--s5) var(--s6) calc(var(--s6) + env(safe-area-inset-bottom)); }
.ai-dock .ai-msg, .ai-dock .ai-call, .ai-dock .ai-cards, .ai-dock .ai-confirm { max-width: 100%; }
.ai-dock .ai-error { padding: 0 var(--s6); }

/* ── the pane as the page (UIC-22) ───────────────────────────────────────── */

/* Wide, the thread and the composer keep a reading measure in the middle
   rather than running the width of a desk: a line of an answer that is
   1400px long is one nobody reads twice. The fold's rows and the reach
   breathe as columns. */
/* Fluid between a phone's inset and a desk's, so no width is asked (UIC-42). */
.ai-dock.is-page .ai-msgs { padding: clamp(var(--s6), 2vw, var(--s9)) clamp(var(--s6), 2vw, var(--s8)) var(--s7); gap: var(--s7); align-items: center; }
.ai-dock.is-page .ai-msgs > * { width: 100%; max-width: var(--read); }
.ai-dock.is-page .ai-msg.is-you { align-self: center; justify-content: flex-start; }
.ai-dock.is-page .ai-composer { padding: clamp(var(--s5), 1.5vw, var(--s6)) clamp(var(--s6), 2vw, var(--s8)) calc(clamp(var(--s6), 2vw, var(--s8)) + env(safe-area-inset-bottom)); align-items: center; }
/* The composer is a column now — the strip of chosen pictures over the row it
   belongs to (LLM-37) — so the reading measure is kept by the row and the
   strip rather than by the composer itself. */
.ai-dock.is-page .ai-composer > .row { justify-content: center; width: 100%; }
.ai-dock.is-page .ai-chosen { justify-content: center; }
.ai-dock.is-page .ai-ask { max-width: calc(var(--read) - var(--s11) - var(--s4)); }
/* The reach as a column (`stack gap-5` on the markup): what it reads, one
   line each. */
.pa-reach-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
/* Hairline rows, the editor's manner (UIC-25): no card under each. */
.pa-reach-row {
  display: flex; flex-direction: column; gap: var(--s1); min-width: 0;
  padding: var(--s4) 0; border-top: 1px solid var(--line);
}
.pa-reach-row:first-child { padding-top: 0; border-top: 0; }
.pa-reach-name { font-weight: 600; font-size: var(--t4); }
.pa-reach-what { color: var(--mute); font-size: var(--t3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* One earlier conversation, in the fold over the current one. */
.ai-past {
  display: block; padding: var(--s3) var(--s4); border-radius: var(--r4);
  color: var(--ink); text-decoration: none; font-size: var(--t4);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ai-past:hover { background: var(--hover); }
.ai-past.is-open { background: var(--accent-soft); }
/* The way to start again, at the head of the same list: it belongs with the
   conversations because choosing between them is the same choice. */
.ai-past.is-fresh { color: var(--accent); font-weight: 600; }

/* A row of the fold (LLM-32) — `flex center gap-1` on the markup — the
   conversation, and beside it the tier's round × that forgets it (UIC-43). */
.ai-past-row > .ai-past { flex: 1; min-width: 0; }

/* ── what the robot can reach (LLM-17, LLM-19) ───────────────────────────── */

/* The surface the gear opens (UI-14, 2026-09-09): the assistant's settings,
   in the tier's overlay and the editor's manner (`ui-part`, KODU-110) like
   every other settings surface — numbered parts, hairlines, no boxes. */
/* How many are connected here, beside the reach's heading. */
.pa-count {
  padding: var(--s1) var(--s3); border-radius: var(--r-pill);
  background: var(--chip); color: var(--mute); font-size: var(--t3); font-weight: 400;
}
.pa-part-title { margin: 0 0 var(--s1); font-size: var(--t6); }
/* A connection is a hairline, not a box: this chat's tick when a chat is
   open, the name, the address in the mono, what it answers, and the way off
   this surface. What a row's tools are folds out under it, full width. */
.pa-eps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.pa-ep {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s4) var(--s5);
  padding: var(--s5) 0; border-top: 1px solid var(--line);
}
.pa-ep:first-child { border-top: 0; padding-top: 0; }
.pa-tick input { width: var(--s8); height: var(--s8); margin: 0; }
.pa-ep-name { flex: 0 0 auto; min-width: calc(2 * var(--s13)); font-weight: 600; font-size: var(--t5); }
.pa-ep-url {
  flex: 1 1 calc(4 * var(--s13)); min-width: 0; color: var(--faint); font-family: var(--mono); font-size: var(--t3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pa-tools {
  flex-basis: 100%; list-style: none; margin: var(--s2) 0 0; padding: 0 0 0 var(--s2);
  display: flex; flex-direction: column; gap: var(--s1);
  border-left: var(--s1) solid var(--line);
}
.pa-tools li { display: flex; gap: var(--s4); align-items: baseline; padding-left: var(--s4); }
.pa-tool-name { font-size: var(--t3); color: var(--ink); }
.pa-tool-about {
  color: var(--faint); font-size: var(--t3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Staged, not saved (LLM-20). One press moves a row into one of these two
   states and the next press at the foot is what makes either of them true;
   what a state does not show is put away by the tier's `[hidden]`. */
.pa-ep.is-going { background: var(--danger-soft); border-radius: var(--r3); padding-inline: var(--s3); }
.pa-ep.is-going .pa-ep-name, .pa-ep.is-going .pa-ep-url { text-decoration: line-through; color: var(--faint); }
.pa-ep.is-new { background: var(--accent-soft); border-radius: var(--r3); padding-inline: var(--s3); }
.pa-ep-going { margin-left: auto; color: var(--mute); font-size: var(--t3); white-space: nowrap; }
.pa-ep.is-going .pa-ep-going { color: var(--danger); }
.pa-dot {
  width: var(--s4); height: var(--s4); border-radius: var(--r-pill); flex: none;
  background: var(--accent);
}

/* Marie's sheet (MARIE-12): comm.ee's manner — the dark or the paper as the
   ground, a light serif for what is said, small labels, thin rules instead
   of boxes, pills — on the house scale and the platform's look. */

:root { --mr-bar: 64px; }

/* A column, so the foot (MARIE-76) falls to the bottom of a short screen
   instead of stopping halfway down a 404. `dvh`, for the address bar. Never
   one screen tall (MARIE-90): a body held at `100%` let everything past the
   first screen overflow it, and the ground stopped where the body did while
   the words went on. The ground is on the root as well, so whatever the body
   measures the page is the page. The type and the ground's colour are the
   tier's, one body for every door (UIC-42). */
html { background: var(--page); }
body { min-height: 100dvh; display: flex; flex-direction: column; }
a { color: var(--hp-link); }
a:hover { color: var(--hp-link-hover); }
::selection { background: var(--hp-gold); color: var(--hp-bg); }

/* What it holds is a page's measure wide, as a reading column's is (UIC-41); the gutter is outside it. */
.mr-wrap { width: 100%; max-width: calc(var(--measure) + 2 * var(--gutter)); margin: 0 auto; padding-inline: var(--gutter); box-sizing: border-box; }
.mr { padding-block: var(--s9) var(--s13); }
/* A reading page brings its own band and column (UIC-41): the wrapper steps aside. */
.mr-wrap:has(> .ui-reading) { max-width: none; padding: 0; }

/* ── the bar ─────────────────────────────────────────────────────────── */
.mr-top { position: sticky; top: 0; z-index: 10; background: var(--hp-nav); backdrop-filter: blur(12px); border-bottom: 1px solid var(--hair); }
.mr-top-inner { position: relative; display: flex; align-items: center; gap: var(--s10); height: var(--mr-bar); }
/* The brand whole, then the product's hand-written name that opens the family (MARIE-12). */
.mr-brandhome { display: inline-flex; height: 26px; color: var(--ink); }
.mr-brandhome svg { height: 100%; width: auto; display: block; }
.mr-product { margin-left: calc(-1 * var(--s6)); }
/* The family's list hangs from the bar, not the word, so it stays on a phone's glass. */
.mr-top .mr-product { position: static; }
.mr-productbtn { display: inline-flex; align-items: center; gap: var(--s2); cursor: pointer; list-style: none; user-select: none; color: var(--ink); padding: var(--s2) var(--s3); border-radius: var(--r2); }
.mr-productbtn::-webkit-details-marker { display: none; }
.mr-productbtn:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.mr-word { display: inline-flex; height: 34px; color: var(--ink); }
.mr-word svg { height: 100%; width: auto; display: block; }
.mr-top .mr-products { left: var(--gutter); right: auto; min-width: min(320px, calc(100% - 2 * var(--gutter))); max-width: calc(100% - 2 * var(--gutter)); display: flex; flex-direction: column; gap: var(--s2); padding: var(--s5); border-radius: 0 var(--r2) var(--r2) var(--r2); }
.mr-productrow { display: flex; flex-direction: column; gap: var(--s1); padding: var(--s4) var(--s5); border-radius: var(--r2); color: var(--ink); text-decoration: none; }
.mr-productrow:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--ink); }
.mr-productrow strong { font-weight: 600; }
.mr-productrow[aria-current] strong { color: var(--accent); }
.mr-productrow span { font-size: var(--t3); color: var(--mute); }
/* The menu is one element at every width (MARIE-80). Wide: the button is not
   drawn and the panel lies flat in the bar, which is the row the nav always
   was. Narrow: the button is the hamburger and the panel is this sheet's drop.
   The wide state has to undo `.mr-droppop`'s own positioning, because the same
   markup wears it in both. */
.mr-menu { margin-left: auto; min-width: 0; }
.mr-menubtn { display: none; }
.mr-menu .mr-droppop { position: static; padding: 0; border: 0; background: none; box-shadow: none; max-width: none; width: auto; display: flex; align-items: center; gap: var(--s9); }
/* A shut `details` hides what is in it, and the wide bar needs it shown — the
   button that would open it is not even drawn up here. Two engines to answer
   and both rules are needed: the older one hides the children with `display`,
   which the line above already overrides, and Chrome 131 and after hide the
   whole of `::details-content` with `content-visibility`, which only this
   reaches. An engine that has never heard of the pseudo drops this rule and is
   right either way. */
.mr-menu::details-content { content-visibility: visible; block-size: auto; }
.mr-menu .mr-menu-cta { display: none; }
.mr-nav { display: flex; gap: var(--s9); font-size: var(--t4); font-weight: 500; white-space: nowrap; }
.mr-nav a { color: var(--ink2); }
.mr-nav a:hover { color: var(--ink); }
.mr-top .ui-theme { margin-left: auto; }
.mr-menu + .ui-theme { margin-left: 0; }
.mr-top-cta { white-space: nowrap; flex: none; }

/* ── the voice ───────────────────────────────────────────────────────── */
.mr-h1, .mr-h2 { font-family: var(--serif); font-optical-sizing: auto; font-weight: 300; letter-spacing: -0.015em; margin: 0; text-wrap: balance; }
.mr-h1 { font-size: clamp(40px, 6vw, 84px); line-height: 1.02; letter-spacing: -0.025em; }
.mr-h2 { font-size: clamp(28px, 3.4vw, 44px); line-height: 1.08; }
.mr-accent { color: var(--hp-gold); }
.mr-lede { font-size: var(--t7); color: var(--ink2); max-width: var(--prose); margin: 0; text-wrap: pretty; }
.mr-meta { color: var(--ink2); margin: 0; font-size: var(--t5); }
.mr-fine { color: var(--mute); font-size: var(--t4); margin: 0; }
.mr-dim { color: var(--mute); font-size: var(--t4); }
.mr-ok { color: var(--hp-teal); font-weight: 500; }
.mr-bad { color: var(--hp-coral); }
.mr-warn { color: var(--hp-amber); }
code { font-family: var(--mono); font-size: var(--t4); padding: 0 var(--s2); border: 1px solid var(--hair); border-radius: var(--r1); }

/* ── the door ────────────────────────────────────────────────────────── */
.mr-hero { display: flex; flex-direction: column; gap: var(--s9); padding-block: clamp(40px, 6vw, 96px) clamp(32px, 4vw, 64px); max-width: 880px; }
.mr-ask { display: flex; flex-direction: column; gap: var(--s5); width: 100%; max-width: 720px; }
.mr-ask-foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s6) var(--s9); }
.mr-gate { padding: var(--s6) var(--s8); border-radius: var(--r2); background: color-mix(in srgb, var(--hp-amber) 18%, transparent); color: var(--ink); margin: 0; max-width: 720px; }
.mr-section { display: flex; flex-direction: column; gap: var(--s8); padding-block: var(--s11); border-top: 1px solid var(--hair); }
.mr-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--s10) var(--s9); }
.mr-tile { display: flex; flex-direction: column; gap: var(--s4); color: inherit; border-top: 1px solid var(--line2); padding-top: var(--s7); }
.mr-tile:hover { color: inherit; }
.mr-tile:hover .mr-name { color: var(--hp-gold); }
.mr-thumb { border: 1px solid var(--hair); border-radius: var(--r1); overflow: hidden; background: var(--surface); }
.mr-thumb svg { width: 100%; height: auto; display: block; }
.mr-name { font-size: var(--t5); font-weight: 500; }

/* ── a plan: the view and the drawing beside what is said about it ───── */
.mr-plan { display: grid; grid-template-columns: minmax(0, 7fr) minmax(300px, 5fr); gap: var(--s11) clamp(var(--s10), 4vw, 64px); align-items: start; }
.mr-plan-main, .mr-plan-side { display: flex; flex-direction: column; gap: var(--s10); min-width: 0; }
.mr-side, .mr-pay { display: flex; flex-direction: column; gap: var(--s6); border-top: 1px solid var(--line2); padding-top: var(--s7); }
.mr-doors { display: flex; flex-wrap: wrap; gap: var(--s4); margin: 0; }
.mr-list { margin: 0; padding-left: var(--s9); line-height: 1.7; }
.mr-scroll { overflow-x: auto; }
.mr-table { border-collapse: collapse; font-size: var(--t4); width: 100%; }
.mr-table th, .mr-table td { text-align: left; padding: var(--s3) var(--s5); border-bottom: 1px solid var(--hair); white-space: nowrap; vertical-align: middle; }
.mr-table th { font-weight: 500; color: var(--mute); letter-spacing: 0.04em; font-size: var(--t3); }
.mr-table code { border: none; padding: 0; }
.mr-acts { display: flex; gap: var(--s3); }
.mr-acts form { margin: 0; }
.mr-addrow { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s5) var(--s6); padding-top: var(--s6); border-top: 1px solid var(--hair); }


/* ── a plan: the picture in a dark box, and the page scrolls past it ───
   (MARIE-76) It was `position: fixed`, pinned under the bar to all four edges
   of the window, which made the plan the one screen on the site with nothing
   under it to reach — no foot, and no scroll at all. Contained: a box on the
   page's own measure, still the thing the screen is about, with the page
   scrolling normally under it.

   `dvh` and not `vh`: a telephone's address bar retracts, and a box measured
   against the tall viewport stands taller than the window it is read in.

   Nothing here gives way to the dock any more. Fixed, it had to — the dock
   makes room by padding the body and padding does not reach a fixed child
   (UIC-26) — and in the flow it is inside that padding like everything else. */
/* No side gutter (MARIE-91): *"make inspired screen full width"* — the
   picture runs from edge to edge and the page's own padding is above and
   below it only. */
.mr-full { padding: var(--s8) 0 var(--s13); }
.mr-stagefull {
  position: relative;
  width: 100%;
  max-width: none;
  margin-inline: auto;
  /* **Nearly the whole screen, and still visibly a box** (MARIE-82), on his
     word: *"can u make marie video area kinda full screen"* — against his own
     earlier *"in a black square contained, so i can scroll down"* (MARIE-76).
     Those pull opposite ways, so the height is **measured from the chrome**
     rather than guessed at: the window less the bar, less the page's own
     padding, less one step of daylight. A first cut at `92dvh` read as full
     screen and put the picture's bottom edge below the fold, which is exactly
     the containment MARIE-76 exists for — `marie_plan` caught it.
     `dvh` and not `vh`: a telephone's address bar retracts. */
  height: min(calc(100dvh - var(--mr-bar) - var(--s13) - var(--s11)), 1180px);
  border-radius: 0;
  overflow: hidden;
}
.mr-stagefull .mr-view { position: absolute; inset: 0; }
/* **The whole screen** (MARIE-91), from the press in the corner block: the
   box is the fullscreen element where a browser lets an element be one, and
   stands fixed over the page where it does not — an iPhone gives full screen
   to a video and nothing else. The author's measured height above would win
   over the browser's own `100%` for a fullscreen element, so it is said again. */
.mr-stagefull:fullscreen, .mr-stagefull.is-screen { height: 100%; max-width: none; margin: 0; }
.mr-stagefull.is-screen { position: fixed; inset: 0; z-index: 100; }
/* The press shows the way in, and inside, the way out. */
.mr-full-out { display: none; }
.mr-stagefull:is(:fullscreen, .is-screen) .mr-full-in { display: none; }
.mr-stagefull:is(:fullscreen, .is-screen) .mr-full-out { display: inline-flex; }
/* The ground is black in either scheme, which is the palette's own black and
   not a colour of this sheet's: `--hp-ink` on paper, `--hp-bg` in the dark.
   The picture drawn on it is lit, and a paper-white surround around a lit room
   reads as a photograph of a screen. Written in the tier's three blocks in the
   tier's order (look.css), or the dark one outweighs the plain `:root` and the
   ground holds in one scheme and is lost in the other. */
/* What stands behind the room, and the same in both schemes (MARIE-76): the
   canvas clears to this exact colour, so the moment before the first frame is
   not a different colour from the frame. One value, no scheme blocks — a
   picture of a room is lit by its own sun and does not follow the page. */
:root { --mr-ground: var(--hp-ink); }
.mr-stagefull .mr-stage { position: absolute; inset: 0; aspect-ratio: auto; max-height: none; border: none; border-radius: 0; background: var(--mr-ground); }
.mr-hudgrid { position: absolute; inset: var(--s6); display: grid; grid-template-columns: minmax(0, 1fr) minmax(320px, 440px); grid-template-rows: auto minmax(0, 1fr) auto; gap: var(--s5); pointer-events: none; }
.mr-hudgrid > * { pointer-events: auto; min-width: 0; }
.mr-hud { background: color-mix(in srgb, var(--surface) 94%, transparent); backdrop-filter: blur(10px); border: 1px solid var(--hair); border-radius: var(--r2); padding: var(--s5) var(--s7); margin: 0; }
.mr-hud-head { grid-column: 1; grid-row: 1; justify-self: start; max-width: 560px; display: flex; flex-direction: column; gap: var(--s2); }
/* The plan's name across the top (MARIE-59): its own child of the grid, spanning both columns and
   centred in them, so it is centred on the screen and not in the corner's own box. */
.mr-hud-title { grid-column: 1 / -1; grid-row: 1; justify-self: center; align-self: start; text-align: center; max-width: min(40ch, 100%); padding: var(--s3) var(--s7); }
/* Smaller on his word (MARIE-82): *"make title Elutuba and köök smaller"*. It
   is a label on a picture, not the headline of a page — at 34px it was reading
   as the loudest thing on a screen whose whole subject is the room behind it.
   The clamp keeps it legible on a telephone, where it is the only thing naming
   what is being looked at. `.mr-h3` is worn by this title and nothing else. */
.mr-h3 { font-family: var(--serif); font-optical-sizing: auto; font-weight: 300; font-size: clamp(15px, 1.5vw, 20px); line-height: 1.15; letter-spacing: -0.01em; margin: 0; }
/* The settings, put away behind one press the way the objects are (MARIE-61) — and dressed the
   same: shut, the tier's pill on the hud's own glass; open, the panel's own width. The wrapper
   takes the corner the bar used to hold; the bar is its body and lays its drops out as it always did. */
/* `start`, or it stretches to the row the title sets and the pill comes out a blob. */
.mr-hud-set { grid-column: 2; grid-row: 1; justify-self: end; align-self: start; padding: 0; }
.mr-hud-set[open] { width: 100%; }
.mr-hud-set:not([open]), .mr-hud-side:not([open]) { border-color: transparent; border-radius: var(--r-pill); overflow: hidden; }
.mr-hud-bar { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: var(--s3); padding: var(--s3); }
.mr-rung { padding-inline: var(--s3); }
/* Every picture is there in one frame now (MARIE-41), so the corner says frames and nothing else;
   the bar that tracked a gathering picture went with the gathering. */
.mr-fps { padding-inline: var(--s3); white-space: nowrap; }
/* The objects panel is one button at the foot on the right; open, it stands up over the picture. */
.mr-hud-side { grid-column: 2; grid-row: 2 / 4; align-self: end; justify-self: end; max-height: 100%; min-height: 0; overflow: auto; overscroll-behavior: contain; padding: 0; }
.mr-hud-side[open] { width: 100%; }
/* What a drag does, and the way back (MARIE-55): on the left, at the height of the
   hand, and there only while the camera is being moved. */
.mr-hud-turn { grid-column: 1; grid-row: 2; align-self: center; justify-self: start; display: flex; align-items: center; gap: var(--s4); padding: var(--s3); opacity: 0; visibility: hidden; transition: opacity var(--quick), visibility var(--quick); }
.mr-view.is-turning .mr-hud-turn { opacity: 1; visibility: visible; }
.mr-hud-hint { grid-column: 1; grid-row: 3; align-self: end; justify-self: start; font-size: var(--t3); color: var(--mute); max-width: 520px; }
/* The row a hud's press stands in; the press is the tier's pill. Open, the row stays at the panel's top. */
.mr-side-sum { display: block; padding: var(--s1); border-radius: var(--r-pill); cursor: pointer; list-style: none; user-select: none; white-space: nowrap; }
.mr-hud-side[open] .mr-side-sum { position: sticky; top: 0; z-index: 1; padding: var(--s3); border-radius: 0; background: var(--surface); border-bottom: 1px solid var(--hair); }
.mr-side-sum::-webkit-details-marker { display: none; }
.mr-when-open, .mr-hud-side[open] .mr-when-shut, .mr-hud-set[open] .mr-when-shut { display: none; }
.mr-hud-side[open] .mr-when-open, .mr-hud-set[open] .mr-when-open { display: inline; }
.mr-side-body { display: flex; flex-direction: column; gap: var(--s8); padding: 0 var(--s7) var(--s7); }
.mr-part { display: flex; flex-direction: column; gap: var(--s5); border-top: 1px solid var(--hair); padding-top: var(--s5); }
.mr-drawn summary { cursor: pointer; list-style: none; }
.mr-drawn summary::-webkit-details-marker { display: none; }
.mr-drawn svg { width: 100%; height: auto; display: block; margin-top: var(--s5); border: 1px solid var(--hair); border-radius: var(--r1); background: var(--surface); }

/* ── the switches: a drop the way Kodu's filters are (KODU-20) ─────────── */
.mr-drop { position: relative; }
.mr-dropsaid { color: var(--accent); font-weight: 500; }
.mr-dropmark { display: inline-flex; color: var(--mute); }
.mr-drop[open] .mr-dropmark { transform: rotate(180deg); }
.mr-droppop { position: absolute; z-index: 45; top: calc(100% + var(--s3)); right: 0; min-width: 100%; width: max-content; max-width: min(30rem, 88vw); padding: var(--s5); border: 1px solid var(--line); border-radius: var(--r4); background: var(--surface); box-shadow: var(--pop); }
.mr-chips { display: flex; flex-wrap: wrap; gap: var(--s2); }
.mr-droppop-sun { display: flex; align-items: center; min-width: 240px; }
/* The Download drop under the title: a list of links, opening to the right. */
/* The first line of the corner block: what the screen is, and the two presses
   (MARIE-82). A row, so the presses sit level with the words instead of under
   them; `wrap` so a narrow telephone puts them back on their own line rather
   than squeezing the name to nothing. */
.mr-headtop { display: flex; align-items: center; gap: var(--s6); flex-wrap: wrap; }
.mr-head-doors { align-items: center; margin-top: 0; }
.mr-head-doors form { margin: 0; }
.mr-hud-head .mr-droppop, .mr-hud-turn .mr-droppop { right: auto; left: 0; }
/* Inside the settings panel a drop opens in the panel's own flow and at its width, its choices
   wrapping under the bar: never past the panel's edge, on a phone or a desk (MARIE-61). */
.mr-hud-set .mr-drop[open] { flex: 1 0 100%; }
.mr-hud-set .mr-droppop { position: static; width: auto; min-width: 0; max-width: none; margin-top: var(--s3); box-shadow: none; }
.mr-droplist { display: flex; flex-direction: column; gap: var(--s1); padding: var(--s3); min-width: 220px; }
.mr-droplist a { display: block; padding: var(--s3) var(--s5); border-radius: var(--r2); color: var(--ink); font-size: var(--t4); text-decoration: none; white-space: nowrap; }
.mr-droplist a:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--accent); }
.mr-droppop-sun input { width: 100%; }
/* What the picture costs (MARIE-13's third addendum): a name on the left, the figure on the right. */
.mr-droppop-cost { display: flex; flex-direction: column; gap: var(--s1); min-width: 260px; font-size: var(--t4); }
.mr-costrow { display: flex; justify-content: space-between; gap: var(--s5); }
.mr-costrow span { color: var(--ink-dim); }
.mr-costrow b { font-weight: 500; text-align: right; }
.mr-costnote { margin: var(--s2) 0 0; color: var(--ink-dim); font-size: var(--t5); }

/* A choice is the tier's pill round its radio, its chosen one the accent's wash (UIC-43). */
.mr-sun { display: inline-flex; align-items: center; gap: var(--s4); font-size: var(--t4); color: var(--ink2); }
.mr-sun input { width: 120px; }
.mr-sun output { font-variant-numeric: tabular-nums; min-width: 3.2em; }
.mr-stage { position: relative; width: 100%; overflow: hidden; touch-action: none; background: var(--surface); }
.mr-stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.mr-view.is-proving .mr-stage canvas { visibility: hidden; }
/* The free version's tile (MARIE-20): the name, slanted, repeated over the picture; a mask, so it is the ink's colour in either scheme. */
.mr-view.is-free .mr-stage::after { content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none; background: var(--ink); opacity: 0.11; -webkit-mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='360' height='200'%3E%3Ctext x='20' y='120' font-family='system-ui,sans-serif' font-size='30' font-weight='600' fill='black' transform='rotate(-24 180 100)'%3Einspired.comm.ee%3C/text%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='360' height='200'%3E%3Ctext x='20' y='120' font-family='system-ui,sans-serif' font-size='30' font-weight='600' fill='black' transform='rotate(-24 180 100)'%3Einspired.comm.ee%3C/text%3E%3C/svg%3E"); -webkit-mask-repeat: repeat; mask-repeat: repeat; -webkit-mask-size: 360px 200px; mask-size: 360px 200px; }
.mr-droplist-note { padding: var(--s3) var(--s5); white-space: normal; max-width: 260px; }

/* ── the month ───────────────────────────────────────────────────────── */
.mr-payrows { margin: 0; display: grid; gap: var(--s3); max-width: 520px; }
.mr-payrow { display: grid; grid-template-columns: 8em 1fr; gap: var(--s6); padding-block: var(--s3); border-bottom: 1px solid var(--hair); }
.mr-payrow dt { color: var(--mute); font-size: var(--t4); }
.mr-payrow dd { margin: 0; font-variant-numeric: tabular-nums; }
/* The one field the transfer is matched on, drawn as the thing it is and not
   as a sixth row of a list (MARIE-39). */
.mr-ref { display: flex; flex-direction: column; gap: var(--s3); padding: var(--s7) var(--s8); max-width: 520px; box-sizing: border-box; border: 1px solid color-mix(in srgb, var(--hp-amber) 55%, transparent); border-radius: var(--r2); background: color-mix(in srgb, var(--hp-amber) 10%, transparent); }
.mr-ref-label { margin: 0; color: var(--mute); font-size: var(--t3); letter-spacing: 0.06em; text-transform: uppercase; }
.mr-ref-code { margin: 0; font-family: ui-monospace, 'JetBrains Mono', monospace; font-size: var(--t7); letter-spacing: 0.08em; user-select: all; }
.mr-ref .mr-fine { margin: 0; }

/* ── the operator's statement ────────────────────────────────────────── */
.mr-bank textarea { font-family: ui-monospace, 'JetBrains Mono', monospace; font-size: var(--t3); }
.mr-said { white-space: normal; max-width: 26em; }

/* ── phones: one column, the bar's words go, the chips wrap ──────────── */
@media (max-width: 980px) {
  .mr-plan { grid-template-columns: 1fr; }
  /* The hamburger (MARIE-80). The links used to be `display: none` here and
     the CTA at 620, which left a telephone with no way to reach Sign in at
     all. They are behind the button now instead of gone. */
  .mr-menubtn { display: inline-flex; }
  /* And here the shut state has to come back — the wide rules above lift it
     for a bar that has no button to open it, and this one does. */
  .mr-menu:not([open])::details-content { content-visibility: hidden; block-size: 0; }
  .mr-menu:not([open]) .mr-droppop { display: none; }
  .mr-menu .mr-droppop { position: absolute; z-index: 45; top: calc(100% + var(--s3)); right: 0; left: auto; width: max-content; min-width: 220px; max-width: min(30rem, 88vw); padding: var(--s5); border: 1px solid var(--line2); border-radius: var(--r2); background: var(--surface); box-shadow: var(--pop); flex-direction: column; align-items: stretch; gap: var(--s5); }
  .mr-menu .mr-nav { flex-direction: column; gap: var(--s1); font-size: var(--t5); }
  /* A visitor's rows are a finger tall, as every door's menu rows are (UIC-38); a reader has the tier's rail. */
  .mr-menu .mr-nav a { display: flex; align-items: center; min-height: var(--finger); color: var(--ink); }
  .mr-menu .mr-menu-cta { display: inline-flex; justify-content: center; }
  .mr-top .mr-menu + .ui-theme { margin-left: 0; }
  /* One column, so the title is simply the first row of it and nothing is centred over anything
     (MARIE-59). */
  /* Six rows (MARIE-66): the slack is row 4, and the two things a thumb reaches for sit under it
     in the bottom right — the settings, then the objects. A phone's screen is mostly picture and
     the controls stand out of the way of it until they are asked for. */
  .mr-hudgrid { inset: var(--s4); grid-template-columns: 1fr; grid-template-rows: auto auto auto minmax(0, 1fr) auto auto; gap: var(--s3); }
  /* Clear of the assistant's tab on the right edge (UIC-14), which stands at
     the title's height on a phone. */
  .mr-hud-title { grid-row: 1; grid-column: 1; justify-self: stretch; max-width: none; padding: var(--s3) var(--s6); margin-right: var(--s11); }
  .mr-hud-head { grid-row: 2; grid-column: 1; max-width: none; padding: var(--s4) var(--s6); }
  .mr-hud-turn { grid-row: 3; grid-column: 1; }
  /* The wrapper is what the grid places (MARIE-61); the bar inside it only lays its drops out.
     Shut it is a round ⋯ in the corner — *"on mobile, hide all gui settings bottom right"* — and
     opened it takes the width, because a row of drops has nowhere else to go on a phone. */
  .mr-hud-set { grid-row: 5; grid-column: 1; align-self: end; justify-self: end; max-width: none; }
  .mr-hud-set[open] { justify-self: stretch; max-height: 60vh; overflow: auto; }
  /* The word stays in the markup for anything that reads the page aloud, and the corner shows the
     dots: a label removed is a label nobody has, and ⋯ says nothing to a screen reader. */
  .mr-hud-set:not([open]) .mr-when-shut { font-size: 0; }
  .mr-hud-set:not([open]) .mr-when-shut::after { content: "\2026"; font-size: var(--t4); letter-spacing: 0.1em; }
  .mr-hud-bar { justify-content: flex-start; }
  .mr-droppop { right: auto; left: 0; }
  .mr-hud-side { grid-row: 6; grid-column: 1; align-self: end; justify-self: end; max-height: 60vh; }
  .mr-hud-hint { display: none; }
}

/* ── one object's form in the middle of the window (MARIE-16) ───────────── */
.mr-row { cursor: pointer; }
.mr-row:hover td, .mr-row:focus-visible td { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.mr-row:focus-visible { outline: none; }
/* The camera's two sliders (MARIE-59): a lens and a stop, each saying what it is set to. */
.mr-droppop-camera { min-width: 260px; display: flex; flex-direction: column; gap: var(--s4); padding: var(--s5) var(--s6); }
.mr-slide { display: flex; flex-direction: column; gap: var(--s2); font-size: var(--t3); color: var(--mute); letter-spacing: 0.04em; }
.mr-slide input { width: 100%; margin: 0; }
/* Every object chosen is marked, in the table and round its own box in the picture (MARIE-54). */
.mr-row.is-picked td { background: color-mix(in srgb, var(--accent) 26%, transparent); }
/* The levers (MARIE-16): a slider a coordinate, its run the room's own size. */
.mr-levers { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3) var(--s5); }
.mr-lever-say { font-size: var(--t3); color: var(--ink2); letter-spacing: 0; }
.mr-view.is-editing .mr-hud-hint { display: none; }
.mr-hover { position: absolute; z-index: 40; pointer-events: none; padding: var(--s2) var(--s5); border-radius: var(--r2); background: var(--ink); color: var(--hp-on-ink); font-size: var(--t3); letter-spacing: 0.04em; white-space: nowrap; }

/* ── pictures become a plan (MARIE-19) ──────────────────────────────────── */
.mr-import { display: flex; flex-direction: column; gap: var(--s10); padding-bottom: var(--s12); }
.mr-what { display: flex; flex-wrap: wrap; gap: var(--s4); }
.mr-sketches { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--s7); }
.mr-sketch { margin: 0; display: flex; flex-direction: column; gap: var(--s3); }
.mr-sketch svg { width: 100%; height: auto; display: block; border: 1px solid var(--hair); border-radius: var(--r1); background: var(--surface); }
.mr-sketch figcaption { font-size: var(--t3); color: var(--mute); letter-spacing: 0.04em; }
.mr-swatch { display: inline-block; width: 1em; height: 1em; border-radius: var(--r1); border: 1px solid var(--hair); vertical-align: -0.15em; margin-right: var(--s2); }
.mr-import .mr-addrow { border-top: none; padding-top: 0; }

/* ── the foot (MARIE-76) ────────────────────────────────────────────────── */
/* Three quiet columns and a line, the portal's arrangement at the portal's
   register (KODU-50, KODU-53) — the box spans and the reading inside it stops
   at the measure, so the hairline ends where the words above it end. */
.mr-foot { margin-top: auto; padding-block: var(--s12) var(--s10); border-top: 1px solid var(--hair); }
.mr-foot > .mr-wrap { gap: var(--s10); }
/* `auto-fit` with a floor rather than a breakpoint: one column on a telephone,
   two from about 560 and three from about 780, and the foot needs no media
   query of its own. `1fr` and not `max-content`, so the heads line up on their
   left edges instead of drifting apart with the longest link in each. */
.mr-footcols { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.mr-footlist { list-style: none; }
/* The size and its leading are one decision and the leading is not the
   ladder's: the links sit close together, so they are tighter than the body's.
   Underlined on hover only — a permanent rule under each would make the column
   a fence. */
.mr-footlink { font-size: var(--t5); line-height: 1.3; text-decoration: none; text-underline-offset: 3px; }
.mr-footlink:hover { color: var(--hp-gold); text-decoration: underline; }
/* The company's facts keep the register the whole design keeps machine facts
   in: mono, small, quiet. The name itself is a step darker, because it is the
   thing being checked and the two numbers under it are what it is checked
   against. */
.mr-footfact { font-size: var(--t3); line-height: 1.35; }
.mr-footfact:first-child { color: var(--ink3); }
.mr-footrule { width: 100%; height: 1px; background: var(--hair); }
.mr-footmark-link { display: inline-flex; align-items: center; gap: .4em; text-decoration: none; text-underline-offset: 3px; }
.mr-footmark-link:hover { color: var(--hp-gold); text-decoration: underline; }
.mr-footmark { display: inline-flex; width: 1.1em; height: 1.1em; }
.mr-footmark svg { width: 100%; height: 100%; }

@media (max-width: 620px) {
  .mr-top-inner { gap: var(--s6); }
  .mr-top-cta { display: none; }
  .mr-tiles { grid-template-columns: 1fr 1fr; }
  .mr-sun input { width: 90px; }
  .mr-stage { aspect-ratio: 1 / 1; }
}

/* The utility layer (UI-42). Served after every application's own sheet, so a
   class here wins the tie against a component class setting the same property.

   Every value is a step on `theme/scale.rs` or a token on `theme/tokens.rs`.
   Nothing in this file names a number or a colour of its own. */

/* ── layout ──────────────────────────────────────────────────────────────── */

.flex { display: flex; }
.stack { display: flex; flex-direction: column; }
.row { display: flex; flex-direction: row; align-items: center; }
.grid { display: grid; }
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* Two columns in a ratio: the shares a document's `:grid-2-1` asks for
   (PAPER-11), and a sidebar beside its page anywhere else. */
.grid-1-2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
.grid-2-1 { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.grid-1-3 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 3fr); }
.grid-3-1 { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 1fr); }
.wrap { flex-wrap: wrap; }
.nowrap { flex-wrap: nowrap; }

.start { align-items: flex-start; }
.center { align-items: center; }
.end { align-items: flex-end; }
.baseline { align-items: baseline; }
.stretch { align-items: stretch; }
.between { justify-content: space-between; }
.around { justify-content: space-around; }
.jstart { justify-content: flex-start; }
.jcenter { justify-content: center; }
.jend { justify-content: flex-end; }

/* What one child says about itself, against what its container said about
   all of them. A chip in a column is the usual case: the column stretches
   its children and the chip is as wide as its own word. */
.self-start { align-self: flex-start; }
.self-center { align-self: center; }
.self-end { align-self: flex-end; }

.grow { flex: 1 1 auto; min-width: 0; }
.none { flex: none; }
.shrink { min-width: 0; }

.block { display: block; }
.inline { display: inline; }
.iblock { display: inline-block; }
.iflex { display: inline-flex; align-items: center; }
.relative { position: relative; }
.absolute { position: absolute; }
.fixed { position: fixed; }
.sticky { position: sticky; }
.inset-0 { inset: 0; }
.clip { overflow: hidden; }
.scroll-y { overflow-y: auto; }
.scroll-x { overflow-x: auto; }

/* What a script puts an element behind. The three sheets each carried their
   own copy of this one declaration, so which one a screen got depended on
   which application was serving it. `!important` because it answers a
   `display` the element already has, and it is the only one in the layer. */
.hidden { display: none !important; }

/* A box that fills what holds it is not a measure — it is the absence of one,
   and the same three declarations were written 157 times between the sheets. */
.w-full { width: 100%; }
.h-full { height: 100%; }
.min-h-full { min-height: 100%; }
.cover { object-fit: cover; }


/* ── space ───────────────────────────────────────────────────────────────── */

.gap-0 { gap: 0; }
.gap-1 { gap: var(--s1); }
.gap-2 { gap: var(--s2); }
.gap-3 { gap: var(--s3); }
.gap-4 { gap: var(--s4); }
.gap-5 { gap: var(--s5); }
.gap-6 { gap: var(--s6); }
.gap-7 { gap: var(--s7); }
.gap-8 { gap: var(--s8); }
.gap-9 { gap: var(--s9); }
.gap-10 { gap: var(--s10); }
.gap-11 { gap: var(--s11); }
.gap-12 { gap: var(--s12); }
.gap-13 { gap: var(--s13); }

.gx-0 { column-gap: 0; }
.gx-1 { column-gap: var(--s1); }
.gx-2 { column-gap: var(--s2); }
.gx-3 { column-gap: var(--s3); }
.gx-4 { column-gap: var(--s4); }
.gx-5 { column-gap: var(--s5); }
.gx-6 { column-gap: var(--s6); }
.gx-7 { column-gap: var(--s7); }
.gx-8 { column-gap: var(--s8); }
.gx-9 { column-gap: var(--s9); }
.gx-10 { column-gap: var(--s10); }
.gx-11 { column-gap: var(--s11); }
.gx-12 { column-gap: var(--s12); }
.gx-13 { column-gap: var(--s13); }

.gy-0 { row-gap: 0; }
.gy-1 { row-gap: var(--s1); }
.gy-2 { row-gap: var(--s2); }
.gy-3 { row-gap: var(--s3); }
.gy-4 { row-gap: var(--s4); }
.gy-5 { row-gap: var(--s5); }
.gy-6 { row-gap: var(--s6); }
.gy-7 { row-gap: var(--s7); }
.gy-8 { row-gap: var(--s8); }
.gy-9 { row-gap: var(--s9); }
.gy-10 { row-gap: var(--s10); }
.gy-11 { row-gap: var(--s11); }
.gy-12 { row-gap: var(--s12); }
.gy-13 { row-gap: var(--s13); }

.p-0 { padding: 0; }
.p-1 { padding: var(--s1); }
.p-2 { padding: var(--s2); }
.p-3 { padding: var(--s3); }
.p-4 { padding: var(--s4); }
.p-5 { padding: var(--s5); }
.p-6 { padding: var(--s6); }
.p-7 { padding: var(--s7); }
.p-8 { padding: var(--s8); }
.p-9 { padding: var(--s9); }
.p-10 { padding: var(--s10); }
.p-11 { padding: var(--s11); }
.p-12 { padding: var(--s12); }
.p-13 { padding: var(--s13); }

.px-0 { padding-inline: 0; }
.px-1 { padding-inline: var(--s1); }
.px-2 { padding-inline: var(--s2); }
.px-3 { padding-inline: var(--s3); }
.px-4 { padding-inline: var(--s4); }
.px-5 { padding-inline: var(--s5); }
.px-6 { padding-inline: var(--s6); }
.px-7 { padding-inline: var(--s7); }
.px-8 { padding-inline: var(--s8); }
.px-9 { padding-inline: var(--s9); }
.px-10 { padding-inline: var(--s10); }
.px-11 { padding-inline: var(--s11); }
.px-12 { padding-inline: var(--s12); }
.px-13 { padding-inline: var(--s13); }

.py-0 { padding-block: 0; }
.py-1 { padding-block: var(--s1); }
.py-2 { padding-block: var(--s2); }
.py-3 { padding-block: var(--s3); }
.py-4 { padding-block: var(--s4); }
.py-5 { padding-block: var(--s5); }
.py-6 { padding-block: var(--s6); }
.py-7 { padding-block: var(--s7); }
.py-8 { padding-block: var(--s8); }
.py-9 { padding-block: var(--s9); }
.py-10 { padding-block: var(--s10); }
.py-11 { padding-block: var(--s11); }
.py-12 { padding-block: var(--s12); }
.py-13 { padding-block: var(--s13); }

.pt-0 { padding-top: 0; }
.pt-1 { padding-top: var(--s1); }
.pt-2 { padding-top: var(--s2); }
.pt-3 { padding-top: var(--s3); }
.pt-4 { padding-top: var(--s4); }
.pt-5 { padding-top: var(--s5); }
.pt-6 { padding-top: var(--s6); }
.pt-7 { padding-top: var(--s7); }
.pt-8 { padding-top: var(--s8); }
.pt-9 { padding-top: var(--s9); }
.pt-10 { padding-top: var(--s10); }
.pt-11 { padding-top: var(--s11); }
.pt-12 { padding-top: var(--s12); }
.pt-13 { padding-top: var(--s13); }

.pb-0 { padding-bottom: 0; }
.pb-1 { padding-bottom: var(--s1); }
.pb-2 { padding-bottom: var(--s2); }
.pb-3 { padding-bottom: var(--s3); }
.pb-4 { padding-bottom: var(--s4); }
.pb-5 { padding-bottom: var(--s5); }
.pb-6 { padding-bottom: var(--s6); }
.pb-7 { padding-bottom: var(--s7); }
.pb-8 { padding-bottom: var(--s8); }
.pb-9 { padding-bottom: var(--s9); }
.pb-10 { padding-bottom: var(--s10); }
.pb-11 { padding-bottom: var(--s11); }
.pb-12 { padding-bottom: var(--s12); }
.pb-13 { padding-bottom: var(--s13); }

/* The only margins there are, and all four are `auto` or nothing. Spacing
   between siblings belongs to the container's gap: a margin that suited one
   parent overlapped the next, which is what this layer exists to stop
   (UI-42). An `auto` is not a length — it is which edge the box is pushed to,
   the one thing a gap cannot say. */
.m-0 { margin: 0; }
.mx-auto { margin-inline: auto; }
.my-auto { margin-block: auto; }
.ml-auto { margin-left: auto; }

/* Where a page stops, and where a reading inside it stops. Every product had
   both and each had named them for itself; the names are the scale's now, so
   a block that lines up with the page says so in the markup.
   **After the margins on purpose**: `m-0 measure` has to mean "no margin of
   its own, and centred", which is what an element wearing both is asking
   for. Written above them it lost the `auto` to the reset, and the foot's
   own hairline was capped at the measure and then left-aligned under bands
   that were centred. */
.measure { width: 100%; max-width: var(--measure); margin-inline: auto; }
.prose { max-width: var(--prose); }
.gutter { padding-inline: var(--gutter); }

/* ── type ────────────────────────────────────────────────────────────────── */

.t-1 { font-size: var(--t1); line-height: var(--lh1); }
.t-2 { font-size: var(--t2); line-height: var(--lh2); }
.t-3 { font-size: var(--t3); line-height: var(--lh3); }
.t-4 { font-size: var(--t4); line-height: var(--lh4); }
.t-5 { font-size: var(--t5); line-height: var(--lh5); }
.t-6 { font-size: var(--t6); line-height: var(--lh6); }
.t-7 { font-size: var(--t7); line-height: var(--lh7); }
.t-8 { font-size: var(--t8); line-height: var(--lh8); }
.t-9 { font-size: var(--t9); line-height: var(--lh9); }
.t-10 { font-size: var(--t10); line-height: var(--lh10); }
.t-11 { font-size: var(--t11); line-height: var(--lh11); }

.serif { font-family: var(--serif); }
.sans { font-family: var(--sans); }
.mono { font-family: var(--mono); }

.fw-4 { font-weight: 400; }
.fw-5 { font-weight: 500; }
.fw-6 { font-weight: 600; }
.fw-7 { font-weight: 700; }

.upper { text-transform: uppercase; }
.track { letter-spacing: 0.08em; }
.tabular { font-variant-numeric: tabular-nums; }
.tleft { text-align: left; }
.tcenter { text-align: center; }
.tright { text-align: right; }
.pretty { text-wrap: pretty; }
.balance { text-wrap: balance; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* `.nowrap` above is the flex one. These two are the text's: a line that may
   not break, and a word — a key, an address, a token — that may break
   anywhere rather than push its column wider. */
.nobr { white-space: nowrap; }
.anywhere { overflow-wrap: anywhere; }

/* ── ink ─────────────────────────────────────────────────────────────────── */

.text-ink { color: var(--ink); }
.text-ink2 { color: var(--ink2); }
.text-ink3 { color: var(--ink3); }
.text-mute { color: var(--mute); }
.text-faint { color: var(--faint); }
.text-accent { color: var(--accent); }
.text-accent-ink { color: var(--accent-ink); }
.text-solid-ink { color: var(--solid-ink); }
.text-danger { color: var(--danger); }
.text-ok { color: var(--ok-ink); }
.text-warn { color: var(--warn-ink); }
.text-info { color: var(--info); }
.text-star { color: var(--star); }

/* ── ground ──────────────────────────────────────────────────────────────── */

.bg-none { background: none; }
.bg-page { background: var(--page); }
.bg-surface { background: var(--surface); }
.bg-rail { background: var(--rail); }
.bg-mid { background: var(--mid); }
.bg-chip { background: var(--chip); }
.bg-field { background: var(--field); }
.bg-accent { background: var(--accent); }
.bg-accent-soft { background: var(--accent-soft); }
.bg-solid { background: var(--solid); }
.bg-danger-soft { background: var(--danger-soft); }
.bg-ok { background: var(--ok-bg); }
.bg-warn { background: var(--warn-bg); }
.bg-info-soft { background: var(--info-soft); }
.bg-star-soft { background: var(--star-soft); }

/* ── edges ───────────────────────────────────────────────────────────────── */

.line { border: 1px solid var(--line); }
.line-t { border-top: 1px solid var(--line); }
.line-b { border-bottom: 1px solid var(--line); }
.line-l { border-left: 1px solid var(--line); }
.line-r { border-right: 1px solid var(--line); }
.line2 { border: 1px solid var(--line2); }
.hair { border: 1px solid var(--hair); }
.hair-t { border-top: 1px solid var(--hair); }
.hair-b { border-bottom: 1px solid var(--hair); }
.line-accent { border: 1px solid var(--accent-line); }
.line-danger { border: 1px solid var(--danger-line); }
.line-warn { border: 1px solid var(--warn-line); }
.line-none { border: 0; }

.r-0 { border-radius: 0; }
.r-1 { border-radius: var(--r1); }
.r-2 { border-radius: var(--r2); }
.r-3 { border-radius: var(--r3); }
.r-4 { border-radius: var(--r4); }
.r-5 { border-radius: var(--r5); }
.r-6 { border-radius: var(--r6); }
.r-7 { border-radius: var(--r7); }
.r-pill { border-radius: var(--r-pill); }

.shadow { box-shadow: var(--shadow); }
.shadow-pop { box-shadow: var(--pop); }
.shadow-none { box-shadow: none; }

/* ── what answers a pointer ──────────────────────────────────────────────── */

/* The sheets spelled twenty-two different transitions between them, and
   fifty-three rules wanted the same one: the colours move, nothing else, and
   the eye reads the change as a state rather than as an animation. */
.transition-colors { transition: background var(--quick), border-color var(--quick), color var(--quick); }
.transition-transform { transition: transform var(--quick); }

.cursor-pointer { cursor: pointer; }
.cursor-default { cursor: default; }
.pointer-none { pointer-events: none; }
.pointer-auto { pointer-events: auto; }

.hover-text-ink:hover { color: var(--ink); }
.hover-text-accent:hover { color: var(--accent); }
.hover-text-danger:hover { color: var(--danger); }
.hover-underline:hover { text-decoration: underline; text-underline-offset: 2px; }

.hover-bg-hover:hover { background: var(--hover); }
.hover-bg-chip:hover { background: var(--chip); }
.hover-bg-surface:hover { background: var(--surface); }
.hover-bg-accent-soft:hover { background: var(--accent-soft); }

.hover-line-accent:hover { border-color: var(--accent); }
.hover-line-ink:hover { border-color: var(--ink); }
.hover-line-danger:hover { border-color: var(--danger); }

.active-bg-down:active { background: var(--accent-down); }

/* A ring the keyboard draws and the mouse does not, taking the corner of
   whatever it is put on rather than deciding one of its own. */
.focus-ring:focus-visible { outline: var(--s1) solid var(--accent); outline-offset: var(--s1); border-radius: inherit; }
.focus-none:focus { outline: none; }

/* Not `[disabled]`: a link and a label can be past answering too, and neither
   takes the attribute. */
.disabled { opacity: 0.5; cursor: default; pointer-events: none; }

/* ── the desk (UIC-23) ───────────────────────────────────────────────────── */

/* Mobile is the ground. A utility above is every width, and its `d-` twin
   is the desk — 641px and up, measured on the page's own width (the body is
   the container, `layouts.css`), so a page narrowed by the dock is a phone
   to it. `row d-stack` is a row on a phone and a column on a desk; the twin
   comes after the base in this file, which is what lets it win the tie.
   Only the arrangement has twins: what changes between a phone and a desk
   is where things stand, not what colour they are. */
@container (min-width: 641px) {
  .d-flex { display: flex; }
  .d-stack { display: flex; flex-direction: column; }
  .d-row { display: flex; flex-direction: row; align-items: center; }
  .d-grid { display: grid; }
  .d-grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .d-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .d-grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .d-grid-7 { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
  .d-grid-1-2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
  .d-grid-2-1 { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
  .d-grid-1-3 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 3fr); }
  .d-grid-3-1 { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 1fr); }
  .d-block { display: block; }
  .d-iflex { display: inline-flex; align-items: center; }
  .d-hidden { display: none; }
  .d-wrap { flex-wrap: wrap; }
  .d-nowrap { flex-wrap: nowrap; }

  .d-start { align-items: flex-start; }
  .d-center { align-items: center; }
  .d-end { align-items: flex-end; }
  .d-stretch { align-items: stretch; }
  .d-between { justify-content: space-between; }
  .d-jstart { justify-content: flex-start; }
  .d-jcenter { justify-content: center; }
  .d-jend { justify-content: flex-end; }

  .d-grow { flex: 1 1 auto; min-width: 0; }
  .d-none { flex: none; }
  .d-w-full { width: 100%; }

  .d-gap-0 { gap: 0; }
  .d-gap-1 { gap: var(--s1); }
  .d-gap-2 { gap: var(--s2); }
  .d-gap-3 { gap: var(--s3); }
  .d-gap-4 { gap: var(--s4); }
  .d-gap-5 { gap: var(--s5); }
  .d-gap-6 { gap: var(--s6); }
  .d-gap-7 { gap: var(--s7); }
  .d-gap-8 { gap: var(--s8); }
  .d-gap-9 { gap: var(--s9); }
  .d-gap-10 { gap: var(--s10); }
  .d-gap-11 { gap: var(--s11); }
  .d-gap-12 { gap: var(--s12); }
  .d-gap-13 { gap: var(--s13); }
}
