@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(/fonts/fraunces-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(/fonts/fraunces-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Fraunces';
  font-style: italic;
  font-weight: 300 700;
  font-display: swap;
  src: url(/fonts/fraunces-italic-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Fraunces';
  font-style: italic;
  font-weight: 300 700;
  font-display: swap;
  src: url(/fonts/fraunces-italic-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/instrumentsans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/instrumentsans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/instrumentsans-italic-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/instrumentsans-italic-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url(/fonts/jetbrainsmono-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url(/fonts/jetbrainsmono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

: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;
  /* A photograph sharpening over its own small copy (KODU-157): slow enough to read as a focus, not a blink. */
  --sharpen: .45s ease-out;

  /* 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;
  /* The window's height: the least a page that stands alone in it is drawn at, its ground running on past it with what it holds. */
  --window: 100dvh;
  /* 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); }
}

:root {
  color-scheme: light dark;

  --page:    oklch(0.95 0.008 85);
  --surface: oklch(0.99 0.004 85);
  --mid:     oklch(0.975 0.005 85);
  --rail:    oklch(0.965 0.006 85);
  --field:   oklch(0.96 0.006 85);
  --hover:   oklch(0.955 0.006 85);
  --chip:    oklch(0.93 0.006 85);
  --line:    oklch(0.91 0.006 85);
  --line2:   oklch(0.88 0.006 85);
  --hair:    oklch(0.93 0.006 85);
  --edge:    oklch(0.82 0.006 85);
  --ring:    oklch(0.78 0.01 85);

  --ink:   oklch(0.22 0.01 85);
  --ink2:  oklch(0.28 0.01 85);
  --ink3:  oklch(0.46 0.01 85);
  --mute:  oklch(0.58 0.01 85);
  --faint: oklch(0.62 0.01 85);
  --idle:  oklch(0.72 0.01 85);
  --rail-ink: oklch(0.55 0.012 85);

  --accent:      oklch(0.52 0.14 265);
  --accent-down: oklch(0.46 0.14 265);
  --accent-idle: oklch(0.82 0.03 265);
  --accent-ink:  oklch(0.99 0.004 265);
  --accent-text: oklch(0.38 0.1 265);
  --chip-ink:    oklch(0.33 0.09 265);
  --wash:        oklch(0.52 0.14 265 / 0.09);
  --wash-faint:  oklch(0.52 0.14 265 / 0.05);
  --accent-soft: oklch(0.52 0.14 265 / 0.12);
  --accent-line: oklch(0.52 0.14 265 / 0.4);

  /* The two colours in here that are not the design's, and must not be. A QR
     is read by a camera, not by a person, and the standard it is read against
     assumes black modules on white paper — a near-white page and a near-black
     ink are close enough to look right and far enough to cost somebody a scan
     on a dim phone. They do not swap with the scheme, and nothing but a QR may
     use them. */
  --qr-paper: oklch(1 0 0);
  --qr-ink:   oklch(0 0 0);

  --star:      oklch(0.58 0.14 145);
  --star-soft: oklch(0.94 0.05 145);
  --star-ink:  oklch(0.4 0.09 145);
  --ok-bg:     oklch(0.55 0.12 145 / 0.12);
  --ok-ink:    oklch(0.42 0.1 145);

  /* The one red. It is spent on deleting and on a deadline that has actually
     passed, and nowhere else — which is what makes it mean anything. */
  --danger:      oklch(0.5 0.16 25);
  --danger-fill: oklch(0.52 0.17 25);
  --danger-down: oklch(0.45 0.17 25);
  --danger-line: oklch(0.88 0.02 25);
  --danger-soft: oklch(0.96 0.03 25);
  --danger-wash: oklch(0.95 0.05 25);
  --danger-deep: oklch(0.45 0.13 25);
  --bad-bg:      oklch(0.55 0.18 25 / 0.1);
  --flag-line:   oklch(0.55 0.18 25 / 0.35);
  --flag-bg:     oklch(0.55 0.18 25 / 0.06);
  --flag-ink:    oklch(0.42 0.14 25);

  /* Neither the accent nor a warning: the tone a note about the product wears
     when it is neither good news nor bad. */
  --alert:     oklch(0.58 0.16 35);
  --info:      oklch(0.5 0.13 285);
  --info-soft: oklch(0.96 0.025 285);

  /* An answer written and not sent is amber: asking to be finished, neither
     the accent nor the attention sand. */
  --draft-line:     oklch(0.88 0.03 60);
  --draft-bg:       oklch(0.985 0.008 60);
  --draft-edge:     oklch(0.7 0.08 60);
  --draft-ink:      oklch(0.58 0.03 60);
  --draft-tag:      oklch(0.55 0.09 60);
  --draft-tag-line: oklch(0.82 0.06 60);

  /* The accent-tinted machine text of the manual's tables and code. */
  --doc-key:  oklch(0.3 0.06 265);
  --doc-code: oklch(0.32 0.03 265);

  /* The attention tone: sand at hue 75, one lightness tinted at five
     strengths. Waiting, Block all, an overdue count and every warning wear it,
     because none of them is an error — they are things asking to be looked at,
     and colouring them red would spend the delete colour on a first-time
     sender. They are not re-answered in the dark: a translucent tint of one
     mid lightness holds under either ground. */
  --attn-h: 75;
  --attn-c: 0.06;
  --attn-fill: oklch(0.68 var(--attn-c) var(--attn-h) / 0.168);
  --warn-bg:   oklch(0.68 var(--attn-c) var(--attn-h) / 0.224);
  --warn-line: oklch(0.68 var(--attn-c) var(--attn-h) / 0.448);
  --badge-bg:   oklch(0.68 var(--attn-c) var(--attn-h) / 0.56);
  --attn-line:  oklch(0.68 var(--attn-c) var(--attn-h) / 0.7);
  --badge-line: oklch(0.68 var(--attn-c) var(--attn-h) / 0.84);
  --warn-ink:  oklch(0.44 0.066 75);
  --warn-dot:  oklch(0.66 0.096 75);

  --solid:     oklch(0.22 0.006 85);
  --solid-ink: oklch(0.95 0.004 85);

  /* An avatar's hue comes from the address, so one person is one colour
     everywhere, for everyone. Only the lightness and chroma change with the
     scheme — the hue is the identity and must not move. */
  --av-bg: 0.87 0.05;
  --av-fg: 0.38 0.075;

  --radius: 10px;

  --serif: 'Fraunces', 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
  --sans:  'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --mono:  'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Shadows are for what floats. Nothing in a column has one. */
  --desk:   0 1px 2px oklch(0.2 0.01 85 / 0.06), 0 24px 60px oklch(0.2 0.01 85 / 0.10);
  --lift:   0 1px 3px oklch(0.2 0.01 85 / 0.12);
  --card:   0 10px 32px oklch(0.2 0.01 85 / 0.08);
  --tip:    0 12px 30px oklch(0.2 0.01 85 / 0.24);
  --tip-sm: 0 8px 20px oklch(0.2 0.01 85 / 0.25);
  --float:  0 10px 28px oklch(0.2 0.01 85 / 0.12);
  --knob:   0 1px 3px oklch(0.2 0.01 85 / 0.25);
  --pop:   0 14px 36px oklch(0.2 0.01 85 / 0.18);
  --sheet: 0 24px 60px oklch(0.2 0.01 85 / 0.3);
  --slide: -18px 0 44px oklch(0.2 0.01 85 / 0.14);
  --rise: 0 -10px 34px oklch(0.15 0.01 85 / 0.18);
  --shadow: 0 1px 2px oklch(0.2 0.01 85 / 0.06), 0 20px 50px oklch(0.2 0.01 85 / 0.09);
  --scrim: oklch(0.25 0.01 85 / 0.45);
  --veil:  oklch(0.2 0.01 85 / 0.16);

  /* The stage a picture fills the screen on (UIC-20): a warm near-black in
     both schemes — the picture is the light there, so the dark scheme has
     nothing to invert — its controls a veil of the ground over the picture,
     their hairline a veil of the ink. And the paper a drawing is contained
     on: white in both schemes, because white is what it was drawn on. */
  --stage: oklch(0.13 0.01 85);
  --stage-ink: oklch(0.98 0.005 85);
  --stage-veil: oklch(0.1 0.01 85 / 0.5);
  --stage-line: oklch(0.98 0.005 85 / 0.28);
  --paper: oklch(0.99 0.003 85);
}


@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --page:    oklch(0.155 0.006 85);
    --surface: oklch(0.195 0.006 85);
    --mid:     oklch(0.185 0.006 85);
    --rail:    oklch(0.17 0.006 85);
    --field:   oklch(0.22 0.006 85);
    --hover:   oklch(0.235 0.006 85);
    --chip:    oklch(0.25 0.006 85);
    --line:    oklch(0.27 0.006 85);
    --line2:   oklch(0.31 0.006 85);
    --hair:    oklch(0.25 0.006 85);
    --edge:    oklch(0.38 0.006 85);
    --ring:    oklch(0.44 0.01 85);

    --ink:   oklch(0.94 0.004 85);
    --ink2:  oklch(0.88 0.004 85);
    --ink3:  oklch(0.72 0.008 85);
    --mute:  oklch(0.58 0.008 85);
    --faint: oklch(0.55 0.008 85);
    --idle:  oklch(0.45 0.008 85);
    --rail-ink: oklch(0.62 0.01 85);

    --accent:      oklch(0.72 0.08 265);
    --accent-down: oklch(0.78 0.09 265);
    --accent-idle: oklch(0.38 0.04 265);
    --accent-ink:  oklch(0.18 0.01 265);
    --accent-text: oklch(0.8 0.09 265);
    --chip-ink:    oklch(0.82 0.08 265);
    --wash:        oklch(0.72 0.08 265 / 0.17);
    --wash-faint:  oklch(0.72 0.08 265 / 0.1);
    --accent-soft: oklch(0.72 0.08 265 / 0.24);
    --accent-line: oklch(0.72 0.08 265 / 0.45);

    --star:      oklch(0.76 0.13 145);
    --star-soft: oklch(0.27 0.04 145);
    --star-ink:  oklch(0.84 0.1 145);
    --ok-bg:     oklch(0.7 0.12 145 / 0.16);
    --ok-ink:    oklch(0.8 0.1 145);

    --danger:      oklch(0.72 0.14 25);
    --danger-fill: oklch(0.68 0.16 25);
    --danger-down: oklch(0.74 0.16 25);
    --danger-line: oklch(0.4 0.08 25);
    --danger-soft: oklch(0.28 0.05 25);
    --danger-wash: oklch(0.3 0.07 25);
    --danger-deep: oklch(0.78 0.12 25);
    --bad-bg:      oklch(0.65 0.18 25 / 0.16);
    --flag-line:   oklch(0.65 0.16 25 / 0.45);
    --flag-bg:     oklch(0.65 0.16 25 / 0.12);
    --flag-ink:    oklch(0.8 0.12 25);

    --alert:     oklch(0.62 0.16 35);
    --info:      oklch(0.76 0.11 285);
    --info-soft: oklch(0.28 0.05 285);

    --draft-line:     oklch(0.35 0.05 60);
    --draft-bg:       oklch(0.22 0.02 60);
    --draft-edge:     oklch(0.55 0.08 60);
    --draft-ink:      oklch(0.7 0.05 60);
    --draft-tag:      oklch(0.75 0.09 60);
    --draft-tag-line: oklch(0.45 0.07 60);

    --doc-key:  oklch(0.82 0.07 265);
    --doc-code: oklch(0.8 0.04 265);

    --warn-ink: oklch(0.85 0.07 75);
    --warn-dot: oklch(0.74 0.1 75);

    --solid:     oklch(0.94 0.004 85);
    --solid-ink: oklch(0.2 0.006 85);

    --av-bg: 0.36 0.06;
    --av-fg: 0.85 0.06;

    --desk:   0 1px 2px oklch(0.05 0.01 85 / 0.3), 0 24px 60px oklch(0.05 0.01 85 / 0.4);
    --lift:   0 1px 3px oklch(0.05 0.01 85 / 0.4);
    --card:   0 10px 32px oklch(0.05 0.01 85 / 0.3);
    --tip:    0 12px 30px oklch(0.05 0.01 85 / 0.5);
    --tip-sm: 0 8px 20px oklch(0.05 0.01 85 / 0.5);
    --float:  0 10px 28px oklch(0.05 0.01 85 / 0.4);
    --knob:   0 1px 3px oklch(0.05 0.01 85 / 0.5);
    --pop:   0 14px 36px oklch(0.05 0.01 85 / 0.5);
    --sheet: 0 24px 60px oklch(0.05 0.01 85 / 0.6);
    --slide: -18px 0 44px oklch(0.05 0.01 85 / 0.45);
    --rise: 0 -10px 34px oklch(0.05 0.01 85 / 0.45);
    --shadow: 0 1px 2px oklch(0.05 0.01 85 / 0.3), 0 20px 50px oklch(0.05 0.01 85 / 0.3);
    --scrim: oklch(0.05 0.01 85 / 0.6);
    --veil:  oklch(0.05 0.01 85 / 0.35);
    --stage: oklch(0.13 0.01 85);
    --stage-ink: oklch(0.98 0.005 85);
    --stage-veil: oklch(0.1 0.01 85 / 0.5);
    --stage-line: oklch(0.98 0.005 85 / 0.28);
    --paper: oklch(0.99 0.003 85);
  }
}
:root[data-theme="dark"] {
    --page:    oklch(0.155 0.006 85);
    --surface: oklch(0.195 0.006 85);
    --mid:     oklch(0.185 0.006 85);
    --rail:    oklch(0.17 0.006 85);
    --field:   oklch(0.22 0.006 85);
    --hover:   oklch(0.235 0.006 85);
    --chip:    oklch(0.25 0.006 85);
    --line:    oklch(0.27 0.006 85);
    --line2:   oklch(0.31 0.006 85);
    --hair:    oklch(0.25 0.006 85);
    --edge:    oklch(0.38 0.006 85);
    --ring:    oklch(0.44 0.01 85);

    --ink:   oklch(0.94 0.004 85);
    --ink2:  oklch(0.88 0.004 85);
    --ink3:  oklch(0.72 0.008 85);
    --mute:  oklch(0.58 0.008 85);
    --faint: oklch(0.55 0.008 85);
    --idle:  oklch(0.45 0.008 85);
    --rail-ink: oklch(0.62 0.01 85);

    --accent:      oklch(0.72 0.08 265);
    --accent-down: oklch(0.78 0.09 265);
    --accent-idle: oklch(0.38 0.04 265);
    --accent-ink:  oklch(0.18 0.01 265);
    --accent-text: oklch(0.8 0.09 265);
    --chip-ink:    oklch(0.82 0.08 265);
    --wash:        oklch(0.72 0.08 265 / 0.17);
    --wash-faint:  oklch(0.72 0.08 265 / 0.1);
    --accent-soft: oklch(0.72 0.08 265 / 0.24);
    --accent-line: oklch(0.72 0.08 265 / 0.45);

    --star:      oklch(0.76 0.13 145);
    --star-soft: oklch(0.27 0.04 145);
    --star-ink:  oklch(0.84 0.1 145);
    --ok-bg:     oklch(0.7 0.12 145 / 0.16);
    --ok-ink:    oklch(0.8 0.1 145);

    --danger:      oklch(0.72 0.14 25);
    --danger-fill: oklch(0.68 0.16 25);
    --danger-down: oklch(0.74 0.16 25);
    --danger-line: oklch(0.4 0.08 25);
    --danger-soft: oklch(0.28 0.05 25);
    --danger-wash: oklch(0.3 0.07 25);
    --danger-deep: oklch(0.78 0.12 25);
    --bad-bg:      oklch(0.65 0.18 25 / 0.16);
    --flag-line:   oklch(0.65 0.16 25 / 0.45);
    --flag-bg:     oklch(0.65 0.16 25 / 0.12);
    --flag-ink:    oklch(0.8 0.12 25);

    --alert:     oklch(0.62 0.16 35);
    --info:      oklch(0.76 0.11 285);
    --info-soft: oklch(0.28 0.05 285);

    --draft-line:     oklch(0.35 0.05 60);
    --draft-bg:       oklch(0.22 0.02 60);
    --draft-edge:     oklch(0.55 0.08 60);
    --draft-ink:      oklch(0.7 0.05 60);
    --draft-tag:      oklch(0.75 0.09 60);
    --draft-tag-line: oklch(0.45 0.07 60);

    --doc-key:  oklch(0.82 0.07 265);
    --doc-code: oklch(0.8 0.04 265);

    --warn-ink: oklch(0.85 0.07 75);
    --warn-dot: oklch(0.74 0.1 75);

    --solid:     oklch(0.94 0.004 85);
    --solid-ink: oklch(0.2 0.006 85);

    --av-bg: 0.36 0.06;
    --av-fg: 0.85 0.06;

    --desk:   0 1px 2px oklch(0.05 0.01 85 / 0.3), 0 24px 60px oklch(0.05 0.01 85 / 0.4);
    --lift:   0 1px 3px oklch(0.05 0.01 85 / 0.4);
    --card:   0 10px 32px oklch(0.05 0.01 85 / 0.3);
    --tip:    0 12px 30px oklch(0.05 0.01 85 / 0.5);
    --tip-sm: 0 8px 20px oklch(0.05 0.01 85 / 0.5);
    --float:  0 10px 28px oklch(0.05 0.01 85 / 0.4);
    --knob:   0 1px 3px oklch(0.05 0.01 85 / 0.5);
    --pop:   0 14px 36px oklch(0.05 0.01 85 / 0.5);
    --sheet: 0 24px 60px oklch(0.05 0.01 85 / 0.6);
    --slide: -18px 0 44px oklch(0.05 0.01 85 / 0.45);
    --rise: 0 -10px 34px oklch(0.05 0.01 85 / 0.45);
    --shadow: 0 1px 2px oklch(0.05 0.01 85 / 0.3), 0 20px 50px oklch(0.05 0.01 85 / 0.3);
    --scrim: oklch(0.05 0.01 85 / 0.6);
    --veil:  oklch(0.05 0.01 85 / 0.35);
    --stage: oklch(0.13 0.01 85);
    --stage-ink: oklch(0.98 0.005 85);
    --stage-veil: oklch(0.1 0.01 85 / 0.5);
    --stage-line: oklch(0.98 0.005 85 / 0.28);
    --paper: oklch(0.99 0.003 85);
}
:root[data-theme="light"] {
  color-scheme: light;
}
:root[data-theme="dark"] {
  color-scheme: dark;
}
@media (prefers-color-scheme: dark) and (max-width: 640px) {
  :root:not([data-theme="light"]) {
    --page:    oklch(0.1 0.004 85);
    --surface: oklch(0.125 0.005 85);
    --mid:     oklch(0.115 0.005 85);
    --rail:    oklch(0.11 0.004 85);
    --field:   oklch(0.16 0.005 85);
    --hover:   oklch(0.175 0.005 85);
    --chip:    oklch(0.19 0.005 85);
    --line:    oklch(0.3 0.006 85);
    --line2:   oklch(0.34 0.006 85);
    --hair:    oklch(0.28 0.006 85);

    --ink:   oklch(0.985 0.002 85);
    --ink2:  oklch(0.94 0.003 85);
    --ink3:  oklch(0.82 0.006 85);
    --mute:  oklch(0.7 0.008 85);
    --faint: oklch(0.64 0.008 85);
  }
}
@media (max-width: 640px) {
  :root[data-theme="dark"] {
    --page:    oklch(0.1 0.004 85);
    --surface: oklch(0.125 0.005 85);
    --mid:     oklch(0.115 0.005 85);
    --rail:    oklch(0.11 0.004 85);
    --field:   oklch(0.16 0.005 85);
    --hover:   oklch(0.175 0.005 85);
    --chip:    oklch(0.19 0.005 85);
    --line:    oklch(0.3 0.006 85);
    --line2:   oklch(0.34 0.006 85);
    --hair:    oklch(0.28 0.006 85);

    --ink:   oklch(0.985 0.002 85);
    --ink2:  oklch(0.94 0.003 85);
    --ink3:  oklch(0.82 0.006 85);
    --mute:  oklch(0.7 0.008 85);
    --faint: oklch(0.64 0.008 85);
  }
}
.ui-paper {
  color-scheme: light;
  color: var(--ink);

  --page:    oklch(0.95 0.008 85);
  --surface: oklch(0.99 0.004 85);
  --mid:     oklch(0.975 0.005 85);
  --rail:    oklch(0.965 0.006 85);
  --field:   oklch(0.96 0.006 85);
  --hover:   oklch(0.955 0.006 85);
  --chip:    oklch(0.93 0.006 85);
  --line:    oklch(0.91 0.006 85);
  --line2:   oklch(0.88 0.006 85);
  --hair:    oklch(0.93 0.006 85);
  --edge:    oklch(0.82 0.006 85);
  --ring:    oklch(0.78 0.01 85);

  --ink:   oklch(0.22 0.01 85);
  --ink2:  oklch(0.28 0.01 85);
  --ink3:  oklch(0.46 0.01 85);
  --mute:  oklch(0.58 0.01 85);
  --faint: oklch(0.62 0.01 85);
  --idle:  oklch(0.72 0.01 85);
  --rail-ink: oklch(0.55 0.012 85);

  --accent:      oklch(0.52 0.14 265);
  --accent-down: oklch(0.46 0.14 265);
  --accent-idle: oklch(0.82 0.03 265);
  --accent-ink:  oklch(0.99 0.004 265);
  --accent-text: oklch(0.38 0.1 265);
  --chip-ink:    oklch(0.33 0.09 265);
  --wash:        oklch(0.52 0.14 265 / 0.09);
  --wash-faint:  oklch(0.52 0.14 265 / 0.05);
  --accent-soft: oklch(0.52 0.14 265 / 0.12);
  --accent-line: oklch(0.52 0.14 265 / 0.4);

  /* The two colours in here that are not the design's, and must not be. A QR
     is read by a camera, not by a person, and the standard it is read against
     assumes black modules on white paper — a near-white page and a near-black
     ink are close enough to look right and far enough to cost somebody a scan
     on a dim phone. They do not swap with the scheme, and nothing but a QR may
     use them. */
  --qr-paper: oklch(1 0 0);
  --qr-ink:   oklch(0 0 0);

  --star:      oklch(0.58 0.14 145);
  --star-soft: oklch(0.94 0.05 145);
  --star-ink:  oklch(0.4 0.09 145);
  --ok-bg:     oklch(0.55 0.12 145 / 0.12);
  --ok-ink:    oklch(0.42 0.1 145);

  /* The one red. It is spent on deleting and on a deadline that has actually
     passed, and nowhere else — which is what makes it mean anything. */
  --danger:      oklch(0.5 0.16 25);
  --danger-fill: oklch(0.52 0.17 25);
  --danger-down: oklch(0.45 0.17 25);
  --danger-line: oklch(0.88 0.02 25);
  --danger-soft: oklch(0.96 0.03 25);
  --danger-wash: oklch(0.95 0.05 25);
  --danger-deep: oklch(0.45 0.13 25);
  --bad-bg:      oklch(0.55 0.18 25 / 0.1);
  --flag-line:   oklch(0.55 0.18 25 / 0.35);
  --flag-bg:     oklch(0.55 0.18 25 / 0.06);
  --flag-ink:    oklch(0.42 0.14 25);

  /* Neither the accent nor a warning: the tone a note about the product wears
     when it is neither good news nor bad. */
  --alert:     oklch(0.58 0.16 35);
  --info:      oklch(0.5 0.13 285);
  --info-soft: oklch(0.96 0.025 285);

  /* An answer written and not sent is amber: asking to be finished, neither
     the accent nor the attention sand. */
  --draft-line:     oklch(0.88 0.03 60);
  --draft-bg:       oklch(0.985 0.008 60);
  --draft-edge:     oklch(0.7 0.08 60);
  --draft-ink:      oklch(0.58 0.03 60);
  --draft-tag:      oklch(0.55 0.09 60);
  --draft-tag-line: oklch(0.82 0.06 60);

  /* The accent-tinted machine text of the manual's tables and code. */
  --doc-key:  oklch(0.3 0.06 265);
  --doc-code: oklch(0.32 0.03 265);

  /* The attention tone: sand at hue 75, one lightness tinted at five
     strengths. Waiting, Block all, an overdue count and every warning wear it,
     because none of them is an error — they are things asking to be looked at,
     and colouring them red would spend the delete colour on a first-time
     sender. They are not re-answered in the dark: a translucent tint of one
     mid lightness holds under either ground. */
  --attn-h: 75;
  --attn-c: 0.06;
  --attn-fill: oklch(0.68 var(--attn-c) var(--attn-h) / 0.168);
  --warn-bg:   oklch(0.68 var(--attn-c) var(--attn-h) / 0.224);
  --warn-line: oklch(0.68 var(--attn-c) var(--attn-h) / 0.448);
  --badge-bg:   oklch(0.68 var(--attn-c) var(--attn-h) / 0.56);
  --attn-line:  oklch(0.68 var(--attn-c) var(--attn-h) / 0.7);
  --badge-line: oklch(0.68 var(--attn-c) var(--attn-h) / 0.84);
  --warn-ink:  oklch(0.44 0.066 75);
  --warn-dot:  oklch(0.66 0.096 75);

  --solid:     oklch(0.22 0.006 85);
  --solid-ink: oklch(0.95 0.004 85);

  /* An avatar's hue comes from the address, so one person is one colour
     everywhere, for everyone. Only the lightness and chroma change with the
     scheme — the hue is the identity and must not move. */
  --av-bg: 0.87 0.05;
  --av-fg: 0.38 0.075;

  --radius: 10px;

  --serif: 'Fraunces', 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
  --sans:  'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --mono:  'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Shadows are for what floats. Nothing in a column has one. */
  --desk:   0 1px 2px oklch(0.2 0.01 85 / 0.06), 0 24px 60px oklch(0.2 0.01 85 / 0.10);
  --lift:   0 1px 3px oklch(0.2 0.01 85 / 0.12);
  --card:   0 10px 32px oklch(0.2 0.01 85 / 0.08);
  --tip:    0 12px 30px oklch(0.2 0.01 85 / 0.24);
  --tip-sm: 0 8px 20px oklch(0.2 0.01 85 / 0.25);
  --float:  0 10px 28px oklch(0.2 0.01 85 / 0.12);
  --knob:   0 1px 3px oklch(0.2 0.01 85 / 0.25);
  --pop:   0 14px 36px oklch(0.2 0.01 85 / 0.18);
  --sheet: 0 24px 60px oklch(0.2 0.01 85 / 0.3);
  --slide: -18px 0 44px oklch(0.2 0.01 85 / 0.14);
  --rise: 0 -10px 34px oklch(0.15 0.01 85 / 0.18);
  --shadow: 0 1px 2px oklch(0.2 0.01 85 / 0.06), 0 20px 50px oklch(0.2 0.01 85 / 0.09);
  --scrim: oklch(0.25 0.01 85 / 0.45);
  --veil:  oklch(0.2 0.01 85 / 0.16);

  /* The stage a picture fills the screen on (UIC-20): a warm near-black in
     both schemes — the picture is the light there, so the dark scheme has
     nothing to invert — its controls a veil of the ground over the picture,
     their hairline a veil of the ink. And the paper a drawing is contained
     on: white in both schemes, because white is what it was drawn on. */
  --stage: oklch(0.13 0.01 85);
  --stage-ink: oklch(0.98 0.005 85);
  --stage-veil: oklch(0.1 0.01 85 / 0.5);
  --stage-line: oklch(0.98 0.005 85 / 0.28);
  --paper: oklch(0.99 0.003 85);
}

/* The ground every surface stands on, first in the tier's sheet (UIC-42). */

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

/* A phone turned on its side keeps the type it was drawn in: Safari's own enlarging reads as the page zooming itself. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* One body for every application door (UIC-42); a storefront's theme sets its own (THEME-7). The
   body is a container (UIC-23), and a contained body's ground stops at its own foot, so it is at
   least the window. */
body {
  min-height: 100dvh;
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t5);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* A tap acts at once; pinch still zooms. */
  touch-action: manipulation;
}

/* A link, a press and a field start bare; the tier's controls draw what they are. */
a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; color: inherit; }
button { margin: 0; padding: 0; border: 0; background: none; cursor: pointer; }
::placeholder { color: var(--faint); }
/* A machine's words are the house's mono, never the browser's. */
:where(code, kbd, samp, pre) { font-family: var(--mono); }
/* A tick, a dot, a slider and a bar the browser draws, in the accent. */
:where(input, progress) { accent-color: var(--accent); }
img { max-width: 100%; }

/* `hidden` hides, whatever display a class gave the element. */
[hidden] { display: none !important; }

/* One focus ring for whatever a keyboard lands on (UIC-42), at a pseudo-class's weight. */
:where(a, button, summary, [tabindex], input, select, textarea):focus-visible {
  outline: var(--s1) solid var(--accent);
  outline-offset: var(--s1);
}

/* One look for every control on every surface (UIC-8), on the house tokens.
   Nothing here defines a colour or a length of its own: the tokens come first
   (`tokens::preamble`) and the application's own sheet comes after, and may
   add to a control but never redraws one.

   The shape is the mailbox's, which is the one in this product worth copying:
   the label above the box and quiet, the box a clean rounded rectangle on the
   surface, and the accent spent on focus rather than on decoration.

   **One name per thing** (UIC-10). The `.field`/`.input`/`.k-field`/`.k-input`
   aliases this file carried are gone: there is one control in the tier now, it
   spells one class, and an application's sheet that wants to add to it spells
   the same one. */

/* ── a glyph, and words only a screen reader hears (UIC-36) ──────────────── */

/* `ui::icon` draws every glyph in the product, so its size is the tier's:
   a letter tall, in the colour of the words it sits in. These two rules were
   Platform's (`theme.css`), and the portal and the sites link no such sheet —
   so a glyph there with no rule of its own drew at the SVG's natural size
   (the arrow under the visitor's sign-in, the arrow on the chat's own press)
   and a label meant for a screen reader was printed on the page. */
.icon {
  width: 1em;
  height: 1em;
  display: inline-block;
  vertical-align: -0.125em;
  fill: currentColor;
  flex-shrink: 0;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── a field: a label, a box, a hint ─────────────────────────────────────── */

.ui-field {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  margin: 0;
  position: static;
}
/* A cell of a table of fields keeps room for a few words however many columns share its row. */
.ui-field.is-cell { min-width: var(--cell-least); }

.ui-label {
  display: block;
  position: static;
  margin: 0;
  font-family: inherit;
  font-size: var(--t4);
  line-height: var(--lh4);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink3);
  pointer-events: auto;
}

/* A label for a screen reader alone (`Field.label_hidden`): out of the column, and the glyph centres in the box. */
.ui-label.sr-only { position: absolute; }
.ui-label.sr-only ~ .ui-field-icon { top: calc((var(--press-tall) - 1em) / 2); }

.ui-hint {
  margin: 0;
  font-size: var(--t4);
  color: var(--mute);
  text-wrap: pretty;
}

.ui-input {
  width: 100%;
  box-sizing: border-box;
  height: var(--press-tall);
  padding: 0 var(--s6);
  border: 1px solid var(--line);
  border-radius: var(--r3);
  /* On the surface, not on `--field` (UIC-8). */
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: var(--t-input);
  outline: none;
  transition: border-color var(--quick);
}

/* A price or a size reads in the figures' face: a column of them lines up. */
.ui-input.mono {
  font-family: var(--mono);
}

/* At least a box tall; its `rows` say the rest, so a note of two lines is not drawn as a page of four. */
textarea.ui-input {
  height: auto;
  min-height: var(--press-tall);
  padding: var(--s5) var(--s6);
  line-height: var(--lh-body);
  resize: vertical;
}

/* A line that grows with what is typed is not a box to drag. */
textarea.ui-input:is([rows="1"], [data-grow]) { resize: none; }

select.ui-input {
  height: var(--press-tall);
  padding: 0 var(--s5);
}

.ui-input:focus {
  border-color: var(--accent-line, var(--accent));
}

/* On a phone `--t-input` is `--t-field`, the size iOS will not zoom into: the scale says so (PBOX-13). */

.ui-input:disabled {
  background: var(--chip);
  color: var(--mute);
}

/* A search is the list's pill (`ui::find`, UIC-43): the field's box on the hover ground, the lens at its left end. */
.ui-input[type="search"] {
  appearance: none;
  border-color: transparent;
  border-radius: var(--r-pill);
  background: var(--hover);
}
.ui-input[type="search"]:focus { border-color: var(--accent-line, var(--accent)); }
.ui-find { margin: 0; min-width: 0; }

/* A question for the assistant (`ui::prompt`, UIC-43): one surface holding
   the assistant's mark, the reader's words at the reading size and the one
   press, ringed in the accent while it is typed into; the tries under it. */
.ui-prompt {
  width: 100%;
  max-width: var(--measure);
  min-width: 0;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--s6);
}
.ui-prompt-box {
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s2) var(--s2) var(--s2) var(--s4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r7);
  transition: border-color var(--quick), box-shadow var(--quick);
}
.ui-prompt-box:focus-within {
  border-color: var(--accent-line, var(--accent));
  box-shadow: 0 0 0 var(--s2) var(--wash);
}
.ui-prompt-box > .ui-field { flex: 1 1 auto; min-width: 0; }
.ui-prompt-box .ui-input {
  height: var(--s13);
  border-color: transparent;
  background: none;
  font-size: var(--t7);
}
.ui-prompt-box .ui-input:focus { border-color: transparent; box-shadow: none; }
.ui-prompt-box .ui-label.sr-only ~ .ui-field-icon { top: 50%; transform: translateY(-50%); color: var(--accent); }
.ui-prompt-box > .ui-pill { flex: none; padding-inline: var(--s9); }
.ui-prompt-box > .ui-round { flex: none; }
.ui-prompt-tries { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); padding-left: var(--s2); }
.ui-prompt-tries-said { margin-right: var(--s2); font-size: var(--t5); color: var(--mute); }

/* A leading glyph, and the room the box makes for it. Measured from the top
   because a field with a hint is taller, and from the bottom the glyph lands
   in the hint instead of in the box. */
.ui-field-icon {
  position: absolute;
  left: var(--s6);
  /* The label's line, its gap, then the middle of the box. */
  top: calc(var(--t4) * var(--lh4) + var(--s3) + (var(--press-tall) - 1em) / 2);
  color: var(--mute);
  pointer-events: none;
  line-height: 1;
}
.ui-field.has-icon {
  position: relative;
}
.ui-field.has-icon .ui-input {
  padding-left: calc(var(--s10) + var(--s2));
}

/* A refused field, marked where it is (UI-29): the problem script hangs its
   sentence off the field itself. */
.is-invalid .ui-input,
.is-invalid .sel {
  border-color: var(--danger);
}
.ui-field-error {
  margin: 0;
  font-size: var(--t4);
  font-weight: 600;
  color: var(--danger);
  text-wrap: pretty;
}

/* A box you tick reads along its label, its hint under the two. */
.ui-field-check {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s5);
}
.ui-field-check > .ui-hint {
  flex-basis: 100%;
  order: 3;
}
.ui-field-check > .ui-label {
  order: 2;
  color: var(--ink2);
}
.ui-field-check .ui-checkbox {
  order: 1;
  width: auto;
  height: auto;
  flex: none;
}

/* UI-37: a price is one box — the amount stretches, the currency sits at its
   right edge. */
.ui-field.money {
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--s5);
}
.ui-field.money > .ui-label {
  flex-basis: 100%;
}
.ui-field.money .money-amount {
  flex: 1 1 calc(4 * var(--s11));
}
.ui-field.money .money-cur {
  flex: 0 0 auto;
  width: auto;
  min-width: calc(2 * var(--s12));
}
.ui-field.money .money-cur:disabled {
  color: var(--mute);
}
.ui-field.money > .ui-hint {
  flex-basis: 100%;
}

/* The media library's field is a strip of pictures, not a box (GAP-216). */
.ui-field.media-field > .ui-label {
  font-size: var(--t4);
}

/* ── a rectangular press: a form's, or a page's ──────────────────────────── */

/* A storefront's press, its `--btn-*` and `--radius-btn` the site's knobs (THEME-7); an application door's is the pill (UIC-43). */
.ui-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s3);
  height: var(--press-tall);
  padding: 0 var(--s8);
  border: 1px solid var(--line2);
  border-radius: var(--radius-btn, var(--r4));
  background: none;
  color: var(--ink2);
  font-size: var(--t5);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: opacity var(--quick), background-color var(--quick), border-color var(--quick), color var(--quick);
}
.ui-btn:hover { border-color: var(--ink); color: var(--ink); }
.ui-btn.is-primary {
  background: var(--btn-fill, var(--accent));
  color: var(--btn-ink, var(--accent-ink));
  border-color: var(--btn-line, var(--accent));
}
.ui-btn.is-primary:hover { opacity: .88; }
.ui-btn.is-danger { color: var(--danger); }
.ui-btn.is-danger:hover { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.ui-btn.is-block { width: 100%; }
/* `Size::Small` is one height in every look (P8): the pill's and the round's `--press-small`. */
.ui-btn.is-small { height: var(--press-small); padding: 0 var(--s6); font-size: var(--t4); }
.ui-btn.is-tall { height: var(--press-tall); }
.ui-btn:disabled, .ui-btn:disabled:hover { opacity: .5; cursor: default; }
.ui-btn .ui-btn-icon { font-size: var(--t6); }

/* A press of two lines, its sub-line under the words, stands as a rectangle (UIC-42). */
:is(.ui-btn, .ui-pill, .ui-submit).has-sub {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s1);
  height: auto;
  min-height: var(--press-tall);
  padding: var(--s4) var(--s7);
  border-radius: var(--r4);
  line-height: var(--lh3);
  white-space: normal;
  text-align: left;
}
.ui-btn-sub { font-size: var(--t3); font-weight: 400; color: var(--mute); }
.is-primary > .ui-btn-sub { color: inherit; }

/* ── a round press whose label unfurls (UI-25) ───────────────────────────── */

/* At rest it is a *circle*, not an oval: `--ui-round-size` is both the height
   and the minimum width, and the horizontal padding is set so that a single
   1em icon lands exactly on that width. Padding alone cannot do it — the old
   `.55rem .7rem` made the box 2.4rem wide and 2.1rem tall, so every icon
   button in the product was subtly egg-shaped. */
.ui-round {
  /* Read off the press heights (P8): its `Size` picks the step. */
  --ui-round-size: var(--press);
  /* Less its two hairlines, or the circle comes out wider than it is tall. */
  --ui-round-pad: calc((var(--ui-round-size) - var(--t6)) / 2 - 1px);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0;
  height: var(--ui-round-size);
  min-width: var(--ui-round-size);
  padding-inline: var(--ui-round-pad);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink3);
  cursor: pointer;
  font-size: var(--t4);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition: border-color var(--quick), background var(--quick), color var(--quick);
}
/* The mailbox's hover: the accent outline, never a shadow (P8). */
.ui-round:hover { border-color: var(--accent); color: var(--accent); }
.ui-round .ui-btn-icon { font-size: var(--t6); }
/* A door's own drawing in the `icon` slot stands at the glyph's size. */
.ui-round > svg:not(.icon) { flex: none; width: var(--s7); height: var(--s7); }
.ui-round.is-small { --ui-round-size: var(--press-small); }
.ui-round.is-tall { --ui-round-size: var(--press-tall); }

/* A file input keeps the browser's own words for what was chosen, beside a
   press drawn as the round pill above (UIC-7) — never the system's grey
   button, which is the one control no sheet had dressed. */
input[type="file"] { max-width: 100%; font: inherit; font-size: var(--t4); color: var(--ink2); }
input[type="file"]::file-selector-button {
  height: var(--press);
  margin-right: var(--s5);
  padding-inline: var(--s7);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: none;
  color: var(--ink3);
  font: inherit;
  font-size: var(--t4);
  cursor: pointer;
  transition: border-color var(--quick), color var(--quick);
}
input[type="file"]::file-selector-button:hover { border-color: var(--accent); color: var(--accent); }

/* A press that must be a label holds its file input (UI-15): out of sight even when `hidden`, still where Tab lands, its ring on the press. */
label:is(.ui-btn, .ui-pill, .ui-round, .ui-submit):has(> input[type="file"]) { position: relative; }
label:is(.ui-btn, .ui-pill, .ui-round, .ui-submit) > input[type="file"] {
  display: block !important;
  position: absolute;
  inset: 0;
  width: 1px;
  height: 1px;
  margin: auto;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  border: 0;
  opacity: 0;
}

/* The scheme press (UIC-18). A ring with a glyph in it, the same object
   whether it is a link the server answers or a button a script does — which
   is the only difference between the two surfaces that wear it, and not a
   difference anybody looking at it can see.

   Its own size and not `.ui-round`'s: this one sits in a masthead beside
   words rather than in a row of controls, and it is drawn at the height of
   the text it stands next to. */
.ui-theme {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--s11);
  height: var(--s11);
  border: 1px solid var(--line2);
  border-radius: var(--r-pill);
  background: none;
  color: var(--ink);
  font-size: var(--t5);
  line-height: 1;
  cursor: pointer;
}
.ui-theme:hover { border-color: var(--ink); }

/* The label unfurls rather than appearing: `max-width` is what a transition
   can actually animate, and the margin keeps it off the icon as it opens. */
.ui-round .ui-btn-label {
  max-width: 0;
  overflow: hidden;
  white-space: nowrap;
  transition: max-width var(--unfold), margin-left var(--unfold);
  margin-left: 0;
}
.ui-round:hover .ui-btn-label,
.ui-round:focus-visible .ui-btn-label,
.ui-round:has(> input[type="file"]:focus-visible) .ui-btn-label,
.ui-round.is-open .ui-btn-label { max-width: var(--menu); margin-left: var(--s3); }
.ui-round.is-closed .ui-btn-label { max-width: 0; margin-left: 0; }

/* One that always shows its label is a pill, not a circle, so it gets the
   breathing room a circle does not need. */
.ui-round.is-open { padding-inline: calc(var(--ui-round-pad) + var(--s1)); }
/* An icon-only round stays a circle whatever state class a script gives it. */
.ui-round.is-closed { padding-inline: var(--ui-round-pad); }

/* A filled press is the accent, never the ink: one primary per screen (P7). */
.ui-round.is-primary {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.ui-round.is-primary:hover { background: var(--accent-down); border-color: var(--accent-down); color: var(--accent-ink); }
/* The red outline opens the question; only its answer is filled (PBOX-18, UIC-44). */
.ui-round.is-danger { color: var(--danger); border-color: var(--danger-line); }
.ui-round.is-danger:hover { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }

/* A press in a dense row draws nothing until the pointer is on it (P20). */
.ui-round.is-quiet { border-color: transparent; background: none; }
.ui-round.is-quiet:hover { border-color: transparent; background: var(--hover); color: var(--ink); }

/* One that cannot be pressed has to look it. A primary that is off keeps the
   ink of a live button otherwise, which is a button lying about itself — and
   the lie is loudest exactly where it matters, on a Save with nothing to save. */
.ui-round:disabled { opacity: .4; cursor: default; }
.ui-round:disabled:hover { border-color: var(--line); color: var(--ink3); }
.ui-round.is-primary:disabled:hover { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.ui-round.is-danger:disabled:hover { background: none; border-color: var(--danger-line); color: var(--danger); }
.ui-round.is-quiet:disabled:hover { background: none; border-color: transparent; }

@media (prefers-reduced-motion: reduce) {
  .ui-round, .ui-round .ui-btn-label { transition: none; }
}

/* ── a pill: small, outlined, inline ─────────────────────────────────────── */

/* The mailbox's pill, everybody's (P8, UIC-42): a hairline on no fill, the accent under the pointer. */
.ui-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s3);
  height: var(--press);
  padding: 0 var(--s7);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: none;
  color: var(--ink3);
  font-size: var(--t4);
  font-weight: 400;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: border-color var(--quick), background var(--quick), color var(--quick);
}
.ui-pill:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.ui-pill svg {
  width: var(--s6);
  height: var(--s6);
  flex: none;
}
.ui-pill-n {
  font-family: var(--mono);
  font-size: var(--t3);
  font-weight: 500;
  color: var(--mute);
}
.ui-pill:hover .ui-pill-n {
  color: inherit;
}
.ui-pill.is-primary {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 500;
}
.ui-pill.is-primary:hover {
  border-color: var(--accent-down);
  background: var(--accent-down);
  color: var(--accent-ink);
}
.ui-pill.is-primary .ui-pill-n {
  color: inherit;
}
.ui-pill.is-small {
  height: var(--press-small);
  padding: 0 var(--s6);
}
.ui-pill.is-tall {
  height: var(--press-tall);
  padding: 0 var(--s8);
  font-size: var(--t5);
}
/* The red outline opens the question; only its answer is filled (PBOX-18). */
.ui-pill.is-danger { border-color: var(--danger-line); color: var(--danger); }
.ui-pill.is-danger:hover { border-color: var(--danger); background: var(--danger-soft); color: var(--danger); }
/* Asking to be looked at is the sand, never the red (PBOX-6). */
.ui-pill.is-attn {
  border-color: var(--attn-line);
  background: var(--attn-fill);
  color: var(--warn-ink);
  font-weight: 500;
}
.ui-pill.is-attn:hover { border-color: var(--attn-line); background: var(--warn-bg); color: var(--warn-ink); }
/* On — the filter a list is under, the choice a pill's radio holds — is the accent's wash, never its fill (P7, UIC-43). */
.ui-pill.is-on,
.ui-pill.is-on:hover,
label.ui-pill:has(> input:is([type="radio"], [type="checkbox"]):checked) {
  border-color: transparent;
  background: var(--accent-soft);
  color: var(--accent-text);
  font-weight: 500;
}
.ui-pill.is-on svg { fill: currentColor; }
/* A choice drawn as pills (`ui::pill_choice`): each a label round its radio or its box, which a keyboard still reaches. */
label.ui-pill:has(> input:is([type="radio"], [type="checkbox"])) { position: relative; }
label.ui-pill > input:is([type="radio"], [type="checkbox"]) { position: absolute; inset: 0; margin: 0; opacity: 0; pointer-events: none; }
label.ui-pill:has(> input:is([type="radio"], [type="checkbox"]):focus-visible) { outline: var(--s1) solid var(--accent); outline-offset: var(--s1); }
/* Words in a row that act, with no ring until the pointer is on them. */
.ui-pill.is-quiet { border-color: transparent; }
.ui-pill.is-quiet:hover { border-color: transparent; background: var(--hover); color: var(--ink); }
/* One that cannot be pressed is solid grey whatever its tone: after the tones, and outweighing their hover. */
.ui-pill:disabled,
.ui-pill:is(.is-plain, .is-danger, .is-attn, .is-on, .is-quiet):disabled,
.ui-pill:is(.is-plain, .is-danger, .is-attn, .is-on, .is-quiet):disabled:hover {
  border-color: var(--line);
  background: none;
  color: var(--idle);
  font-weight: 400;
  cursor: default;
}
.ui-pill.is-quiet:disabled,
.ui-pill.is-quiet:disabled:hover { border-color: transparent; }
.ui-pill.is-primary:disabled,
.ui-pill.is-primary:disabled:hover {
  border-color: var(--accent-idle);
  background: var(--accent-idle);
  color: var(--accent-ink);
  cursor: default;
}

/* ── the press that finishes a form ──────────────────────────────────────── */

/* The mailbox's *Add task* (P7, P12): its height a floor, so long words take a second line. */
.ui-submit {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s4);
  min-height: var(--press-tall);
  margin-left: auto;
  padding: var(--s2) var(--s9);
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--t5);
  font-weight: 500;
  line-height: var(--lh3);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: border-color var(--quick), background var(--quick), color var(--quick);
}
.ui-submit:hover {
  border-color: var(--accent-down);
  background: var(--accent-down);
}
/* The second way of doing the same thing, offered beside the first: the same
   button, not filled. Two filled buttons on one card is two primary actions,
   which is none. */
.ui-submit.is-plain {
  border-color: var(--line);
  background: none;
  color: var(--ink3);
  font-weight: 400;
}
.ui-submit.is-plain:hover {
  border-color: var(--accent);
  background: none;
  color: var(--accent);
}
/* A form whose press deletes asks first; only the answer is filled red (PBOX-18). */
.ui-submit.is-danger {
  border-color: var(--danger-line);
  background: none;
  color: var(--danger);
}
.ui-submit.is-danger:hover {
  border-color: var(--danger);
  background: var(--danger-soft);
}
.ui-submit:disabled,
.ui-submit:disabled:hover {
  border-color: var(--accent-idle);
  background: var(--accent-idle);
  color: var(--accent-ink);
  cursor: default;
}
.ui-submit:is(.is-plain, .is-danger):disabled,
.ui-submit:is(.is-plain, .is-danger):disabled:hover {
  border-color: var(--line);
  background: none;
  color: var(--idle);
}
.ui-submit.is-small { min-height: var(--press); padding: var(--s1) var(--s7); font-size: var(--t4); }
.ui-submit svg {
  width: var(--s7);
  height: var(--s7);
  flex: none;
}
.ui-submit.is-block {
  margin-left: 0;
  width: 100%;
}

/* ── a modal's committing action (KODU-25) ───────────────────────────────── */

/* The question's answer, the tall pill (P8, UIC-44): a long name takes a second line, never off the card. */
.ui-modal {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s4);
  min-height: var(--press-tall);
  padding: var(--s3) var(--s8);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: none;
  color: var(--ink2);
  font-family: inherit;
  font-size: var(--t5);
  font-weight: 400;
  line-height: var(--lh-body);
  text-align: center;
  overflow-wrap: anywhere;
  cursor: pointer;
  transition: border-color var(--quick), background var(--quick), color var(--quick);
}
.ui-modal:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.ui-modal.is-primary {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 500;
}
.ui-modal.is-primary:hover {
  background: var(--accent-down);
  border-color: var(--accent-down);
  color: var(--accent-ink);
}
/* The one red fill (UIC-44): the press that answers a question by deleting.
   The press that *opens* the question is the outline `is-danger` of a round
   or a button; this is the yes. */
.ui-modal.is-danger {
  border-color: var(--danger-fill);
  background: var(--danger-fill);
  color: var(--accent-ink);
  font-weight: 500;
}
.ui-modal.is-danger:hover {
  background: var(--danger-down);
  border-color: var(--danger-down);
  color: var(--accent-ink);
}
.ui-modal:disabled { opacity: .5; cursor: default; }

/* ── words alone ─────────────────────────────────────────────────────────── */

.ui-text-btn {
  color: var(--accent);
  font-size: var(--t5);
  cursor: pointer;
}
.ui-text-btn:hover {
  text-decoration: underline;
  text-underline-offset: var(--s1);
}

/* ── one focus ring on every press (UIC-42) ──────────────────────────────── */

/* The base's ring with a class's weight, so no door's outline replaces it; a field rings its border. */
:is(.ui-btn, .ui-pill, .ui-round, .ui-submit, .ui-modal, .ui-text-btn, .ui-theme):focus-visible {
  outline: var(--s1) solid var(--accent);
  outline-offset: var(--s1);
}
/* A label press wears it while the file input inside has the keyboard. */
label:is(.ui-btn, .ui-pill, .ui-round, .ui-submit):has(> input[type="file"]:focus-visible) {
  outline: var(--s1) solid var(--accent);
  outline-offset: var(--s1);
}

/* ── a thing's acts: a card's answers, a walker's steps (UIC-43, PBOX-13) ── */

/* On a phone each is a finger's target; from 641px it stands at its `Size` again. */
.ui-acts :is(.ui-pill, .ui-btn) { min-height: var(--finger); min-width: var(--finger); }
.ui-acts .ui-round { min-height: var(--finger); min-width: var(--finger); }
@container (min-width: 641px) {
  .ui-acts :is(.ui-pill, .ui-btn) { min-height: auto; min-width: auto; }
  .ui-acts .ui-round { min-height: auto; min-width: var(--ui-round-size); }
}

/* ── a question and its answer, one press apart (UIC-34) ─────────────────── */

/* Folds stacked are one list: a hairline over each, and one under the last; a frame that folds is another `ui-fold`. */
details.ui-fold { display: block; border-top: 1px solid var(--hair); }
details.ui-fold:last-child { border-bottom: 1px solid var(--hair); }
/* The question is the whole row and the press, the mark at its far end. */
.ui-fold-q {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s8);
  padding: var(--s8) 0;
  font-size: var(--t6);
  line-height: var(--lh6);
  font-weight: 500;
  color: var(--ink);
}
.ui-fold-q::-webkit-details-marker { display: none; }
.ui-fold-q:hover { color: var(--accent); }
.ui-fold-q:focus-visible { outline: var(--s1) solid var(--accent); outline-offset: var(--s2); border-radius: var(--r2); }
/* A plus that turns to a cross when the answer is out. */
.ui-fold-mark { flex: none; display: inline-flex; color: var(--mute); transition: transform var(--quick); }
.ui-fold-mark .icon { width: var(--s7); height: var(--s7); }
.ui-fold[open] .ui-fold-mark { transform: rotate(45deg); }
/* The answer in the quiet voice, clear of the mark's column. */
.ui-fold-a {
  margin: 0;
  max-width: var(--prose);
  padding: 0 var(--s12) var(--s8) 0;
  font-size: var(--t5);
  line-height: var(--lh5);
  color: var(--mute);
}

/* ── a group of fields held shut (UI-13) ─────────────────────────────────── */

/* The border is what makes a folded group read as a lid rather than as the
   end of the form: closed, the summary is the only thing on that line. */
.ui-advanced {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--field);
  padding: var(--s4) var(--s5);
}
.ui-advanced > summary {
  display: flex;
  align-items: center;
  gap: var(--s3);
  list-style: none;
  cursor: pointer;
  font-size: var(--t3);
  font-weight: 600;
  color: var(--ink3);
}
.ui-advanced > summary::-webkit-details-marker { display: none; }
.ui-advanced > summary:hover { color: var(--ink); }
.ui-advanced > summary:focus-visible { outline: var(--s1) solid var(--accent); outline-offset: var(--s1); }
/* The arrow points along the fold: right when shut, down when open. */
.ui-advanced-caret { flex: none; width: var(--s4); height: var(--s4); transition: transform var(--unfold); }
.ui-advanced[open] > summary .ui-advanced-caret { transform: rotate(90deg); }
.ui-advanced-body { margin-top: var(--s7); }
/* A fold that is a quiet second way rather than a group (UIC-53): no box,
   its press centred and at the running text's weight. */
.ui-advanced.is-quiet { border: 0; background: none; padding: 0; }
.ui-advanced.is-quiet > summary { justify-content: center; font-weight: 500; font-size: var(--t5); }
.ui-advanced-body > * + * { margin-top: var(--s8); }

@media (prefers-reduced-motion: reduce) {
  .ui-advanced-caret { transition: none; }
}

/* The ⋯ menu is `lists.css` (`ui::more`, UIC-43). */

/* ── one press that hands a page on (UIC-32) ─────────────────────────────── */

/* A fold, so the whole control works with nothing running: shut it is a
   pill, open it is a sheet of ordinary links. Where the browser has a sheet
   of its own the script takes the press and none of this is ever seen. */
.ui-share { position: relative; display: inline-flex; }
/* The pill is the whole press, so the browser's own triangle goes. */
.ui-share > summary { list-style: none; cursor: pointer; }
.ui-share > summary::-webkit-details-marker { display: none; }
/* A disclosure that wears a control is that control: no triangle beside
   it, in any engine. */
summary:is(.ui-pill, .ui-round) { list-style: none; cursor: pointer; }
summary:is(.ui-pill, .ui-round)::-webkit-details-marker { display: none; }
.ui-share-menu {
  position: absolute;
  top: calc(100% + var(--s3));
  right: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  min-width: var(--menu);
  max-width: 80vw;
  padding: var(--s2);
  border: 1px solid var(--line);
  border-radius: var(--r5);
  background: var(--surface);
  box-shadow: var(--float);
}
.ui-share-row {
  display: flex;
  align-items: center;
  gap: var(--s5);
  padding: var(--s4) var(--s5);
  border: 0;
  border-radius: var(--r4);
  background: none;
  color: var(--ink2);
  font: inherit;
  font-size: var(--t5);
  text-align: left;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.ui-share-row:hover { background: var(--hover); color: var(--ink); }
.ui-share-row[hidden] { display: none; }
.ui-share-ico { flex: none; width: var(--s8); height: var(--s8); color: var(--mute); }
/* Hung from the press's left corner where the press itself is near the left
   edge: a sheet 200 wide hanging from the right corner of a control at the
   start of its own line opens off the screen. */
@media (max-width: 560px) {
  .ui-share-menu { right: auto; left: 0; }
}

/* The controls an answer is chosen with, drawn here for every door (UI-15, UI-36, UIC-42). */

/* ── a switch, and a run of cards answering one question (UI-36) ─────────── */

/* The mailbox's settings card (UIC-42): the words, how it stands, the track. */
.sw {
  /* The hidden box stands inside its card, or focusing it scrolls the frame to where it would be. */
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--s8);
  padding: var(--s8);
  border: 1px solid var(--line);
  border-radius: var(--r4);
  background: var(--surface);
  cursor: pointer;
}
.sw + .sw { margin-top: var(--s4); }
.sw:hover { border-color: var(--accent-line); }
.sw:has(.sw-in:focus-visible) { outline: var(--s1) solid var(--accent); outline-offset: var(--s1); }
.sw.is-locked { cursor: default; opacity: .72; }
.sw.is-locked:hover { border-color: var(--line); }
/* A line of a form or a card's head: the same switch with no box of its own. */
.sw.is-bare { padding: 0; border: 0; background: none; }

/* The box carries the state and the keyboard, hidden but still in the tab order. */
.sw-in {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

.sw-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--s3); }
.sw-label { font-size: var(--t5); font-weight: 600; color: var(--ink); }
.sw-note { font-size: var(--t4); line-height: var(--lh-body); color: var(--ink3); }
/* How the answer stands now, a fact the machine keeps (SET-19). */
.sw-stands {
  padding-top: var(--s1);
  font-family: var(--mono);
  font-size: var(--t2);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--mute);
}

.sw-track {
  flex: none;
  display: flex;
  align-items: center;
  width: var(--switch-w);
  height: var(--switch-h);
  padding: calc((var(--switch-h) - var(--s9)) / 2);
  border-radius: var(--r-pill);
  background: var(--line2);
  transition: background var(--quick);
}
.sw-knob {
  width: var(--s9);
  height: var(--s9);
  border-radius: 50%;
  background: var(--surface);
  box-shadow: var(--knob);
  transition: transform var(--quick);
}
.sw:has(.sw-in:checked) .sw-track { background: var(--accent); }
.sw:has(.sw-in:checked) .sw-knob { transform: translateX(calc(var(--switch-w) - var(--switch-h))); }

/* One of a run, so a tick rather than a track, and the chosen card washed. */
.sw-pick:has(.sw-in:checked) { border-color: var(--accent); background: var(--accent-soft); }
.sw-mark {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--s9);
  height: var(--s9);
  margin-top: var(--s1);
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface);
  color: var(--accent-ink);
}
.sw-mark svg { width: var(--s5); height: var(--s5); display: none; }
.sw-pick:has(.sw-in:checked) .sw-mark { background: var(--accent); border-color: var(--accent); }
.sw-pick:has(.sw-in:checked) .sw-mark svg { display: block; }

@media (prefers-reduced-motion: reduce) {
  .sw-track, .sw-knob { transition: none; }
}

/* ── the same answer as a segmented row (UI-15, `seg_of`) ────────────────── */

/* The mailbox's pill track (P18, UIC-43): one row, never a silent scroll — cut segments show their ellipsis. */
.seg {
  display: inline-flex;
  flex: 0 1 auto;
  gap: var(--s1);
  min-width: 0;
  max-width: 100%;
  padding: var(--s1);
  overflow: hidden;
  border-radius: var(--r-pill);
  background: var(--hover);
}
.seg-item {
  flex: 0 1 auto;
  display: block;
  min-width: 0;
  height: var(--press-small);
  padding: 0 var(--s4);
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--ink3);
  font-size: var(--t4);
  line-height: var(--press-small);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--quick), color var(--quick);
}
.seg-item:hover { color: var(--ink); }
/* A view named by its drawing (cards, lines): the glyph centred in its segment, the words its name. */
.seg-item:has(> svg) { display: inline-flex; align-items: center; justify-content: center; padding: 0 var(--s5); }
.seg-item > svg { width: var(--s8); height: var(--s8); }
.seg-item:focus-visible { outline-offset: calc(-1 * var(--s1)); }
.seg-item[aria-pressed="true"],
.seg-item[aria-current="page"],
.segf input:checked + .seg-item { background: var(--surface); color: var(--ink); font-weight: 600; box-shadow: var(--lift); }

/* The chips answering a form's question: a label wearing the chip, its radio inside. */
.segf { display: inline-flex; }
.segf-item { flex: 0 1 auto; min-width: 0; display: flex; position: relative; cursor: pointer; }
.segf-item .seg-item { flex: 1 1 auto; }
.segf-item:has(.sw-in:focus-visible) .seg-item { outline: var(--s1) solid var(--accent); outline-offset: calc(-1 * var(--s1)); }

@container (min-width: 641px) {
  .seg-item { padding: 0 var(--s7); }
}

/* ── a list that folds to the chosen row (`ui::select`, P11) ─────────────── */

.choice-field { position: relative; }
.choice-field.has-icon .sel { padding-left: calc(var(--s10) + var(--s4)); }

.sel {
  display: grid;
  gap: 0;
  padding: var(--s3) var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--r3);
  background: var(--surface);
  transition: border-color var(--quick);
}
.sel:hover { border-color: var(--mute); }
.sel:focus-within { border-color: var(--accent-line); }

.sel-multi { max-height: var(--list-max); overflow-y: auto; }

.sel-node {
  visibility: visible;
  transition: max-height var(--unfold), opacity var(--quick), visibility 0s;
}

/* Closed with a choice: every branch without it folds, and `hidden` takes it out of the tab order. */
.sel[data-open="false"]:has(input:checked) .sel-node:not(:has(input:checked)) {
  max-height: 0;
  opacity: 0;
  visibility: hidden;
  transition: max-height var(--unfold), opacity var(--quick), visibility var(--unfold);
}

.sel-opt {
  display: flex;
  align-items: flex-start;
  gap: var(--s4);
  padding: var(--s2) 0;
  font-size: var(--t5);
  line-height: var(--lh3);
  cursor: pointer;
}
.sel-opt:hover .opt-mark { border-color: var(--ink3); }
.sel-opt input { position: absolute; opacity: 0; width: 0; height: 0; }

.opt-mark {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: var(--s8);
  height: var(--s8);
  margin-top: var(--s1);
  border: 1px solid var(--mute);
  background: var(--surface);
  transition: background var(--quick), border-color var(--quick);
}
.opt-dot { border-radius: 50%; }
.opt-box { border-radius: var(--r2); }

.opt-mark::after {
  content: "";
  opacity: 0;
  transform: scale(.4);
  transition: opacity var(--quick), transform var(--spring);
}
.opt-dot::after { width: var(--s3); height: var(--s3); border-radius: 50%; background: var(--surface); }
.opt-box::after {
  width: var(--s3);
  height: var(--s5);
  margin-top: calc(-1 * var(--s1));
  border: solid var(--surface);
  border-width: 0 var(--s1) var(--s1) 0;
  transform: rotate(45deg) scale(.4);
}

input:checked + .opt-mark { background: var(--ink); border-color: var(--ink); }
input:checked + .opt-mark::after { opacity: 1; transform: scale(1); }
input:checked + .opt-box::after { transform: rotate(45deg) scale(1); }
input:focus-visible + .opt-mark { outline: var(--s1) solid var(--accent); outline-offset: var(--s1); }

.sel-text { min-width: 0; }
.sel-value { margin-left: var(--s3); font-size: var(--t3); opacity: .35; }

/* Nesting, marked by the line it hangs from. */
.sel-kids {
  margin-left: var(--s4);
  padding-left: var(--s6);
  border-left: 1px dotted var(--line);
}

@media (prefers-reduced-motion: reduce) {
  .sel-node, .opt-mark, .opt-mark::after { transition: none; }
}

/* ── a colour answered by pointing at it (UI-15, `ui::swatches`) ─────────── */

/* The paint is the chip's own `--swatch`; a chip with none is the surface. */
.swatches { display: flex; flex-wrap: wrap; gap: var(--s4); }
.swatch {
  position: relative;
  width: var(--swatch-size);
  height: var(--swatch-size);
  border: var(--s1) solid transparent;
  border-radius: var(--r3);
  outline: 1px solid var(--line2);
  outline-offset: -1px;
  background: var(--swatch, var(--surface));
  cursor: pointer;
}
.swatch-round { border-radius: 50%; }
.swatch:has(input:checked) { border-color: var(--accent); }
.swatch:focus-within { border-color: var(--accent); }
.swatch > input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* ── a starting point chosen by its picture (`ui::picker`) ───────────────── */

.picker { display: grid; grid-template-columns: repeat(auto-fit, minmax(var(--tile), 1fr)); gap: var(--s5); }
.pick {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--r6);
  background: var(--surface);
  cursor: pointer;
  transition: border-color var(--quick), background var(--quick);
}
/* Out of sight, still in the tab order and still announced. */
.pick-radio {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
}
.pick:hover { border-color: var(--mute); }
.pick:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.pick:has(input:focus-visible) { outline: var(--s1) solid var(--accent-line); outline-offset: var(--s1); }
.pick-name { font-size: var(--t4); font-weight: 600; }
.pick-meta { margin-top: var(--s4); }

/* The card draws the page it makes: a bar, a hero, a row of cards. */
.pick-plan { display: flex; flex-direction: column; gap: var(--s1); padding: var(--s2); border-radius: var(--r3); background: var(--field); }
.pick-bar { display: flex; align-items: center; gap: var(--s1); }
.pick-dot { width: var(--s2); height: var(--s2); border-radius: 50%; background: var(--ink3); flex: none; }
.pick-nav { height: var(--s1); flex: 1; border-radius: var(--r1); background: var(--line); }
.pick-hero { display: flex; flex-direction: column; align-items: center; gap: var(--s1); padding-block: var(--s2); }
.pick-line { width: 60%; height: var(--s1); border-radius: var(--r1); background: var(--ink3); }
.pick-cta { width: 22%; height: var(--s2); border-radius: var(--r1); background: var(--accent); }
.pick-row { display: flex; gap: var(--s1); }
.pick-card { flex: 1; height: var(--s5); border-radius: var(--r1); background: var(--line); }

/* A prepared look paints its own plan from the card's `--pk-*`, the house's without them. */
.pick-plan.tinted { background: var(--pk-bg, var(--field)); }
.pick-plan.tinted .pick-dot { background: var(--pk-accent, var(--accent)); }
.pick-plan.tinted .pick-nav { background: color-mix(in srgb, var(--pk-ink, var(--ink)) 22%, var(--pk-bg, var(--field))); }
.pick-plan.tinted .pick-line { background: color-mix(in srgb, var(--pk-ink, var(--ink)) 85%, var(--pk-bg, var(--field))); }
.pick-plan.tinted .pick-cta { background: var(--pk-accent, var(--accent)); }
.pick-plan.tinted .pick-card { background: color-mix(in srgb, var(--pk-ink, var(--ink)) 16%, var(--pk-bg, var(--field))); }

/* The surfaces a screen is made of (UIC-11), on the house tokens and defining
   nothing of its own.

   These are the shapes all three applications drew for themselves and each
   named separately — `k-quiet`/`pb-note`/`cn-note`, `k-group`/`cn-set`,
   `k-card`, `k-panel`/`pb-modal` — which is why a screen was still a
   `format!` template: there was nothing here to call. Each rule below is the
   union of the three, taking the mailbox's measure where they disagreed. */

/* ── what the screen says for itself ─────────────────────────────────────── */

.ui-note {
  margin: 0;
  font-size: var(--t4);
  line-height: var(--lh4);
  color: var(--mute);
  text-wrap: pretty;
}

/* News, in a box, in one of three weathers. Amber is the commonest and the
   one worth spelling out: nothing was lost, the form came back with what was
   typed still in it, and red would say otherwise. */
.ui-say {
  margin: 0;
  padding: var(--s5) var(--s7);
  border: 1px solid transparent;
  border-radius: var(--r5);
  font-size: var(--t5);
  line-height: var(--lh5);
  text-wrap: pretty;
}
.ui-say.is-warn {
  border-color: var(--warn-line);
  background: var(--warn-bg);
  color: var(--warn-ink);
}
.ui-say.is-wrong {
  border-color: var(--danger-line);
  background: var(--danger-soft);
  color: var(--danger);
}
.ui-say.is-done {
  border-color: var(--star-soft);
  background: var(--ok-bg);
  color: var(--ok-ink);
}

/* ── what the screen says at its foot (UIC-24) ───────────────────────────── */

/* Report a problem (ISS-8): the box stands at the page's bottom-left corner,
   over every surface and under the confirm dialog; beside a rail standing
   down the left, where the face that opened it is. Shut until it is asked for. */
.ui-report { position: fixed; left: var(--s8); bottom: var(--s8); z-index: 120; }
@container (min-width: 641px) {
  body:has(.ui-rail-nav) .ui-report { left: calc(var(--rail-col) + var(--s6)); }
}
.ui-report-box {
  display: flex; flex-direction: column; gap: var(--s5);
  width: min(var(--report), calc(100vw - 2 * var(--s8))); padding: var(--s7);
  border: 1px solid var(--line); border-radius: var(--r7); background: var(--surface); box-shadow: var(--shadow);
}
.ui-report-box[hidden] { display: none; }

/* The page's own right-click menu (UIC-35): at the pointer, over the page and
   under the report box it opens. */
.ui-context {
  position: fixed; z-index: 115; min-width: var(--menu); padding: var(--s3);
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: var(--r6); background: var(--surface); box-shadow: var(--shadow);
}
.ui-context[hidden] { display: none; }
.ui-context-item {
  padding: var(--s4) var(--s5); border: 0; border-radius: var(--r4); background: none;
  font: inherit; font-size: var(--t5); color: var(--ink); text-align: left; cursor: pointer;
}
.ui-context-item:hover, .ui-context-item:focus-visible { background: var(--chip); outline: none; }
.ui-context-hint {
  margin: var(--s2) 0 0; padding: var(--s3) var(--s5) var(--s2); border-top: 1px solid var(--hair);
  font-size: var(--t3); color: var(--mute);
}

/* Where the notices stand: fixed at the foot wherever the page drew them,
   clear of a phone's safe area, over every surface (the overlay is 110, the
   drawer 130). The strip takes the pointer; the space around it does not. */
.ui-notices {
  position: fixed; left: 0; right: 0; z-index: 140;
  bottom: max(var(--s9), calc(env(safe-area-inset-bottom) + var(--s9)));
  display: flex; flex-direction: column; align-items: center; gap: var(--s4);
  padding: 0 var(--gutter);
  pointer-events: none;
}

/* The mailbox's strip (PBOX-25), now everybody's: the dark solid rather than
   the red, because it is a thing that happened and not a thing to fix here —
   the thing to fix is marked where it is. The dot says which weather. */
.ui-notice {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: var(--s6);
  width: min(var(--notice), 100%);
  padding: var(--s6) var(--s6) var(--s6) var(--s8);
  border-radius: var(--r5);
  background: var(--solid);
  color: var(--solid-ink);
  box-shadow: var(--sheet);
  animation: ui-notice-in var(--arrive);
}
.ui-notice.is-leaving { animation: ui-notice-out var(--leave) forwards; }
.ui-notice-dot {
  flex: none; width: var(--s5); height: var(--s5); border-radius: 50%;
  transform: translateY(var(--s3));
  background: var(--solid-ink); opacity: .45;
}
.ui-notice.is-wrong .ui-notice-dot { background: var(--danger-fill); opacity: 1; }
.ui-notice.is-warn .ui-notice-dot { background: var(--warn-dot); opacity: 1; }
.ui-notice.is-done .ui-notice-dot { background: var(--star); opacity: 1; }
.ui-notice-body { flex: 1; min-width: 0; }
.ui-notice-said, .ui-notice-more {
  margin: 0;
  font-size: var(--t5);
  line-height: var(--lh5);
  text-wrap: pretty;
}
.ui-notice-said { font-weight: 600; }
.ui-notice-more { opacity: .78; }
.ui-notice-close {
  flex: none; width: var(--s10); height: var(--s10); padding: 0; margin: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; background: none;
  color: inherit; font: inherit; font-size: var(--t7); line-height: 1;
  opacity: .7; cursor: pointer;
  transform: translateY(calc(var(--s1) * -1));
}
.ui-notice-close:hover { opacity: 1; }
.ui-notice-close:focus-visible { outline: var(--s1) solid var(--accent); outline-offset: var(--s1); }
@keyframes ui-notice-in {
  from { opacity: 0; transform: translateY(var(--s4)); }
  to { opacity: 1; transform: none; }
}
@keyframes ui-notice-out {
  to { opacity: 0; transform: translateY(var(--s3)); }
}
@media (prefers-reduced-motion: reduce) {
  .ui-notice, .ui-notice.is-leaving { animation: none; }
}

/* ── who somebody is (UI-19) ─────────────────────────────────────────────── */

/* Initials on the address's hue (`--hue` from `ui::face`), lit by the scheme, a press tall. */
.ui-face {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--press);
  height: var(--press);
  border-radius: 50%;
  background: oklch(var(--av-bg) var(--hue));
  color: oklch(var(--av-fg) var(--hue));
  font-size: var(--t3);
  font-weight: 600;
  line-height: 1;
  user-select: none;
  position: relative;
  overflow: hidden;
  box-sizing: border-box;
  text-transform: uppercase;
}
/* One step per place a person stands; the letters a step under the disc. */
.ui-face.is-tiny { width: var(--s9); height: var(--s9); font-size: var(--t1); }
.ui-face.is-small { width: var(--s10); height: var(--s10); font-size: var(--t1); }
.ui-face.is-mid { width: var(--back); height: var(--back); font-size: var(--t4); }
.ui-face.is-big { width: var(--s11); height: var(--s11); font-size: var(--t4); }
.ui-face.is-row { width: var(--s12); height: var(--s12); font-size: var(--t5); }
.ui-face.is-portrait { width: calc(var(--s13) * 2); height: calc(var(--s13) * 2); font-size: var(--t9); }
/* Overlapping faces keep a ring of the ground they stand on between them (`--face-ring`, the surface unless a column says). */
.ui-face.is-ring { border: var(--s1) solid var(--face-ring, var(--surface)); }
.ui-faces.is-crowd { display: inline-flex; flex: none; align-items: center; }
.ui-faces.is-crowd > .ui-face + .ui-face { margin-left: calc(-1 * var(--s4)); }
/* A photograph covers the letters, which stay under it for when it does not load. */
/* A third of the way down, not the middle: a portrait taller than the circle keeps the face and loses the chest (KODU-126). */
.ui-face-photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 33%; border-radius: 50%; }
/* A group on a row: two small faces at opposite corners of one row face's room. */
.ui-faces.is-pair { position: relative; flex: none; width: var(--s12); height: var(--s12); }
.ui-faces.is-pair > .ui-face:first-child { position: absolute; top: 0; left: 0; }
.ui-faces.is-pair > .ui-face:last-child { position: absolute; right: 0; bottom: 0; }

/* ── one word about the thing beside it ──────────────────────────────────── */

.ui-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s1) var(--s4);
  border-radius: var(--r4);
  background: var(--chip);
  color: var(--ink3);
  font-size: var(--t3);
  font-weight: 600;
  white-space: nowrap;
}
.ui-chip.is-live { background: var(--ok-bg); color: var(--ok-ink); }
.ui-chip.is-held { background: var(--chip); color: var(--mute); }
.ui-chip.is-owed { background: var(--warn-bg); color: var(--warn-ink); }
/* Late, refused, not answering: the red's wash, never its fill (the mailbox's `is-late`). */
.ui-chip.is-late { background: var(--danger-wash); color: var(--danger-deep); }
/* Set for a day and not past it: the mailbox's due date, in the accent's wash. */
.ui-chip.is-due { background: var(--accent-soft); color: var(--accent-text); }
/* How much is not yet seen: a count in the accent's fill, as round as its one figure allows. */
.ui-chip.is-new { justify-content: center; min-width: var(--s9); padding: var(--s1) var(--s3); background: var(--accent); color: var(--accent-ink); font-variant-numeric: tabular-nums; }
/* A word somebody chose rather than a state the machine printed: the mono, on the hover's grey (P14). */
.ui-chip.is-tag { font-family: var(--mono); font-size: var(--t2); font-weight: 400; background: var(--hover); color: var(--ink3); }
/* Who somebody is: their face before the words, in the reading weight (a recipient). */
.ui-chip.is-who { padding-left: var(--s1); font-size: var(--t4); font-weight: 400; color: var(--ink2); background: var(--field); }
/* A chip that fills its line, its words ending in an ellipsis where the line is shorter (a task in a calendar's cell). */
.ui-chip.is-line { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
a.ui-chip { text-decoration: none; }
a.ui-chip:hover { color: var(--accent-down); }
/* A filter's chip is never wider than its row: its words end in an ellipsis and the × stays on the screen (UIC-51). */
.ui-chip:has(> .ui-chip-off) { max-width: 100%; }
.ui-chip-said { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* The × that takes a filter off, a round inside the chip. */
.ui-chip-off {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--s8);
  height: var(--s8);
  margin-right: calc(-1 * var(--s2));
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: inherit;
  font-size: var(--t5);
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
}
.ui-chip-off:hover { background: var(--accent-soft); }
/* A status on a press is pressed where it is read; one that discloses what it counts waits until it is live. */
button.ui-chip:hover { text-decoration: underline; text-underline-offset: var(--s1); }
button.ui-chip[aria-expanded]:not(.is-live) { cursor: default; text-decoration: none; }

/* ── a run of controls under one label ───────────────────────────────────── */

/* A column, because a form is filled in in order, and a measure rather than
   the full width, because a line of text nobody can follow to the end is not
   a form anybody finishes. */
.ui-group {
  display: flex;
  flex-direction: column;
  gap: var(--s8);
  max-width: var(--group);
}
.ui-group > .ui-label {
  margin: 0;
}

/* ── a surface ───────────────────────────────────────────────────────────── */

.ui-card {
  padding: clamp(var(--s8), 1.4vw, var(--s9));
  border: 1px solid var(--line);
  border-radius: var(--r7);
  background: var(--surface);
}

/* ── a detail beside what is open, or a record's form (UI-8, UIC-43) ───────── */

/* `ui::drawer` and `ui::panel`, in the mailbox's look: what it is about stays
   in sight behind it under a light veil, and it slides in from the right. Here
   and not in Platform's sheet, so a door that loads only the tier has it. */
.drawer {
  position: fixed; inset: 0; z-index: 120;
  display: flex; justify-content: flex-end;
  background: var(--veil);
  animation: drawer-backdrop-in var(--exit);
}
.drawer-panel {
  width: min(var(--drawer), 100%); height: 100%;
  display: flex; flex-direction: column;
  background: var(--surface); border-left: 1px solid var(--line); box-shadow: var(--slide);
  animation: drawer-in var(--enter);
}
@keyframes drawer-backdrop-in { from { opacity: 0; } }
@keyframes drawer-backdrop-out { to { opacity: 0; } }
@keyframes drawer-in { from { transform: translateX(var(--s10)); opacity: 0; } }
@keyframes drawer-out { to { transform: translateX(var(--s10)); opacity: 0; } }
.drawer.is-closing { animation: drawer-backdrop-out var(--exit) forwards; pointer-events: none; }
.drawer.is-closing .drawer-panel { animation: drawer-out var(--exit) forwards; }
@media (prefers-reduced-motion: reduce) {
  .drawer, .drawer-panel, .drawer.is-closing, .drawer.is-closing .drawer-panel { animation: none; }
}
.drawer-head {
  flex: none;
  display: flex; align-items: center; gap: var(--s6);
  padding: var(--s9) var(--s10) var(--s8);
  border-bottom: 1px solid var(--line);
}
.drawer-head strong {
  font-family: var(--serif); font-size: var(--t8); font-weight: 500; line-height: var(--lh8);
  letter-spacing: -0.015em; color: var(--ink); overflow-wrap: anywhere;
}
.drawer-body { flex: 1; min-height: 0; overflow-y: auto; padding: var(--s9) var(--s10); }
/* A drawer's or a record's form stands its parts one under the other, a step apart (UI-20). */
:is(.drawer-body, .rec) { display: flex; flex-direction: column; gap: var(--s8); }
/* What a refused save says, at the form's head (UIC-24). */
.drawer-alert {
  padding: var(--s5) var(--s7);
  border-radius: var(--r4);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: var(--t4);
}
.drawer-alert a { color: inherit; font-weight: 600; text-decoration: underline; text-underline-offset: .2em; }
/* A record's staged pictures (UI-20): the strip, a picture with its red ×, the first one's cover tag. */
.ui-media-strip { display: flex; flex-wrap: wrap; gap: var(--s5); margin-block: var(--s5); }
.ui-thumb { position: relative; flex: none; width: calc(var(--s13) + var(--s10)); height: calc(var(--s13) + var(--s10)); }
.ui-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; border-radius: var(--r4); background: var(--field); }
.ui-thumb > .ui-round { position: absolute; top: var(--s2); right: var(--s2); }
.ui-thumb-cover {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: var(--s1) 0;
  border-radius: 0 0 var(--r4) var(--r4);
  background: var(--stage-veil);
  color: var(--stage-ink);
  font-size: var(--t1);
  letter-spacing: .06em;
  text-align: center;
  text-transform: uppercase;
}
.drawer-foot {
  flex: none;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s5);
  padding: var(--s8) var(--s10) var(--s9);
  border-top: 1px solid var(--hair);
}
/* Its acts stand as a record's head's do (UI-20): Delete, then the commit last, at the right; a finger tall on a phone (`ui-acts`). */
.drawer-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--s4); }
.drawer-status { font-size: var(--t3); color: var(--mute); }
.drawer-status.is-saved { color: var(--star); }
/* Settings with an endpoint of their own, below the form rather than in it. */
.drawer-extra { border-top: 1px solid var(--line); padding: var(--s9) var(--s10); background: var(--field); }
/* The panel is the page's one live surface while it is open: the page behind it does not scroll. */
body.drawer-open { overflow: hidden; }

/* ── the surface a whole job sits on ─────────────────────────────────────── */

/* UI-14: `ui::overlay`. The lightbox is one of these too — it adds a stage
   and a zoom, not a way of covering the page. Here in the tier and not in
   any one application's sheet, because the components that wear it are the
   tier's: until 2026-09-08 these rules lived in Platform's theme sheet,
   which only the console loads, and the question `ui::confirm` asks stood
   unstyled at the foot of the mailbox's document, off the screen — a press
   that did nothing anybody could see (LLM-32). */
.overlay {
  position: fixed; inset: 0; z-index: 110;
  background: var(--surface);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.overlay-head {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; gap: var(--s8);
  padding: var(--s8) var(--s10);
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.overlay-body { padding: var(--s10); }
/* One scrollbar, one wheel: the page behind stays where it was. */
body.overlay-open { overflow: hidden; }

/* UI-22, UIC-44: `ui::dialog` — the question a press asks, and a job small
   enough for one card. The mailbox's delete-task card: a card over a dimmed
   page rather than the whole surface, because the page behind is the thing
   being decided about. Over the drawer's 120 and the dock's 60 — one of the
   things it asks about is a drawer being closed — and under the strip. */
.ui-dialog {
  position: fixed; inset: 0; z-index: 130;
  display: flex; align-items: safe center; justify-content: center;
  padding: var(--s8);
  background: var(--scrim);
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* The whole glass, whatever column it is drawn in: a page that is the
   question stands in a pane whose `> *` caps and centres its children, and
   that must not make the veil a column too. */
.ui-dialog[role="dialog"] { width: auto; max-width: none; margin: 0; }
.ui-dialog-card {
  display: flex; flex-direction: column; gap: var(--s7);
  width: min(var(--dialog), 100%);
  padding: var(--s10);
  border-radius: var(--r6);
  background: var(--surface);
  box-shadow: var(--sheet);
}
.ui-dialog-card.is-wide { width: min(var(--dialog-wide), 100%); }
/* A card with a way out on every step wears it beside its title. */
.ui-dialog-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s5); }
/* A long address or a file's name breaks inside the card (UIC-44). */
.ui-dialog-title {
  margin: 0;
  font-family: var(--serif); font-size: var(--t8); line-height: var(--lh8); font-weight: 500;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.ui-dialog-says { margin: 0; font-size: var(--t5); line-height: var(--lh6); color: var(--ink3); overflow-wrap: anywhere; }
/* The answers, the safe one first. A phone stacks them across the card, so
   neither is ever pushed off it; a desk sets them side by side at the right.
   Each is the tier's tall pill (`.ui-modal`), focus ring and all. */
.ui-dialog-acts {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
}
/* Two that do not fit one row take two, each whole (UIC-44). */
@container (min-width: 641px) {
  .ui-dialog-acts { flex-direction: row; flex-wrap: wrap; justify-content: flex-end; }
  /* An action that is not one of the answers — adding a tag — stands apart. */
  .ui-dialog-acts > .is-away { margin-right: auto; }
}
/* Docked (UIC-43): at the foot of what it is about, no veil, the thing live beside it. */
.ui-dialog.is-docked[role="dialog"] {
  position: absolute; inset: auto var(--s4) var(--s4) var(--s4); z-index: 60;
  display: block; max-height: 60%; padding: 0;
  border-radius: var(--r6); background: none; overflow: auto;
}
/* Its paint is its own: a pinned row over a live canvas is otherwise painted again where it is not. */
.ui-dialog.is-docked .ui-dialog-card { width: auto; gap: var(--s5); padding: var(--s7) var(--s8); contain: paint; }
/* Three fifths of the thing on any width, so its own head stays in reach; the title stays in view while the rest scrolls,
   and on a desk, where the answers are one row, they do too. */
.ui-dialog.is-docked .ui-dialog-head { position: sticky; top: 0; z-index: 1; background: var(--surface); }
@container (min-width: 641px) {
  .ui-dialog.is-docked[role="dialog"] {
    right: auto; left: var(--s6); bottom: var(--s6);
    width: min(var(--dialog-wide), calc(100% - var(--s12)));
  }
  .ui-dialog.is-docked .ui-dialog-acts {
    position: sticky; bottom: 0; z-index: 1;
    margin-bottom: calc(-1 * var(--s7)); padding: var(--s4) 0 var(--s7); background: var(--surface);
  }
}

/* ── the card a whole screen is filled in on ─────────────────────────────── */

/* `nowrap` is load-bearing: the moment a sheet gives this a height, a
   wrapping column folds its tail off the right-hand edge. */
.ui-form {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: stretch;
  gap: var(--s9);
  margin: 0 auto;
  padding: clamp(var(--s8), 1.4vw, var(--s9));
  border: 1px solid var(--line);
  border-radius: var(--r7);
  background: var(--surface);
}

.ui-form-head {
  display: flex;
  align-items: center;
  gap: var(--s6);
}
/* The heading takes the room and the way out is pushed to the far edge. */
.ui-form-head > .ui-h2 { flex: 1; min-width: 0; }

/* The press stands on a line of its own under a rule, with whatever the form
   has to say about itself opposite it. */
.ui-form-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s6);
  padding-top: var(--s8);
  border-top: 1px solid var(--hair);
}
.ui-form-aside {
  font-size: var(--t4);
  color: var(--mute);
  text-wrap: pretty;
}

/* Two or three fields that belong to one fact, sharing a line and going back
   under each other where there is no room. */
.ui-field-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--s5);
}
/* `min-width: 0` is not decoration: without it one long unbreakable value —
   a pasted URL, an IBAN — sets the field's floor and pushes its neighbour off
   the row. Kodu's `.k-row` learned this the hard way and the tier inherits
   the lesson. */
.ui-field-row > .ui-field { flex: 1 1 calc(6 * var(--s11)); min-width: 0; }

/* ── headings, at the house sizes ────────────────────────────────────────── */

.ui-h1 {
  margin: 0;
  font-family: var(--serif);
  font-size: var(--t8);
  line-height: var(--lh8);
  font-weight: 500;
  color: var(--ink);
}
.ui-h2 {
  margin: 0;
  font-family: var(--serif);
  font-size: var(--t7);
  line-height: var(--lh7);
  font-weight: 500;
  color: var(--ink);
}

/* The arrangements (UIC-6, UI-18), on the house tokens and defining no colour
   and no length of its own.

   The frame fills the browser and does not scroll: `html` and `body` stay put
   and each column scrolls itself. That is the one thing the mailbox, the
   console and the portal already agreed on and each had written out
   separately.

   **Which columns a screen has is the application's; how wide each is, the
   tier's.** A column is as wide as what it is (UIC-38): the rail
   `--rail-col`, the list `--list-col`, the list of what its chosen row holds
   `--sublist-col`, the part of the thing beside it `--side-col`, and the thing
   itself takes the rest. An application sheet writes no track and no step. */

/* The page is the container (UIC-23): every question about width — an
   application's own queries, the layer's `d-` twins — is asked of the
   body's own width and not the window's, so a page narrowed by the dock
   (UIC-13) is a phone to everything on it. */
body { container-type: inline-size; }

.ui-frame {
  display: flex;
  width: 100%;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
  position: relative;
  background: var(--surface);
}
.ui-frame > .ui-rail { flex: none; width: var(--rail-col); }
.ui-frame > .ui-list { flex: none; width: var(--list-col); }
.ui-frame > .ui-sublist { flex: none; width: var(--sublist-col); }
.ui-frame > .ui-pane { flex: 1 1 0; }
.ui-frame > .ui-props { flex: none; width: var(--side-col); }
/* What a column and a pane hold stands in from their edges a thumb's width on a phone, the heads' own inset on a desk. */
@container (min-width: 641px) {
  .ui-frame { --list-inset: var(--s9); --pane-inset: var(--s10); }
}

/* Every column is a flex column that can shrink: without the two `min-*`
   zeroes a long word in one of them widens the track and pushes the others
   off the screen, which is the bug every one of these three surfaces has had
   at least once. */
.ui-col {
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.ui-rail {
  background: var(--rail);
  border-right: 1px solid var(--line);
  /* Visible, not hidden: a rail holds popovers that must be allowed out of
     it, and it is narrow enough never to need to scroll sideways. */
  overflow: visible;
}

/* The list, and the list of what its chosen row holds — the mailbox's threads of a conversation. */
.ui-list, .ui-sublist {
  background: var(--surface);
  border-right: 1px solid var(--line);
  overflow-y: auto;
}

/* A list column's head and rows are `lists.css` (UIC-43). */

.ui-pane {
  background: var(--surface);
  overflow-y: auto;
}

/* The fourth column: what is chosen inside the pane, when that is a different
   thing from what the pane is about. On the far side, so the reading goes
   rail → list → the thing → the part of it. */
.ui-props {
  background: var(--surface);
  border-left: 1px solid var(--line);
  overflow-y: auto;
}

/* Below the wide desk a frame holds the rail and two columns (UIC-38). One with three names the
   column that steps out (`data-narrow`): the list once something it holds is open, the thing while
   nothing is, the part of the thing while the list is chosen from; a sublist takes the list's place
   or the thing's room. The way to a list stepped out (`is-to-list`) stands there and on a phone. */
@container (min-width: 641px) {
  .ui-fold .is-to-list { display: none; }
}
@container (min-width: 641px) and (max-width: 1080px) {
  .ui-fold[data-narrow="list"] > .ui-list,
  .ui-fold[data-narrow="pane"] > .ui-pane,
  .ui-fold[data-narrow="props"] > .ui-props { display: none; }
  .ui-fold[data-narrow="list"] > .ui-sublist { width: var(--sublist-narrow); }
  .ui-fold[data-narrow="pane"] > .ui-sublist { flex: 1 1 0; }
  .ui-fold[data-narrow="list"] .is-to-list { display: inline-flex; }
  /* A thing beside its list is narrow here: its head's words take their own row under the title, as on a phone. */
  .ui-fold > .ui-list ~ .ui-pane .ui-pane-acts:has(> :is(.ui-pill, .ui-btn, .ui-submit)) {
    grid-row: auto;
    grid-column: 1 / -1;
    flex-wrap: wrap;
    margin-left: 0;
  }
}

/* A frame that folds (`ui-fold`, UIC-42): on a phone the column its `data-phone` names fills the screen and the rail lies down under it, reversed so the reading order keeps the rail first. Where two things stand side by side, the later — opened from the first — is the screen; the part chosen inside the thing rises from the foot as a sheet. */
@container (max-width: 640px) {
  .ui-fold[data-phone] { display: flex; flex-direction: column-reverse; }
  .ui-fold > .ui-col { width: auto; }
  .ui-fold > .ui-col:not(.ui-rail) { flex: 1; border-right: 0; }
  .ui-fold[data-phone="list"] > .ui-col:not(.ui-rail, .ui-list),
  .ui-fold[data-phone="sublist"] > .ui-col:not(.ui-rail, .ui-sublist) { display: none; }
  .ui-fold[data-phone="pane"] > .ui-list { display: none; }
  .ui-fold[data-phone="pane"] > .ui-sublist,
  .ui-fold[data-phone="pane"] > .ui-pane:has(~ .ui-pane) { display: none; }
  .ui-fold > .ui-props {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 30;
    max-height: var(--sheet-rise);
    border-left: 0;
    border-top: 1px solid var(--line);
    border-radius: var(--r6) var(--r6) 0 0;
    box-shadow: var(--rise);
  }
  /* A thing that is read (`data-reading`: a letter open in the mailbox) is the whole screen; the rail waits on the list it came from, and the pane's ring is the way back. */
  .ui-fold[data-reading] > .ui-rail { display: none; }
  /* A column scrolls as one: a list's head and its search leave with its rows and are there again at the top. */
  .ui-fold > .ui-col > .ui-list-rows { flex: none; min-height: auto; overflow-y: visible; }
  /* While a column scrolls down the bar steps under the foot, and comes back the moment it scrolls up (`TUCK_SCRIPT`). */
  .ui-fold > .ui-rail { transition: margin-bottom var(--unfold); }
  .ui-fold > .ui-rail.is-tucked { margin-bottom: calc(-1 * var(--rail-h, 0px)); }
  /* A page ends with room after its last thing, so the last row is read clear of the foot and of the bar (UIC-51). */
  .ui-fold[data-phone]:not([data-still]) > .ui-col:not(.ui-rail)::after { content: ""; flex: none; height: var(--page-end); }
}

/* A phone's page scrolls as the document (UIC-51): a browser takes its own bars away only while the document scrolls, and a frame of the window whose columns scroll themselves kept Safari's toolbar on the screen for good. So a folded frame is as tall as what it shows; the bar rides the foot and the pane's head the top, and both step aside as the browser's do (`TUCK_SCRIPT`). A screen that is the screen — a map, a picture, a chat over its composer — says `data-still` and keeps the window's frame. The device's width asks: a desk the dock narrows keeps its columns. */
@media (max-width: 640px) {
  html:has(.ui-fold[data-phone]:not([data-still])),
  html:has(.ui-fold[data-phone]:not([data-still])) > body { height: auto; overflow: visible; }
  .ui-fold[data-phone]:not([data-still]) { height: auto; min-height: 100dvh; min-width: 0; overflow: visible; }
  .ui-fold[data-phone]:not([data-still]) > .ui-col:not(.ui-rail) { overflow: visible; overflow-x: clip; }
  .ui-fold[data-phone]:not([data-still]) > .ui-rail { position: sticky; bottom: 0; z-index: 20; transition: transform var(--unfold); }
  .ui-fold[data-phone]:not([data-still]) > .ui-rail.is-tucked { margin-bottom: 0; transform: translateY(100%); }
  /* A sheet the frame lays over itself (`ui-over`, the mailbox's composer) is the window's, its head and its press on the screen however far down the page is. */
  .ui-fold[data-phone]:not([data-still]) > .ui-over { position: fixed; }
}

/* The part of a pane that scrolls under its head on a desk. On a phone the pane scrolls as one, so what stands over the words — the head, a search — leaves with them and the words have the screen. */
.ui-pane-scroll { flex: 1; min-height: 0; overflow-y: auto; }
@container (max-width: 640px) {
  .ui-pane-scroll { flex: none; min-height: auto; overflow-y: visible; }
}

/* ── a thing and a part of it beside it (`layouts::sided`, UIC-38) ── */

/* The side keeps `--side-col` while the thing keeps `--main-least`; once the pane holds less the side
   stands under the thing, a hairline over it, and the two scroll as one. */
.ui-sided { flex: 1; min-height: 0; display: flex; flex-wrap: wrap; overflow-y: auto; }
.ui-sided-main {
  flex: 999 1 var(--main-least);
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s10);
  padding: var(--s8) var(--s7) var(--s10);
}
.ui-sided-main > * { max-width: var(--sheet-col); }
.ui-sided-side {
  flex: 1 1 var(--side-col);
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s9);
  margin: -1px 0 0 -1px;
  padding: var(--s10) var(--s9);
  border-top: 1px solid var(--hair);
  border-left: 1px solid var(--hair);
  background: var(--mid);
}
@container (min-width: 641px) {
  .ui-sided-main { padding: var(--s10) var(--s10) var(--s13); }
}

/* ── a page that is read or filled in (UIC-41): its band over one centred column ── */

.ui-reading { display: flex; flex-direction: column; min-width: 0; }
.ui-reading[data-measure="prose"] { --read: var(--prose); }
.ui-reading[data-measure="wide"] { --read: var(--measure); }
.ui-reading-body { padding: clamp(var(--s8), 3vw, var(--s10)) var(--gutter) var(--s13); }
/* The column the band's title stands over, whatever its children say about their own margins. */
.ui-reading-col {
  display: flex;
  flex-direction: column;
  gap: var(--s10);
  width: 100%;
  min-width: 0;
  max-width: var(--read);
  margin-inline: auto;
}
/* A card filled in on a reading page is the column: its edges are the title's, not its content's. */
.ui-reading-col > .ui-form { margin-inline: 0; }

/* A working surface's list and the thing open beside it (UIC-43), phone first: the list's head,
   its rows, its caption, the ⋯, what an empty one says, and the pane's head — the mailbox's,
   every door's. The desk is the container rules at the foot of each part. */

/* ── the list column's head ──────────────────────────────────────────────── */

.ui-list-head {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--s6);
  padding: var(--s7) var(--s7) var(--s5);
}
/* The title asks no room of its own before it wraps, and grows to its words: the + stays beside it, the ⋯ at the end. */
.ui-list-head-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s5) var(--s3); min-width: 0; }
.ui-list-title {
  flex: 1 1 0;
  min-width: 0;
  max-width: max-content;
  margin: 0;
  font-family: var(--serif);
  font-size: var(--t9);
  font-weight: 500;
  line-height: var(--lh9);
  letter-spacing: -0.015em;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.ui-list-acts { position: relative; flex: none; display: flex; align-items: center; gap: var(--s2); margin-left: auto; }
/* Words take a row of their own under the title on a phone, a finger tall, as a page head's do. */
.ui-list-acts:has(> :is(.ui-pill, .ui-btn, .ui-text-btn)) { order: 1; flex: 1 1 100%; flex-wrap: wrap; gap: var(--s4); margin-left: 0; }
.ui-list-acts :is(.ui-pill, .ui-btn) { min-height: var(--finger); }
/* The chip the list is narrowed by is as wide as its words. */
.ui-list-head > .ui-chip { align-self: flex-start; }
/* What the list is of is a desk's: a phone gives its rows the room (UIC-51). */
.ui-list-lede { display: none; }
/* The search takes its row, up to a search's measure. */
.ui-list-tools { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s4); min-width: 0; }
.ui-list-tools > .ui-find { flex: 1 1 var(--list-max); min-width: 0; max-width: var(--find); }
/* The views keep the title's row, at its end, whether or not a search stands under it: they never move under the pointer. */
.ui-list-head-row > .seg { margin-left: auto; }
.ui-list-head-row > .seg + .ui-list-acts { margin-left: 0; }
/* A board's head stands on the board's own gutter; a hairline closes it, over its caption where it has one. */
.ui-list-head.is-board { padding: 0 0 var(--s4); gap: var(--s8); }
.ui-list-head.is-board > .ui-caption-row { padding-top: var(--s8); border-top: 1px solid var(--line); }
.ui-list-head.is-board:not(:has(> .ui-caption-row)) { padding-bottom: var(--s8); border-bottom: 1px solid var(--line); margin-bottom: var(--s8); }

/* What the one + makes, under it while the pointer or the keyboard is on it. */
.ui-tipped { position: relative; display: inline-flex; flex: none; }
.ui-tip {
  display: none;
  position: absolute;
  top: calc(100% + var(--s3));
  right: calc(-1 * var(--s3));
  z-index: 30;
  width: max-content;
  max-width: var(--list-max);
  padding: var(--s5) var(--s6);
  border-radius: var(--r3);
  background: var(--solid);
  color: var(--solid-ink);
  font-size: var(--t3);
  font-weight: 400;
  line-height: var(--lh6);
  box-shadow: var(--tip);
  pointer-events: none;
}
.ui-tipped:hover > .ui-tip, .ui-tipped:focus-within > .ui-tip { display: block; }
/* It opens leftward, into the room, from the row's end where a + stands; beside a list's title, rightward. */
.ui-list-head-row > .ui-tipped > .ui-tip { right: auto; left: calc(-1 * var(--s3)); }
/* A tip a press opens and shuts, a phone having no hover (the address a name wrote from): rightward from the line's start. */
.ui-tipped.is-press > .ui-tip { right: auto; left: 0; overflow-wrap: anywhere; }
.ui-tipped.is-press:is(:hover, :focus-within) > .ui-tip { display: none; }
.ui-tipped.is-press.is-open > .ui-tip { display: block; }

/* ── a caption: a group, a key, a machine fact (P22) ─────────────────────── */

.ui-caption {
  min-width: 0;
  font-family: var(--mono);
  font-size: var(--t2);
  line-height: var(--lh2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mute);
  overflow-wrap: anywhere;
}
/* An address or a path is printed as it is spelled: one put through text-transform is one nobody can type. */
.ui-caption.is-spelled { text-transform: none; letter-spacing: 0.02em; }
a.ui-caption { text-decoration: none; }
a.ui-caption:hover { color: var(--accent-down); text-decoration: underline; }
.ui-caption-row { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s9); min-width: 0; }
/* The order a list is in and its ⋯ (`flex center none gap-4`), after what the caption says at its end. */
.ui-caption-row:has(> .ui-caption-acts) { align-items: center; }
/* What it says keeps its words on one line; what stands at its end gives way first, cut short, and the order and the ⋯ never leave the row (UIC-51). */
.ui-caption-row > .ui-caption:first-child { flex: none; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-caption-row > .ui-caption-acts { flex: 1 1 auto; min-width: 0; justify-content: flex-end; }
.ui-caption-row > .ui-caption:last-child:not(:first-child),
.ui-caption-acts > .ui-caption { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-caption-acts > :not(.ui-caption) { flex: none; }

/* A record's facts: each key a caption over its value on a phone, beside it on a desk; a hairline between them. */
.ui-fact { display: flex; flex-direction: column; gap: var(--s1); padding: var(--s5) 0; border-bottom: 1px solid var(--hair); }
.ui-fact:last-child { border-bottom: 0; }
.ui-fact > dd { margin: 0; min-width: 0; font-size: var(--t5); line-height: var(--lh5); color: var(--ink2); overflow-wrap: anywhere; }
.ui-facts.is-mono .ui-fact > dd { font-family: var(--mono); font-size: var(--t3); line-height: var(--lh3); }

/* ── the rows ────────────────────────────────────────────────────────────── */

.ui-list-rows { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: var(--s1); padding: 0 var(--s4) var(--s6); }
.ui-list-rows > * { flex: none; }
/* A run of rows that belong together — a category, a day — under its caption, on the rows' own edge. */
.ui-list-rows > .ui-caption-row { padding: var(--s9) var(--s6) var(--s3); }
.ui-list-rows > .ui-caption-row:first-child { padding-top: var(--s3); }
/* A board's rows stand on its head's edge, their wash out in the board's gutter; the board scrolls, not they. */
.ui-list-rows.is-board { flex: none; overflow: visible; padding: 0; margin-inline: calc(-1 * var(--s6)); }
/* Its rows' presses end on the head's edge too, as their faces start on it. */
.ui-list-rows.is-board .ui-list-row.is-box:has(> .ui-list-row-acts),
.ui-list-rows.is-board .ui-list-row.has-acts > .ui-list-row-acts { padding-right: var(--s6); }

.ui-list-row {
  display: flex;
  align-items: center;
  gap: var(--s6);
  min-width: 0;
  padding: var(--s6);
  border-radius: var(--r4);
  color: inherit;
  text-decoration: none;
}
.ui-list-row:hover { background: var(--hover); }
/* The open one, and the one a fragment lands on (the listing you came back from): washed. */
.ui-list-row:is(.is-on, :target) { background: var(--wash); }
/* A row with presses of its own is a box around its link and them. */
.ui-list-row.has-acts { padding: 0; gap: 0; }
.ui-list-row-open {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--s6);
  padding: var(--s6);
  border-radius: inherit;
  color: inherit;
  text-decoration: none;
}
.ui-list-row-acts { flex: none; display: flex; align-items: center; gap: var(--s1); padding-right: var(--s4); }
/* Words among a row's presses are a finger tall on a phone, as a thing's acts are (PBOX-13); so is a round beside them. */
.ui-list-row-acts :is(.ui-pill, .ui-btn) { min-height: var(--finger); }
.ui-list-row-acts .ui-round { min-width: var(--finger); min-height: var(--finger); }
/* Rounds keep the row's line; words take a line of their own under it on a phone, at its right, as a pane's acts do. */
.ui-list-row.has-acts:has(> .ui-list-row-acts :is(.ui-pill, .ui-btn, .ui-submit)) { flex-wrap: wrap; }
.ui-list-row.has-acts:has(> .ui-list-row-acts :is(.ui-pill, .ui-btn, .ui-submit)) > .ui-list-row-open { flex-basis: 100%; }
.ui-list-row.has-acts:has(> .ui-list-row-acts :is(.ui-pill, .ui-btn, .ui-submit)) > .ui-list-row-acts {
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--s3);
  margin: calc(-1 * var(--s3)) 0 0 auto;
  padding-bottom: var(--s6);
}
/* A row that opens nothing, or holds what a link cannot (a tag's link, a form): a box, its link stretched over it and what hangs under its lines above the link. */
.ui-list-row.is-box { position: relative; flex-wrap: wrap; align-items: flex-start; }
/* Its words keep a reading width beside the face; the presses step under them, at the right, where both do not fit. */
.ui-list-row.is-box > .ui-list-row-text { flex: 1 1 min(var(--list-max), 60%); }
.ui-list-row.is-box:not(:has(.ui-list-row-link)):not(summary > *):hover { background: none; }
.ui-list-row.is-box:has(> .ui-list-row-acts) { padding-right: var(--s4); }
.ui-list-row.is-box > .ui-list-row-acts { position: relative; flex-wrap: wrap; justify-content: flex-end; gap: var(--s3); margin-left: auto; padding-right: 0; }
.ui-list-row-link { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; color: inherit; text-decoration: none; }
.ui-list-row-link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.ui-list-row-subject.is-none { font-style: italic; color: var(--mute); }
/* A thing that can be finished, ticked at its row's left; done is green, the accent being yours, unread, chosen. */
.ui-tick {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--s9);
  height: var(--s9);
  padding: 0;
  border: var(--s1) solid var(--ring);
  border-radius: 50%;
  background: none;
  color: var(--accent-ink);
  cursor: pointer;
}
.ui-tick:hover { border-color: var(--star); }
.ui-tick.is-done { background: var(--star); border-color: var(--star); }
.ui-tick > svg { width: var(--s6); height: var(--s6); }
/* Finished: its words struck through and faint. */
.ui-list-row.is-done .ui-list-row-subject { text-decoration: line-through; color: var(--faint); }
/* The sub-list's open row is ruled in the accent, not washed: two washed rows in two columns read as one choice. */
.ui-sublist .ui-list-row.is-on { background: none; border-radius: 0; box-shadow: inset var(--s1) 0 var(--accent); }
.ui-sublist .ui-list-row.is-on:hover { background: var(--hover); }
.ui-list-row-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--s1); }
.ui-list-row-top { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s4); }
.ui-list-row-line { display: flex; align-items: center; gap: var(--s3); min-width: 0; }
.ui-list-row-line > :not(.ui-list-row-subject, .ui-list-row-under) { flex: none; }
/* A record's facts: one line of captions (`grow flex center clip gap-3 nobr`) that gives way before the chips after it do. */
.ui-list-row-under > * { min-width: 0; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-list-row-who, .ui-list-row-subject { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-list-row-who { font-size: var(--t5); font-weight: 500; color: var(--ink2); }
.ui-list-row-when { flex: none; font-size: var(--t3); color: var(--mute); font-variant-numeric: tabular-nums; }
.ui-list-row-subject { flex: 1 1 auto; font-family: var(--serif); font-size: var(--t5); color: var(--ink3); }
.ui-list-row:is(.is-unseen, .is-on) .ui-list-row-who { font-weight: 600; color: var(--ink); }
.ui-list-row.is-unseen .ui-list-row-when { font-weight: 600; color: var(--accent-down); }
.ui-list-row.is-unseen .ui-list-row-subject { color: var(--ink2); }
/* A row whose words are its subject (a report): two lines of them before the cut, the chips on a line under. */
.ui-list-row.is-wrap, .ui-list-row.is-wrap > .ui-list-row-open { align-items: flex-start; }
.ui-list-row.is-wrap .ui-list-row-line { flex-wrap: wrap; row-gap: var(--s3); }
.ui-list-row.is-wrap .ui-list-row-subject {
  flex: 1 1 100%;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-height: var(--lh5);
  white-space: normal;
  overflow-wrap: anywhere;
}
.ui-list-row-line > .ui-caption { flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A thing's picture at the left, where a person's face would stand; the hatch holds its place until it has one. */
.ui-list-row-thumb { flex: none; width: var(--s12); height: var(--s12); border-radius: var(--r3); object-fit: cover; background: var(--field); }
.ui-list-row-thumb.is-empty { background: repeating-linear-gradient(135deg, var(--chip) 0 var(--s3), var(--line) var(--s3) var(--s6)); }
/* A door's own picture of a thing (a listing's photograph over its tint), framed as the thumbnail. */
.ui-list-row-thumb.is-framed { overflow: hidden; }
.ui-list-row-thumb.is-framed > * { width: 100%; height: 100%; aspect-ratio: auto; }
.ui-list-row-thumb.is-framed svg { width: var(--s9); height: var(--s9); }
/* A document's tile: its kind spelt at its foot, where a picture would be. */
.ui-list-row-thumb.is-kind {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: var(--s2);
  border: 1px solid var(--line);
  font-family: var(--mono);
  font-size: var(--t1);
  letter-spacing: 0.04em;
  color: var(--ink3);
}

/* ── nothing to show (P6) ────────────────────────────────────────────────── */

.ui-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s12) var(--s8);
  text-align: center;
}
.ui-empty-in { max-width: var(--list-max); display: flex; flex-direction: column; align-items: center; gap: var(--s4); }
.ui-empty-glyph { width: var(--s10); height: var(--s10); color: var(--faint); }
.ui-empty-title { font-family: var(--serif); font-size: var(--t7); font-weight: 500; line-height: var(--lh7); color: var(--ink3); }
.ui-empty-says { margin: 0; font-size: var(--t4); line-height: var(--lh5); color: var(--mute); }
/* A board's: where its first row would stand, not in the middle of the room the rows would fill. */
.ui-empty.is-left { flex: none; justify-content: flex-start; text-align: left; padding: var(--s11) var(--s1); }
.ui-empty.is-left .ui-empty-in { align-items: flex-start; max-width: var(--group); }

/* ── the ⋯ (P20) ─────────────────────────────────────────────────────────── */

.ui-more { position: relative; display: inline-flex; flex: none; }
.ui-more-menu {
  position: absolute;
  top: calc(100% + var(--s3));
  right: 0;
  z-index: 60;
  min-width: var(--menu);
  display: flex;
  flex-direction: column;
  padding: var(--s2);
  border: 1px solid var(--line);
  border-radius: var(--r4);
  background: var(--surface);
  box-shadow: var(--pop);
}
.ui-more-menu > .ui-caption { display: block; padding: var(--s3) var(--s5) var(--s2); }
.ui-more-item {
  display: flex;
  align-items: center;
  gap: var(--s5);
  width: 100%;
  padding: var(--s4) var(--s5);
  border: 0;
  border-radius: var(--r3);
  background: none;
  color: var(--ink2);
  font-size: var(--t5);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.ui-more-item:hover { background: var(--hover); color: var(--ink); }
.ui-more-item.is-on { color: var(--accent-down); font-weight: 600; }
.ui-more-item.is-danger { color: var(--danger); }
.ui-more-item.is-danger:hover { background: var(--danger-soft); color: var(--danger); }
.ui-more-form { display: contents; }
.ui-more-rule { height: 1px; margin: var(--s2) 0; background: var(--hair); }

/* ── a face beside running text (`ui-hang`): a letter's sender, a comment's writer ── */

/* On a desk the face hangs to the left of the name and of everything under it; on a phone it joins the name's line and the words take the width. */
.ui-hang {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "face head" "face body";
  column-gap: var(--s6);
  row-gap: var(--s2);
  align-items: start;
}
.ui-hang-face { grid-area: face; display: flex; }
.ui-hang-head { grid-area: head; min-width: 0; }
.ui-hang-body { grid-area: body; min-width: 0; }
/* A face beside words no name heads (`is-lone`, the assistant's reply): on a phone it stands down and the words take the width, and so does what hangs on its edge (`--hang`, UIC-51). */
.ui-hang.is-lone { grid-template-areas: "face body"; }
@container (max-width: 640px) {
  .ui-hang { grid-template-areas: "face head" "body body"; column-gap: var(--s5); row-gap: var(--s4); align-items: center; }
  .ui-hang-body { align-self: start; }
  .ui-hang.is-lone { grid-template-areas: "body"; }
  .ui-hang.is-lone > .ui-hang-face { display: none; }
}

/* ── the thing itself: the pane's head ───────────────────────────────────── */

/* Ring, face, title and acts on one row; the chips on their own under it (the whole width on a phone). */
.ui-pane-head {
  flex: none;
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  align-items: start;
  row-gap: var(--s4);
  padding: var(--s7);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.ui-pane-head > .ui-back { grid-area: 1 / 1; margin-block: calc((var(--t8) * var(--lh10) - var(--back)) / 2); margin-right: var(--s6); }
.ui-pane-face { grid-area: 1 / 2; align-self: center; display: flex; margin-right: var(--s6); }
.ui-pane-titled { grid-area: 1 / 3; min-width: 0; display: flex; flex-direction: column; gap: var(--s2); }
.ui-pane-title {
  margin: 0;
  min-width: 0;
  font-family: var(--serif);
  font-size: var(--t8);
  font-weight: 500;
  line-height: var(--lh10);
  letter-spacing: -0.015em;
  color: var(--ink);
  /* Wraps between words; a word wider than the column ends in an ellipsis, never split. */
  overflow-x: clip;
  text-overflow: ellipsis;
}
.ui-pane-title.is-none { font-style: italic; font-weight: 400; color: var(--ink3); }
.ui-pane-meta { margin: 0; min-width: 0; font-size: var(--t3); line-height: var(--lh3); color: var(--mute); overflow-wrap: anywhere; }
/* A pane that reads one thing stands its head and its words on the reading column (UIC-41): the same two edges. */
.ui-pane-head.is-read { padding-inline: max(var(--gutter), (100% - var(--read)) / 2); }
.ui-pane-body { padding: var(--s9) var(--gutter) var(--s13); }
.ui-pane-chips { grid-row: 2; grid-column: 1 / -1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); }
.ui-pane-chips > * { min-width: 0; max-width: 100%; }
/* Rounds and the ⋯ keep the title's row; words take a row of their own on a phone, a finger tall. */
.ui-pane-acts { grid-area: 1 / 4; display: flex; align-items: center; gap: var(--s3); min-height: calc(var(--t8) * var(--lh10)); margin-left: var(--s6); }
.ui-pane-acts:has(> :is(.ui-pill, .ui-btn, .ui-submit)) { grid-row: auto; grid-column: 1 / -1; flex-wrap: wrap; margin-left: 0; }
.ui-pane-acts :is(.ui-pill, .ui-btn) { min-height: var(--finger); }
/* On a phone the head rides the top of a pane that scrolls as one, steps up out of the way while the words scroll down and comes back the moment they scroll up (`TUCK_SCRIPT`). */
@container (max-width: 640px) {
  .ui-pane-head { position: sticky; top: 0; z-index: 2; transition: transform var(--unfold); }
  .ui-pane-head.is-tucked { transform: translateY(-100%); }
}

@container (min-width: 641px) {
  .ui-list-head { padding: var(--s10) var(--s9) var(--s8); }
  .ui-list-head.is-board { padding: 0 0 var(--s4); }
  .ui-list-head.is-board:not(:has(> .ui-caption-row)) { padding-bottom: var(--s8); }
  .ui-list-lede { display: block; }
  .ui-frame, .ui-dock { --hang: calc(var(--s10) + var(--s4)); }
  .ui-list-acts:has(> :is(.ui-pill, .ui-btn, .ui-text-btn)) { order: 0; flex: none; flex-wrap: nowrap; margin-left: auto; }
  .ui-list-acts :is(.ui-pill, .ui-btn) { min-height: 0; }
  .ui-list-rows { padding: 0 var(--s6) var(--s6); }
  .ui-fact { flex-direction: row; align-items: baseline; gap: var(--s7); }
  .ui-fact > dt { flex: none; width: var(--part-label); }
  .ui-list-row-acts :is(.ui-pill, .ui-btn) { min-height: 0; }
  .ui-list-row-acts .ui-round { min-width: var(--ui-round-size); min-height: 0; }
  .ui-list-row.has-acts:has(> .ui-list-row-acts :is(.ui-pill, .ui-btn, .ui-submit)) { flex-wrap: nowrap; }
  .ui-list-row.has-acts:has(> .ui-list-row-acts :is(.ui-pill, .ui-btn, .ui-submit)) > .ui-list-row-open { flex-basis: auto; }
  .ui-list-row.has-acts:has(> .ui-list-row-acts :is(.ui-pill, .ui-btn, .ui-submit)) > .ui-list-row-acts { margin: 0; padding-bottom: 0; }
  .ui-pane-head { row-gap: var(--s3); padding: var(--s9) var(--s10) var(--s8); }
  .ui-pane-head > .ui-back { margin-right: var(--s7); }
  .ui-pane-chips { grid-column: 3 / -1; }
  .ui-pane-acts { margin-left: var(--s7); }
  .ui-pane-acts:has(> :is(.ui-pill, .ui-btn, .ui-submit)) { grid-area: 1 / 4; flex-wrap: nowrap; margin-left: var(--s7); }
  .ui-pane-face { margin-right: var(--s7); }
  .ui-pane-acts :is(.ui-pill, .ui-btn) { min-height: 0; }
}

/* The head of a page or a sheet (UIC-43), phone first: the desk is the container rule at the foot. */

/* ── the way back: one ring before the title ─────────────────────────────── */

.ui-back {
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--back);
  height: var(--back);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink3);
  text-decoration: none;
  transition: border-color var(--quick), color var(--quick);
}
.ui-back .icon { width: var(--s8); height: var(--s8); }
.ui-back:hover, .ui-back:focus-visible { border-color: var(--accent); color: var(--accent); }
/* A finger's target around a ring the eye reads smaller. */
.ui-back::before { content: ""; position: absolute; inset: calc((var(--back) - var(--finger)) / 2); }

/* ── the band ─────────────────────────────────────────────────────────────── */

/* Its hairline reaches whatever edge clips it: the pane, the sheet, the glass. */
.ui-head {
  --ui-head-line: calc(clamp(var(--t9), 3vw, var(--t10)) * var(--lh10));
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--s6);
  padding: var(--s8) var(--gutter) 0;
  border-bottom: 1px solid var(--line);
  border-image: linear-gradient(var(--line), var(--line)) 0 0 1 / 0 0 1px / 0 100vw;
}
/* The band stands at the column's measure, its title over what it heads (UIC-41). */
.ui-head-row, .ui-head > .ui-doc-tabs { width: 100%; max-width: var(--read); margin-inline: auto; }
.ui-head:not(:has(> .ui-doc-tabs)) { padding-bottom: var(--s8); }
/* A sheet's head stands on the sheet's column, the one its job is done in (UIC-43). */
.ui-head.is-sheet { --read: var(--sheet-col); }
.ui-head.is-sheet.is-wide { --read: var(--sheet-wide); }
/* A job over the page keeps its band, and its way out, in view while its column scrolls under it. */
.overlay > .ui-reading > .ui-head { position: sticky; top: 0; z-index: 1; background: var(--surface); }

.ui-head-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--s6);
}
.ui-head-row > .ui-back { margin-block: calc((var(--ui-head-line) - var(--back)) / 2); }
/* Where the band's gutter holds the ring, the row reaches out by it and the title stands on the column's edge;
   a ring the desk does not draw (`d-hidden`, the parent beside it) asks for no room. */
.ui-head-row:has(> .ui-back:not(.d-hidden, .hidden)) {
  --ui-head-hang: clamp(0px, ((100% - var(--read)) / 2 - var(--back) - var(--s6)) * 1000, var(--back) + var(--s6));
  max-width: calc(var(--read) + var(--ui-head-hang));
  margin-left: calc((100% - min(100%, var(--read))) / 2 - var(--ui-head-hang));
  margin-right: auto;
}

/* The path wraps under the title before the acts leave the row. */
.ui-head-titled {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s1) var(--s7);
}
.ui-head-title {
  margin: 0;
  min-width: 0;
  font-family: var(--serif);
  font-size: clamp(var(--t9), 3vw, var(--t10));
  font-weight: 500;
  line-height: var(--lh10);
  letter-spacing: -0.02em;
  color: var(--ink);
  overflow-wrap: anywhere;
}
/* A path printed as it is spelled: one put through text-transform is one nobody can type. */
.ui-head-meta {
  min-width: 0;
  font-family: var(--mono);
  font-size: var(--t2);
  line-height: var(--lh2);
  letter-spacing: 0.05em;
  color: var(--mute);
  overflow-wrap: anywhere;
}

/* On a phone the acts take a row of their own under the title, at the left, a finger tall. */
.ui-head-acts {
  order: 1;
  flex: 1 1 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s4);
  min-width: 0;
}
.ui-head-acts [hidden] { display: none; }
/* Acts with nothing to show yet — a press a script reveals, a line it fills — take no row. */
.ui-head-acts:not(:has(> :not(.hidden, [hidden], span:empty))) { display: none; }
.ui-head-acts :is(.ui-pill, .ui-btn) { min-height: var(--finger); }

/* A sheet's way out keeps the title's row at every width, its acts before it on a desk. */
.ui-head-close {
  flex: none;
  display: flex;
  align-items: center;
  min-height: var(--ui-head-line);
}

@container (min-width: 641px) {
  .ui-head-row { flex-wrap: nowrap; }
  .ui-head-acts { order: 0; flex: none; min-height: var(--ui-head-line); margin-left: auto; }
  .ui-head-acts :is(.ui-pill, .ui-btn) { min-height: 0; }
}

/* The manual (UIC-12), phone first: the band is head.css, the column is the reading layout's. */

/* ── the books: words with a line under the one being read ───────────────── */

/* One row a thumb swipes on a phone, never a second line under the first. */
.ui-doc-tabs {
  display: flex;
  gap: clamp(var(--s7), 2vw, var(--s10));
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
}
.ui-doc-tabs::-webkit-scrollbar { display: none; }
/* A finger's target on a phone; a word longer than one starts on the column's edge. */
.ui-doc-tab {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--finger);
  min-width: var(--finger);
  padding: var(--s6) 0;
  font-size: var(--t5);
  color: var(--ink3);
  white-space: nowrap;
  box-shadow: inset 0 -2px 0 transparent;
}
.ui-doc-tab:hover { color: var(--ink); }
.ui-doc-tab.is-on { color: var(--ink); font-weight: 600; box-shadow: inset 0 -2px 0 var(--accent); }

/* ── the pages of a book: the same words a step smaller, over the document ── */

.ui-doc-pills {
  display: flex;
  gap: clamp(var(--s6), 2vw, var(--s9));
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  margin-bottom: var(--s9);
  border-bottom: 1px solid var(--hair);
}
.ui-doc-pills::-webkit-scrollbar { display: none; }
.ui-doc-pill {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--finger);
  min-width: var(--finger);
  padding: var(--s4) 0;
  font-size: var(--t4);
  color: var(--ink3);
  white-space: nowrap;
  box-shadow: inset 0 -2px 0 transparent;
}
.ui-doc-pill:hover { color: var(--ink); }
.ui-doc-pill.is-on { color: var(--ink); font-weight: 600; box-shadow: inset 0 -2px 0 var(--accent); }

/* ── the document, read two ways ─────────────────────────────────────────── */

.ui-doc-body { display: flex; flex-direction: column; min-width: 0; }
/* A class that sets display outranks the browser's own rule for hidden. */
.ui-doc-raw[hidden], .ui-doc-md[hidden] { display: none; }

.ui-doc-raw {
  margin: 0;
  padding: var(--s8);
  border: 1px solid var(--hair);
  border-radius: var(--r4);
  background: var(--mid);
  font-family: var(--mono);
  font-size: var(--t4);
  line-height: 1.7;
  color: var(--ink2);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.ui-doc-md h1 {
  margin: 0 0 var(--s7);
  font-family: var(--serif);
  font-size: var(--t9);
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.ui-doc-md h2 {
  margin: var(--s11) 0 var(--s6);
  font-family: var(--serif);
  font-size: var(--t8);
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.ui-doc-md h3 {
  margin: var(--s9) 0 var(--s5);
  font-family: var(--serif);
  font-size: var(--t6);
  font-weight: 500;
  color: var(--ink);
}
.ui-doc-md p { margin: 0 0 var(--s6); font-size: var(--t5); line-height: 1.65; color: var(--ink3); }
.ui-doc-md ul, .ui-doc-md ol { margin: 0 0 var(--s7); padding-left: var(--s9); }
.ui-doc-md li { margin-bottom: var(--s3); font-size: var(--t5); line-height: 1.65; color: var(--ink3); }
.ui-doc-md strong { color: var(--ink2); font-weight: 600; }
.ui-doc-md a { color: var(--accent); }
.ui-doc-md a:hover { text-decoration: underline; }
.ui-doc-md hr { margin: var(--s9) 0; border: 0; border-top: 1px solid var(--hair); }
.ui-doc-md > :first-child { margin-top: 0; }
.ui-doc-md > :last-child { margin-bottom: 0; }
/* A letter's picture (INBOX-75), held as a place with its words until asked for. */
.ui-doc-md img { max-width: 100%; height: auto; vertical-align: middle; border-radius: var(--r2); }
.ui-doc-held {
  display: inline-flex; align-items: center; flex-wrap: wrap; gap: var(--s3); max-width: 100%;
  padding: var(--s1) var(--s4);
  border: 1px dashed var(--line); border-radius: var(--r2);
  font-size: var(--t3); color: var(--faint); overflow-wrap: anywhere;
}
.ui-doc-md blockquote {
  margin: 0 0 var(--s7);
  padding: var(--s6) var(--s8);
  border-left: 2px solid var(--accent-line);
  background: var(--wash);
  font-size: var(--t5);
  line-height: 1.65;
  color: var(--ink2);
}
.ui-doc-md .code, .ui-doc-md code {
  padding: var(--s1) var(--s2);
  border: 0;
  border-radius: var(--r2);
  background: var(--hover);
  font-family: var(--mono);
  font-size: var(--t3);
  color: var(--ink2);
}
.ui-doc-md .md-code {
  margin: 0 0 var(--s8);
  padding: var(--s6) var(--s8);
  border: 1px solid var(--hair);
  border-radius: var(--r4);
  background: var(--mid);
  font-family: var(--mono);
  font-size: var(--t3);
  line-height: 1.75;
  color: var(--doc-code);
  overflow-x: auto;
}
.ui-doc-md .md-code code { padding: 0; background: none; font-size: inherit; }

/* A wider table cannot reflow, so on a phone it scrolls inside itself. */
.ui-doc-md .md-table {
  display: block;
  width: 100%;
  margin: 0 0 var(--s8);
  border: 1px solid var(--hair);
  border-radius: var(--r4);
  border-collapse: collapse;
  overflow-x: auto;
  text-align: left;
}
.ui-doc-md .md-table th, .ui-doc-md .md-table td {
  padding: var(--s5) var(--s7);
  border-bottom: 1px solid var(--hair);
  vertical-align: baseline;
  font-size: var(--t4);
  line-height: 1.5;
  color: var(--ink3);
}
.ui-doc-md .md-table th {
  font-family: var(--mono);
  font-size: var(--t1);
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mute);
}
.ui-doc-md .md-table tr:last-child td { border-bottom: 0; }

/* A reference table is rows of a key and what it does: on a phone the key over its sentence. */
.ui-doc-md .md-table[data-cols="2"],
.ui-doc-md .md-table[data-cols="2"] tbody,
.ui-doc-md .md-table[data-cols="2"] tr,
.ui-doc-md .md-table[data-cols="2"] th,
.ui-doc-md .md-table[data-cols="2"] td { display: block; }
.ui-doc-md .md-table[data-cols="2"] thead { display: none; }
.ui-doc-md .md-table[data-cols="2"] tr {
  padding: var(--s5) var(--s7);
  border-bottom: 1px solid var(--hair);
}
.ui-doc-md .md-table[data-cols="2"] tr:last-child { border-bottom: 0; }
.ui-doc-md .md-table[data-cols="2"] th,
.ui-doc-md .md-table[data-cols="2"] td { padding: 0; border-bottom: 0; }
.ui-doc-md .md-table[data-cols="2"] td:first-child {
  padding-bottom: var(--s2);
  font-family: var(--mono);
  font-size: var(--t3);
  color: var(--doc-key);
  overflow-wrap: anywhere;
}

/* ── the Test panel ──────────────────────────────────────────────────────── */

.ui-doc-test { margin-bottom: var(--s10); }
.ui-doc-strip {
  height: 36px;
  padding: 0 var(--s8);
  border: 1px dashed var(--line2);
  border-radius: var(--r-pill);
  background: var(--surface);
  display: inline-flex;
  align-items: center;
  gap: var(--s4);
  align-self: flex-start;
  cursor: pointer;
  color: var(--ink3);
  font-family: var(--sans);
  font-size: var(--t4);
}
.ui-doc-strip:hover { border-color: var(--accent); color: var(--accent); }
.ui-doc-strip[hidden], .ui-doc-card[hidden], .ui-doc-x-body[hidden] { display: none; }

.ui-doc-card {
  border: 1px solid var(--line);
  border-radius: var(--r6);
  background: var(--surface);
  box-shadow: var(--card);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.ui-doc-card-head {
  flex: none;
  padding: var(--s5) var(--s6) var(--s5) var(--s8);
  border-bottom: 1px solid var(--hair);
  display: flex;
  align-items: center;
  gap: var(--s6);
  flex-wrap: wrap;
}
.ui-doc-card-head p {
  margin: 0;
  flex: 1;
  min-width: 0;
  font-size: var(--t4);
  line-height: 1.45;
  color: var(--ink3);
}
.ui-doc-card-body { display: flex; gap: var(--s9); padding: var(--s8); flex-wrap: wrap; }
.ui-doc-ask {
  flex: none;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--s7);
}
.ui-doc-card-head .ui-pill, .ui-doc-ask .ui-pill { min-height: var(--finger); }
.ui-doc-said { flex: 1; min-width: 240px; display: flex; flex-direction: column; gap: var(--s3); }
.ui-doc-field { display: flex; flex-direction: column; gap: var(--s3); }

/* The label a reference wears over a box: a word, in the mono face, quiet. */
.ui-doc-label {
  font-family: var(--mono);
  font-size: var(--t2);
  letter-spacing: 0.08em;
  color: var(--mute);
  text-transform: uppercase;
}
.ui-doc-empty { margin: 0; padding: var(--s3) 0; font-size: var(--t4); color: var(--mute); }

/* ── one exchange: what was asked, and what came back ────────────────────── */

.ui-doc-xs { display: flex; flex-direction: column; gap: var(--s4); }
.ui-doc-x { border: 1px solid var(--hair); border-radius: var(--r4); overflow: hidden; }
.ui-doc-x-head {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s4) var(--s5);
  border: 0;
  background: var(--rail);
  cursor: pointer;
  text-align: left;
  font-family: var(--sans);
}
.ui-doc-x-head:hover { background: var(--hover); }
.ui-doc-x-chev { font-family: var(--mono); font-size: var(--t2); color: var(--mute); }
.ui-doc-x-title {
  font-family: var(--mono);
  font-size: var(--t3);
  color: var(--doc-key);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ui-doc-x-part { padding: var(--s4) var(--s5); border-top: 1px solid var(--hair); }
.ui-doc-x-part.is-in { background: var(--mid); }
.ui-doc-x-tag { font-family: var(--mono); font-size: var(--t1); letter-spacing: 0.1em; padding-bottom: var(--s2); }
.ui-doc-x-tag.is-out { color: var(--accent-down); }
.ui-doc-x-tag.is-in { color: var(--ok-ink); }
.ui-doc-x-text {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--t3);
  line-height: 1.6;
  color: var(--doc-code);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 320px;
  overflow: auto;
}
/* After a written-down write: the way to its form, offered rather than opened. */
.ui-doc-x-part .ui-doc-x-open { margin-top: var(--s1); }

/* ── the desk ────────────────────────────────────────────────────────────── */

@container (min-width: 641px) {
  .ui-doc-tabs { flex-wrap: wrap; overflow-x: visible; }
  .ui-doc-tab { min-height: 0; min-width: 0; padding: 0 0 var(--s6); }
  .ui-doc-pills { flex-wrap: wrap; overflow-x: visible; }
  .ui-doc-pill { min-height: 0; min-width: 0; padding: 0 0 var(--s5); }
  .ui-doc-raw { padding: var(--s9) var(--s10); }

  .ui-doc-md .md-table { display: table; overflow: hidden; }
  .ui-doc-md .md-table[data-cols="2"],
  .ui-doc-md .md-table[data-cols="2"] thead,
  .ui-doc-md .md-table[data-cols="2"] tbody { display: block; }
  .ui-doc-md .md-table[data-cols="2"] tr { display: flex; align-items: baseline; gap: var(--s8); }
  .ui-doc-md .md-table[data-cols="2"] th:first-child,
  .ui-doc-md .md-table[data-cols="2"] td:first-child { flex: none; min-width: 246px; padding-bottom: 0; }
  .ui-doc-md .md-table[data-cols="2"] th:last-child,
  .ui-doc-md .md-table[data-cols="2"] td:last-child { flex: 1; min-width: 0; }

  .ui-doc-ask { width: 320px; max-width: 100%; }
  .ui-doc-card-head .ui-pill, .ui-doc-ask .ui-pill { min-height: 0; }
}

/* The dock (UIC-13, UIC-14): a surface that opens beside the page rather than
   over it, on the house tokens and defining nothing of its own but its width.

   On a desk the page moves aside and both are readable at once — which is the
   point of asking something while looking at the thing you are asking about.
   On a phone there is no beside, so it takes the screen.

   Three shares, not open and shut (UIC-14): the page, half each, and the
   whole screen. The whole contract with the page is one attribute on `<html>`,
   which carries which share it is. A surface's own sheet can join in —
   narrowing a map, moving a sticky bar — by matching `[data-dock="half"]`
   without knowing the dock's script exists. */

:root {
  /* A knob, not a token: how wide the dock is at the share it is on, which a
     surface may turn down. The script writes this one as pixels when the
     reader has dragged the boundary (UIC-15). */
  --ui-dock-w: 0px;
  /* Neither side goes below this. A column narrower than this is one nobody
     reads — the drag stops here rather than squeezing a half out of use, and
     a window with no room for two of them is not draggable at all. */
  --ui-dock-min: 420px;
  /* How tall the panel is (UIC-31). `100dvh` is the screen and stays the
     screen while a software keyboard is up: iOS shrinks the *visual*
     viewport and leaves the layout one — and every `dvh` and every
     `@media (max-height:)` with it — alone, so a panel fixed at the full
     height keeps its composer under the keys and its head above the top of
     what can be seen. The script writes what `visualViewport` says; with no
     script, and with no keyboard, the two are the same number. */
  --ui-dock-vh: 100dvh;
  /* And how far down the visible band starts (UIC-31). A fixed panel is
     anchored to the layout viewport, which does not move when the keys come
     up; the browser scrolls the *visual* viewport inside it to show what is
     focused, and this is that scroll. Zero everywhere else. */
  --ui-dock-top: 0px;
  /* How far the tab on the rim reaches in from the right edge, on a phone's finger as on a desk. */
  --ui-dock-reach: var(--s13);
}

/* Beside the page the dock opens at the floor — a column, not half the
   window — and the reader drags it wider from there (UIC-15); a dragged
   width is written over this one as pixels. Full is the screen. */
[data-dock="half"] { --ui-dock-w: var(--ui-dock-min); }
[data-dock="full"] { --ui-dock-w: 100%; }

/* ── the handle on the rim the panel comes out of ────────────────────────── */

/* A tab against the right edge, not a button floating in the corner: an arrow
   pointing the way the panel will travel and the two letters saying what is
   behind it. Small and quiet — everything it covers is something somebody
   came for. It sits below the header band every surface keeps its own face
   in, which is the knob a surface turns if its own is taller. */
.ui-dock-handle {
  position: fixed;
  top: var(--ui-dock-handle-top, 76px);
  right: 0;
  z-index: 61;
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s3) var(--s4) var(--s3) var(--s3);
  border: 1px solid var(--line);
  border-right: 0;
  border-radius: var(--r4) 0 0 var(--r4);
  background: var(--surface);
  color: var(--mute);
  font-family: var(--mono);
  font-size: var(--t2);
  font-weight: 600;
  letter-spacing: .06em;
  cursor: pointer;
  box-shadow: var(--float);
  transition: color .2s, background .2s, padding-right .2s;
}
.ui-dock-handle:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  padding-right: var(--s6);
}
.ui-dock-handle:focus-visible { outline: var(--s1) solid var(--accent); outline-offset: var(--s1); }
.ui-dock-handle[hidden] { display: none; }
.ui-dock-handle-arrow { flex: none; }
.ui-dock-handle-word { line-height: 1; }
/* A page's band under the tab keeps clear of it: the title wraps and the acts end before it (UIC-43). */
body:has(.ui-dock-handle:not([hidden])) .ui-head-row {
  padding-inline-end: max(0px, var(--ui-dock-reach) - var(--gutter) - (100% - min(100%, var(--read))) / 2);
}

/* ── and the same again as a row in the surface's own menu ───────────────── */

/* Nothing floats over the page. The dock is reached from wherever that
   surface already keeps "Assistant", so the control sits among the menu's
   other rows and inherits their shape: the class the surface passes does the
   dressing, and these rules only lay the row out and place the share on the
   right of it, where a value belongs in a list of settings. */
.ui-dock-toggle {
  display: flex;
  align-items: center;
  gap: var(--s5);
  width: 100%;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.ui-dock-toggle-word { flex: 1; min-width: 0; }

/* ── the panel ───────────────────────────────────────────────────────────── */

.ui-dock {
  position: fixed;
  top: var(--ui-dock-top);
  right: 0;
  z-index: 60;
  width: var(--ui-dock-w);
  height: var(--ui-dock-vh);
  /* Three rows and not three flex items (UIC-31): the head, the conversation
     and the composer each have a row of their own, so nothing the fold or a
     keyboard does can push one of them off the panel. It is the arrangement
     the dock already wears where it *is* the page (UIC-22), lifted to the
     panel itself. */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas: "head" "body" "foot";
  min-height: 0;
  background: var(--surface);
  /* The hairline is the edge; no shadow (2026-09-07: *"dont use drop shadow
     for the assistant"*) — the dock is a column beside the page, not a
     sheet lying on it. */
  border-left: 1px solid var(--line);
}
/* Only once the share has settled, and never under a hand (UIC-15): a page
   that draws itself already open would otherwise slide out on every
   navigation, and an animated width chases a dragging pointer rather than
   following it. */
[data-dock-ready]:not([data-dock-drag]) .ui-dock {
  transition: width .28s cubic-bezier(.4, 0, .2, 1);
}

/* ── the boundary, dragged ──────────────────────────────────────────────── */

/* The line itself, on the panel's own edge: a hit area wide enough for a
   hand, drawn as nothing until it is reached for. The two arrows in the head
   move the same boundary in steps; this is it dragged. */
.ui-dock-grip {
  position: absolute;
  top: 0;
  /* Twelve pixels, half of them over the page: a boundary somebody has to
     aim at is one they miss, and a hand is not a pixel. */
  left: -6px;
  width: 12px;
  height: 100%;
  z-index: 1;
  cursor: col-resize;
  touch-action: none;
  background: none;
}
.ui-dock-grip:hover, .ui-dock-grip:focus-visible {
  background: var(--accent);
  outline: none;
}
/* Nothing to drag when the panel is the screen, or when the screen has no
   room for two readable halves. */
[data-dock="full"] .ui-dock-grip { display: none; }
@media (max-width: 839px) {
  .ui-dock-grip { display: none; }
}

/* A shut dock is shut. `display: flex` above is an author rule and the
   browser's own `[hidden] { display: none }` is not, so without this line the
   panel stays on the screen with the attribute set — covering the page it is
   supposed to sit beside. */
.ui-dock[hidden] { display: none; }

.ui-dock > .ui-dock-head { grid-area: head; }
.ui-dock > .ui-dock-body { grid-area: body; }
.ui-dock > .ui-dock-foot { grid-area: foot; }

.ui-dock-head {
  display: flex;
  align-items: flex-start;
  gap: var(--s5);
  padding: var(--s8) var(--s8) var(--s6);
  border-bottom: 1px solid var(--hair);
}
/* Whose panel this is, beside its name: a portrait is who a reader is
   talking to, which the name alone only says once you have read it. */
.ui-dock-face {
  flex: none;
  width: var(--s11);
  height: var(--s11);
  border-radius: 50%;
  object-fit: cover;
  /* A third of the way down: a tall portrait keeps the face in the circle. */
  object-position: 50% 33%;
  background: var(--page);
}
.ui-dock-titled { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--s2); }
/* The name, and whatever small thing the filling keeps beside it. A row, so
   the two sit on one line; the name may shrink and the small thing may not,
   because a picker that has lost its own width is one nobody can read. */
.ui-dock-name { display: flex; align-items: baseline; gap: var(--s3); min-width: 0; }
.ui-dock-title { margin: 0; font-size: var(--t6); font-weight: 600; color: var(--ink); }
.ui-dock-said { margin: 0; font-size: var(--t4); line-height: var(--lh4); color: var(--mute); }

/* The boundary, with a hand on each side of it. Two small arrows, together,
   so the pair reads as one control with two directions rather than as two
   unrelated buttons that happen to sit next to each other. */
.ui-dock-edge { flex: none; display: flex; align-items: center; gap: var(--s1); }
/* The two presses in the corner (UIC-21): the whole screen, and shut. The
   first is a switch and shows it — pressed, it is the way back beside the
   page. */
.ui-dock-full,
.ui-dock-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--s11);
  height: var(--s11);
  border: 0;
  border-radius: var(--r3);
  background: none;
  color: var(--mute);
  cursor: pointer;
}
.ui-dock-full svg, .ui-dock-close svg { width: 16px; height: 16px; }
.ui-dock-full:hover,
.ui-dock-close:hover { background: var(--hover); color: var(--ink); }
.ui-dock-full[aria-pressed="true"] { background: var(--hover); color: var(--ink); }
/* A press the filling keeps beside its own name wears the head's manner,
   like the two in the corner (2026-09-09): their size, no ring and no ground
   until the hand is on it, and centred on the name rather than hung from
   its baseline — a ringed circle beside a title read as a badge, not as a
   press. */
.ui-dock-name .ui-round {
  --ui-round-size: var(--s11);
  align-self: center;
  border-color: transparent;
  background: none;
  color: var(--mute);
  box-shadow: none;
}
.ui-dock-name .ui-round:hover,
.ui-dock-name .ui-round:focus-visible {
  border-color: transparent;
  background: var(--hover);
  color: var(--ink);
  box-shadow: none;
}

/* ── what was said before ────────────────────────────────────────────────── */

/* Top left, where a list of what there is belongs — the same corner the
   console, the mailbox and the portal all keep their rail in. */
.ui-dock-past-open {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--s11);
  height: var(--s11);
  margin-top: calc(var(--s1) * -1);
  border: 0;
  border-radius: var(--r3);
  background: none;
  color: var(--mute);
  cursor: pointer;
}
.ui-dock-past-open:hover { background: var(--hover); color: var(--ink); }
.ui-dock-past-open[aria-expanded="true"] { background: var(--hover); color: var(--ink); }
/* The filling's own press beside it (UIC-21): the same quiet square, so the
   corner reads as one row of two — the list, and a fresh one. */
.ui-dock-fresh {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--s11);
  height: var(--s11);
  margin-top: calc(var(--s1) * -1);
  border: 0;
  border-radius: var(--r3);
  background: none;
  color: var(--mute);
  cursor: pointer;
}
.ui-dock-fresh:hover { background: var(--hover); color: var(--ink); }
.ui-dock-fresh .icon { width: var(--s5); height: var(--s5); }

/* A cover over the conversation, not a box above it (UIC-31): the reader is
   choosing between conversations, so the one underneath is not what they are
   reading — and a fold that pushed the composer off the bottom is one nobody
   opens twice. `100%` is the conversation's own row, so the head stays over
   it and the composer under it whatever the list is; `50dvh` was half of the
   *screen*, which with a keyboard up is more than the whole panel. */
.ui-dock-past {
  grid-area: body;
  z-index: 2;
  align-self: start;
  max-height: 100%;
  overflow-y: auto;
  padding: var(--s5) var(--s8) var(--s6);
  background: var(--page);
  border-bottom: 1px solid var(--line);
}
.ui-dock-past[hidden] { display: none; }
.ui-dock-past-head {
  margin: 0 0 var(--s4);
  font-size: var(--t3);
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--faint);
}
.ui-dock-past-body { display: flex; flex-direction: column; gap: var(--s1); }

.ui-dock-body { min-height: 0; overflow-y: auto; padding: var(--s8); }
.ui-dock-foot { padding: var(--s6) var(--s8) var(--s8); border-top: 1px solid var(--hair); }
/* A body that is a pane scrolling itself — the assistant's conversation — stands flush and does not scroll twice. */
.ui-dock.is-flush > .ui-dock-body { padding: 0; overflow: hidden; }


/* ── the page moves aside ────────────────────────────────────────────────── */

/* On the padding rather than on a transform: a transform would move the page
   without resizing it, so a column that scrolls would keep the width it had
   and hide its right-hand edge under the dock. The frame is `width: 100%`, so
   padding on the body is what narrows it.

   Nothing is done to the body while the dock is shut, and that is deliberate:
   a `scrollbar-gutter` here would have been a quiet fifteen pixels off the
   width of every page of every surface, dock or no dock, which is the sort of
   thing a shared sheet must not do. */
[data-dock="half"] body { padding-right: var(--ui-dock-w); }

/* At full there is nothing to narrow: padding the body by its own width would
   collapse the page rather than move it, so the panel simply covers it and
   the page underneath stops scrolling. */
[data-dock="full"] body { overflow: hidden; }

/* Armed one frame after the dock settles, and dropped while the boundary is
   under a hand: a page that arrives already narrowed has not *moved*, and
   animating it on arrival is the twitch on every navigation and every redraw
   (UIC-15). */
@media (prefers-reduced-motion: no-preference) {
  [data-dock-ready]:not([data-dock-drag]) body {
    transition: padding-right .28s cubic-bezier(.4, 0, .2, 1);
  }
}

/* ── a phone has no beside ───────────────────────────────────────────────── */

@media (max-width: 640px) {
  /* Both open shares are the screen: half of a phone is not a column anybody
     can read. */
  [data-dock="half"] { --ui-dock-w: 100%; }
  .ui-dock { border-left: 0; }
  /* Nothing moves: the dock is the screen, and padding the body would leave
     the page scrolled sideways underneath it. */
  [data-dock="half"] body { padding-right: 0; overflow: hidden; }
  /* A 24px tab is a pointer's target and not a finger's (docs.css says the
     same of its own) — and here it is the only way back in (UIC-16, UIC-26):
     Marie and a Kodu visitor have no menu row to reach the dock from. */
  .ui-dock-handle { min-height: 44px; padding-left: var(--s5); padding-right: var(--s5); }
  /* The panel is the screen already: a press to make it the screen is one that does nothing (UIC-51). */
  .ui-dock-full { display: none; }
}

/* ── the smallest screens there are (UIC-26) ─────────────────────────────── */

/* A watch is 184 wide and 224 tall, and on it the head was more than half the
   screen: a title, a line of subtitle under it, and a row of three presses,
   over a conversation reduced to a sliver. The head is chrome — it says what
   this is, which you know, because you opened it. So below this width the
   subtitle goes, the padding comes in, and the title steps down a size; the
   presses stay, because closing the thing must not become the hard part. */
@media (max-width: 400px) {
  .ui-dock-head { gap: var(--s3); padding: var(--s5) var(--s5) var(--s4); }
  .ui-dock-said { display: none; }
  .ui-dock-title { font-size: var(--t5); }
}

/* Shorter than a phone lying down: the head gives up its border and its
   bottom padding too, so what is left of the screen is the conversation. */
@media (max-height: 420px) {
  .ui-dock-head { padding-block: var(--s4) var(--s3); border-bottom-color: transparent; }
  .ui-dock-said { display: none; }
}

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

/* Where the dock *is* the page (`ai.<base>`) it stands in the flow instead of
   fixed beside it: nothing to move aside, no share to walk. The head, the
   fold, the body and the foot are the same four things, laid out as columns
   where the window has room — the fold on the left as the list of what there
   is, the foot on the right as what the assistant can reach — and stacked on
   a phone, the fold covering the conversation when it is asked for, as it
   does in the panel. The page hands it the height it has to fill. */
[data-dock="page"] body { padding-right: 0; }
/* The rows are the panel's own now (UIC-31) and so is the fold's cover; what
   is left here is standing in the flow instead of fixed beside the page —
   and the height the pane hands it, which must beat the panel's own knob or
   this dock is a whole visible viewport tall inside a pane. */
.ui-dock.is-page {
  position: relative;
  top: auto;
  right: auto;
  z-index: auto;
  width: 100%;
  height: 100%;
  min-height: 0;
  border-left: 0;
}
/* The fold and the foot stand as columns on a page, so they breathe as columns. */
.ui-dock.is-page > :is(.ui-dock-past, .ui-dock-foot) { padding: var(--s7) var(--s6); }
/* A page's foot stands only as the third column: under the composer it took a third of a phone, and the head's gear opens the same list. */
.ui-dock.is-page > .ui-dock-foot { display: none; }
/* Its columns are the frame's (UIC-38): the fold is a list (`--list-col`), the
   foot the part beside the thing (`--side-col`), and they step where the
   frame steps — the page's own width, never the window's. */
@container (min-width: 641px) {
  /* Two columns: the fold stands as the list on the left, always, and the
     press that opened it as a cover is put away. A dock with nothing behind
     it keeps the one column. */
  .ui-dock.is-page:has(> .ui-dock-past) {
    grid-template-columns: var(--list-col) minmax(0, 1fr);
    grid-template-areas: "past head" "past body" "past foot";
  }
  /* `!important` answers the tier's own `[hidden]` (UIC-42): on the page the
     fold is a column that stands whatever the cover's press last said. */
  .ui-dock.is-page > .ui-dock-past,
  .ui-dock.is-page > .ui-dock-past[hidden] {
    display: block !important;
    grid-area: past;
    z-index: auto;
    align-self: stretch;
    max-height: none;
    overflow-y: auto;
    border-bottom: 0;
    border-right: 1px solid var(--line);
  }
  .ui-dock.is-page > .ui-dock-head > .ui-dock-past-open { display: none; }
}
@container (min-width: 1081px) {
  /* Three: and the foot — what the assistant can reach — as a column on the
     right, beside the conversation rather than under it. */
  .ui-dock.is-page {
    grid-template-columns: minmax(0, 1fr) var(--side-col);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas: "head foot" "body foot";
  }
  .ui-dock.is-page:has(> .ui-dock-past) {
    grid-template-columns: var(--list-col) minmax(0, 1fr) var(--side-col);
    grid-template-areas: "past head foot" "past body foot";
  }
  .ui-dock.is-page > .ui-dock-foot {
    display: block;
    border-top: 0;
    border-left: 1px solid var(--line);
    overflow-y: auto;
  }
}

/* The gallery (UIC-20, out of KODU-69): one strip that snaps a picture at a
   time, the arrows and the count over it, the thumbnails and the captions
   under it, and the same strip filling the screen in a <dialog>. Scroll-snap
   does the leafing, so a finger, a wheel and the keyboard all work before any
   script does; the script adds the arrows, the jump from a thumbnail, the
   count and the full-screen view. */

/* Never wider than the column it stands in: a caption that cannot break —
   an address, say — would otherwise widen the album to its own length and
   push the picture past the screen's edge, and a strip turning to a longer
   caption jumped wider on every turn (2026-09-07, on a phone). */
.ui-gallery { min-width: 0; max-width: 100%; }
.ui-gal-main {
  width: 100%;
  aspect-ratio: 3 / 2;
  max-height: 480px;
  background: var(--chip);
}
.ui-gal-track, .ui-light-track {
  display: flex;
  width: 100%;
  height: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.ui-gal-track::-webkit-scrollbar, .ui-light-track::-webkit-scrollbar, .ui-gal-thumbs::-webkit-scrollbar { display: none; }
.ui-gal-track:focus-visible { outline: var(--s1) solid var(--accent); outline-offset: calc(-1 * var(--s1)); }
.ui-gal-shot {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  cursor: zoom-in;
}
.ui-gal-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Blur-up (KODU-157): a picture with a small copy stands on it, softened,
   and fades in once all of it has arrived — so it sharpens in place rather
   than painting from the top down. Without the script it simply paints. */
.ui-gal-shot, .ui-light-shot {
  --ui-gal-small: none;
}
.ui-gal-shot[style] { position: relative; overflow: hidden; }
.ui-gal-shot[style]::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--s5));
  background: var(--ui-gal-small) center / cover no-repeat;
  filter: blur(var(--s4));
}
.ui-gal-shot[style] img { position: relative; transition: opacity var(--sharpen); }
.ui-gal-shot[style] img.is-arriving { opacity: 0; }
.ui-light-shot[style] { background: var(--ui-gal-small) center / contain no-repeat; }

/* The arrows over the picture: shown when the pointer is over it, always on a
   screen with no pointer. */
.ui-gal-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: color-mix(in oklab, var(--surface) 88%, transparent);
  color: var(--ink);
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s ease, border-color .15s ease, color .15s ease;
}
.ui-gal-arrow.is-prev { left: var(--s3); }
.ui-gal-arrow.is-next { right: var(--s3); }
.ui-gal-arrow svg { width: 18px; height: 18px; }
.ui-gal-arrow:hover { border-color: var(--accent); color: var(--accent); }
.ui-gal-main:hover .ui-gal-arrow, .ui-gal-arrow:focus-visible { opacity: 1; }
@media (hover: none) { .ui-gal-arrow { opacity: 1; } }

/* Full screen, with the count on it: one press for "see it big" and "which
   one am I on", in the corner a thumb reaches. */
.ui-gal-full {
  position: absolute;
  right: var(--s3);
  bottom: var(--s3);
  display: flex;
  align-items: center;
  gap: var(--s2);
  height: 32px;
  padding: 0 var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: color-mix(in oklab, var(--surface) 88%, transparent);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
.ui-gal-full svg { width: 16px; height: 16px; }
.ui-gal-full:hover { border-color: var(--accent); color: var(--accent); }
.ui-gal-count, .ui-light-count {
  font-family: var(--mono);
  font-size: var(--t3);
  font-variant-numeric: tabular-nums;
}

/* One caption under the strip, the one for the picture in view; the rest
   are there for a reader with no script, who sees the first alone. */
/* `contain: inline-size`: the row's own width is the album's, never its
   words' — a caption's minimum width is otherwise its longest unbreakable
   run, and that is what an address is. */
.ui-gal-captions { margin: 0; min-height: 1.5em; min-width: 0; contain: inline-size; overflow-wrap: anywhere; }
.ui-gal-caption { display: none; margin: 0; color: var(--ink2); font-size: var(--t4); line-height: 1.5; text-wrap: pretty; }
.ui-gal-caption.is-on { display: block; }

.ui-gal-thumbs {
  display: flex;
  gap: var(--s2);
  overflow-x: auto;
  scrollbar-width: none;
}
/* The ring is an inset outline rather than a shadow around the box: the row
   clips nothing that way, and it needs no room kept for it. */
.ui-gal-thumb {
  flex: none;
  width: 84px;
  aspect-ratio: 3 / 2;
  border-radius: var(--r4);
  overflow: hidden;
  opacity: .7;
  outline: var(--s1) solid transparent;
  outline-offset: calc(-1 * var(--s1));
  transition: opacity .15s ease, outline-color .15s ease;
}
.ui-gal-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ui-gal-thumb:hover { opacity: 1; }
.ui-gal-thumb.is-on { opacity: 1; outline-color: var(--accent); }

/* The dots, for a strip that turns by itself: one a picture, the one in view
   filled and a touch larger, each a press to its picture. */
.ui-gal-dots { display: flex; justify-content: center; gap: var(--s3); padding: var(--s2) 0; }
.ui-gal-dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--line);
  cursor: pointer;
  transition: background .15s ease, transform .15s ease;
}
.ui-gal-dot:hover { background: var(--ink3); }
.ui-gal-dot.is-on { background: var(--accent); transform: scale(1.25); }

/* Until the script is there, the controls it serves are not either: the
   strip scrolls on its own and every picture is a link to its file, and a
   dot that leads nowhere is not drawn. */
.ui-gallery:not(.is-live) .ui-gal-arrow, .ui-gallery:not(.is-live) .ui-gal-full, .ui-gallery:not(.is-live) .ui-gal-dots { display: none; }

/* Drawings rather than photographs: contained on paper, not cropped to the
   frame — in the strip, in the thumbnails and on the dark full screen alike,
   since white paper is what they were drawn on whichever scheme the page is
   in. */
.ui-gallery.is-paper .ui-gal-shot img, .ui-gallery.is-paper .ui-gal-thumb img { object-fit: contain; background: var(--paper); }
.ui-gallery.is-paper .ui-gal-shot img { padding: var(--s4); }
.ui-gallery.is-paper .ui-light-shot { background: var(--paper); padding: var(--s6); }

/* The full-screen view: the whole viewport, the picture contained in it, the
   same arrows in the stage's ink, the count at the foot. The stage is a warm
   near-black in both schemes — the picture is the light there. */
.ui-light {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--stage);
  color: var(--stage-ink);
}
.ui-light::backdrop { background: var(--stage); }
.ui-light-shot {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  object-fit: contain;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  /* A press closes it, the way a press opened it. */
  cursor: zoom-out;
}
.ui-light .ui-gal-arrow, .ui-light-close {
  opacity: 1;
  border-color: var(--stage-line);
  background: var(--stage-veil);
  color: var(--stage-ink);
}
.ui-light .ui-gal-arrow:hover, .ui-light-close:hover { border-color: var(--stage-ink); color: var(--stage-ink); }
.ui-light-close {
  position: absolute;
  top: var(--s3);
  right: var(--s3);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 1px solid;
  border-radius: 50%;
  cursor: pointer;
}
.ui-light-close svg { width: 20px; height: 20px; }
.ui-light-count {
  position: absolute;
  left: 50%;
  bottom: var(--s4);
  transform: translateX(-50%);
  padding: var(--s1) var(--s3);
  border-radius: var(--r-pill);
  background: var(--stage-veil);
}
html:has(.ui-light[open]) { overflow: hidden; }
@media (prefers-reduced-motion: reduce) {
  .ui-gal-track, .ui-light-track { scroll-behavior: auto; }
  .ui-gal-shot[style] img { transition: none; }
}

/* ── the editor's manner (KODU-110, the tier's since 2026-09-09) ─────────── */

/* A section: a rule over it, its number and name down the left in the
   serif, its controls to the right. The same drawing Kodu's *Lisa kuulutus*
   makes, so a settings surface on any door reads as one of that family.
   No box and no ground: the rule is what separates one from the next.
   Phone first: the name stands over its controls, and beside them only
   where the part itself holds both — a phone, a form's `--prose` column. */
.ui-part {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s5) clamp(var(--s10), 3vw, var(--s13));
  padding-block: clamp(var(--s9), 2.4vw, var(--s11));
  border-top: 1px solid var(--line2);
}
.ui-part:first-child { padding-top: var(--s4); border-top: 0; }
.ui-part > .ui-part-body { flex: 999 1 calc(2 * var(--part-label)); min-width: 0; }
.ui-part-no {
  flex: 1 1 var(--part-label);
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s3);
  margin: 0;
  font-family: var(--serif);
  font-size: var(--t7);
  font-weight: 500;
  line-height: 1.2;
  color: var(--ink);
}
.ui-part-n {
  font-family: var(--mono);
  font-size: var(--t3);
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--mute);
}
/* The foot of the column: what one press will do, and the press. */
.ui-sheet-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s6);
  flex-wrap: wrap;
  padding-top: clamp(var(--s9), 2.4vw, var(--s11));
  border-top: 1px solid var(--line2);
}

/* The rail's contents and your own menu (UIC-38), on the house tokens.

   A phone is the ground (UIC-23): the rail lies down as a bar along the foot
   of the screen, the frame putting it there with `column-reverse`. From 641px
   of the page's own width it stands down the left as a column. The column's
   width is the application's grid track; nothing here decides it. */

.ui-rail-nav {
  flex: none;
  flex-direction: row;
  align-items: center;
  justify-content: space-around;
  gap: 0;
  border-right: 0;
  border-top: 1px solid var(--line);
  /* Installed to a Home Screen the page runs full screen, and the home
     indicator is inside it: the bar steps over, not under. */
  padding: var(--s3) var(--s2) calc(var(--s4) + env(safe-area-inset-bottom));
  position: relative;
}

.ui-rail-brand { display: none; flex: none; }
.ui-rail-mark { display: block; width: calc(var(--s11) - var(--s1)); height: calc(var(--s11) - var(--s1)); }
.ui-rail-mark svg { display: block; width: 100%; height: 100%; }
/* Letters in place of a drawing: a site's initials on the accent. */
.ui-rail-mark.is-letters {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r4);
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--serif);
  font-size: var(--t5);
  font-weight: 600;
}

.ui-rail-list {
  display: flex;
  flex: 1;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-around;
  gap: var(--s1);
  min-width: 0;
}
.ui-rail-rule, .ui-rail-space { display: none; }

/* An entry is an icon over its word, the tint on the icon's box and never on
   the row: a band behind a centred icon is what makes a column read as a list. */
.ui-rail-item {
  display: flex;
  flex: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-width: calc(var(--s12) + var(--s2));
  min-height: calc(var(--s12) + var(--s2));
  max-width: 100%;
  color: var(--mute);
  font-size: var(--t2);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}
.ui-rail-box {
  position: relative;
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: var(--s12);
  height: calc(var(--s11) - var(--s1));
  border-radius: var(--r5);
  color: var(--rail-ink);
  transition: background var(--quick), color var(--quick);
}
.ui-rail-box svg { width: var(--s9); height: var(--s9); stroke-width: 1.7; }
.ui-rail-label { max-width: 100%; overflow-wrap: break-word; text-wrap: balance; }
.ui-rail-item:hover .ui-rail-box { background: var(--hover); color: var(--ink); }
.ui-rail-item.is-open { color: var(--ink); font-weight: 600; }
.ui-rail-item.is-open .ui-rail-box { color: var(--ink); }
.ui-rail-item.is-here { color: var(--accent-down); font-weight: 600; }
.ui-rail-item.is-here .ui-rail-box { background: var(--accent-soft); color: var(--accent-down); }
/* The lit tile tints a stroke drawing's first, closed shape under its strokes: the mailbox's duotone, every door's (UIC-42). */
.ui-rail-item.is-here .ui-rail-box svg[fill="none"] > :first-child { fill: currentColor; fill-opacity: .16; }
.ui-rail-item.is-primary { color: var(--accent-down); font-weight: 600; }
.ui-rail-item.is-primary .ui-rail-box,
.ui-rail-item.is-primary:hover .ui-rail-box { background: var(--accent); color: var(--accent-ink); }
/* A plus fills its box edge to edge where the other drawings keep a margin. */
.ui-rail-item.is-primary .ui-rail-box svg { width: var(--s8); height: var(--s8); }

.ui-rail-badge {
  position: absolute;
  top: var(--s1);
  right: var(--s1);
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: var(--s8);
  height: var(--s8);
  padding: 0 var(--s2);
  border-radius: var(--r4);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--t1);
  font-weight: 600;
  line-height: 1;
}
/* A queue of exceptions is the attention tone, not the accent of unread mail. */
.ui-rail-badge.is-attn { background: var(--badge-bg); color: var(--warn-ink); border: 1px solid var(--badge-line); }
.ui-rail-badge.is-quiet { background: var(--line); color: var(--mute); }

/* An entry's sections sit in a tray right under it. Every tray is drawn and
   the shut ones fold to nothing, so opening one is an easing; a shut tray is
   hidden too, which takes its links out of the tab order once it has folded.
   On a phone the open one is a row of its own over the entries. */
.ui-rail-tray {
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  order: -1;
  flex-basis: 100%;
  border-bottom: 1px solid transparent;
  transition: grid-template-rows .24s ease, margin .24s ease, padding .24s ease,
              border-color .24s ease, background-color .24s ease, visibility 0s linear .24s;
}
.ui-rail-tray.is-open {
  grid-template-rows: 1fr;
  visibility: visible;
  border-bottom-color: var(--hair);
  padding-bottom: var(--s2);
  margin-bottom: var(--s2);
  transition-delay: 0s;
}
.ui-rail-tray-in {
  display: flex;
  align-items: center;
  justify-content: space-around;
  gap: var(--s1);
  min-height: 0;
  overflow: hidden;
}
.ui-rail-tray .ui-rail-box svg { width: calc(var(--s9) - var(--s1)); height: calc(var(--s9) - var(--s1)); }
@media (prefers-reduced-motion: reduce) {
  .ui-rail-tray, .ui-rail-tray.is-open { transition: none; }
}

.ui-rail-foot {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  gap: var(--s1);
  padding: 0 var(--s2);
  align-self: flex-end;
}

/* ── the phone's menu (KODU-65) ──────────────────────────────────────────── */

/* A rail with more than a bar holds: the bar is the mark, the menu and your
   face, and everything else waits in the menu — the scheme included. */
.ui-rail-menu { display: none; }
@container (max-width: 640px) {
  /* Three even thirds — the mark, the menu, the face — each centred in its
     own, so the bar is level and nothing hugs an edge. */
  .ui-rail-nav[data-phone="menu"] { justify-content: space-between; }
  .ui-rail-nav[data-phone="menu"] .ui-rail-brand { display: flex; flex: 1; justify-content: center; }
  .ui-rail-nav[data-phone="menu"] .ui-rail-list { display: none; }
  .ui-rail-nav[data-phone="menu"] .ui-rail-foot { flex: 2; align-self: center; justify-content: space-around; padding: 0; }
  .ui-rail-nav[data-phone="menu"] .ui-rail-foot > :not(.ui-rail-menu, .ui-you) { display: none; }
  .ui-rail-nav[data-phone="menu"] .ui-rail-menu { display: block; }
  /* The scheme's ring stands in the open menu's head, left of its ✕: a phone reaches it on every door. */
  .ui-rail-nav[data-phone="menu"]:has(> .ui-rail-foot > .ui-rail-menu[open]) .ui-rail-foot > .ui-theme {
    display: flex;
    position: fixed;
    z-index: 71;
    top: calc((var(--s13) + var(--s4) - var(--s11)) / 2);
    right: calc(var(--s7) + var(--s9) + var(--s6));
  }
}
.ui-rail-menu > summary { list-style: none; }
.ui-rail-menu > summary::-webkit-details-marker { display: none; }
.ui-rail-menu-shut { display: none; }
/* Open, the summary is the sheet's head and the list is the screen under it. */
.ui-rail-menu[open] > .ui-rail-menu-open {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 70;
  flex-direction: row;
  justify-content: space-between;
  min-height: calc(var(--s13) + var(--s4));
  max-width: none;
  padding: 0 var(--s7);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.ui-rail-menu[open] > .ui-rail-menu-open .ui-rail-box { display: none; }
.ui-rail-menu[open] > .ui-rail-menu-open .ui-rail-label { font-size: var(--t7); font-weight: 600; color: var(--ink); text-align: left; }
.ui-rail-menu[open] > .ui-rail-menu-open .ui-rail-menu-shut { display: block; font-size: var(--t8); color: var(--ink3); }
.ui-rail-menu-pop {
  position: fixed;
  inset: calc(var(--s13) + var(--s4)) 0 0;
  z-index: 70;
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  overflow-y: auto;
  padding: var(--s5) var(--s5) calc(var(--s7) + env(safe-area-inset-bottom));
  background: var(--surface);
}
.ui-rail-menu-row {
  display: flex;
  align-items: center;
  gap: var(--s5);
  min-height: var(--s13);
  padding: 0 var(--s5);
  border-radius: var(--r5);
  color: var(--ink2);
  font-size: var(--t6);
  font-weight: 500;
  text-decoration: none;
}
.ui-rail-menu-row.is-here { color: var(--accent); background: var(--accent-soft); }
.ui-rail-menu-icon { display: flex; flex: none; color: var(--mute); }
.ui-rail-menu-icon svg { width: var(--s9); height: var(--s9); }
.ui-rail-menu-rule { height: 1px; margin: var(--s2) var(--s3); background: var(--hair); }

/* ── your face and its menu ──────────────────────────────────────────────── */

/* The menu is placed against the rail and not against the face: on a phone a
   menu hung off a 44px target near the edge lands half inside the screen, and
   the bar is the full width, so its top edge is the offset nobody has to keep. */
.ui-you { position: static; }
.ui-you > summary { list-style: none; }
.ui-you > summary::-webkit-details-marker { display: none; }
.ui-you-open {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: calc(var(--s12) + var(--s2));
  min-height: calc(var(--s12) + var(--s2));
  border-radius: var(--r4);
  color: var(--ink3);
  cursor: pointer;
}
.ui-you-open:hover, .ui-you[open] > .ui-you-open { background: var(--chip); color: var(--ink); }
.ui-you-glyph { display: flex; }
.ui-you-glyph svg { width: var(--s9); height: var(--s9); }
/* Your colour, off your address and the same on every door (UI-19): only the
   hue rides on the element, and how light it is drawn is the scheme's. */
.ui-you-face {
  --ui-hue: 265;
  position: relative;
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  width: calc(var(--s11) - var(--s1));
  height: calc(var(--s11) - var(--s1));
  border-radius: var(--r-pill);
  background: oklch(var(--av-bg) var(--ui-hue));
  color: oklch(var(--av-fg) var(--ui-hue));
  font-size: var(--t3);
  font-weight: 700;
  letter-spacing: .02em;
  line-height: 1;
}
.ui-you-photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ui-you-pop {
  position: absolute;
  z-index: 60;
  right: var(--s4);
  bottom: calc(100% + var(--s4));
  display: flex;
  flex-direction: column;
  width: max-content;
  min-width: calc(var(--s13) * 4.5);
  max-width: calc(100vw - var(--s8));
  padding: var(--s2);
  border: 1px solid var(--line);
  border-radius: var(--r6);
  background: var(--surface);
  box-shadow: var(--pop);
}
.ui-you-who { display: flex; flex-direction: column; gap: var(--s1); padding: var(--s3) var(--s5) var(--s4); overflow: hidden; }
.ui-you-name, .ui-you-address { overflow: hidden; text-overflow: ellipsis; }
.ui-you-name { color: var(--ink); font-size: var(--t5); font-weight: 600; }
.ui-you-address { color: var(--mute); font-family: var(--mono); font-size: var(--t3); }
.ui-you-rule { height: 1px; margin: var(--s2) var(--s3); background: var(--hair); }
.ui-you-form { display: contents; }
.ui-you-row {
  display: flex;
  align-items: center;
  gap: var(--s5);
  width: 100%;
  min-height: calc(var(--s11) + var(--s2));
  padding: 0 var(--s5);
  border: 0;
  border-radius: var(--r3);
  background: none;
  color: var(--ink2);
  font: inherit;
  font-size: var(--t5);
  text-align: left;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.ui-you-row:hover { background: var(--hover); color: var(--ink); }
.ui-you-row.is-here { color: var(--accent-down); font-weight: 600; }
.ui-you-icon { display: flex; flex: none; color: var(--mute); }
.ui-you-icon svg { width: calc(var(--s8) + var(--s1)); height: calc(var(--s8) + var(--s1)); }
.ui-you-row:hover .ui-you-icon, .ui-you-row.is-here .ui-you-icon { color: currentColor; }

/* ── a desk: the column down the left ────────────────────────────────────── */

@container (min-width: 641px) {
  .ui-rail-nav {
    flex-direction: column;
    justify-content: flex-start;
    gap: var(--s8);
    padding: var(--s9) 0 var(--s8);
    border-top: 0;
    border-right: 1px solid var(--line);
  }
  .ui-rail-brand { display: flex; }
  .ui-rail-list {
    flex: none;
    flex-direction: column;
    flex-wrap: nowrap;
    justify-content: flex-start;
    align-self: stretch;
    --ui-rail-gap: var(--s7);
    gap: var(--ui-rail-gap);
  }
  /* The first list is what gives way on a short window, and it scrolls
     inside itself: the others and the foot must not go off the bottom. */
  .ui-rail-brand + .ui-rail-list { flex: 0 1 auto; min-height: 0; overflow-y: auto; scrollbar-width: none; }
  .ui-rail-brand + .ui-rail-list::-webkit-scrollbar { display: none; }
  .ui-rail-rule { display: block; flex: none; width: calc(var(--s11) + var(--s2)); height: 1px; background: var(--line); }
  .ui-rail-space { display: block; flex: 1 1 auto; min-height: var(--s6); }
  .ui-rail-item { min-width: 0; min-height: 0; }
  /* A word longer than the column breaks rather than crossing into the pane.
     Only in the list, which is the column's width: a tray is its contents'. */
  .ui-rail-list > .ui-rail-item { max-width: calc(100% - var(--s4)); }
  .ui-rail-box { width: calc(var(--s12) + var(--s2)); height: calc(var(--s11) + var(--s3)); }
  /* The sections stand on a soft panel as wide as the column, never as wide
     as what is in it: a box sized by its words is centred differently by
     every engine, and a border around it is what read as crooked. */
  .ui-rail-tray {
    order: 0;
    flex-basis: auto;
    align-self: stretch;
    margin: calc(var(--ui-rail-gap) * -1) var(--s4) 0;
    padding: 0;
    border: 0;
    border-radius: var(--r6);
  }
  .ui-rail-tray.is-open {
    margin: calc(var(--s3) - var(--ui-rail-gap)) var(--s4) 0;
    padding: var(--s4) 0;
    background: var(--hover);
  }
  .ui-rail-tray .ui-rail-item:not(.is-here):hover .ui-rail-box { background: var(--chip); }
  .ui-rail-tray-in { flex-direction: column; justify-content: flex-start; gap: var(--s4); }
  .ui-rail-foot { flex-direction: column; gap: var(--s5); padding: 0; align-self: center; }
  /* The column is too narrow to hold a menu, so it stands clear of the
     column on the right, level with the foot — why the rail may not clip. */
  .ui-you-open { min-width: 0; min-height: 0; width: calc(var(--s12) + var(--s2)); height: calc(var(--s11) + var(--s3)); }
  .ui-you-pop { right: auto; bottom: var(--s8); left: calc(100% + var(--s3)); }
  /* A laptop's window once its browser has drawn a bar: the gaps close up
     before the first list has to scroll. */
  @media (max-height: 820px) {
    .ui-rail-nav { gap: var(--s4); }
    .ui-rail-list { --ui-rail-gap: var(--s5); }
  }
}

/* The foot of every public screen (UIC-39), on the house tokens.

   Three bands, each quieter than the one above it: the columns a reader might
   want, a hairline, then the machine facts nobody reads until they need them.
   Not a card — a rule and space. The box spans what holds it; the bands stop
   at the page's measure. Where a frame stands the foot is the application's
   class, never a redrawing of it. */

.ui-foot {
  display: flex;
  flex-direction: column;
  gap: clamp(var(--s8), 2vw, var(--s10));
  width: 100%;
}
.ui-foot-cols, .ui-foot-rule, .ui-foot-line {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
}

/* `auto-fit` with a floor rather than a breakpoint: one column on a phone,
   more as the room allows, the heads lined up on their left edges. */
.ui-foot-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(calc(var(--s12) * 5), 1fr));
  gap: var(--s9) var(--s12);
}
.ui-foot-col { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
/* The smallest tracked-out type on the page, so a head reads as a label. */
.ui-foot-head {
  margin: 0;
  color: var(--ink3);
  font-size: var(--t3);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.ui-foot-list { display: flex; flex-direction: column; gap: var(--s3); margin: 0; padding: 0; list-style: none; }

/* Close together, so tighter than the ladder's leading; underlined on hover
   only, under the text, or the column would be a fence. */
.ui-foot-link {
  color: var(--ink3);
  font-size: var(--t5);
  line-height: 1.3;
  text-decoration: none;
  text-underline-offset: 3px;
}
.ui-foot-link:hover { color: var(--accent); text-decoration: underline; }
.ui-foot-link.is-on { color: var(--ink); font-weight: 600; }
/* A press in a link's dress: it loses the button's box and keeps the word. */
button.ui-foot-link { width: auto; padding: 0; border: 0; background: none; font: inherit; font-size: var(--t5); cursor: pointer; }

/* A company's facts keep the register machine facts wear: mono, small,
   quiet — the name itself a step darker, being what the numbers check. */
.ui-foot-fact { color: var(--mute); font-family: var(--mono); font-size: var(--t3); line-height: 1.35; }
.ui-foot-fact:first-child { color: var(--ink3); }

.ui-foot-rule { height: 1px; margin-block: 0; border: 0; background: var(--hair); }

.ui-foot-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4) var(--s10);
  color: var(--mute);
  font-family: var(--mono);
  font-size: var(--t3);
}
.ui-foot-door { min-width: 0; overflow-wrap: anywhere; }
.ui-foot-by { display: flex; align-items: baseline; gap: var(--s3); margin: 0; }
/* The platform's name a step darker than the words before it: it is the link. */
.ui-foot-platform {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  color: var(--ink3);
  font-weight: 500;
  text-decoration: none;
  text-underline-offset: 3px;
}
.ui-foot-platform:hover { color: var(--accent); text-decoration: underline; }
.ui-foot-mark { display: inline-flex; width: 1.1em; height: 1.1em; }
.ui-foot-mark svg { width: 100%; height: 100%; }
/* The platform's logo at a foot's far end, alone on the line or not: grey
   and faint until a hand or a key comes to it, then in its own colours (UIC-53). */
.ui-foot-logo { display: inline-flex; margin-inline-start: auto; line-height: 0; filter: grayscale(1); opacity: .35; transition: filter var(--quick), opacity var(--quick); }
.ui-foot-logo svg { height: var(--s9); width: auto; }
.ui-foot-logo:hover, .ui-foot-logo:focus-visible { filter: none; opacity: 1; }

/* 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);
}

/* 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))); }

/* Kodu's stylesheet, served at /kodu.css with the faces and the house palette
   in front of it (portal/css.rs).

   Two authorities. The prototype — designs/kodu/Kodu24 Portal.dc.html —
   decides what is on a screen: the brand, the four actions, the five deals,
   the operator line. The shipped mail app decides what it looks like and how
   it is arranged: the frame is the viewport at 100dvh, the pane scrolls
   itself, and the two schemes are the same token names with different values
   so nothing below the tokens ever asks which scheme it is in.

   Five places they disagree, and Postbox wins all five:

   1. Full-bleed, and the measure is capped per element. The prototype is a
      centred 1020px column on a page that scrolls. Here the frame is the
      window: the rail is a fixed column down the left and the pane takes the
      rest, and inside the pane it is the *element* that says what width it
      wants. Prose stops at --k-prose because a line of text has a length a
      person can read; a list of a hundred and eleven place names, and the
      results grid S3 brings, fill whatever they are given, because a wider
      window is more of them and not longer lines. Capping the page instead
      would leave a thin ribbon of content in the middle of a 2 400px screen
      and call it a design.
   2. A dark scheme, which neither prototype has. Mechanical: the prototype's
      `white` is var(--surface), its oklch(0.22 0.01 85) is var(--ink), its
      oklch(0.55 0.01 85) is var(--mute), and the tests in portal/css.rs are
      what prove none was missed.
   3. Shadows go. The prototype puts one under every card. Shadows are for what
      floats, and nothing in a column floats — a card is a surface with a
      hairline. --shadow is spent on the lightbox, the area popover, the chip
      menu and the two folds at the foot of the rail, which do.
   4. One navigation, down the left (KODU-18). The prototype's header and its
      underlined tab bar were a hundred and forty pixels of chrome across the
      top of every screen doing one job between them; they are the rail, which
      is what the mail app has and for the same reason. It is still the deal
      control (KODU-13) — the prototype's second one, a segmented row at the
      top of the panel, is gone from search and kept only where a deal is being
      said rather than navigated to.
   5. /theme.css is not linked. The tokens are served in front of this sheet
      instead, so a storefront rule cannot reach a portal screen.

   Nothing below the token blocks names a colour. A value the design needs that
   is not a token belongs in theme/tokens.rs, not here — with the one exception
   directly below, which is a rule rather than a shade. */

/* CTX-11: the agency accents. Kodu's own table, kept with the application
   whose rule it is: separated in lightness as well as hue, so two chips differ
   in greyscale and to an eye that does not split red from green. Amber, green
   and red are left to the statuses that mean them. The person context takes no
   slot — it wears --accent, because being yourself is the default state.

   The measures are here for the same reason a token is anywhere: the pane's
   own gutter is used by the pane and by the operator line inside it, and two
   copies of one clamp is how one edge stops being one. */
:root {
  /* The portal's measures are every product's, and the scale names them
     (UI-42): `--gutter`, `--prose` and `--measure`. The portal's own names
     are kept as aliases rather than chased through four hundred rules — one
     line each, and what they point at is the house's. The rail's track is
     the tier's `--rail-col`, every door's (UIC-38, 2026-10-02). */
  --k-gutter: var(--gutter);
  --k-prose: var(--prose);
  --k-page: var(--measure);
  /* Flat (KODU-102): one small corner on anything that is a box — a
     picture, a card, a field, a panel, a map. Every step of the ladder is
     the same 3px, which takes the edge off a box without making it a card
     again; the pill and the circle keep their shape, because a pill that is
     not round is a rectangle with a name. */
  --r1: 3px;
  --r2: 3px;
  --r3: 3px;
  --r4: 3px;
  --r5: 3px;
  --r6: 3px;
  --r7: 3px;

  /* A listing's own tint, until there are photographs (KODU-10). Like the
     accent table above it this is a *rule* and not a shade: the hue comes from
     the element — a hash of the listing (portal/thumb.rs) — and only the
     lightness and the chroma are decided here, which is what lets the same
     listing be the same colour in both schemes.

     The chroma is near nothing on purpose. It was 0.035, which spread twelve
     cards across pink, mint, lilac and peach — a grid of colours that mean
     nothing, in a product whose whole palette is one warm neutral and one
     accent. What is left is the same per-listing hue at a strength you notice
     only when two cards are side by side, which is the only place it was ever
     doing any work.

     A lightness and a chroma, not a whole colour, which is `--av-bg`'s shape
     exactly and for a reason worth writing down: a custom property holding
     `oklch(0.92 0.035 var(--hue))` is substituted where it is *declared*, and
     `--hue` is not set on `:root` — so the token is invalid and every
     thumbnail comes out transparent, with no error anywhere. The rule
     assembles the colour instead. */
  --k-thumb-bg: 0.945 0.012;
  --k-thumb-fg: 0.68 0.03;

  /* The full-screen view's ground and ink (KODU-69): a warm near-black in
     both schemes — the photograph is the light there, so the dark scheme has
     nothing to invert — its controls a veil of the ground over the picture,
     and their hairline a veil of the ink. */
  --k-light-bg: oklch(0.13 0.01 85);
  --k-light-fg: oklch(0.98 0.005 85);
  --k-light-veil: oklch(0.1 0.01 85 / 0.5);
  --k-light-line: oklch(0.98 0.005 85 / 0.28);

  /* The paper a floor plan is drawn on (KODU-77): white in both schemes,
     because the drawing was made on white and its own ground merges with
     this one, where a tinted surface would show the sheet's edge. */
  --k-paper: oklch(1 0 0);

  /* The ground, a shade lighter than the house's (2026-09-07: *"praegune on
     natukene liiga hall"*): not white, because the cards have to be lighter
     than what they stand on, but no longer a grey a reader notices. A card
     is `--surface` — lighter than this and still not white — and under the
     pointer it goes to white, the one step left above it. */
  --page: oklch(0.975 0.007 85);
  --k-card-hover: oklch(1 0 0);

  --k-accent-1: oklch(0.52 0.13 215);
  --k-accent-2: oklch(0.48 0.13 340);
  --k-accent-3: oklch(0.58 0.13 115);
  --k-accent-4: oklch(0.44 0.13 185);
  --k-accent-5: oklch(0.56 0.13 305);
  --k-accent-6: oklch(0.62 0.13 90);
}

/* Every lightness gains 0.10; hue and chroma do not move, so an agency is the
   same colour in both themes (CTX-11). */
/* The reader's choice wins (KODU-98), the way the tier's own dark table is
   written: a plain `:root` under this query answered a reader who had
   chosen light on a system that prefers dark, and once the ground was in
   this table (2026-09-07) their content went dark while the rail — the
   tier's tokens — did as it was told. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* The tint turns dark and its ink turns light; the hue does not move, so a
       listing keeps the colour a reader recognises it by. */
    --k-thumb-bg: 0.245 0.014;
    --k-thumb-fg: 0.46 0.03;

    /* The same four: the full-screen view is dark whichever scheme the page
       is in, because the picture is what is lit. */
    --k-light-bg: oklch(0.13 0.01 85);
    --k-light-fg: oklch(0.98 0.005 85);
    --k-light-veil: oklch(0.1 0.01 85 / 0.5);
    --k-light-line: oklch(0.98 0.005 85 / 0.28);

    /* Paper stays paper. */
    --k-paper: oklch(1 0 0);

    /* The pointer's step up, in the dark: the chip's lightness, since white
       under the pointer is a flash and not a lift. */
    --k-card-hover: oklch(0.25 0.006 85);
    /* The house's own dark ground, restated: only the light one moved. */
    --page: oklch(0.155 0.006 85);

    --k-accent-1: oklch(0.62 0.13 215);
    --k-accent-2: oklch(0.58 0.13 340);
    --k-accent-3: oklch(0.68 0.13 115);
    --k-accent-4: oklch(0.54 0.13 185);
    --k-accent-5: oklch(0.66 0.13 305);
    --k-accent-6: oklch(0.72 0.13 90);
  }
}

/* The frame is the viewport. Nothing outside it scrolls — the pane does its
   own scrolling, and a page-level scrollbar here would only ever be something
   overflowing that should not. The ground is the tier's (UIC-42). */
html, body { height: 100%; overflow: hidden; }
/* The page does not pinch; the map does (KODU-60). `pan-x pan-y` is what
   holds Android's pinch-zoom off the page — the map's own svg declares
   `touch-action: none` and keeps its two fingers. */
html { touch-action: pan-x pan-y; }
/* Two rows, and the second one is usually not there: the shell, and under it
   the greeting a first visit is met with (KODU-97).

   It was `position: fixed` for an afternoon, and that was wrong twice over —
   the rail's foot is where the reader's own face hangs, and the area picker
   opens onto the whole screen with **Kinnita valik** along its bottom edge,
   so a band laid over the last eighty points of the window covered the
   account fold on every screen and the picker's only way out on one. A first
   visit is not a reason to make the page unusable until it is answered. In a
   row of its own it takes its space from the shell instead of borrowing it.

   Every other child of the body is out of the flow — the skip link is
   absolute, the assistant and the report box are fixed, the scripts draw
   nothing — so there are never three rows. */
body {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  height: 100dvh;
}

::selection { background: var(--accent-soft); color: var(--accent); }

/* The pane takes focus when the skip link is used, and it is not a control —
   a ring around the whole page would be startling. */
.k-pane:focus { outline: none; }

/* The first thing a keyboard meets and the last thing anybody else sees.
   Without it, reaching the results means tabbing past the brand and eight rail
   entries, on every page. */
.k-skip {
  z-index: 100;
  top: 8px;
  left: 8px;
  transform: translateY(-200%);
}
.k-skip:focus { transform: none; }

/* Somebody who has asked their machine to stop moving things has asked this
   one too. Everything in this design that moves is decoration — a toggle's
   knob, a hover — so there is nothing to keep. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ── the frame ───────────────────────────────────────────────────────────── */

/* `display: grid` stays: the tier's fold turns it into a `column-reverse`
   flex column on a phone, and a utility would win that tie. */
/* The height is the body's row, not the window's: with the greeting standing
   under it the shell is shorter than the viewport by exactly the band, and
   `100dvh` here would push that band off the bottom edge. */
.k {
  display: grid;
  grid-template-columns: var(--rail-col) minmax(0, 1fr);
  width: 100%;
  min-height: 0;
}
/* A phone lays the rail down as a bar along the foot and scrolls the page as the document: the tier's fold
   (`ui-fold`, UIC-38, UIC-51), the map keeping the window (`data-still`). */

/* Three of the prototype's nine views — Lisa kuulutus, Uus büroo and the
   terms — replace the shell rather than sitting in it: nothing to browse the
   market with while you are filling a form in, and no operator line under a
   takeover. The rail stays, because a takeover a reader cannot get out of is
   a trap; what goes is everything on it that is about the market. */
.k[data-depth="sheet"] .k-market,
.k[data-depth="sheet"] .k-foot { display: none; }

/* ── the assistant stands beside the page (KODU-89) ──────────────────────── */

/* The dock's whole contract with a surface is one attribute on `<html>`, and
   the narrowing it asks for is padding on the body. `.k` is `width: 100%`
   inside that body, so the frame — the rail, the pane and everything in them
   — gives way on its own and there is nothing to write for the ordinary
   screens.

   A map that has been taken full screen is the exception: it is
   `position: fixed`, so the body's padding does not reach it and it would
   lie *under* the panel. It is still the page, so it gives up the panel's
   width rather than hiding behind it — both the map door's screen and the
   listing's own map, which grow the same way. On a phone the dock is the
   whole screen and there is no beside, which is the width dock.css itself
   stops narrowing at. */
[data-dock="half"] .k-mapscreen.is-full,
[data-dock="half"] .k-mapview.is-full { right: var(--ui-dock-w); }
/* At full there is no page left to give up anything: the panel is the screen
   and the map is behind it, so the map keeps the size it had rather than
   collapsing to nothing and having to lay itself out again on the way back. */
@media (max-width: 640px) {
  [data-dock="half"] .k-mapscreen.is-full,
  [data-dock="half"] .k-mapview.is-full { right: 0; }
}

/* The map keeps its zoom in the bottom right of the drawing and the launcher
   stands in the bottom right of the window, so on any screen carrying a map
   the launcher steps left of that column rather than on top of it. */
body:has(.mp-page) .ui-dock-launcher { right: calc(var(--s12) + var(--s9)); }

/* ── the rail ────────────────────────────────────────────────────────────── */

/* What it holds and how it stands — a column on a desk, a bar on a phone —
   are the tier's (UIC-38, `rail.css`), on Postbox's clothes (KODU-21), and so
   is the width of its track (`--rail-col`): Turuülevaade and Uusarendused
   fit the mailbox's eighty-four at the rail's type. */

/* ── the pane ────────────────────────────────────────────────────────────── */

/* The one thing on the page that scrolls, and the reason the rail does not
   slide away and nothing has to guess a page height. */
.k-pane {
  --k-pane-gap: clamp(var(--s10), 2.4vw, var(--s11));
  --k-pane-top: clamp(var(--s9), 2.6vw, var(--s12));
  gap: var(--k-pane-gap);
  padding: var(--k-pane-top) var(--k-gutter) clamp(var(--s10), 3vw, var(--s13));
  overflow-x: hidden;
  scrollbar-gutter: stable;
}

/* **The page's measure, and the way out of it** (KODU-92, widened
   2026-09-06). Everything the pane holds — the content and the foot under it
   — stops at the publish form's 1240 and centres, so a desk-wide window does
   not leave a column of text jammed against the left edge with half the glass
   empty beside it.

   `is-wide` is the door out, and it is a **class a section asks for** rather
   than a list of exceptions kept here: a band of colour, a map, a gallery
   that wants the glass says so on itself, and this sheet does not have to
   know which sections those are.

   The filter bar is the one exception spelt out, because it was flush edge to
   edge before this rule existed (KODU-27) and is drawn by a component that
   every screen shares — it is a bar across the top of the window, not a card
   on the page, and a 1240 cap would have made it a card. */
.k-pane > * {
  width: 100%;
  max-width: var(--k-page);
  margin-inline: auto;
}
/* All three properties go back, not just the cap: the filter bar runs edge to
   edge by *escaping* the pane's padding (KODU-27), so a `width: 100%` of the
   content box pins it inside the gutters it was drawn to cross — and the
   landing's spec measures exactly that overhang. */
.k-pane > .is-wide,
.k-pane > .k-panel,
.k-pane > .k-foot {
  width: auto;
  max-width: none;
  margin-inline: 0;
}
/* A reading page wears this to take the pane glass to glass; its band and column are the tier's (UIC-41). */
.k-pane > .k-glass {
  width: auto;
  max-width: none;
  margin-inline: calc(var(--k-gutter) * -1);
}
.k-pane > .k-glass:first-child { margin-top: calc(var(--k-pane-top) * -1); }

/* ── the measure, per element ────────────────────────────────────────────── */

.k-h1 {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(var(--t10), 3.2vw, var(--t11));
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.15;
  text-wrap: pretty;
}
.k-h2 {
  margin: 0;
  font-family: var(--serif);
  font-size: var(--t8);
  font-weight: 500;
  letter-spacing: -0.015em;
}

/* The design's strongest recurring signature: mono, uppercase, letter-spaced,
   in the accent, above nearly every section. */
.k-label {
  margin: 0 0 var(--s6);
  font-family: var(--mono);
  font-size: var(--t3);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}

.k-link:hover { text-decoration: underline; }

/* ── surfaces ────────────────────────────────────────────────────────────── */

/* A card is the tier's (`ui-card`, UIC-11). Something the pane has to say
   rather than something it holds wears it centred in what is left of the
   frame and no wider than a line worth reading — `stack mx-auto my-auto
   prose` on the element — with a step more air than a card in a column. */
.k-note { padding: clamp(var(--s9), 2vw, var(--s11)); }

/* ── controls ────────────────────────────────────────────────────────────── */

/* The pill, the submit, the modal's button, the text button and the field
   are the components tier's (UIC-4, `commee-ui-components`), served in front
   of this sheet; what follows here is what the portal adds on top of them. */

/* The language is a *setting*, and it is in the foot with the other one
   (KODU-98); the phone menu's four in a row are the tier's segmented links (UIC-43, KODU-65). */

/* ── the search panel (SPEC 1.1) ─────────────────────────────────────────── */

/* The prototype's white card, and the one place in the portal a shadow is
   allowed on something that does not float: the panel is the page's subject
   on the landing, and lifting it off the ground is what says so. Everything
   inside it is a real form control. */
.k-panel {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--s5);
  padding: clamp(var(--s7), 1.4vw, var(--s8));
}
.k-panel > .k-row { flex-basis: 100%; }
/* The phone's furniture (KODU-64, KODU-65) — the Filtrid pill, the sheet's
   head and foot, the menu and its ✕ — is markup the desktop never shows. */
.k-fbtn, .k-fhead, .k-ffoot { display: none; }

/* The search panel's own arrangement (KODU-28): every filter on screen,
   flowing left to right and wrapping down. The submit is off the screen
   entirely (KODU-36) — the panel applies itself, and Enter, a screen reader
   and a scriptless browser still have the sr-only button. */
.k-panel.k-stack { flex-wrap: nowrap; align-items: flex-start; }
/* A filter is just its words — no box, no background, no chevron, and no
   padding of its own either: the first word starts where the prose under it
   starts, and the row's gap is what separates the filters. Pressing the
   words is what pops the choices up. The one mark that stays is the area
   picker's way out, which is a whole screen's (KODU-25).

   The open picker is excluded outright rather than left to be overridden
   below (KODU-44). It was not: four classes and an attribute beat the modal's
   three and one, so the head that is supposed to be a screen's heading was
   flattened to a filter in a bar — unpadded against the left edge while the
   tree under it started twenty pixels in. */
.k-panel.k-stack .k-drop:not(.is-modal[open]) > .k-dropbtn {
  border: 0;
  padding: 0;
  background: none;
  box-shadow: none;
}
.k-panel.k-stack .k-dropmark { display: none; }
/* Open as a whole screen, the head's said-text stops being a word in the bar
   and the cross goes back to the right-hand edge it is the way out at. */
.k-panel.k-stack .k-drop.is-modal[open] .k-dropmark { display: flex; margin-left: auto; }
.k-panel.k-stack .k-drop.is-modal[open] .k-dropsaid { max-width: none; }
.k-panel.k-stack .k-dropbtn:hover .k-dropsaid { color: var(--accent); }
/* The label and the value are one size; what separates them is weight and
   ink. An untouched filter is only its quiet name (KODU-34), and a value —
   which only exists once something is chosen — steps forward in the panel's
   own ink. */
.k-panel.k-stack .k-dropname { font-weight: 400; color: var(--mute); }
/* Content-sized in the flow, and still capped: a filter holding five place
   names is one wide word with an ellipsis, not a row of its own. */
.k-panel.k-stack .k-dropsaid { color: var(--ink); flex: none; max-width: 260px; }

/* On the landing and on the results the panel is not a card at all — it is
   the top of the screen (KODU-23, KODU-27): pull out of the pane by exactly
   the padding the pane put there, then put the gutter back *inside*, so the
   bar reaches the glass while its controls still line up with the prose under
   them. */
.k-panel.is-flush {
  align-items: center;
  gap: var(--s4);
  margin: calc(clamp(var(--s9), 2.6vw, var(--s12)) * -1) calc(var(--k-gutter) * -1) 0;
  /* The **box** reaches the glass and the **filters inside it** stop where
     the page does (2026-09-06). The bar is the top of the screen, so its
     ground and its hairline have to run edge to edge; the controls standing
     in it are page content, and on a 2 400px monitor they were strung right
     across the glass while everything under them stopped at 1240.
     `max()` and not a wrapper: the inset is the gutter until the window is
     wider than the measure, and half the difference after that — so the
     filters line up with the listings under them at every width, with no
     element in the markup whose only job is to be narrower.

     The `100%` is the pane's content box — a percentage in a padding
     resolves against the containing block, not against this bar, which is
     two gutters wider than that box because it escapes them. Half the
     difference measured off the box lands one gutter short of where the
     listings start, and the filters stood a gutter wider than the list and
     the foot on every screen wider than the measure where the bar is on top
     (2026-09-09: the dock open on a wide desk, or a desk under 2120). The
     gutter goes back on. */
  padding: var(--s5) max(var(--k-gutter), calc((100% - var(--measure)) / 2 + var(--k-gutter)));
  border-width: 0 0 1px;
  border-radius: 0;
  /* The bar is the top of the page, not a card on it (KODU-34): it wears the
     body's own ground, and the hairline below is what says where it ends. */
  background: var(--page);
}
/* ── the filters and what they narrow (KODU-99) ──────────────────────────── */

/* `panel::browse` boxes what the filters narrow, and the box spaces what it
   holds the way the pane spaces its own children. */
.k-pane > .k-browse-body { gap: var(--k-pane-gap); }

/* ── and a column, where the window is wide enough for one (KODU-99) ─────── */

/* Three readings across, on a desk-wide screen: the rail, the filters, the
   listings. The bar is right for a laptop — it is one row of the page and the
   results start under it — but past about 1400px it was a line of eight
   controls strung across the top of a very wide screen with the listings
   starting a long way below, and the room to its right doing nothing.
   Standing the filters up puts the three columns in the order a person reads
   them: where you are, what you are narrowing by, and what you found.
   `:has()` and not a second render: it is the same markup either way, and a
   panel that had to be told which shape it was in is a screen paying for the
   breakpoint (UIC-6). Its specificity is also what wins the tie against the
   `stack` utility the pane wears, which is served after this sheet. */
/* **The listings are exactly as wide as the foot under them**, which is the
   thing to hold on to. The pane is the grid — not a wrapper round the
   filters — because the foot is the pane's child too, and a foot in a
   different column from the listings is the misalignment this whole shape
   exists to avoid. One content column holds the letterhead, the listings and
   the foot; the filters are the column beside it.

   The two `1fr` edges are the slack, so the pair sits on the page's centre
   line. Pinned to the far left the filters sat against the rail with a
   hand's width of nothing between them and the thing they narrow.

   **2120, and the number is arithmetic rather than taste.** The listings sit
   where content sits on every screen — 1240 centred — and the filters go in
   the gutter *left over*, which has to be 300 wide plus the gap. Two gutters
   of 340 either side of 1240 is 1920 of content box, plus the pane's own
   gutters and the 96 of rail and a scrollbar: about 2120. Below that there is
   no room to put a column without taking it out of the reading, so the bar is
   the right answer and the listings keep their place. A reader walking from a
   listing to a search to a büroo's page never sees the page shift under
   them.

   **And the dock is not in the window's width** (UIC-14). A media query
   measures the window; the assistant takes half of it, so at 2400 with the
   panel open the page has about 1200 to lay out in and the query still said
   "wide" — the filter gutter had nowhere to go and slid under the rail.
   There is no way to ask a query about an attribute, so the shape asks about
   it in the selector instead: with the dock at half or full there is no room
   for a third column and the bar is the right answer, which is exactly what
   the dock's own sheet says a surface may join in on. */
@media (min-width: 2120px) {
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush) {
    display: grid;
    grid-template-columns:
      minmax(0, 1fr) minmax(0, var(--measure)) minmax(0, 1fr);
    column-gap: clamp(var(--s10), 2.4vw, var(--s12));
    align-items: start;
  }
  /* Everything the pane holds is content except the filters — the
     letterhead, the listings and the foot — and it all shares one column, so
     it all shares one width. */
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush) > * { grid-column: 2; }
  /* **Not the foot.** Its box spans the pane on every other screen and the
     bands *inside* it are what stop at the measure (KODU-50) — so putting
     the box in the middle column here would centre those bands inside a
     column that is itself centred with a gap beside it, and the foot would
     land twenty pixels off where the very same foot lands on a listing
     page. Spanning, it is the same foot everywhere. */
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush) > .k-foot { grid-column: 1 / -1; }
  /* A büroo's letterhead is a row of its own above the listings (KODU-95),
     and the filters stand beside both. Spanning, not placed in the first row
     alone: that sized the row to the filters and dropped the listings an inch
     and a half below the brand. */
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush):has(> .k-browse-head) > .k-panel.is-flush { grid-row: 1 / span 2; }
  /* It stops being the top of the page, so it gives back the full-bleed
     ground, the hairline and the negative margins that made it one. The
     leading `.k` is also what wins the tie against the büroo letterhead's own
     rule further down, which is five classes deep and comes later.

     Not `sticky`. A column of eight filters that followed the scroll was the
     obvious next thought and it does not survive contact: the pane is what
     scrolls, so a sticky child re-sticks under anything trying to scroll one
     of its filters into view, and the filter lands somewhere else. */
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush) > .k-panel.is-flush {
    /* The gutter beside the reading, hugging its left edge. The listings do
       not move to make room — they are where content is on every other
       screen in the portal, so walking from a listing to a search to a
       büroo's page does not shift the page under the reader. */
    grid-column: 1;
    justify-self: end;
    /* The listings' row and no further: the foot is auto-placed in the first
       row every column leaves free, so it lands under both with one gap. */
    grid-row: 1;
    align-self: start;
    width: 300px;
    max-width: none;
    margin: 0;
    padding: 0;
    border-width: 0;
    background: none;
    align-items: stretch;
  }
  /* One filter to a line, each as wide as the column. */
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush) > .k-panel.is-flush .k-fstack {
    flex-direction: column;
    align-items: stretch;
  }
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush) > .k-panel.is-flush .k-drop,
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush) > .k-panel.is-flush .k-dropbtn { width: 100%; }
  /* **The chosen value stands under the name** (2026-09-07). In the bar a
     button is one line — name, value, chevron — and the value sits at the
     far end of it. In a column that end is the edge nearest the listings,
     so every value the reader had chosen was crammed against the very
     things it narrows while the column's own left half stood empty:
     *"it goes 0px to right side where kuulutused are … bring üür under
     Tehingu tüüp"*. Two lines, both at the left: the name with the chevron
     after it, and what it is set to under that. */
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush) > .k-panel.is-flush .k-dropbtn {
    flex-wrap: wrap;
    align-items: baseline;
    row-gap: var(--s1);
    padding-block: var(--s3);
  }
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush) > .k-panel.is-flush .k-dropname { flex: 1 1 auto; }
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush) > .k-panel.is-flush .k-dropsaid {
    order: 1;
    flex: 1 1 100%;
  }
  /* **An open filter pushes the others down rather than covering them.** In
     the bar a popover hangs below the row and lands on the page; in a column
     it lands on the filters underneath it, and a filter you cannot press
     because another one is open over it is worse than no column at all. So
     in this shape the choices are in the flow — the accordion a sidebar of
     filters has always been — and they keep the bounded height they have
     everywhere, so a long list still scrolls itself rather than pushing the
     rest of the column off the screen.

     Not the area picker: `is-modal` open is the whole screen (KODU-25) and
     has nothing to do with the column it was opened from. */
  html:not([data-dock="half"]):not([data-dock="full"]) .k .k-pane:has(> .k-panel.is-flush) .k-drop:not(.is-modal) > .k-droppop {
    position: static;
    width: auto;
    min-width: 0;
    max-width: none;
    box-shadow: none;
    border-radius: 0 0 var(--r6) var(--r6);
  }
}

/* Flat, which is the other half of "no rounded stuff": nothing standing in
   the bar is a lozenge either. Compact is fewer pixels of air and never
   smaller type — every button keeps its 13.5px, and Otsi comes down to the
   38px the filters are rather than the filters coming up to it. */
.k-panel.is-flush .k-dropbtn,
.k-panel.is-flush .k-drop[open] .k-dropbtn,
.k-panel.is-flush .k-droppop { border-radius: 0; }
.k-panel.is-flush > .k-row { flex-basis: auto; }

/* **A flush panel that follows a letterhead is not the top of the page**
   (2026-09-06). The rules above are written for a bar that *is* the top: a
   negative top margin pulls it into the pane's own padding and a negative
   inline one carries it across the gutters. Standing after a bureau's brand
   it did both — climbing over the letterhead and running wider than every
   other block on a page that stops at 1240.

   No new flag for it: the condition is exactly what the selector says, and
   the bureau page is the one screen where the panel has something above it.
   What survives is the *look* — flat, no card, hairlines rather than a
   border — which is the manner the rest of the page took from the editor;
   what goes is the climbing. A rule over it as well as under, so it reads as
   a band between two lines like every other section here. */
/* The letterhead is inside the browse body now (KODU-99) and no longer a
   sibling of the panel, so the condition is "this browse has a letterhead in
   it" rather than "a letterhead is directly before this panel". Same rule,
   same reason — the two adjacency spellings are kept for anything still
   drawing the old shape. */
.k-browse-head + .k-chk + .k-fbtn + .k-panel.is-flush,
.k-bureau + .k-chk + .k-fbtn + .k-panel.is-flush,
.k-bureau + .k-panel.is-flush {
  width: 100%;
  max-width: var(--k-page);
  margin-block: 0;
  margin-inline: auto;
  padding-inline: 0;
  border-width: 1px 0;
  border-color: var(--line2);
}


/* ── the area tree's boxes (a choice's words are the tier's pill, `ui::pill_choice`, UIC-43) ── */

/* Every tile and every tree node is one of these: a real input, off the
   screen but not out of the document, so a keyboard reaches it and a screen
   reader announces it. What is visible is the label next to it. */
.k-chk {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}
.k-tick { display: none; width: 13px; height: 13px; flex: none; }
.k-chk:focus-visible + .k-arow .k-acheck { outline: var(--s1) solid var(--accent); outline-offset: var(--s1); }

/* ── the area picker (SPEC 1.2, KODU-25) ─────────────────────────────────── */

/* The picker fills the modal's body row: the list on the left, the map on the
   right, and the committing press across the bottom of both. It is never
   anywhere else, so it positions nothing and floats above nothing — the
   surface it is drawn on is `.k-drop.is-modal[open]`. */
.k-apop {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  grid-template-rows: minmax(0, 1fr) auto;
  min-height: 0;
}
.k-atree {
  min-height: 0;
  overscroll-behavior: contain;
  padding: var(--s6) clamp(var(--s4), 1.4vw, var(--s9));
  border-right: 1px solid var(--hair);
}
/* Half a screen is not a measure. The count sits at the end of its row, so a
   row as wide as the column puts a name and its number a hand's width apart. */
.k-atree > * { max-width: 34rem; }
/* Under both columns and against the bottom of the screen, because the press
   that finishes a full-screen surface is the last thing on it. */
.k-afoot {
  grid-column: 1 / -1;
  padding: var(--s6) clamp(var(--s7), 2vw, var(--s10));
}

/* Typing in the picker narrows it (SPEC 1.2). A hundred and eleven places is
   a list nobody scrolls, and the box the prototype drew was a placeholder
   people typed into while nothing happened. It sticks to the top of the
   list's own scroll, because a filter that scrolls away is a filter you
   cannot clear without scrolling back. */
.k-asift { top: calc(-1 * var(--s6)); z-index: 2; }
/* The other half of the same control, and now the very map every other
   screen draws (KODU-36): the reusable component fills the column, pressing
   a county's number pulls it full, and the tree on the left is what picks. */
.k-amap { min-height: 0; }
.k-pickmap .mp-page { position: absolute; }
/* What a press means here (KODU-44). The search's map zooms on a press and
   this one picks, so the map that is a control says which it is — over the
   drawing, out of the way of the hand doing the picking. */
.k-amaphint {
  z-index: 3;
  left: 12px;
  top: 12px;
  pointer-events: none;
}

/* ── a filter, as a button that opens (KODU-20) ───────────────────────────── */

/* The panel was forty controls in eight rows, so the answer to "what am I
   searching for" was to read all of them. A filter is one question, so it is
   one button: its name, what it is set to, and the choices inside. */
.k-dropbtn {
  display: flex;
  align-items: center;
  gap: var(--s4);
  max-width: 100%;
  min-height: 38px;
  padding: 0 var(--s5) 0 var(--s6);
  border: 1px solid var(--line2);
  border-radius: var(--r6);
  background: var(--surface);
  font-size: var(--t5);
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.k-dropbtn::-webkit-details-marker { display: none; }
.k-dropbtn:hover { border-color: var(--accent-line); }
/* What it is set to, which is the half a reader is actually scanning. It
   truncates rather than wrapping: a filter button that grows to three lines
   pushes the rest of the panel about every time somebody ticks something. */
.k-dropsaid {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--mute);
}
.k-dropbtn.is-on { border-color: var(--accent-line); background: var(--chip); }
.k-dropbtn.is-on .k-dropsaid { color: var(--accent); font-weight: 600; }
.k-dropmark svg { width: 15px; height: 15px; }
.k-drop[open] .k-dropmark { transform: rotate(180deg); }
/* Open, the button and its panel are one surface — which is what makes the
   chips inside read as being *in* the button. */
.k-drop[open] .k-dropbtn {
  border-color: var(--line);
  border-bottom-color: transparent;
  border-radius: var(--r6) var(--r6) 0 0;
  background: var(--surface);
}

.k-droppop {
  position: absolute;
  z-index: 45;
  top: calc(100% - 1px);
  left: 0;
  min-width: 100%;
  width: max-content;
  max-width: min(30rem, 88vw);
  padding: var(--s5);
  border: 1px solid var(--line);
  border-radius: 0 var(--r6) var(--r6) var(--r6);
  background: var(--surface);
  box-shadow: var(--shadow);
}
/* And it stops before the window does (2026-09-06). A filter with a long
   list — the types, the extras, the condition — opened a box as tall as its
   options and let it run off the bottom of the screen: the choices below the
   fold were unreachable, because the page behind it does not scroll
   (`html, body { overflow: hidden }`) and the box had no scroll of its own.
   So it is bounded and scrolls itself, which is what every other column in
   this application does.

   Not the picker: `is-modal` open *is* the screen (KODU-25), and it manages
   its own height between a fixed head and the tree under it. */
.k-drop:not(.is-modal) > .k-droppop {
  max-height: min(60dvh, 480px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* A filter near the right edge of the bar opens the other way (the drop
   script measures and says so), and the square corner moves to the corner
   that is anchored. */
.k-drop.is-flip > .k-droppop {
  left: auto;
  right: 0;
  border-radius: var(--r6) 0 var(--r6) var(--r6);
}

/* What is already chosen, each the tier's chip with the way to take it off (UIC-43). */
.k-dropnow { margin-bottom: var(--s4); }

/* ── a filter that opens onto the whole screen (KODU-25) ─────────────────── */

/* Open, the fold *is* the screen: a `<details>` and not a `<dialog>`, because
   `showModal()` needs a script and this portal's budget is spent (KODU-20) —
   which is also why the head is the summary, the only thing that can shut a
   `<details>` with none. It clears the rail and stays under the skip link.

   Head and body are each fixed against the screen rather than two rows of one
   grid: a `<details>` in a current browser puts its contents in a
   `::details-content` box of its own, so a row handed to `.k-droppop` goes to
   that box instead and the picker ends up a tall popover on a blank modal. */
.k-drop.is-modal[open] {
  --k-pickhead: 56px;
  position: fixed;
  inset: 0;
  z-index: 60;
  background: var(--surface);
  overscroll-behavior: contain;
}
/* The head of a screen, and the gutter is the tree's own (KODU-44): the
   heading has to start where the list under it starts, or the screen reads as
   two things that were laid out separately. */
.k-drop.is-modal[open] > .k-dropbtn {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1;
  height: var(--k-pickhead);
  padding: 0 clamp(var(--s4), 1.4vw, var(--s9));
  border-width: 0 0 1px;
  border-radius: 0;
  border-color: var(--line);
  background: var(--surface);
  font-size: var(--t6);
}
/* And it is the screen's heading rather than a filter's quiet label
   (KODU-44): the panel's own muted 400 still reaches in here, and drew
   "Piirkonnad" as the faintest thing on a screen it names. */
.k-drop.is-modal[open] .k-dropname {
  font-family: var(--serif);
  font-size: var(--t8);
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--ink);
}
/* Which of the two marks the head carries. Both are in the markup because
   nothing on the server knows whether the fold is up — a `<details>` is always
   sent shut — so the sheet is what picks, and it picks by state.

   Shut, this filter is standing in the bar between seven others and says what
   they say: *this opens*. Open, there is a screen to leave and the mark is the
   way out. `display: none` and not `visibility` or an opacity, so the half that
   is not showing leaves the accessibility tree with its word — a shut button
   announcing "Sulge" names a gesture that is not available. */
.k-drop.is-modal:not([open]) .k-markout { display: none; }
.k-drop.is-modal[open] .k-markshut { display: none; }
/* Open, the mark is the way out and not a chevron, so it is a target and it
   does not turn over. */
.k-drop.is-modal[open] .k-dropmark {
  margin-left: var(--s4);
  padding: var(--s4);
  border-radius: var(--r5);
  transform: none;
  color: var(--ink3);
}
.k-drop.is-modal[open] .k-dropmark:hover { background: var(--chip); color: var(--ink); }
.k-drop.is-modal[open] .k-dropmark svg { width: 16px; height: 16px; }
.k-drop.is-modal[open] > .k-droppop {
  position: fixed;
  inset: var(--k-pickhead) 0 0;
  z-index: 0;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  min-height: 0;
  width: auto;
  max-width: none;
  padding: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
/* Both rows are named, because the chips row is only drawn once something is
   chosen — left to place itself, the picker took the `auto` row on an empty
   filter and sat at the top of a modal that was blank underneath. */
.k-drop.is-modal[open] .k-dropnow {
  grid-row: 1;
  margin: 0;
  padding: var(--s5) clamp(var(--s7), 2vw, var(--s10));
  border-bottom: 1px solid var(--hair);
}
.k-drop.is-modal[open] .k-dropopts {
  grid-row: 2;
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  min-height: 0;
  gap: 0;
}

/* ── the area tree ───────────────────────────────────────────────────────── */

.k-tree { margin: 0; padding: 0; list-style: none; }
.k-tree .k-tree { margin: var(--s1) 0 var(--s2) var(--s5); padding-left: var(--s5); border-left: 1px solid var(--hair); }
/* The node that actually matched, as against the parents kept so it can be
   reached. */
.k-node.is-sifted > .k-arow .k-aname { color: var(--accent); }
/* The whole row is the checkbox's label (KODU-63): one press target from
   the drawn box to the count, tall enough for a thumb. */
.k-arow {
  display: flex;
  align-items: center;
  gap: var(--s4);
  min-height: 38px;
  padding: var(--s1) var(--s2);
  border-radius: var(--r4);
  cursor: pointer;
}
.k-arow:hover { background: var(--chip); }
/* Every row keeps clear of the fold's slot — on leaves too, so the counts
   stand in one column whether or not a chevron follows them. */
.k-arow { width: calc(100% - 44px); box-sizing: border-box; }
.k-acheck {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  flex: none;
  border: 1.5px solid var(--line2);
  border-radius: var(--r3);
  background: var(--surface);
  color: var(--accent-ink);
  transition: background 0.12s ease, border-color 0.12s ease;
}
.k-acheck .k-tick { width: 14px; height: 14px; }
.k-chk:checked + .k-arow .k-acheck { border-color: var(--accent); background: var(--accent); }
.k-chk:checked + .k-arow .k-acheck .k-tick { display: block; }
/* The fold is the chevron alone, standing where the row left room for it —
   opening a county's towns and picking the county are two targets on one
   line, never one press doing both. */
.k-akids > summary.k-afold {
  position: absolute;
  top: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 38px;
  border-radius: var(--r4);
  color: var(--ink3);
  cursor: pointer;
  list-style: none;
}
.k-afold::-webkit-details-marker { display: none; }
.k-afold:hover { background: var(--chip); color: var(--accent); }
.k-afold svg { width: 16px; height: 16px; transition: transform 0.12s ease; }
.k-akids[open] > .k-afold svg { transform: rotate(180deg); }
.k-aname {
  flex: 1;
  min-width: 0;
  color: var(--ink2);
}

/* ── the results (SPEC 1.3) ──────────────────────────────────────────────── */

/* The narrow query below turns both of these round, so neither can move onto
   the element: a utility would win the tie and the row would stay wrapped. */
.k-rtools { margin-left: auto; flex-wrap: wrap; }


/* The search, drawn, standing where the list would (KODU-32): full width
   under the switch, tall enough to be the screen's subject, and carrying
   nothing on it but the zoom buttons — the un-sum card a pressed pin opens
   is the one thing allowed to float over it. */
.k-results.is-map .k-rhead { margin-bottom: var(--s7); }
.k-mapview {
  position: relative;
  height: calc(100vh - 250px);
  min-height: 440px;
  overflow: hidden;
  background: var(--surface);
}
.k-mapview .mp-page { position: absolute; }
/* ⤢ means the whole screen here (KODU-49), so the component's own
   pull-the-country-full button steps aside for Kodu's. */
.k-mapview .mp-all { display: none; }
/* Over the zoom's two presses, on their edge and at their size: it stood apart, 4px in from them, at a size of its own. */
.k-mapfull {
  position: absolute;
  right: 12px;
  bottom: 96px;
  z-index: 5;
  width: 38px;
  height: 38px;
  border: 1px solid var(--line);
  border-radius: var(--r3);
  background: var(--surface);
  color: var(--ink);
  font-size: var(--t4);
  cursor: pointer;
}
.k-mapfull:hover { background: var(--page); }
.k-mapview.is-full {
  position: fixed;
  inset: 0;
  z-index: 400;
  height: auto;
  min-height: 0;
  border: 0;
  border-radius: 0;
}
/* The map view has no box (KODU-32), so its search column is an empty strip
   over the drawing: the map is dragged straight through it. The card is not in
   it — it hangs off the map itself — so nothing here has to be excepted. */
.k-mapview .mp-search { pointer-events: none; }

/* The same answer, drawn (KODU-17). It stands beside the sort and the layout
   because it is the third thing you can do to a page of results and not a
   fourth kind of control. */
.k-rtool {
  display: inline-flex;
  align-items: center;
  gap: var(--s3);
  height: 32px;
  padding: 0 var(--s6);
  border-radius: var(--r6);
  background: var(--chip);
  font-size: var(--t4);
  font-weight: 600;
  color: var(--ink3);
}
.k-rtool:hover { color: var(--accent); }
.k-rtool svg { width: 15px; height: 15px; }


/* The grid fills whatever the pane gives it — a wider window is more listings
   per row and not wider cards (S1's first departure). The gap is the widest
   step on the scale because nothing else holds the cards apart now that they
   have no frame (KODU-102). */
.k-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(236px, 1fr)); gap: var(--s11); }

/* A card and its heart are siblings, because a form inside a link is invalid
   markup and a browser repairs it by throwing one of the two away. The wrapper
   is what lets the heart sit over the picture anyway. Not the layer's `row`:
   stretch is what makes a whole row of cards one height. */
/* On the picture, in the corner the badges are not in — where every listing
   site has put it since there were listing sites. It was pinned 92px down,
   which was the picture's height written out as a number, and it slid onto the
   address the moment the picture grew. */
.k-cardw .k-heartf { position: absolute; top: 10px; right: 10px; }

/* A card has no edge (KODU-102): the picture and the words stand on the
   surface, and the pointer lifts the surface rather than lighting a frame —
   to white in the light scheme (2026-09-07: *"hoverdades list item läheb
   full white"*), a step up in the dark. */
.k-card2:hover { background: var(--k-card-hover); }

/* The listing the reader just came back from (KODU-30): the arrow's fragment
   lands on its own card and `:target` lights it — a little lift — so the eye
   finds where it left off without reading the page again; a row is the tier's, washed. */
.k-cardw:target .k-card2 {
  box-shadow: var(--shadow);
}

/* The heart itself: a round button on a card, and a labelled pill on a
   listing page. Filled means saved — the shape is the same either way, which
   is what makes the two states one control rather than two icons. */
.k-heart { width: 30px; height: 30px; border-radius: 50%; }
.k-heart:hover { color: var(--accent); border-color: var(--accent); }
.k-heart svg { width: 15px; height: 15px; }
.k-heart.is-on { color: var(--accent); border-color: var(--accent); }
.k-heart.is-on svg { fill: currentColor; }
/* A listing's own colour, until there are photographs (KODU-10). The hue comes
   from the markup; the two tokens above decide everything else about it, and
   nothing here names a shade. */
/* A ratio and not a height: the card is as wide as the grid gives it, and a
   picture that is 128px tall at every width is a letterbox on a wide screen
   and a stamp on a narrow one. Three by two is what a property photograph is
   shot in, so the slot is the right shape for the day there are photographs
   in it (KODU-10). */
.k-thumb {
  --hue: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 3 / 2;
  background: oklch(var(--k-thumb-bg) var(--hue));
  color: oklch(var(--k-thumb-fg) var(--hue));
}
.k-thumb svg { width: 34px; height: 34px; }
/* A photograph fills the same slot, and the tint stays behind it: a picture
   that has not arrived yet is the listing's own colour, not a white hole. */
.k-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The price is what a card is scanned by, so it leads and it is the largest
   thing on it. The unit is a unit: it belongs to the number and is not a
   second fact competing with it. The two sizes stay here because each surface
   overrides them below, and a number wants its own leading and not the
   ladder's. */
.k-pnum { font-size: var(--t8); letter-spacing: -0.01em; }
.k-punit { font-size: var(--t5); }

/* Where the card's chips stand on its picture (the tier's `ui::chip`, UIC-43). */
.k-badges { position: absolute; top: 10px; left: 10px; display: flex; }


/* ── the pager ───────────────────────────────────────────────────────────── */

.k-step {
  width: 34px;
  height: 34px;
  /* The pager's arrows are the same family as the back button (KODU-51). */
  border-radius: 50%;
}
.k-step svg { width: 14px; height: 14px; }
.k-step:hover { border-color: var(--accent); color: var(--accent); }
.k-step.is-spent { color: var(--faint); border-color: var(--hair); }

/* ── My account (SPEC 6) ─────────────────────────────────────────────────── */

.k-atitle { margin-right: auto; }

/* The profile's own doors across the top of every profile screen (KODU-73):
   pills in a row, the one the reader is on filled with the accent. The rail's
   fold lists the same three; this is them again where the profile is open. */
.k-ptab {
  display: inline-flex;
  align-items: center;
  height: 34px;
  padding: 0 var(--s6);
  border: 1px solid var(--line2);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink2);
  font-size: var(--t5);
  font-weight: 600;
  white-space: nowrap;
}
.k-ptab:hover { border-color: var(--accent); color: var(--accent); }
.k-ptab.is-here { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

/* A month's lines on the invoices screen: what, what it cost, what a code
   took off, what is owed — the numbers in the mono face, right-aligned. */
.k-invline { padding: var(--s3) 0; border-top: 1px solid var(--line); }
.k-invline:first-of-type { border-top: 0; }
.k-invwhat { flex: 1 1 200px; }
.k-invlist { text-decoration: line-through; }
.k-invpay { min-width: 84px; text-align: right; }

/* A person's face is the tier's `ui::face` (UIC-43): round, on their hue, the same on every door. */
/* A bureau's logo (KODU-80): the picture wide, at its own proportions, on
   the portal's paper — a wordmark set in black draws on white in both
   schemes, which is what the paper token is for. Never a circle.

   **And no corner of ours** (2026-09-06): the rounding was this portal
   drawing a card behind somebody else's mark, and a wordmark with our radius
   cut into its own square corners is not their logo any more. The paper stays
   — a black wordmark needs white under it in the dark scheme — and it is the
   plain rectangle the mark was drawn on. */
.k-logo {
  display: inline-flex;
  align-items: center;
  flex: none;
  height: 34px;
  max-width: 180px;
  padding: var(--s2) var(--s3);
  background: var(--k-paper);
}
.k-logo.is-big { height: 48px; max-width: 260px; padding: var(--s3) var(--s4); }
.k-logopic { display: block; height: 100%; width: auto; max-width: 100%; object-fit: contain; }
/* The settings screen's face is the control for changing it (SPEC 3.2): the
   tier's portrait face, the file input laid invisibly over the whole of it so
   a press anywhere on the face opens the picker with no script at all, a
   camera showing through on hover, and a ring while a file is being dragged
   over it. `is-busy` is the moment between choosing and the page coming
   back with the new picture. */
.k-facefile {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}
.k-faceveil {
  position: absolute;
  inset: 0;
  border-radius: var(--r-pill);
  background: var(--k-light-veil);
  color: var(--k-light-fg);
  opacity: 0;
  transition: opacity .15s ease;
  pointer-events: none;
}
.k-faceveil svg { width: 28px; height: 28px; }
.k-facedrop:hover .k-faceveil,
.k-facedrop:focus-within .k-faceveil,
.k-photorow.is-over .k-faceveil,
.k-photorow.is-busy .k-faceveil { opacity: 1; }
.k-photorow.is-over .k-facedrop { outline: var(--s1) solid var(--accent); outline-offset: var(--s1); }
.k-photorow.is-busy .k-facepill { visibility: hidden; }


/* The operator's word (KODU-107, KODU-108): the refusal's reason opens under
   its press, over the row rather than pushing it down, and the press itself
   wears the danger the word carries. */
.k-deny > summary { list-style: none; cursor: pointer; color: var(--danger); border-color: var(--danger); }
.k-deny > summary:hover { background: var(--danger-soft); }
.k-deny > summary::-webkit-details-marker { display: none; }
/* The one surface on the queue: a popover over the row, not a box under it
   (KODU-110), so its ground and edge are the sheet's and not a utility's. */
.k-denyform {
  position: absolute; right: 0; z-index: 5;
  top: calc(100% + var(--s3));
  width: min(420px, calc(100vw - var(--s11)));
  padding: var(--s6);
  border: 1px solid var(--line2);
  border-radius: var(--r6);
  background: var(--surface);
  box-shadow: var(--shadow);
}
/* The word over a waiting listing's own page: a line across the top, under
   a rule, the way the editor's bar stands. */
.k-reviewbar { margin-bottom: var(--s8); padding-block: var(--s5); border-bottom: 1px solid var(--line2); }

/* ── the editor's manner, on every screen that manages something (KODU-110) ──
   Lisa kuulutus's column of numbered sections, without the pictures beside
   it: `.k-editor.is-one` is the same grid with one column, and what stands
   in it is `.k-edsec` from the editor. A head over the sections, a form that
   holds its sections without being a box round them, rows under hairlines,
   and figures in a strip — none of it a card. */
.k-editor.is-one { grid-template-columns: minmax(0, 1fr); }
.k-sheetform { display: contents; }
.k-sheethead {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s6) var(--s8);
  padding-bottom: clamp(var(--s8), 2vw, var(--s10));
  border-bottom: 1px solid var(--line2);
}
.k-lines > * { padding-block: var(--s6); border-top: 1px solid var(--line); }
.k-lines > :first-child { padding-top: 0; border-top: 0; }
.k-lines > :last-child { padding-bottom: 0; }
.k-figures { display: flex; flex-wrap: wrap; gap: var(--s6) var(--s11); }
.k-figures > * + * { padding-left: var(--s11); border-left: 1px solid var(--line); }

/* The contexts this login can act as (CTX-15): rows, the one being acted in
   marked down its leading edge. CTX-12: the accent paints that edge, and an
   agency's is its own slot from the CTX-11 table. The person context has no
   slot and takes --accent, because being yourself is the default state. */
.k-ctx:hover { color: var(--accent); }
.k-ctx.is-on { padding-left: var(--s5); box-shadow: inset 3px 0 0 var(--accent); }
.k-ctx.is-on[data-slot="1"] { box-shadow: inset 3px 0 0 var(--k-accent-1); }
.k-ctx.is-on[data-slot="2"] { box-shadow: inset 3px 0 0 var(--k-accent-2); }
.k-ctx.is-on[data-slot="3"] { box-shadow: inset 3px 0 0 var(--k-accent-3); }
.k-ctx.is-on[data-slot="4"] { box-shadow: inset 3px 0 0 var(--k-accent-4); }
.k-ctx.is-on[data-slot="5"] { box-shadow: inset 3px 0 0 var(--k-accent-5); }
.k-ctx.is-on[data-slot="6"] { box-shadow: inset 3px 0 0 var(--k-accent-6); }


/* ── the listing page (SPEC 2.2) ─────────────────────────────────────────── */

/* Its head — the ring, the address, the place, Jaga and Lemmik at the column's edge — is the tier's (UIC-43). */
.k-h1.is-page { font-size: clamp(var(--t8), 3vw, var(--t10)); }

/* Two columns that become one on a narrow screen: the picture and the place
   on the left, everything a reader *reads* on the right (KODU-42) — the
   price, the numbers, the words and who is selling, in the order those
   questions get asked. The two widths are measures and not steps. */
.k-lmain { flex: 1 1 380px; }
.k-lside { flex: 1 1 360px; }
/* And air between them (2026-09-07): the pictures, the reading and the map
   stood sixteen apart — *"picture and details and map are really stuck
   together"* — which read as one block once the frames came off (KODU-102).
   The two columns are a gutter apart, and the pictures' own stack — gallery,
   plan, tour, map — the widest step of the scale. */
.k-lbody.row { gap: var(--s11) clamp(var(--s11), 3vw, var(--s13)); }
.k-lbody > .k-lmain { gap: var(--s11); }

/* And on a wide screen the reading column keeps pace with the pictures
   (2026-09-06). Scrolling through a gallery of twenty photographs used to
   leave the price, the facts and the way to reach the seller somewhere above
   the top of the window — the reader is looking at the flat and the answer to
   "how much, and who do I call" has scrolled off.

   `.start` on the row is what makes this possible at all: `align-items:
   flex-start` gives the column its own content height rather than the row's,
   and a sticky box the height of its row has nowhere to travel.

   Never a scroller of its own (2026-09-09). It was capped at the window and
   scrollable, with `overscroll-behavior: contain`, because the description
   lives in here and is as long as the seller made it — and a wheel over the
   description then moved the description and never the page: *"mouse scroll
   does not work for full page in kuulutus when i am on details description
   on the right side"*. The page is the one thing that scrolls, wherever the
   pointer is. A column taller than the window sticks by its *foot* instead
   of its head (`is-tall`, stamped by the column's own script, because a
   sheet cannot know a column's height): it travels with the page until the
   seller's card at the bottom is in view, and stays there — so nothing in it
   is unreachable, and nothing in it catches the wheel. */
@media (min-width: 861px) {
  .k-lside {
    position: sticky;
    top: var(--s6);
    align-self: flex-start;
  }
  .k-lside.is-tall { top: auto; bottom: var(--s6); }
}

/* A hand on a touch screen scrolls the page by dragging, and a column that
   catches the drag is a page that will not move. Nothing sticks there. */
@media (hover: none) {
  .k-lside { position: static; }
}

/* Where it stands, under the photographs (KODU-42). Tall enough to read the
   block it is in, and the component's own zoom is the only control on it. */
.k-onehouse {
  position: relative;
  height: clamp(240px, 30vw, 360px);
  overflow: hidden;
}
.k-onehouse .mp-page { position: absolute; }
.k-onehouse .kp-pin { cursor: default; }

.k-hero-shot .k-thumb { height: clamp(220px, 32vw, 340px); }
.k-hero-shot .k-thumb svg { width: 54px; height: 54px; }

/* The gallery (KODU-69) is the tier's now (UIC-20): the strip, the arrows,
   the count, the thumbnails and the full-screen view are `.ui-gal-*` and
   `.ui-light*` in the tier's own sheet, the plans on paper its `is-paper`.
   What is left here is the tint a listing with no photograph stands in. */

/* The 3D tour (KODU-77): the provider's own player under the gallery, at the
   gallery's proportions, fetched when the reader reaches it. The one frame of
   anybody else's on the page, because the walk through the flat is what a
   renter came for. */
.k-tour {
  width: 100%;
  aspect-ratio: 3 / 2;
  max-height: 480px;
  background: var(--chip);
}
.k-tour iframe { display: block; width: 100%; height: 100%; border: 0; }

/* The reading column is one flow (KODU-102): the price, the facts, the words
   and who is selling stand on the page with a rule between them, not in
   four boxes. */
.k-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: var(--s7);
  padding: var(--s8) 0;
  border-top: 1px solid var(--line);
}

.k-prose {
  margin: 0;
  max-width: var(--k-prose);
  font-size: var(--t5);
  line-height: 1.65;
  color: var(--ink2);
  text-wrap: pretty;
  white-space: pre-line;
}

.k-bigprice .k-pnum { font-size: var(--t9); }
.k-bigprice .k-punit { font-size: var(--t5); }

/* The partner's own front door under the source button (KODU-85): a link,
   and dressed as one rather than as a line of grey text. */
.k-agent a.k-cmeta { color: var(--ink3); text-decoration: underline; text-underline-offset: 3px; }
.k-agent a.k-cmeta:hover { color: var(--accent); }

/* The photograph rides over the initials (SPEC 3.2): a file that fails to
   load leaves the face this screen always had, never a broken image. */
.k-facepic {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 33%;
  border-radius: inherit;
}

/* The one primary action that is not in a form's footer. */

/* ── Lisa kuulutus (SPEC 4.2) ────────────────────────────────────────────── */

/* The editor (KODU-81, KODU-82, 2026-09-04) wears the front page's manner
   (SITE-3): the ground is the paper, a light serif for the one heading and
   for each section's name, small tracked labels in the sans, thin rules
   where cards stood, pills for every press and every chip, and the fields
   as hairline boxes with the label floating inside. Two columns on a wide
   window — the form, and beside it the pictures — one under the other
   below 1 100px. The form itself is not a box: `display: contents` hands its
   two children to the grid, which is what lets the staging column stand
   inside the form on a first publish (nothing in it is a form) and outside
   it on an edit (everything in it is). */
.k-editor {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 380px);
  gap: var(--s10) clamp(var(--s10), 4vw, 64px);
  align-items: start;
  width: 100%;
  max-width: var(--k-page);
  margin-inline: auto;
}
.k-publish { display: contents; }
/* Three items on the grid: the sections, the foot under them, and the
   pictures down the second column beside both — whichever side of the
   `</form>` they stand on. */
.k-edmain { display: flex; flex-direction: column; min-width: 0; grid-column: 1; order: 1; }
.k-pfoot { grid-column: 1; order: 3; }
.k-editor .k-pictures { grid-column: 2; grid-row: 1 / span 2; order: 2; }
.k-edmain > .k-problem { margin-bottom: var(--s9); }
/* A section: a rule over it, its number and name down the left in the
   serif, its controls to the right. Three fields share a line in the
   building's facts, so the row basis is a third of the body. */
.k-edsec {
  display: grid;
  grid-template-columns: minmax(0, 200px) minmax(0, 1fr);
  gap: var(--s8) clamp(var(--s10), 3vw, var(--s13));
  padding-block: clamp(var(--s10), 2.6vw, var(--s12));
  border-top: 1px solid var(--line2);
}
.k-edmain > .k-edsec:first-of-type { padding-top: var(--s4); border-top: 0; }
.k-edsec .k-row > .ui-field { flex: 1 1 160px; }
.k-edno {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  margin: 0;
  font-family: var(--serif);
  font-size: var(--t8);
  font-weight: 400;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--ink);
  text-wrap: balance;
}
.k-edn {
  font-family: var(--mono);
  font-size: var(--t3);
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--mute);
}
/* The bar over the editor: the way back, what the screen is, the listing's
   own address as the heading, its state, its page and the one press — level
   across the editor's width, sticky at the top of the pane so the press is
   never a scroll away, and standing still on a phone, where the foot's
   press is one flick away. */
.k-edbar {
  position: sticky;
  top: calc(-1 * clamp(var(--s9), 2.6vw, var(--s12)));
  z-index: 30;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s5) var(--s6);
  width: 100%;
  max-width: var(--k-page);
  margin-inline: auto;
  padding: var(--s6) 0 var(--s8);
  border-bottom: 1px solid var(--line2);
  background: var(--page);
}
.k-edtitle { display: flex; flex-direction: column; gap: var(--s2); flex: 1 1 320px; min-width: 0; }
.k-edkick { margin: 0; font-size: var(--t4); font-weight: 500; letter-spacing: 0.06em; color: var(--mute); }
.k-edh1 {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(var(--t9), 2.4vw, var(--t11));
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.1;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.k-edmeta:empty { display: none; }
.k-edbar .ui-submit, .k-editor .ui-submit { margin: 0; }
/* A field is where the address's suggestions hang from. */
.k-editor .ui-field { position: relative; }
.k-editor .k-hints { top: calc(100% + 4px); }
/* The chips are the tier's pills, the one that is held its wash (UIC-43). */
.k-editor .k-group { gap: var(--s4); }
.k-editor .k-fold > summary {
  font-family: var(--sans);
  font-size: var(--t4);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: none;
  color: var(--mute);
}
.k-editor .k-chips { gap: var(--s3); }
.k-editor .k-regcard { padding: var(--s6) 0 0; border-top: 1px solid var(--line); background: transparent; }
/* The translations fold: a rule, its name, and a chevron — a row that
   opens, not a box that looks like a select. */
.k-editor .k-fold { padding: 0; border-top: 1px solid var(--line); }
.k-editor .k-fold[open] { display: flex; flex-direction: column; gap: var(--s6); padding-bottom: var(--s6); }
.k-editor .k-fold > summary {
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s7) 0;
  list-style: none;
  cursor: pointer;
  color: var(--ink);
}
.k-editor .k-fold > summary:hover { color: var(--accent); }
.k-editor .k-fold > summary::-webkit-details-marker { display: none; }
.k-editor .k-fold > summary::after {
  content: "";
  width: 6px;
  height: 6px;
  border: solid currentColor;
  border-width: 0 1.5px 1.5px 0;
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.15s ease;
}
.k-editor .k-fold[open] > summary::after { transform: translateY(2px) rotate(225deg); }
/* What went wrong, said where it went wrong: amber, since nothing was lost. */
.k-problem {
  margin: 0;
  padding: var(--s5) var(--s7);
  border-radius: var(--r5);
  background: var(--warn-bg);
  color: var(--warn-ink);
  font-size: var(--t5);
  text-wrap: pretty;
}
@media (max-width: 1100px) {
  .k-editor { grid-template-columns: minmax(0, 1fr); }
  .k-editor .k-pictures { grid-column: 1; grid-row: auto; padding-top: var(--s8); border-top: 1px solid var(--line2); }
}
/* The dock beside the page takes its 420px column from the editor's room,
   and a media query cannot ask about that (KODU-99): with the robot's dock
   open the pictures go under the sections up to 1700 — the 1100 the query
   alone stacks at, plus the dock and the rail — or the staging column
   stands on top of the fields it was drawn beside. */
@media (max-width: 1700px) {
  html[data-dock="half"] .k-editor { grid-template-columns: minmax(0, 1fr); }
  html[data-dock="half"] .k-editor .k-pictures { grid-column: 1; grid-row: auto; padding-top: var(--s8); border-top: 1px solid var(--line2); }
}
@media (max-width: 760px) {
  .k-edsec { grid-template-columns: minmax(0, 1fr); gap: var(--s6); }
  .k-edbar { position: static; align-items: center; }
  .k-edbar .ui-submit { flex: 1 1 auto; }
}

/* A field is a label over a control and nothing else.
   `flex: 1 1 220px` used to be here, and 220px is a *basis* — which in a
   column is a **height**. Every field that was not inside a row (the address,
   the place, the description, both boxes on the sign-in card) therefore asked
   for 220px of it, and the publish form came out with a hand's width of empty
   page under half its controls. The basis belongs to the fields that share a
   line, so it is written where that is true. */
/* What the address box found (KODU-40). It hangs under the very box it
   belongs to: the script puts the list inside the field, the field is the
   measure, and `top: 100%` is what actually hangs it — an absolutely
   positioned child of a flex column takes its static position at the
   column's *top*, so without the `top` the list stood over the label and
   the box it was suggesting for. */
.k-group .ui-field { position: relative; }
.k-hints {
  z-index: 40;
  top: 100%;
  left: 0;
  right: 0;
  max-height: 260px;
}
.k-hint-row:last-child { border-bottom: 0; }
.k-hint-row:hover, .k-hint-row.is-hot { background: var(--chip); color: var(--ink); }

.k-row > .ui-field { flex: 1 1 220px; min-width: 0; }

.k-slot {
  width: 110px;
  height: 82px;
  border: 1.5px dashed var(--line2);
}
.k-slot svg { width: 18px; height: 18px; }

/* What the register knows about the picked building (KODU-67): the resolved
   ID-address as one quiet line under the box, and the mirror's facts as a
   card — read-only, because the fields above are the seller's. */
.k-regfacts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s3) var(--s7);
  margin: 0;
}
.k-regrow dt { color: var(--mute); }
.k-regrow dd { margin: 0; font-weight: 600; color: var(--ink); text-align: right; }
@media (max-width: 560px) {
  .k-regfacts { grid-template-columns: 1fr; }
}

/* ── the payment card (BILL-4) ───────────────────────────────────────────── */

.k-payrows { flex: 1 1 260px; }
.k-payrow .ui-label { width: 90px; flex: none; margin: 0; }
/* An IBAN is one word twenty characters long, and a column 260px wide is
   narrower than it. */
.k-payv { word-break: break-all; }

/* ── Turuülevaade (SPEC 7) ───────────────────────────────────────────────── */

/* In the editor's manner (KODU-112): the sheet's own measure, the counties
   as rows under hairlines (`.k-lines`) and no box round them — a table of
   fifteen counties stretched over a whole desk is a row somebody has to
   track with a finger. A row lights its name on hover, the way a context
   row does, rather than painting a ground under itself. */
.k-mkrow:hover .k-mkname { color: var(--accent); }
.k-mkname { flex: 1 1 140px; font-size: var(--t5); font-weight: 600; color: var(--ink2); }
.k-mkhead .k-mkname,
.k-mkhead .k-mkn,
.k-mkhead .k-mksqm,
.k-mkhead .k-mkd {
  font-family: var(--mono);
  font-size: var(--t3);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--mute);
}
.k-mkn { width: 76px; flex: none; text-align: right; font-family: var(--mono); font-size: var(--t4); color: var(--ink3); }
.k-mkbar { flex: 2 1 120px; min-width: 40px; height: 6px; border-radius: var(--r1); background: var(--chip); overflow: hidden; }
.k-mkfill {
  /* How far along this county is, as a percentage of the dearest. The value
     comes from the element because it is a fact about the county; the default
     is here so a bar whose markup forgot is empty rather than absent. */
  --of: 0;
  display: block;
  width: calc(var(--of) * 1%);
  height: 100%;
  border-radius: var(--r1);
  background: var(--accent);
}
.k-mksqm { width: 110px; flex: none; text-align: right; font-family: var(--mono); font-size: var(--t4); color: var(--ink); }
.k-mkd { width: 56px; flex: none; text-align: right; font-family: var(--mono); font-size: var(--t4); color: var(--faint); }
@container (max-width: 640px) {
  /* A phone holds a county, its count and its price: the bar and the year's
     change are the desk's, and with them the row ran off the glass. */
  .k-mkbar, .k-mkd { display: none; }
  /* Three figures never share a phone's row: two a row, and no rule left
     standing where one wrapped. */
  .k-figures { display: grid; grid-template-columns: 1fr 1fr; }
  .k-figures > * + * { padding-left: 0; border-left: 0; }
}

/* The manual, the Terms, the policies and the roots are the tier's reading page (UIC-41, KDOC-12). */

/* ── the popular places ──────────────────────────────────────────────────── */

.k-pop {
  padding: var(--s3) var(--s6);
  border: 1px solid var(--line);
  border-radius: var(--r5);
  background: var(--surface);
  font-size: var(--t5);
  color: var(--ink2);
}
.k-pop:hover { border-color: var(--accent); color: var(--accent); }

/* ── signing in (AUTH-3) ─────────────────────────────────────────────────── */

/* `.hidden` is the utility layer's now — the same declaration stood in three
   sheets, so which one a screen got depended on who was serving it. */

/* The measure, which is not a step: a sign-in is the narrowest screen the
   portal has because there is one thing to do on it. How the column inside it
   is spaced is the markup's (UI-42). */
.k-signin {
  margin: auto;
  width: min(420px, 100%);
  padding: clamp(var(--s4), 3vh, var(--s12)) 0;
}

/* The two ways in, each said as itself (KODU-52). Both screens' arrangement is
   now steps in their own markup (UI-42); what is left here is the one thing a
   utility cannot draw — the rule either side of the word between them. */
.k-or::before, .k-or::after { content: ""; flex: 1; height: 1px; background: var(--hair); }

/* ── the map (KODU-17) ───────────────────────────────────────────────────── */

/* The map is the maps door's own component (MAPS-17): `.mp-page` and
   everything under it is `maps::STYLE`, served with this sheet, so the two
   maps are one drawing rather than two that happen to be of Estonia. What is
   here is only what stands *on* it — the pins, and what a pin opens.

   `.mp-page` is `position: fixed` on the door where the map is the whole
   page. Inside the portal's frame it is the pane instead, so the one rule
   that would take it out of the frame is the one rule overridden.

   It crosses the pane's padding on all four sides, so it gives back the
   measure's `width: 100%` too: kept, the margins only moved a content-wide
   map one gutter left and it stopped two gutters short of the right edge,
   with the page showing through (2026-09-29). */
.k-mapscreen {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  width: auto;
  max-width: none;
  margin: calc(clamp(var(--s9), 2.6vw, var(--s12)) * -1) calc(var(--k-gutter) * -1) calc(clamp(var(--s10), 3vw, var(--s13)) * -1);
}
/* Nor does the pane keep a scrollbar's room beside it: the map is the whole
   pane, and a gutter held for a scrollbar that is not there is the same strip
   of page down the right edge, fifteen points of it. */
.k-pane:has(> .k-mapscreen) { scrollbar-gutter: auto; }
.k-mapscreen .mp-page { position: absolute; }
/* The developer lines — the level and corners, the ground and its frame
   rate — are not hidden here by the sheet any more: the server draws them
   only for a superadmin and for nobody else (MAPS-29), so what is on
   screen is what was sent. */
/* ⤢ means the whole screen on this door too (KODU-49). */
.k-mapscreen .mp-all { display: none; }
.k-mapscreen.is-full {
  position: fixed;
  inset: 0;
  z-index: 400;
  margin: 0;
  background: var(--page);
}

/* A mark on the map (KODU-57): a pill in the map's own material — the
   surface the floating controls wear, a hairline round it, the number in the
   house mono — so it reads as part of the drawing, the way the search box
   and the zoom do, rather than as a coin dropped on it. The accent arrives
   only when the pointer does. */
/* Except on the listing's own map, where the pin is a drawing (KODU-42). */
.k-onehouse .kp-pin { cursor: default; }
/* Bare numbers on the drawing (KODU-60): the rect is the pointer's target
   and nothing else — no surface, no hairline — and the number reads over
   the map through its halo, the way the place names do. The surface comes
   back only under the pointer, so hover still answers before a press. */
/* A count or a price is a pill (KODU-104): the surface with a quiet edge,
   so it reads over any land, water or road it stands on — bare figures on
   the ground were the thing nobody could see. Under the pointer the edge is
   the accent and the ground the chip, as a chosen chip's are. */
.kp-pill {
  fill: var(--surface);
  stroke: var(--line);
  stroke-width: 1;
  pointer-events: all;
  transition: fill .18s ease, stroke .18s ease;
}
.kp-pin:hover .kp-pill, .kp-pin.is-hot .kp-pill { stroke: var(--accent); stroke-width: 1.5; fill: var(--chip); }
.kp-n {
  fill: var(--ink);
  font-family: var(--mono);
  font-weight: 600;
  text-anchor: middle;
  pointer-events: none;
  paint-order: stroke;
  stroke: var(--mp-land);
  stroke-width: 2.4px;
  stroke-linejoin: round;
  transition: fill .18s ease;
}
/* How many is quieter than how much: a count pill's number steps back a
   shade, so a price among counts reads as the answer it is. */
.kp-pill.is-count + .kp-n { fill: var(--ink2); font-weight: 500; }
/* A district's name over its district (KODU-60): the basemap's own voice —
   information under everything, answering no pointer. */
.kp-district {
  font-family: var(--sans);
  font-size: var(--t3);
  letter-spacing: 0.04em;
  text-anchor: middle;
  fill: var(--mute);
  paint-order: stroke;
  stroke: var(--mp-land);
  stroke-width: 2px;
  stroke-linejoin: round;
  pointer-events: none;
  user-select: none;
}
.kp-pin:hover .kp-n { fill: var(--accent-down); }
/* The browser's own focus rectangle is a black box over the drawing; the
   focus is said on the mark itself instead, and only for a keyboard —
   a click's focus already has the pointer on it. */
.kp-pin:focus { outline: none; }
.kp-pin:focus-visible .kp-pill { stroke: var(--ink); stroke-width: 2; }
/* The label's whole box answers the pointer, not just the glyphs' own ink. */
.kp-hit { fill: none; pointer-events: all; }
/* A house that has something for sale (KODU-57): the footprint itself,
   tinted — the mark is part of the map because it *is* the map's own shape.
   The basemap draws the same outline underneath in `--mp-house`; this one
   sits over it in the soft accent with the accent's own edge. */
.kp-house {
  fill: var(--accent-soft);
  stroke: var(--accent);
  stroke-width: 1;
  stroke-linejoin: round;
  transition: fill .18s ease, stroke-width .18s ease;
}
.kp-pin.is-house:hover .kp-house, .kp-pin.is-house:focus-visible .kp-house {
  fill: color-mix(in oklab, var(--accent) 32%, var(--accent-soft));
  stroke-width: 2;
}
/* A named place (KODU-49): the name with the count under it, the site's own
   type at the site's own weight, haloed so it reads over whatever the map
   drew beneath. The basemap's label for the same shape bows out — one name,
   said once. Pointing at the name warms the land, and the other way round:
   they are one thing. */
.k-named .mp-name { display: none; }
/* The searched place's dot sat under the name the portal draws for it, the dot over the words. */
.k-named .mp-dot { display: none; }
.k-named .mp-land { cursor: pointer; }
.kp-place, .kp-count {
  text-anchor: middle;
  /* The halo is legibility, not weight: three pixels of it read as a bold
     the site does not use; one and a half vanished on the darker ground
     (KODU-104), so it is a little over two. */
  paint-order: stroke;
  stroke: var(--mp-land);
  stroke-width: 2.2px;
  stroke-linejoin: round;
  font-family: var(--sans);
  font-weight: 400;
  fill: var(--ink);
  transition: fill .18s ease, stroke .18s ease;
}
/* The warmth is the rail's own hover — the chip tone, eased in, because the
   pointer crossing a border toggles the shape off and on again and an
   instant fill read as a flicker. */
.mp-land { transition: fill .18s ease; }
.mp-land.is-hot { fill: var(--chip); }
.kp-pin.is-named.is-hot .kp-place,
.kp-pin.is-named.is-hot .kp-count { stroke: var(--chip); }
.kp-pin.is-named.is-hot .kp-place,
.kp-pin.is-named.is-hot .kp-count,
.kp-pin.is-named:focus-visible .kp-place { fill: var(--accent-down); }
/* The map keeps a map's own colours (MAPS-31, 2026-09-08: *"kodu map in
   light theme is really hard to see … use more standard map colors"*). It
   wore the site's palette — land as the page, water mixed onto it — and on
   the light page that was near-white land against a hard grey-blue sea with
   grey scratches for roads. The component's palette answers both schemes
   now, and the portal overrides nothing of it; only the furniture below
   is the portal's. */
/* In the dark the furniture was a ghost: surface on land measured nine
   steps of 255 apart and the hairlines vanished with it. The map's own scope
   lifts both a notch — the cards and pills ride the same tokens, so the whole
   deck rises together. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .k-mapview .mp-page,
  :root:not([data-theme="light"]) .k-mapscreen .mp-page,
  :root:not([data-theme="light"]) .k-pickmap .mp-page,
  :root:not([data-theme="light"]) .k-onehouse .mp-page {
    --surface: color-mix(in oklab, var(--ink) 8%, var(--page));
    --line: color-mix(in oklab, var(--ink) 24%, var(--page));
  }
}
/* A picked area on the picker's map (KODU-41). The map is a control there,
   and a control has to show what it is set to — the pill turns accent, the
   way a chosen chip does. */
.kp-pin.is-picked .kp-pill { fill: var(--accent); stroke: var(--accent-down); }
.kp-pin.is-picked .kp-n { fill: var(--accent-ink); }
/* And the land is pressable there, so it says so under the pointer. */
.k-pickmap .mp-land { cursor: pointer; }
/* The drawing wears the choice (KODU-63): a picked area's land keeps a
   standing wash, beside the fading is-on the search map plays. */
.k-pickmap .mp-land.is-picked { fill: var(--accent-soft); stroke: var(--accent); }
.k-pickmap .mp-map { cursor: default; }
.k-pickmap .mp-map.is-dragging { cursor: grabbing; }

/* The switch to the other view of the same search, floating under the
   standalone map's box (KODU-32). It rides in the box's own hits panel, and
   that panel is as wide as the box — so the two words stood at the left end
   of a bar of nothing, inside a second frame of their own (2026-09-07: *"the
   nimekiri kaart thingy is broken"*). The panel hugs the switch now, and the
   switch gives up its own edge: one small floating pill, two words. */
.mp-mine { display: flex; align-items: center; gap: var(--s4); width: max-content; padding: var(--s1); }
.mp-mine > nav { border-color: transparent; }

/* The door from the drawing to the rows (KODU-58): what the rectangle holds,
   as a list. Bottom left — the one quiet corner: the zoom keeps the right,
   and the bottom centre is the densest band of a short map, where the door
   was found sitting on the very pills it counted. */
.k-maplist {
  position: absolute;
  left: var(--s7);
  bottom: var(--s7);
  z-index: 4;
  padding: var(--s3) var(--s6);
  border: 1px solid var(--line);
  border-radius: var(--r3);
  background: var(--surface);
  box-shadow: var(--shadow);
  font-size: var(--t4);
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
}
.k-maplist:hover { border-color: var(--accent); color: var(--accent-down); }

/* The map's rectangle, worn as a chip on the list it narrows (KODU-58). */
.k-rectchip {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s1) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--r3);
  background: var(--chip);
  font-size: var(--t4);
  color: var(--ink2);
}
.k-rectchip:hover { border-color: var(--accent); color: var(--ink); }
.k-rectchip span { color: var(--mute); }

/* What a pin un-sums into. `display: flex` beats the `hidden` attribute, so
   the attribute is spelt out — three screens in this tree have been shipped
   with a panel that would not close for exactly this reason. */
/* `left` and `top` are the loader's (`kBeside`): the card stands beside the
   mark it is about, and where that is depends on where the pin is and how
   tall the card came out. The two here are only where it would sit if it were
   ever shown before it has been placed — and never `bottom`, because a box
   with a `top` and a `bottom` and no height is one stretched between them. */
.k-mapcard {
  position: absolute;
  left: 14px;
  top: 14px;
  z-index: 5;
  display: flex;
  flex-direction: column;
  width: min(320px, calc(100% - 28px));
  max-height: 60%;
  border: 1px solid var(--line);
  border-radius: var(--r7);
  background: var(--surface);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.k-maphead2 {
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s5) var(--s4) var(--s5) var(--s7);
  border-bottom: 1px solid var(--hair);
}
.k-maphead2 strong { flex: 1; font-size: var(--t5); font-weight: 600; color: var(--ink); }
.k-mapclose {
  flex: none;
  width: 26px; height: 26px;
  /* And the map card's dismiss (KODU-51). */
  border-radius: 50%;
  font-size: var(--t6);
  color: var(--mute);
  cursor: pointer;
}
.k-mapclose:hover { background: var(--chip); color: var(--ink); }
.k-mapline {
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s3) var(--s4);
  border-radius: var(--r4);
  font-size: var(--t5);
  color: var(--ink2);
}
.k-mapline:hover { background: var(--chip); color: var(--ink); }
/* The row wears the listing's own picture, small — the same thumb every
   card wears, so a reader recognises the flat before opening it (KODU-57). */
.k-mapline .k-thumb { flex: none; width: 56px; height: 40px; border-radius: var(--r4); }
.k-mapline .k-thumb svg { width: 18px; height: 18px; }
.k-mapline-what { flex: 1; display: flex; flex-direction: column; gap: var(--s1); min-width: 0; }
.k-mapline-price { font-family: var(--mono); font-size: var(--t4); font-weight: 600; color: var(--ink); }

/* What a house says when it is pointed at (KODU-43). The same three things a
   card leads with — the price, the picture, the address — because a preview
   that showed something else would be a second way of describing a listing.
   A `<title>` said the address alone, in a tooltip nobody chooses by. */
/* A link where there is one listing to open and a button where there are
   several to un-sum, so the two are one control drawn one way. */
.k-mappeek {
  display: flex;
  align-items: center;
  gap: var(--s5);
  width: 100%;
  padding: var(--s4);
  border: 0;
  border-radius: var(--r6);
  background: none;
  color: var(--ink2);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.k-mappeek:hover { background: var(--chip); }
/* The thumbnail is three by two everywhere it is drawn; here it is small,
   because the card is a preview and not a result. */
.k-mappeek .k-thumb { flex: none; width: 78px; height: 52px; border-radius: var(--r5); }
.k-mappeek .k-thumb svg { width: 22px; height: 22px; }
.k-peekprice {
  font-family: var(--mono);
  font-size: var(--t6);
  font-weight: 600;
  color: var(--ink);
}
.k-peekwhere {
  font-size: var(--t4);
  color: var(--mute);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── the operator line ───────────────────────────────────────────────────── */

/* Inside the pane's scroll now that the rail owns the left column, and pushed
   to the bottom of it by `margin-top: auto` — so it is on the bottom edge of
   a short screen and at the end of a long one, rather than floating under
   three counties. */
/* The drawing is the tier's (UIC-39, `foot.css`); where it stands in the
   pane is the portal's. Three times the room it had (KODU-128, 2026-09-26):
   the foot opens on the questions, and they read as part of the listings
   above them until there is a clear break. */
.k-foot {
  margin-top: auto;
  padding-top: clamp(calc(var(--s10) * 3), 10.5vw, calc(var(--s12) * 3));
}

/* ── narrow ──────────────────────────────────────────────────────────────── */

/* The rail's bar is the tier's and lies down at its width (UIC-38, the frame
   rule above); what is narrow here is the portal's own furniture. */
@media (max-width: 860px) {
  /* A finger picks places too (KODU-63). */
  .k-arow { min-height: 44px; }
  .k-akids > summary.k-afold { width: 44px; height: 44px; }

  /* The filters behind one pill (KODU-64): the sheet is the very panel,
     stood up as a screen, opened and closed by the nameless gate. */
  .k-fbtn {
    display: inline-flex;
    align-items: center;
    gap: var(--s3);
    align-self: flex-start;
    margin: var(--s4) 0;
    padding: var(--s3) var(--s6);
    border: 1px solid var(--line2);
    border-radius: var(--r5);
    background: var(--surface);
    font-size: var(--t5);
    font-weight: 600;
    color: var(--ink2);
    cursor: pointer;
  }
  form.k-panel { display: none; }
  .k-fgate:checked ~ form.k-panel {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 0;
    position: fixed;
    inset: 0;
    z-index: 55;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: var(--surface);
    overflow-y: auto;
  }
  .k-fgate:checked ~ form.k-panel .k-fhead { display: block; position: sticky; top: 0; z-index: 1; background: var(--surface); }
  .k-fgate:checked ~ form.k-panel .k-fstack {
    flex-direction: column;
    align-items: stretch;
    gap: var(--s6);
    padding: var(--s6) var(--gutter);
  }
  .k-fgate:checked ~ form.k-panel .k-ffoot {
    display: flex;
    position: sticky;
    bottom: 0;
    padding: var(--s5) var(--gutter) calc(var(--s5) + env(safe-area-inset-bottom));
    background: var(--surface);
    border-top: 1px solid var(--line);
  }
  .k-fgate:checked ~ form.k-panel .k-ffoot .ui-modal { width: 100%; text-align: center; justify-content: center; }
}

/* Narrower still. */
@media (max-width: 560px) {
  /* One thing to do on the sign-in, and a phone's worth of room to do it
     in (2026-09-01, iPhone mini): the intro tightens so the first field is
     on the first screenful instead of a hand's width of air and prose. */
  .k-signin { padding-top: var(--s4); gap: var(--s6); }
  .k-signin .k-lede { font-size: var(--t5); }
  .k-signin .ui-card { padding: var(--s6); gap: var(--s7); }
  .k-pane { --k-pane-top: var(--s6); }
  .k-tree { columns: auto; }

  /* The picker is the list alone — still the whole screen (KODU-25), just
     one column of it. The cartogram and the list are one control in two
     places (SPEC 1.2) and the second of them is fifteen counties in three
     hundred points, a target nobody hits with a finger. The list picks the
     same places, and the box above it finds them by name. */
  .k-apop { grid-template-columns: minmax(0, 1fr); }
  .k-amap { display: none; }
  .k-atree { border-right: 0; }

  /* The order's pill and the layout's track take a row of their own, the sheet free to open under the pill. */
  .k-rtools { width: 100%; margin-left: 0; }

  /* The map takes what is left of a phone, so the operator line does not eat
     a third of it — it is on every other screen, and this one is a map. */
  .k-mapscreen ~ .k-foot, .k-pane > .k-mapscreen + .k-foot { display: none; }
  /* Nearly the width of the map, which is what tells the loader there is no
     "beside" here and puts it along the bottom instead — so the position
     stays in one place and this rule is about the size only. */
  .k-mapcard { width: calc(100% - 20px); max-height: 45%; }
}

/* Teavita probleemist (ISS-1): the tier's report box (ISS-8), opened from the
   person's own menu (`rail.rs`) and standing by the rail's foot where that
   menu is — clear of the rail, over the portal's own layers. */
.ui-report { z-index: 320; }
/* A phone's bottom edge is the rail's bar (UIC-38), so the box stands above
   it and takes the width — the menu entry is reachable there too, and a tool
   that opened into nothing would be a menu that lies. */
@container (max-width: 640px) {
  .ui-report { left: 12px; right: 12px; bottom: 84px; }
  .ui-report-box { width: auto; }
}

/* ── the 360° pictures (KODU-78) ─────────────────────────────────────────── */

/* The portal's own viewer: a WebGL2 canvas in the gallery's box, turned by
   dragging, the plain picture underneath it for a browser with no script.
   The hint is a pill over the foot of the box until the first turn. */
.k-panobox {
  width: 100%;
  aspect-ratio: 3 / 2;
  max-height: 480px;
  background: var(--chip);
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.k-panobox:active { cursor: grabbing; }
.k-panobox:focus-visible { outline: var(--s1) solid var(--accent); outline-offset: calc(-1 * var(--s1)); }
.k-panoflat img { width: 100%; height: 100%; object-fit: cover; display: block; }
.k-pano.is-live .k-panoflat { display: none; }
.k-panohint {
  position: absolute;
  left: 50%;
  bottom: var(--s4);
  transform: translateX(-50%);
  margin: 0;
  padding: var(--s2) var(--s5);
  border-radius: var(--r7);
  background: var(--k-light-veil);
  color: var(--k-light-fg);
  font-size: var(--t4);
  white-space: nowrap;
  pointer-events: none;
  transition: opacity 0.4s;
}
.k-pano:not(.is-live) .k-panohint, .k-pano.is-moved .k-panohint { opacity: 0; }
.k-pano:not(.is-live) .k-panofull { display: none; }
.k-pano .k-panofull { opacity: 1; }
.k-panothumb img { object-fit: cover; }
/* Full screen is the same box in the dialog, the whole viewport of it. */
.k-panolight .k-panobox {
  width: 100vw;
  height: 100vh;
  aspect-ratio: auto;
  max-height: none;
  border-radius: 0;
}

/* ── the owner's pictures on the edit screen (KODU-78) ───────────────────── */

/* Beside the form (KODU-81, KODU-82): a column with a rule over each kind,
   three tiles to a row, every tile the card's own proportion, and the way
   to add one is a tile too — a dashed slot at the end of the row that is
   the file picker itself, and the drop zone. A standing picture carries its
   ✕ over the corner; a plan and a panorama are contained on paper the way
   the page draws them; a picture on its way up pulses until it is back. */
.k-pictures { display: flex; flex-direction: column; min-width: 0; scroll-margin-top: var(--s8); }
.k-picsec {
  display: flex;
  flex-direction: column;
  gap: var(--s6);
  margin: 0;
  padding-block: var(--s9);
  border-top: 1px solid var(--line2);
  transition: background 0.15s ease;
}
.k-picsec.is-over { background: var(--chip); }
.k-piclabel { margin: 0; font-size: var(--t4); font-weight: 500; letter-spacing: 0.06em; color: var(--mute); }
.k-pictures > .k-problem { margin-bottom: var(--s8); }
.k-pic {
  position: relative;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: var(--chip);
}
.k-pic.is-pending { animation: k-pulse 1.2s ease-in-out infinite; }
@keyframes k-pulse { 50% { opacity: 0.45; } }
.k-picbadge {
  position: absolute;
  left: var(--s3);
  bottom: var(--s3);
  padding: var(--s1) var(--s4);
  border-radius: var(--r-pill);
  background: var(--k-light-veil);
  color: var(--k-light-fg);
  font-size: var(--t2);
  font-weight: 500;
  letter-spacing: 0.06em;
  pointer-events: none;
}
/* Every photograph carries the badge; the first alone shows it, so a tile
   taken off hands the word to the next without a redraw. */
.k-pic.is-photo ~ .k-pic.is-photo .k-picbadge { display: none; }
/* The standing pictures keep their own box for the suites to count in, and
   `contents` lets its tiles sit in the grid beside the add tile. */
.k-picstanding { display: contents; }
.k-pictile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  aspect-ratio: 3 / 2;
  border: 1px dashed var(--line2);
  border-radius: var(--r6);
  color: var(--ink3);
  font-size: var(--t4);
  font-weight: 500;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.k-pictile svg { width: 18px; height: 18px; }
.k-pictile:hover, .k-picpick:focus-within, .k-picsec.is-over .k-pictile {
  border-color: var(--ink);
  background: var(--surface);
  color: var(--ink);
}
/* The tour's press stands under its box, at the right. */
.k-tourrow .ui-field { width: 100%; flex: none; }
.k-pic a, .k-pic img { display: block; width: 100%; height: 100%; }
.k-pic img { object-fit: cover; }
.k-pic.is-plan img, .k-pic.is-pano img { object-fit: contain; background: var(--k-paper); }
.k-pic form, .k-pic > .k-picdrop { position: absolute; top: var(--s2); right: var(--s2); margin: 0; }
/* Off the screen, not out of the document: a keyboard still reaches the
   picker, and the tile — the label itself — lights when it does. */
.k-picpick input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
/* With the script a choice is the press, so the button has nothing to do;
   without it a first publish has no way to stage, and says so instead —
   its empty kinds stand down with their pickers. */
.k-pictures.is-live .k-picsend { display: none; }
.k-pictures.is-live .k-picnojs { display: none; }
.k-pictures:not(.is-live) .k-picadd[data-stage] { display: none; }
.k-pictures[data-staging]:not(.is-live) .k-picsec:has(.k-picadd[data-stage]) { display: none; }
.k-picadd.is-busy .k-picpick { opacity: 0.6; pointer-events: none; }
.k-pictures .ui-submit { flex: 0 0 auto; }
.k-pictures .ui-pill { align-self: flex-start; }
@media (prefers-reduced-motion: reduce) {
  .k-pic.is-pending { animation: none; opacity: 0.6; }
}

/* ── the seller's strip, and the bureau's page (KODU-83) ─────────────────── */

/* Over the similar ones: whose, the name as a link, and "show all" at the
   row's end when there is more than the strip holds. */
.k-sellerhead .ui-label { margin: 0; }
.k-sellerhead .k-link { font-weight: 600; color: var(--ink); }
.k-sellerhead .k-link:hover { color: var(--accent); text-decoration: none; }
.k-sellerhead .ui-pill { margin-left: auto; }
/* The bureau's page: its picture and its name over a rule, then a strip per
   deal, then the people who sell for it as a row of pills — a `stack` with
   no gap of its own, because each section brings its rule. */
/* The bureau's letterhead in the editor's manner (2026-09-06). The publish
   screen has read this way since KODU-81 and is the best-looking page here —
   paper for a ground, a light serif for the headings, thin rules where cards
   would stand, and each section's name down the left with its content to the
   right. This page borrows the shape rather than inventing a third one.

   No card, no border-bottom under the head: the sections below bring their
   own rule, and a rule under the brand *and* over the first section is two
   lines doing one job. */
.k-bhead {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: clamp(var(--s8), 2vw, var(--s10));
  padding-bottom: clamp(var(--s10), 2.6vw, var(--s12));
}
/* The brand is the page's one heading, and it is set like one. */
.k-bhead .k-edh1 { font-size: clamp(var(--t9), 2.6vw, var(--t11)); }
/* The contact reads as rows of a table, not as a paragraph: the label in the
   quiet tracked sans, the answer in the ink beside it, and the columns line
   up down the block. */
.k-reach .k-reachrow { display: grid; grid-template-columns: minmax(0, 150px) minmax(0, 1fr); align-items: baseline; }
@media (max-width: 560px) {
  .k-reach .k-reachrow { grid-template-columns: 1fr; gap: var(--s2); }
}
.k-btitle { display: flex; flex-direction: column; gap: var(--s2); flex: 1 1 260px; min-width: 0; }
/* How to reach them, on the brand's own line (2026-09-06). The header row
   already wraps, so this asks for no breakpoint of its own: beside the name
   where there is room, under it where there is not. No card and no rule
   around it — it is part of the letterhead, not a section standing after. */
.k-bhead .k-reach { flex: 0 1 auto; padding: 0; border: 0; background: none; }
.k-bhead .k-reach .k-reachrows { gap: var(--s3); }
.k-btitle .k-edh1 { white-space: normal; text-wrap: balance; }
.k-bhead .k-edmeta a { color: var(--ink2); }
.k-bhead .k-edmeta a:hover { color: var(--accent); }
.k-broker {
  display: inline-flex;
  align-items: center;
  gap: var(--s5);
  padding: var(--s3) var(--s8) var(--s3) var(--s3);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink);
  transition: color 0.15s ease;
}
.k-broker:hover { color: var(--accent); }
/* A seller on the results, worn where the reader can take it off, the way
   the map's rectangle is (KODU-58). */
.k-sellerchip { margin-left: var(--s2); }
/* The bureau's own site under its name (KODU-85), and how to reach it: a
   rule, the label, and each way as a row — the telephone with its hours
   beside it. The picture on a listing's card leads where the name does. */
.k-bsite { align-self: flex-start; color: var(--accent); font-size: var(--t6); font-weight: 600; }
.k-bsite:hover { text-decoration: underline; }
/* The reader's own two settings in the foot (KODU-98): the language and the
   scheme, each a row of words rather than a fold. A footer has room for the
   name — the rail said `RU` because a ninety-six point column has room for
   two letters — and a row of four that are all visible is one press, where a
   fold is two. The one that is on wears the ink and the rule under it; the
   rest are quiet until pointed at. */
/* The greeting on a first visit (KODU-97): a band across the foot of the
   window, because it is about the reader's arrival and not about the market.
   It goes away for a year the moment they answer it, so it is never
   something anybody has to live with.

   It is the body's second row (see `body` at the head of this sheet) and not
   a sheet laid over the window: the shell gives up the height rather than
   lending it, so the rail's own foot — the reader's face, and on a phone the
   whole bar — stays where it is and stays pressable. Not inside the pane
   either: the pane scrolls, and a notice that scrolled away unanswered would
   be a question asked and then withdrawn. The safe-area inset is for a phone
   whose home bar would otherwise sit on the answers.

   Never more than a share of the window, though: taking its height from the
   shell is only fair while the share is small, and on a narrow screen the
   question, the four answers and the notice wrap into a column. Past that it
   scrolls inside itself and the portal keeps most of the glass. */
/* On a phone the page scrolls as the document (UIC-51), and the question stays at the window's foot while it does. */
.k-greet {
  position: sticky;
  bottom: 0;
  z-index: 25;
  max-height: 45dvh;
  overflow-y: auto;
  padding: var(--s6) clamp(var(--s7), 2vw, var(--s10));
  padding-bottom: calc(var(--s6) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line2);
  background: var(--surface);
}
.k-greetin {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5) clamp(var(--s8), 3vw, var(--s12));
  width: 100%;
  max-width: var(--k-page);
  margin-inline: auto;
}
.k-greetnote { flex: 1 1 320px; font-size: var(--t4); color: var(--ink3); }
.k-greetmore { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

/* ── the blog (KODU-103) ─────────────────────────────────────────────────

   A reading page and nothing else on it: the title in the serif, who wrote
   it and when, the lead, the prose at the reading measure, and the writer
   again at the foot. The prose is a size up from the portal's own text and
   leaded like a book, because a post is read from top to bottom and a
   screen is skimmed. */
.k-blog {
  display: flex;
  flex-direction: column;
  gap: var(--s8);
  width: 100%;
  max-width: var(--prose);
  margin-inline: auto;
  padding-top: clamp(var(--s9), 3vw, var(--s11));
}
.k-blogrow + .k-blogrow { padding-top: var(--s10); border-top: 1px solid var(--hair); }
.k-posttitle {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(var(--t9), 3vw, var(--t11));
  font-weight: 600;
  line-height: var(--lh11);
  letter-spacing: -0.01em;
}
.k-posttitle.is-row { font-size: var(--t9); line-height: var(--lh9); }
.k-postlink { text-decoration: none; }
.k-postlink:hover { color: var(--accent); }
.k-byline { display: flex; align-items: center; gap: var(--s5); margin: 0; font-size: var(--t5); color: var(--ink3); }
.k-portrait { flex: none; width: 44px; height: 44px; border-radius: 50%; object-fit: cover; }
.k-portrait.is-big { width: 96px; height: 96px; }
.k-postbody { font-size: var(--t7); line-height: var(--lh6); padding-top: var(--s5); }
.k-postbody h2 {
  margin: var(--s11) 0 var(--s5);
  font-family: var(--serif);
  font-size: var(--t9);
  font-weight: 600;
  line-height: var(--lh9);
}
.k-postbody p { margin: 0 0 var(--s8); }
.k-postbody ul { margin: 0 0 var(--s8); padding-left: var(--s9); }
.k-postbody li { margin-bottom: var(--s4); }
.k-postbody a { color: var(--accent); text-underline-offset: 3px; }
.k-author {
  display: flex;
  align-items: center;
  gap: var(--s8);
  margin-top: var(--s10);
  padding-top: var(--s10);
  border-top: 1px solid var(--hair);
}

/* The dark phone keeps the house's deeper ground: the portal's own sheet
   restates `--page` in the dark (only the light one moved, 2026-09-07), and
   a restatement served after the tier's phone override would have undone
   it, so the phone's is restated here too. */
@media (prefers-color-scheme: dark) and (max-width: 640px) {
  :root:not([data-theme="light"]) { --page: oklch(0.1 0.004 85); }
}

.k-reachrow dt {
  flex: 0 0 120px;
  font-family: var(--mono);
  font-size: var(--t3);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--mute);
}
.k-reachrow dd { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s3) var(--s5); margin: 0; font-size: var(--t6); }
.k-reachrow dd a { color: var(--ink); font-weight: 600; }
.k-reachrow dd a:hover { color: var(--accent); }

  :root[data-theme="dark"] {
    /* The tint turns dark and its ink turns light; the hue does not move, so a
       listing keeps the colour a reader recognises it by. */
    --k-thumb-bg: 0.245 0.014;
    --k-thumb-fg: 0.46 0.03;

    /* The same four: the full-screen view is dark whichever scheme the page
       is in, because the picture is what is lit. */
    --k-light-bg: oklch(0.13 0.01 85);
    --k-light-fg: oklch(0.98 0.005 85);
    --k-light-veil: oklch(0.1 0.01 85 / 0.5);
    --k-light-line: oklch(0.98 0.005 85 / 0.28);

    /* Paper stays paper. */
    --k-paper: oklch(1 0 0);

    /* The pointer's step up, in the dark: the chip's lightness, since white
       under the pointer is a flash and not a lift. */
    --k-card-hover: oklch(0.25 0.006 85);
    /* The house's own dark ground, restated: only the light one moved. */
    --page: oklch(0.155 0.006 85);

    --k-accent-1: oklch(0.62 0.13 215);
    --k-accent-2: oklch(0.58 0.13 340);
    --k-accent-3: oklch(0.68 0.13 115);
    --k-accent-4: oklch(0.54 0.13 185);
    --k-accent-5: oklch(0.66 0.13 305);
    --k-accent-6: oklch(0.72 0.13 90);
  }
  :root[data-theme="dark"] .k-mapview .mp-page,
  :root[data-theme="dark"] .k-mapscreen .mp-page,
  :root[data-theme="dark"] .k-pickmap .mp-page,
  :root[data-theme="dark"] .k-onehouse .mp-page {
    --surface: color-mix(in oklab, var(--ink) 8%, var(--page));
    --line: color-mix(in oklab, var(--ink) 24%, var(--page));
  }
@media (max-width: 640px) {
  :root[data-theme="dark"] { --page: oklch(0.1 0.004 85); }
}


  /* The map is the page (MAPS-10): everything else floats over it, the way a
     map with a window's worth of world in it has to. */
  /* The map's own palette (MAPS-12). Its own values and not house tokens:
     a console's palette has a surface, a line and an accent, and a map needs
     water, land, and roads that contrast the ground. Drawn on the house
     tokens the whole thing was one near-white sheet with hairlines on it —
     every colour within a few percent of every other, which is a diagram
     and not a map. */
  .mp-page {
    /* The reading every map has taught (MAPS-31): land a warm grey a step
       darker than the page, so the panels float and a white road reads;
       water the soft blue a map's water is; a street white with a grey
       edge, and a trunk road the soft yellow with a firmer one. Not black
       in the dark and not white in the light — a map a step darker than
       the page it sits on, in both. */
    --mp-land: oklch(0.945 0.008 85);
    --mp-edge: oklch(0.860 0.010 85);
    --mp-water: oklch(0.860 0.045 235);
    /* A street is white with a grey edge — the casing is what draws it,
       and at far zooms, where the casing is put away, the road takes the
       casing's grey so the line does not vanish into the ground. */
    --mp-fill-3: oklch(0.990 0.002 85);
    --mp-case-3: oklch(0.850 0.008 85);
    --mp-fill-2: oklch(0.985 0.003 85);
    --mp-case-2: oklch(0.800 0.010 85);
    /* A trunk road: the soft yellow, edged. Quiet enough not to shout over
       the answer, coloured enough to be the road it is. */
    --mp-fill-1: oklch(0.930 0.070 92);
    --mp-case-1: oklch(0.780 0.070 85);
    /* A house is a shade *darker* than the ground, where a road is lighter —
       the two layers then separate without either taking a colour. */
    --mp-house: oklch(0.900 0.012 75);
    --mp-house-edge: oklch(0.820 0.014 75);
    position: fixed; inset: 0; background: var(--mp-water);
  }
  /* Three states (UIC-17): the system's dark unless light was chosen, and
     dark chosen. Lighter than the dark ground — the rule is *separate
     from*, not *darker than* — and the ground itself a dark grey rather
     than the page's near-black, so the sea, the roads and the marks have
     room to differ. */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .mp-page {
      --mp-land: oklch(0.300 0.008 85);
      --mp-edge: oklch(0.380 0.010 85);
      --mp-water: oklch(0.340 0.050 235);
      --mp-fill-3: oklch(0.420 0.006 85);
      --mp-case-3: oklch(0.260 0.006 85);
      --mp-fill-2: oklch(0.480 0.008 85);
      --mp-case-2: oklch(0.270 0.007 85);
      --mp-fill-1: oklch(0.580 0.070 85);
      --mp-case-1: oklch(0.300 0.020 85);
      --mp-house: oklch(0.350 0.008 75);
      --mp-house-edge: oklch(0.430 0.010 75);
    }
  }
  :root[data-theme="dark"] .mp-page {
    --mp-land: oklch(0.300 0.008 85);
    --mp-edge: oklch(0.380 0.010 85);
    --mp-water: oklch(0.340 0.050 235);
    --mp-fill-3: oklch(0.420 0.006 85);
    --mp-case-3: oklch(0.260 0.006 85);
    --mp-fill-2: oklch(0.480 0.008 85);
    --mp-case-2: oklch(0.270 0.007 85);
    --mp-fill-1: oklch(0.580 0.070 85);
    --mp-case-1: oklch(0.300 0.020 85);
    --mp-house: oklch(0.350 0.008 75);
    --mp-house-edge: oklch(0.430 0.010 75);
  }
  .mp-map {
    position: absolute; inset: 0; width: 100%; height: 100%;
    display: block; touch-action: none; cursor: grab;
  }
  .mp-map.is-dragging { cursor: grabbing; }
  /* The GPU ground (MAPS-25): a canvas under the svg, never touched by a
     pointer — everything pressable lives in the svg above it. */
  .mp-gpu {
    position: absolute; inset: 0; width: 100%; height: 100%;
    display: block; pointer-events: none;
  }
  /* Until the layer has proved a frame it is not looked at (2026-09-09): an
     opaque context is black before its first clear, and under an svg whose
     sea is the page's ground, that black was the sea for the first beat.
     `visibility`, not `display` — it keeps its size, which is what the
     proving frame is drawn for. */
  .mp-gpu.is-proving { visibility: hidden; }
  /* With the ground painted below, the svg's own land turns to glass: still
     there to be pressed and to light up, no longer paint. Lit, warmed and
     picked shapes come back in colour — they are answers, not ground. */
  .mp-map.is-gpu .mp-land:not(.is-hot):not(.is-picked) { fill: transparent; stroke: transparent; }
  /* Hovering an area fades rather than flips. The shapes are replaced on
     every redraw, so the transition is what makes a pointer crossing a border
     read as a hand moving over a map instead of two colours flickering. */
  /* No hover. An area lighting up under the pointer is a map answering a
     question nobody asked — what is lit is what was searched for. */
  .mp-land {
    fill: var(--mp-land); stroke: var(--mp-edge); stroke-width: 1; stroke-linejoin: round;
  }
  /* An answer lights up and lets go (MAPS-27): the wash says "this is the
     place" and fades to the shape's own paint — a fill that stayed was a
     highlight nobody could put down. No `to` frame: the destination is the
     shape's computed paint, which is the land on the SVG ground and glass on
     the GPU one. */
  .mp-land.is-on { animation: mp-activate 1.6s ease-out; }
  @keyframes mp-activate {
    from { fill: var(--accent-soft); stroke: var(--accent); }
  }
  /* The streets (MAPS-9). Three widths, because three classes: the ones that
     say where the country is, where a town is, and where a house is. Drawn
     under the labels and never filled — a road is a line. */
  /* A road is a light line with a darker edge, which is what makes it read as
     something you could drive on rather than a pencil stroke. Two paths per
     class over the same `d`: every casing first, then every fill, so a road's
     middle is not overdrawn by the next road's edge. */
  /* Houses (MAPS-13): a filled footprint a shade off the ground with a line
     round it, so a terrace reads as houses rather than one blob. Under the
     roads, because a road runs over the ground and a house sits on it. */
  .mp-house {
    fill: var(--mp-house); stroke: var(--mp-house-edge); stroke-width: 0.5;
    stroke-linejoin: round; fill-rule: evenodd;
  }
  /* Width follows the zoom (`--mp-rw`, set per draw). A trunk road drawn five
     pixels wide across a whole country is a string of fat dots: the fragments
     it is made of are two points long at that width, and a round cap on each
     end of a two-point line is a circle. Thinner far out, wider close in. */
  .mp-road, .mp-case { fill: none; stroke-linecap: round; stroke-linejoin: round; }
  .mp-case.is-c1 { stroke: var(--mp-case-1); stroke-width: calc(var(--mp-rw, 1) * 3.4px); }
  .mp-case.is-c2 { stroke: var(--mp-case-2); stroke-width: calc(var(--mp-rw, 1) * 2.3px); }
  .mp-case.is-c3 { stroke: var(--mp-case-3); stroke-width: calc(var(--mp-rw, 1) * 1.4px); }
  .mp-road.is-c1 { stroke: var(--mp-fill-1); stroke-width: calc(var(--mp-rw, 1) * 2px); }
  .mp-road.is-c2 { stroke: var(--mp-fill-2); stroke-width: calc(var(--mp-rw, 1) * 1.2px); }
  .mp-road.is-c3 { stroke: var(--mp-fill-3); stroke-width: calc(var(--mp-rw, 1) * 0.6px); }
  /* Far out there is no middle to show, so the casing is the whole line twice
     — and two strokes a pixel apart is what made them look furry. */
  .mp-map.is-far .mp-case { display: none; }
  /* And with no casing to draw it, a road far out is its own edge's grey:
     a white line on the land was a road nobody could see (MAPS-31). */
  .mp-map.is-far .mp-road.is-c3 { stroke: var(--mp-case-3); }
  .mp-map.is-far .mp-road.is-c2 { stroke: var(--mp-case-2); }
  .mp-map.is-far .mp-road.is-c1 { stroke: var(--mp-case-1); }
  .mp-name { font-size: 11px; fill: var(--mute); text-anchor: middle; pointer-events: none; user-select: none; }
  /* A street's name lies along its street (MAPS-28), in the road's own quiet
     colour — information for a reader already looking there, never a shout.
     The ground-coloured halo keeps it legible across a casing. */
  .mp-road-name {
    font-size: 10.5px; fill: var(--mute); text-anchor: middle;
    paint-order: stroke; stroke: var(--mp-land); stroke-width: 2.6px;
    pointer-events: none; user-select: none;
  }
  .mp-road-name.is-c1 { font-size: 11.5px; }
  .mp-dot { fill: var(--accent); stroke: var(--surface); stroke-width: 1.5; }
  /* Search, floating over the map at the top left. */
  .mp-search {
    position: absolute; top: 12px; left: 12px; z-index: 2;
    width: min(24rem, calc(100vw - 24px));
    display: flex; flex-direction: column; gap: 8px;
  }
  /* `[hidden]` first: an explicit display beats the attribute's own, so
     without this the box stays on screen under the card that replaced it. */
  .mp-ask[hidden] { display: none; }
  .mp-ask {
    display: flex; align-items: center; gap: 4px;
    padding: 4px 4px 4px 14px; border-radius: 999px;
    background: var(--surface); box-shadow: 0 2px 10px rgb(0 0 0 / 0.14);
  }
  .mp-ask input {
    flex: 1; min-width: 0; border: 0; background: none; outline: none;
    font: inherit; font-size: 14px; color: var(--ink2); padding: 8px 0;
  }
  .mp-ask button {
    display: grid; place-items: center; width: 34px; height: 34px;
    border: 0; border-radius: 999px; background: none; color: var(--mute); cursor: pointer;
  }
  .mp-ask button:hover { background: var(--surface2); color: var(--accent); }
  .mp-clear { font-size: 20px; line-height: 1; }
  .mp-clear[hidden] { display: none; }
  /* Suggestions while typing (MAPS-11). `[hidden]` first: an explicit display
     beats the attribute's own, and the list would be open before it is asked. */
  .mp-suggest[hidden] { display: none; }
  .mp-suggest {
    list-style: none; margin: 0; padding: 6px; max-height: 20rem; overflow-y: auto;
    border-radius: 12px; background: var(--surface); box-shadow: 0 2px 10px rgb(0 0 0 / 0.14);
  }
  .mp-suggest li {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px;
    padding: 8px 10px; border-radius: 8px; font-size: 13px; cursor: pointer;
  }
  .mp-suggest li.is-on, .mp-suggest li:hover { background: var(--surface2); }

  /* What has been chosen, and the trip it is one end of (MAPS-16). A chosen
     place looks chosen: a card with its name and where it is, not text left
     in a field. */
  .mp-trip[hidden] { display: none; }
  .mp-trip {
    display: flex; flex-direction: column; gap: 8px;
    padding: 8px; border-radius: 12px;
    background: var(--surface); box-shadow: 0 2px 10px rgb(0 0 0 / 0.14);
  }
  .mp-card {
    position: relative; padding: 8px 32px 8px 10px; border-radius: 8px;
    background: var(--surface2);
  }
  .mp-card-name { margin: 0; font-size: 13px; font-weight: 600; }
  .mp-card-at { margin: 2px 0 0; font-family: var(--mono); font-size: 11px; color: var(--faint); }
  .mp-card-drop {
    position: absolute; top: 6px; right: 6px; width: 22px; height: 22px;
    display: grid; place-items: center; border: 0; border-radius: 999px;
    background: none; color: var(--mute); font-size: 16px; line-height: 1; cursor: pointer;
  }
  .mp-card-drop:hover { background: var(--surface); color: var(--accent); }
  .mp-trip-link {
    align-self: flex-start; padding: 4px 6px; border: 0; border-radius: 6px;
    background: none; color: var(--accent); font: inherit; font-size: 12.5px;
    cursor: pointer; text-decoration: underline; text-underline-offset: .2em;
  }
  .mp-trip-link:hover { background: var(--surface2); }
  .mp-trip-said { margin: 0 6px 2px; font-size: 12.5px; color: var(--mute); }
  .mp-ask-to { box-shadow: none; background: var(--surface2); }

  /* The route: the accent, which nothing on the map itself uses. */
  .mp-trip-case {
    fill: none; stroke: var(--surface); stroke-width: 7;
    stroke-linecap: round; stroke-linejoin: round;
  }
  .mp-trip-line {
    fill: none; stroke: var(--accent); stroke-width: 4;
    stroke-linecap: round; stroke-linejoin: round;
  }
  .mp-hits[hidden] { display: none; }
  .mp-hits {
    max-height: min(28rem, calc(100vh - 8rem)); overflow-y: auto;
    border-radius: 12px; background: var(--surface); box-shadow: 0 2px 10px rgb(0 0 0 / 0.14);
  }
  .mp-hits ul { list-style: none; margin: 0; padding: 6px; }
  .mp-hits li {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    gap: 0 10px; padding: 8px 10px; border-radius: 8px; font-size: 13px;
  }
  .mp-hits li { cursor: pointer; }
  .mp-hits li:hover, .mp-hits li:focus-visible { background: var(--surface2); outline: none; }
  .mp-hits li.is-on { background: var(--accent-soft); }
  .mp-hit-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .mp-hit-kind { color: var(--faint); font-size: 11.5px; text-align: right; }
  .mp-hit-at { grid-column: 1 / -1; font-family: var(--mono); font-size: 11px; color: var(--faint); }
  .mp-nothing { margin: 0; padding: 12px 14px; color: var(--mute); font-size: 13px; }

  /* The controls, bottom right, where a hand on a trackpad already is. */
  .mp-zoom {
    position: absolute; right: 12px; bottom: 12px; z-index: 2;
    display: flex; flex-direction: column;
    border-radius: 10px; overflow: hidden; background: var(--surface);
    box-shadow: 0 2px 10px rgb(0 0 0 / 0.14);
  }
  .mp-zoom button {
    width: 38px; height: 38px; border: 0; border-bottom: 1px solid var(--line);
    background: none; color: var(--ink2); font-size: 18px; line-height: 1; cursor: pointer;
  }
  .mp-zoom button:last-child { border-bottom: 0; font-size: 14px; }
  .mp-zoom button:hover { background: var(--surface2); color: var(--accent); }
  .mp-where {
    position: absolute; left: 12px; bottom: 12px; z-index: 2; margin: 0;
    padding: 4px 8px; border-radius: 6px; background: var(--surface);
    font-family: var(--mono); font-size: 10.5px; color: var(--mute);
    box-shadow: 0 1px 4px rgb(0 0 0 / 0.10);
  }
  /* The debug line (MAPS-27): which rung the ground is on and how the frames
     come. Above `.mp-where`, because the door shows both. */
  .mp-debug {
    position: absolute; left: 12px; bottom: 40px; z-index: 2; margin: 0;
    padding: 4px 8px; border-radius: 6px; background: var(--surface);
    font-family: var(--mono); font-size: 10.5px; color: var(--mute);
    box-shadow: 0 1px 4px rgb(0 0 0 / 0.10);
  }
  .mp-debug:empty { display: none; }
  .mp-about {
    position: absolute; right: 12px; top: 12px; z-index: 2;
    max-width: min(26rem, calc(100vw - 24px));
    padding: 8px 12px; border-radius: 10px; background: var(--surface);
    font-size: 12px; color: var(--mute); box-shadow: 0 1px 4px rgb(0 0 0 / 0.10);
  }
  .mp-about summary { cursor: pointer; color: var(--ink2); }
  .mp-about p { margin: 8px 0 0; }
/* 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; }
.bottom-0 { bottom: 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); }
}
