/* glue — the design language.
 *
 * The vocabulary: tokens, and the primitives built from them. Screens consume these; they do not
 * restate them. `styles.css` holds what has not been lifted into a primitive yet.
 *
 * ── Why layers ────────────────────────────────────────────────────────────────────────────────
 * The stylesheet had grown five theme passes deep, and every one of them settled its arguments by
 * source order — a rule won because it was written later. That is invisible in a diff, it broke
 * silently whenever a block moved, and it is why a global `* { box-shadow: none !important }` was
 * able to suppress the whole 2.5D system without anyone noticing.
 *
 * `@layer` makes the argument explicit instead. The order below is the whole rule:
 *
 *     tokens  <  legacy  <  primitives  <  components
 *
 * A primitive beats a legacy rule no matter where either is written, and a rewritten component
 * beats both. So a screen can be lifted out of `legacy` one at a time without a flag day, and
 * without appending to the end of a 2500-line file to make it stick. The migration is literally
 * moving a block from `@layer legacy` to `@layer components` and deleting what it replaces.
 */
@layer tokens, legacy, primitives, components;

/* ── The handoff's tokens, imported rather than retyped ───────────────────────────────────────
 * `glue-tokens.css` is generated from the design source. Copying its values into this file by
 * hand is how the two drift: a colour gets nudged here, the handoff ships a new one there, and
 * nothing says which is current. It is imported instead, and the aliases below name our own
 * vocabulary in terms of it — so a token change arrives by replacing one file.
 *
 * It sits in the `tokens` layer, under everything, because tokens are values rather than rules. */
@import url("glue-tokens.css") layer(tokens);

@layer tokens {
  /* ── Icons have a size ────────────────────────────────────────────────────────────────────
     `svg()` emits a viewBox and no dimensions, which is right — the size belongs to where a mark
     is used, not to the mark. But an SVG with no size and no rule takes the space it is offered,
     and in Settings' 210px label column that was a 290px icon that read as a broken image.

     This is a floor, so it lives in the lowest layer. Put in `primitives` it beat every sizing
     rule in `legacy` — across layers the layer decides and the selector does not — and silently
     shrank all nine dock glyphs from 25px to 16px, taking the tiles from 48px to 39px. A default
     that outranks the thing it is a default for is not a default.  */
  svg:not([width]):not([height]) { width: 16px; height: 16px; flex: 0 0 auto; }

@font-face { font-family: "Instrument Sans"; font-weight: 400 600; font-display: block;
             src: url("fonts/instrument-sans-400-600.woff2") format("woff2"); }
@font-face { font-family: "Space Grotesk"; font-weight: 500 700; font-display: block;
             src: url("fonts/space-grotesk-500-700.woff2") format("woff2"); }
@font-face { font-family: "Jost"; font-weight: 500; font-display: block;
             src: url("fonts/jost-500.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-weight: 400; font-display: block;
             src: url("fonts/ibm-plex-mono-400.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-weight: 500; font-display: block;
             src: url("fonts/ibm-plex-mono-500.woff2") format("woff2"); }

:root {
  /* Paper, in three weights. The handoff has no pure white anywhere: content sits on paper, a
     recess is sunk, and a pill or an inset row is raised. `--surface` was #ffffff, which made
     every card brighter than the ground it stood on. */
  --bg: var(--glue-paper);
  --surface: var(--glue-paper);   /* a card is told apart by its line and its drop, not by being brighter */
  --surface-2: var(--glue-paper-sunk);   /* rails, wells, anything recessed */
  --surface-3: var(--glue-paper-raised); /* inset rows and soft pills */
  /* Ink is near-black. It had been a very dark green, which tinted every heading and every line
     of body text on all nine screens away from the handoff's palette. */
  --ink: var(--glue-ink);
  --ink-2: var(--glue-ink);
  --muted: var(--glue-ink-muted);
  --line: var(--glue-line);
  --line-2: #ebe6d9;
  --line-strong: var(--glue-line-strong);

  /* The accent is coral, and it means *this is staged, new, or waiting on you*. Green is not the
     accent: green is Approve and the local promise, and nothing else. The two had been one token
     — a single green doing both jobs — so "unread" and "committed" were the same colour, and the
     handoff's coral appeared nowhere. */
  --brand: #ff5a1f;
  --brand-deep: #a82f06;
  --accent: var(--glue-accent);
  --accent-text: var(--glue-accent-edge);
  --accent-weak: #ffe0d4;
  --accent-on-weak: #a82f06;

  /* Affirmative. Approve, a healthy account, the local dot — everything that means "this is
     fine", which is a different claim from "this needs you". */
  --ok: var(--glue-positive);
  --ok-text: var(--glue-positive-edge);
  --err: #b3261e;

  /* ── The surface colour code ────────────────────────────────────────────────────────────
     Taken from the design file rather than approximated: `Glue Demo.dc.html` carries a `FAM`
     table and a `tile` triple per surface, and the app had been using near-misses of both.

     Two levels, and the distinction is what makes the rule workable:

     **Families** are the soft treatments — a fill, a border, an ink. Five of them, and surfaces
     share them on purpose: Calendar and Command are both coral, Tasks and Research are both
     green, People and Read are both blue. A soft chip only has to say roughly where something
     came from.

     **Tiles** are the vivid identity, one per surface and never shared. This is what "two
     categories must not share a shade" is about: the families group, the tiles distinguish. */
  --fam-ink-soft: #f1eee6;    --fam-ink-bd: #e5e0d3;    --fam-ink-fg: #3c3a30;
  --fam-blue-soft: #d7f2fb;   --fam-blue-bd: #b6e4f2;   --fam-blue-fg: #06414f;
  --fam-green-soft: #dcf7ec;  --fam-green-bd: #b6ecd6;  --fam-green-fg: #0b6b52;
  --fam-yellow-soft: #fff6cf; --fam-yellow-bd: #ffe98f; --fam-yellow-fg: #7a5c05;
  --fam-coral-soft: #ffe0d4;  --fam-coral-bd: #ffcdb8;  --fam-coral-fg: #a82f06;

  /* One per surface. The second value is the hard shadow partner; the third is what sits on it. */
  /* face · edge · ink, per the design language's own triple. The edge is not a border: it is
     the hard drop the tile stands on, which is why pressing a tile just removes it. */
  --tile-email: #eceae4;     --tile-email-edge: #c9c5bb;     --tile-email-on: #23261f;
  --tile-calendar: #fb7185;  --tile-calendar-edge: #a51e33;  --tile-calendar-on: #ffffff;
  --tile-tasks: #5eead4;     --tile-tasks-edge: #1f9182;     --tile-tasks-on: #032b26;
  --tile-people: #a5e8ff;    --tile-people-edge: #3d8ba8;    --tile-people-on: #04212b;
  --tile-command: #ff5a1f;   --tile-command-edge: #a82f06;   --tile-command-on: #ffffff;
  --tile-read: #0e7490;      --tile-read-edge: #06414f;      --tile-read-on: #eaf9ff;
  --tile-research: #a3e635;  --tile-research-edge: #5d8a10;  --tile-research-on: #1d2a04;
  --tile-notes: #ffe93d;     --tile-notes-edge: #bfa500;     --tile-notes-on: #3b3400;

  /* The coral a *button* is filled with, as distinct from the coral a tile is.
     A tile carries a glyph and its label sits outside it on the page ground, so the tile keeps the
     designer's `#ff5a1f` exactly. A button carries words on the fill, and white on `#ff5a1f` is
     3.12:1 — under the 4.5 a 13px label needs, and the contrast guard's exemptions are for marks
     rather than words, by their own stated rule.
     `#d44513` is halfway from that coral to its own edge colour: still plainly the brand, and white
     on it measures 4.51:1. The edge stays `#a82f06`, so the 2.5D drop is unchanged. */
  --btn-command: #d44513;

  /* The same argument for the affirmative fill. Approve carries words too, and white on
     `--positive` is 2.93:1. `#118559` is that green darkened toward its own edge and puts white at
     4.64:1, so every filled action button in the app now carries a white label on a fill dark
     enough to have earned it. The chip and dot forms keep `--positive` — they are marks. */
  --btn-positive: #118559;
  --tile-settings: #2a2e26;  --tile-settings-edge: #000000;  --tile-settings-on: #f4f2ec;

  /* Tile fills and their hard shadow partners, measured from the design's own status strip and
     block grid. The shadow is a solid offset with no blur — that is the whole trick of this
     system, and a blurred one reads as a different product. */
  /* One chip triple per surface, straight from glue-design.json's `surface[*].chip`.
     Four families had been standing in for nine surfaces, so a People, Command, Research or
     Notes chip fell back to the grey default — the surface code quietly stopped carrying
     information for five of the nine, and would have kept doing so silently as those chips
     were added.

     `bd` doubles as the drop here. The demo hand-picks a third colour between bg and bd for the
     four surfaces that already had chips, and those four keep their measured value below; there
     is no formula behind them to extend, and the design's own `.glue-chip` primitive carries a
     border and no drop at all, so border-as-drop is the sanctioned shape rather than a guess. */
  --chip-email-bg: #f4f2ec;     --chip-email-bd: #ddd9cf;     --chip-email-fg: #23261f;
  --chip-calendar-bg: #ffe4e8;  --chip-calendar-bd: #ffc9d2;  --chip-calendar-fg: #a51e33;
  --chip-tasks-bg: #d8f8f1;     --chip-tasks-bd: #a9ece0;     --chip-tasks-fg: #0d5f54;
  --chip-people-bg: #e6f7ff;    --chip-people-bd: #b6e4f2;    --chip-people-fg: #0e5f7a;
  --chip-command-bg: #ffe0d4;   --chip-command-bd: #ffcdb8;   --chip-command-fg: #a82f06;
  --chip-read-bg: #cfe9f2;      --chip-read-bd: #a9d9e8;      --chip-read-fg: #06414f;
  --chip-research-bg: #eff8d5;  --chip-research-bd: #dcefa2;  --chip-research-fg: #4a6b09;
  --chip-notes-bg: #fff8c2;     --chip-notes-bd: #ffe98f;     --chip-notes-fg: #7a5c05;
  --chip-settings-bg: #f4f2ec;  --chip-settings-bd: #ddd9cf;  --chip-settings-fg: #23261f;
  /* The four measured drops, kept because they are what the design renders. */
  --chip-email-sh: #e4e0d6;  --chip-calendar-sh: #ffd6dc;
  --chip-tasks-sh: #c9f2e9;  --chip-read-sh: #bfe1ed;

  --tile-ink-soft: #f4f2ec;    --tile-ink-edge: #ddd9cf;    --tile-ink-drop: #e4e0d6;
  --tile-blue-soft: #cfe9f2;   --tile-blue-edge: #bfe1ed;   --tile-blue-drop: #bfe1ed;
  --tile-green-soft: #d8f8f1;  --tile-green-edge: #a9ece0;  --tile-green-drop: #c9f2e9;
  --tile-pink-soft: #ffe4e8;   --tile-pink-edge: #ffc9d2;   --tile-pink-drop: #ffd6dc;
  --tile-ink-fg: #3c3a30;
  --tile-blue-fg: #06414f;
  --tile-green-fg: #0d5f54;
  --tile-pink-fg: #a51e33;

  /* The sky, the sun and the hills. */
  --sky-top: #d9f4ff;  --sky-mid: #eafbff;  --sky-low: #f4fbeb;  --sky-ground: #fdfcfa;

  /* Pastel card fills, each with the two-steps-darker shadow the system runs on. Kept for the
     2.5D card treatments that predate the surface code and are not about surfaces. */
  --card-teal: #d8f8f1;  --card-teal-deep: #a8ded2;
  --card-pink: #ffe4e8;  --card-pink-deep: #e7b9c1;
  --card-peach: #ffe0d4; --card-peach-deep: #e5b69f;
  --card-sky: #d7f2fb;   --card-sky-deep: #a8d3e2;
  --card-yellow: #fdf3c9; --card-yellow-deep: #e3d296;

  --on-sky: #14506b;
  --on-yellow: #6b5410;
  --on-pink: #8a3550;
  --on-peach: #8a4420;

  /* The hard shadow. No blur — that is the whole trick. */
  --shadow: 0 3px 0 0 var(--line);
  --press: 0.14s cubic-bezier(0.2, 0.8, 0.2, 1);
  /* The only green that means "this commits". Named because a filled Approve is a decision
     about consequence, and the app should not have two greens arguing about which one it is. */
  --positive: #14ac72;       --positive-edge: #0f6b48;
  --ink-soft: var(--glue-ink-soft);
  /* DEVIATION FROM THE HANDOFF, recorded on purpose.
     `--glue-ink-faint` is #8d8676, which is 3.53:1 on paper. This token carries reading text —
     screen taglines, thread summaries, email meta, the sort label — all at 10.5-13px, where the
     floor is 4.5:1. 13% darker in the same hue reaches 4.53:1 and still reads as a caption
     rather than as content. Keep the design's intent, meet the floor.
     Tracked in specs/glue-handoff-gap.md as an open question for the designer. */
  --ink-faint: #7a7466;
  /* Marks only — the search glyph and the ⌘K key. Never words: at 3.0:1 it is under the text
     floor and only clears the 3:1 a non-text mark needs. */
  --ink-ghost: var(--glue-ink-ghost);
  --line-soft: var(--glue-line-soft);   /* a button's drop: one step softer than a border */
  --space-snug: 10px;
  /* relationState, from the design's vocabulary: where a person is in their own story.
     One tint per state, used on the card rail and its chip so the two cannot drift apart. */
  /* The assistant's avatar chip. Command's coral at the weight a 18px square wants — named
     because a hex in a rule is a colour nobody can find again. */
  --command-avatar-bg: #ffe4d8;
  --rel-waiting-bg: #fff4ee;  --rel-waiting-bd: #ffcdb8;  --rel-waiting-fg: #a82f06;
  --rel-warm-bg: #d7f2fb;     --rel-warm-bd: #b6e4f2;     --rel-warm-fg: #0e7490;
  --rel-quiet-bg: #f5f2eb;    --rel-quiet-bd: #e9e4d9;    --rel-quiet-fg: #6c6657;
  --dock-edge: #e5e0d3;      /* the dock's hairline, and the ink family's line */
  --dock-tray: #ddd7c9;      /* the home bar under it */
  --dock-ring: #faf8f2;      /* the ring that lifts a badge off whatever it sits on */
  /* The active tile's glow is its *family's*, not its own face — five families, nine surfaces. */
  --glow-ink: rgba(35, 38, 31, 0.35);
  --glow-coral: rgba(255, 90, 31, 0.45);
  --glow-green: rgba(20, 172, 114, 0.45);
  --glow-blue: rgba(14, 116, 144, 0.45);
  --glow-yellow: rgba(255, 209, 43, 0.5);
  /* The header's two pills. The index reading is a quiet control on the page ground; "local" is
     a claim, so it gets the accent's own soft/ink pair rather than a neutral. */
  --pill-edge: #e2ddd0;
  --pill-ink: #55503f;       /* 7.2:1 on --bg */
  --local-soft: #dcf7ec;  --local-line: #b6ecd6;  --local-ink: #0b6b52;   /* 6.4:1 on --local-soft */
  --lift: 4px;

  /* Rounded, where the previous system was strictly square. */
  /* ── The handoff's scales, by name ─────────────────────────────────────────────────────────
     Radius and depth are a ladder with six and five rungs, and a value off the ladder is a bug.
     They had been hand-written at every call site — 8, 10, 11, 14, 18 for radius; 2, 3, 4 for
     depth, applied inconsistently — which is how a card ends up a different shape from the card
     beside it. `--r` and `--r-sm` stay as aliases so the older markup keeps working. */
  --r-chip: 6px;
  --r-control: 9px;
  --r-card: 13px;
  --r-panel: 17px;
  --r-sheet: 20px;
  --r-tile: 22px;
  --r: var(--r-card);
  --r-sm: var(--r-control);
  --r-pill: 999px;

  /* Depth is a hard drop with no blur. The number is the drop *and* the distance a press moves. */
  --depth-chip: 2px;
  --depth-control: 3px;
  --depth-card: 4px;
  --depth-tile: 6px;
  --depth-dock: 12px;

  /* Type, eight steps. Ours had 10, 11, 12.5, 13, 14, 16, 17.5 and 25 in circulation. */
  --text-micro: 9.5px;
  --text-meta: 10.5px;
  --text-small: 12px;
  --text-body: 13.5px;
  --text-lead: 15px;
  --text-title: 19px;
  --text-screen: 24px;
  --text-hero: 34px;

  /* Space, six steps. */
  --space-hair: 4px;
  --space-tight: 7px;
  --space-base: 14px;
  --space-roomy: 20px;
  --space-section: 26px;

  --font-display: "Space Grotesk", -apple-system, system-ui, sans-serif;
  --font: "Instrument Sans", -apple-system, system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --font-brand: "Jost", var(--font);
}
}

@layer primitives {
  /* ── Depth ────────────────────────────────────────────────────────────────────────────────
     The whole 2.5D vocabulary in one place: a raised thing stands on a hard drop of its own
     depth, in its own edge colour, with no blur — and pressing it takes the drop away and moves
     it down by exactly that much. Nothing else is allowed to mean "pressed".

     Written as two custom properties rather than as a class per component. A button, a chip and
     a card differ only in how deep they stand, so `--depth` and `--edge` are the whole argument
     and every raised element sets those two. This is what stops the next component from
     restating `box-shadow: 0 3px 0 0 #e9e4d9` in its own words for the eleventh time. */
  .u-raise {
    box-shadow: 0 var(--depth, 3px) 0 0 var(--edge, var(--line));
    transition: transform var(--press), box-shadow var(--press);
  }
  .u-raise:active { transform: translateY(var(--depth, 3px)); box-shadow: none; }

  /* The scale, from glue-design.json's `shape.elevation`. A name per step, so a component says
     how raised it is rather than how many pixels it happens to want. */
  .d-chip    { --depth: 2px; }
  .d-control { --depth: 3px; }
  .d-card    { --depth: 4px; }
  .d-tile    { --depth: 6px; }

  /* ── Buttons ──────────────────────────────────────────────────────────────────────────────
     Every button is raised. They had all been flat — not by decision but by accident: they were
     written while a global `* { box-shadow: none !important }` was in force, so there was
     nothing to restore when it went. Putting the drop on the base class is the fix and also the
     guard, because a new button now gets it without anyone remembering to.

     `.btn-quiet` is the default. A filled variant is a claim about consequence, so it is spelled
     out: coral commits, green approves, ink is the neutral strong action. */
  /* The app's markup says `.primary` and `.ghost` in ninety-six places. Declaring those two names
     to *be* the primitive migrates every one of them at once, which is the point of having a
     language: you change the vocabulary, not the ninety-six call sites. New markup should say
     `.btn` — these aliases exist so the old markup does not have to be found first. */
  .btn, button.primary, button.ghost, .primary, .ghost {
    display: inline-flex; align-items: center; gap: var(--space-snug, 10px);
    padding: 8px 15px; border: 1px solid var(--btn-bd, var(--pill-edge)); border-radius: 11px;
    font-family: var(--font); font-size: 13px; font-weight: 600; line-height: 1.25;
    cursor: pointer;
    background: var(--bg); color: var(--pill-ink);
    --edge: var(--line-soft, var(--line)); --depth: 3px;
    box-shadow: 0 var(--depth) 0 0 var(--edge);
    transition: transform var(--press), box-shadow var(--press);
  }
  .btn:active, .primary:active, .ghost:active { transform: translateY(var(--depth)); box-shadow: none; }
  .btn[disabled], .primary[disabled], .ghost[disabled] { opacity: 0.55; cursor: default; }

  /* `.primary` is the affirmative action on every screen — Add, Connect, Commit, Download — so
     it takes the accent fill. `.ghost` is the quiet one beside it and keeps the default paper.
     Approving a staged item is the one affirmative action with consequence, so it is green
     rather than coral: the difference between "do this" and "send this" is worth a colour. */
  /* `button.primary` appears in the base selector list above, which is 0,1,1 — so a variant
     written as a bare `.primary` (0,1,0) loses to it and the label silently kept the base
     colour. The variants match the base's specificity for that reason, not for tidiness. */
  /* A quiet button is paper with a line round it and a drop under it. Without the line it was
     the same colour as the page it sat on, so "Load older", "Fetch" and "Summarise" read as
     floating labels with a shadow rather than as controls. A filled button brings its own edge
     and suppresses the line instead of doubling it. */
  button.primary, .primary, .btn-ink, .btn-approve, .read-open, .sync-btn {
    --btn-bd: transparent;
  }
  button.primary, .primary { background: var(--btn-command); color: var(--tile-command-on); --edge: var(--tile-command-edge); }
  button.primary[data-commit], .primary[data-commit] { background: var(--btn-positive); color: var(--tile-command-on); --edge: var(--positive-edge); }
  button.ghost, .ghost { background: var(--bg); color: var(--pill-ink); }
  button.ghost.danger, .ghost.danger { color: var(--err); }

  /* The label on the coral is white, which is what the handoff's own token says
     (`--glue-command-ink: #fff`) and what the site has always shipped on the same fill.

     This used to be ink, on the grounds that white measures 3.12:1 here against the 4.5 a 13px
     label needs. That reasoning was sound in isolation and wrong in place: it left the app and the
     site rendering the same brand button two different ways, which is a worse failure than the one
     it fixed — a reader sees the inconsistency, and nobody reads a contrast ratio.

     The ratio is a real cost and it is not being pretended away. The fix, if it is taken, is to
     darken the fill rather than to re-darken the label: #d44513 is halfway to the edge colour,
     still unmistakably the brand, and puts white at 4.51:1. That is a change to the brand fill in
     both places at once, which is a decision for the designer and not for this file. Recorded in
     specs/glue-handoff-gap.md with the handoff's other contrast failures. */
  .btn-primary { background: var(--btn-command); color: var(--tile-command-on); --edge: var(--tile-command-edge); }
  /* Approve is the only control on a staged item that commits anything, so it is the only one
     that gets a fill. An outline here read as secondary, which is backwards. */
  .btn-approve { background: var(--btn-positive); color: var(--tile-command-on); --edge: var(--positive-edge); }
  .btn-ink     { background: var(--ink-2); color: var(--bg); --edge: #000; }

  /* ── Chips ────────────────────────────────────────────────────────────────────────────────
     A chip is a surface's soft half: its own fill, its own line, its own ink, and a 2px drop.
     Set `--chip-*` and nothing else changes. */
  .u-chip {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 5px 11px; border-radius: 9px;
    background: var(--chip-bg); border: 1px solid var(--chip-bd); color: var(--chip-fg);
    font-family: var(--font-mono); font-size: 10.5px;
    box-shadow: 0 2px 0 0 var(--chip-sh, var(--chip-bd));
  }

  /* ── Segmented control ────────────────────────────────────────────────────────────────────
     One choice, contained. The five tabs on Tasks had an icon and a count each, which made five
     buttons out of what is a single decision; inside a track with the active one lifted to
     paper, the row reads as one control and the eye picks the state rather than five labels. */
  .u-seg {
    display: inline-flex; align-items: center; gap: 2px; padding: 4px;
    background: var(--fam-ink-soft); border: 1px solid var(--line);
    border-radius: 11px;
  }
  .u-seg > button {
    padding: 6px 13px; border: 0; border-radius: 8px;
    background: none; box-shadow: none;
    font-family: var(--font); font-size: 13px; font-weight: 600;
    color: var(--ink-faint); cursor: pointer; white-space: nowrap;
  }
  .u-seg > button:hover { color: var(--ink-2); }
  .u-seg > button.active, .u-seg > button[aria-selected="true"] {
    background: var(--bg); color: var(--ink-2); box-shadow: 0 1px 0 0 var(--line-soft);
  }

  /* ── A row that runs off the edge ─────────────────────────────────────────────────────────
     Scrolls sideways and fades rather than clipping or wrapping. A chip cut off by a hard edge
     reads as a rendering fault; the same chip under a fade reads as "there is more". Wrapping is
     the third option and the worst one — a control row that grows a second line pushes whatever
     it sits beside off its own baseline. */
  .u-scroll-x {
    flex: 0 1 auto; min-width: 0;
    display: flex; align-items: center; flex-wrap: nowrap;
    overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 16px), transparent 100%);
    mask-image: linear-gradient(90deg, #000 calc(100% - 16px), transparent 100%);
  }
  .u-scroll-x::-webkit-scrollbar { display: none; }
  .u-scroll-x > * { flex: 0 0 auto; }

  /* ── Glyph square ─────────────────────────────────────────────────────────────────────────
     An icon in a tinted rounded square, sized by `--glyph-size`. The design uses it wherever a
     row needs to say what kind of thing it is before you read the words — a person's feed, a
     journey entry, a section heading. Set the family tokens; nothing else changes. */
  .u-glyph {
    flex: 0 0 auto; display: grid; place-items: center;
    width: var(--glyph-size, 22px); height: var(--glyph-size, 22px);
    border-radius: calc(var(--glyph-size, 22px) / 3.6);
    background: var(--chip-bg); color: var(--chip-fg);
  }
  .u-glyph > svg { width: 62%; height: 62%; }

  /* ── Paper ────────────────────────────────────────────────────────────────────────────────
     Content stands on opaque paper and the scene fills the gaps around it. Every screen root
     that is not the cockpit takes this, so no decorative layer can end up under text. */
  .u-paper { background: var(--bg); color: var(--ink); }
  .u-paper-sunk { background: var(--surface-2); }
  .u-card {
    background: var(--surface); border: 1px solid var(--line-2);
    border-radius: var(--card-radius, 14px); box-shadow: 0 4px 0 0 var(--line);
  }

  /* ── Cover band ───────────────────────────────────────────────────────────────────────────
     A card that stands for a *thing* — an article, a brief — opens with a tinted band carrying
     two readings: what kind it is, and how long it takes. The band is where the screen gets its
     colour; without it a queue of articles is a page of grey paragraphs and nothing on it
     distinguishes a talk from a blog post before you read the words.

     The card needs `overflow: hidden` for the band to take its corners. */
  .u-cover {
    height: var(--cover-h, 112px);
    display: flex; align-items: flex-end; justify-content: space-between; gap: 10px;
    padding: 12px 15px; border-bottom: 1px solid var(--line);
    background: var(--chip-bg); color: var(--chip-fg);
    font-family: var(--font-mono); font-size: 10px;
  }
  .u-cover > :first-child { letter-spacing: 0.1em; text-transform: uppercase; }

  /* ── Type roles ───────────────────────────────────────────────────────────────────────────
     Three roles, named by what they are for rather than by size. `meta` is the one that keeps
     being re-typed by hand at four different sizes. */
  .t-display { font-family: var(--font-display); font-weight: 700; letter-spacing: -0.03em; }
  .t-meta {
    font-family: var(--font-mono); font-size: 10.5px; color: var(--pill-ink);
    letter-spacing: 0.02em;
  }
  .t-micro { font-family: var(--font-mono); font-size: 9.5px; color: var(--pill-ink); }
  /* A screen's name. 24/700 from the design's `text.screen`, and the weight matters twice over:
     Space Grotesk is bundled as a variable face with the range 500-700, so the `font-weight: 200`
     four screens were asking for is outside it and could never render as written — the browser
     clamps it to 500 and the heading comes out lighter than anything intended it. */
  .t-screen {
    font-family: var(--font-display); font-size: 24px; font-weight: 700;
    line-height: 1.1; letter-spacing: -0.03em;
  }
}
