/* mama — the approval queue, as a Telegram Mini App.
 *
 * Two materials, deliberately kept apart:
 *   CONTENT (the tweet) is rendered faithful to X — reading-size text, X's
 *   metric row, its restraint. It is a quotation from somewhere else.
 *   CHROME (everything the app itself says) rides Telegram's theme variables,
 *   so dark/light and the user's accent come free and the page reads as part of
 *   the client rather than a website stuffed into a window.
 *
 * The system's own voice — #seq, score, metrics, keys — is monospace with
 * tabular numerals. That typographic line is the whole point: at a glance you
 * can tell what a stranger wrote from what the machine measured.
 *
 * Every length below comes from a scale. It did not used to: paddings were
 * `11px 13px`, gaps were 9px, type was 12.5px — each one tuned by hand and
 * agreeing with nothing else, which is most of why the app read as functional
 * rather than designed. Rounding them onto a shared ladder costs a pixel here
 * and there and buys a rhythm you can feel without being able to name.
 */

/* Rubik, self-hosted. A CDN would be blocked outright: the CSP has no font-src,
 * so fonts fall under `default-src 'self'`. Two subsets, split by unicode-range
 * so an English-only session never downloads the Hebrew one — and Hebrew is not
 * optional here, since the tweets, the banner and the user's own notes are in
 * it. A Latin-only face would have handed those glyphs to a fallback font and
 * put two different typographies on one screen. */
@font-face {
  font-family: "Rubik";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/static/fonts/rubik-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: "Rubik";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/static/fonts/rubik-hebrew.woff2") format("woff2");
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC,
                 U+FB1D-FB4F;
}

:root {
  /* The palette is OURS now, not Telegram's.
   *
   * It used to read straight off --tg-theme-*, which bought dark/light and the
   * user's accent for free — and cost the design any say in its own depth: the
   * client can hand us a near-black #17212b and a stark blue, and the soft,
   * warm, layered surface this screen is built around cannot be assembled on
   * top of colours we do not choose. What is kept instead is the part that
   * actually matters — a dark scheme and a light one, chosen by the OS below.
   * A Telegram theme no longer tints the app; that is the trade. */
  --bg:        #2c3240;   /* warm dark blue-gray — the room, not a void */
  --bg-deep:   #262b38;   /* rail and action bar: the frame around the room */
  --bg-soft:   #363d4d;   /* every card — deliberately LIGHTER than the page */
  /* The quoted tweet, and only it. Pure black or pure white by the OS scheme:
   * the tweet is somebody else's writing on somebody else's canvas, and giving
   * it our warm blue-gray made it read as one more panel the app had authored.
   * This is the two-materials rule at the top of the file, finally made
   * literal — chrome is ours and tinted, content is X's and untinted. */
  --paper:     #000000;
  --bg-soft-2: #3f4657;   /* nested surfaces: inputs, prompts, quiet fills */
  --fg:        #f2f5f7;   /* soft warm white, never #fff */
  --fg-dim:    #a8afbd;   /* warm gray for everything the app murmurs */
  --link:      #7cc3f2;
  --accent:    #4ecdc4;   /* soft deep turquoise: the one thing that is loud */
  --accent-fg: #12312f;

  /* Ours, not Telegram's and not X's: the signal colour marks machine
   * annotation only, so it can never be mistaken for either. Both of these are
   * muted a step from where they were — mustard rather than ochre, coral rather
   * than alarm red, because nothing on this screen is an emergency. */
  --signal:  #d9b268;
  --danger:  #e8938a;
  /* the selected filter chip: the signal colour let down to a pastel, with a
   * text colour dark enough to sit on it in either scheme */
  --chip-on-bg: #e3c894;
  --chip-on-fg: #33291a;
  --hairline: color-mix(in srgb, var(--fg) 10%, transparent);
  --raise:    color-mix(in srgb, var(--fg) 6%, transparent);
  --raise-2:  color-mix(in srgb, var(--fg) 10%, transparent);

  /* space — 2/4/6/8/12/16/20/24/32. Not a strict 4pt grid: 6 and 20 earn their
   * place in a 375px-wide column where 4 is invisible and 8 is a gulf. */
  --s0: 2px;  --s1: 4px;  --s2: 6px;  --s3: 8px;
  --s4: 12px; --s5: 16px; --s6: 20px; --s7: 24px; --s8: 32px;

  /* type — one ladder for chrome. --read is separate on purpose: it is X's
   * reading size for quoted content, not a step in our own scale. */
  --t1: 11px; --t2: 12px; --t3: 13px; --t4: 14px; --t5: 15px; --t6: 17px;
  --t7: 20px; --t-hero: 34px;
  --read: 16px;      /* X's reading size, nudged up for a webview */

  /* radius — the ladder now climbs two rungs higher. A 16px corner on a
   * full-width card is a rounded rectangle; 22px is a pebble, and the whole
   * point of this pass is that the surfaces read as objects you could pick up. */
  --r-xs: 8px; --r-sm: 10px; --r: 14px; --r-lg: 18px;
  --r-xl: 22px; --r-2xl: 28px; --r-full: 999px;
  --tap: 46px;       /* smallest thing worth aiming a thumb at */

  /* Depth. There was none — every surface sat flat on the page and the only way
   * to tell a card from the background was a 12%-alpha hairline.
   *
   * Three steps now instead of two, and every one of them is a soft wide
   * penumbra rather than a hard 1px line: --e0 for a pill that should feel
   * pickable, --e1 for a card resting on the page, --e2 for something floating
   * above it. The hairline stays, but only as the highlight along the top edge
   * (--lift) — a lit rim is what makes a surface read as raised rather than
   * merely outlined. */
  --shadow:   rgba(0, 0, 0, .34);
  --shadow-2: rgba(0, 0, 0, .20);
  --e0: 0 1px 2px var(--shadow-2), 0 3px 8px rgba(0, 0, 0, .14);
  --e1: 0 2px 5px var(--shadow-2), 0 8px 22px rgba(0, 0, 0, .18);
  --e2: 0 3px 8px var(--shadow-2), 0 16px 40px var(--shadow);
  --lift: inset 0 1px 0 color-mix(in srgb, #fff 8%, transparent);

  /* One easing curve for everything that moves: a fast start that settles,
   * which is what makes a tap feel answered rather than animated. */
  --ease: cubic-bezier(.2, .8, .25, 1);
  --fast: .14s;
  --slow: .26s;

  --ui: "Rubik", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;

  /* The action bar grew: taller pills, more padding around them. This number is
   * what keeps the last line of a card from ending up underneath it, and it has
   * to CLEAR the bar rather than merely match it — the bar measures 84px, and at
   * 88 the last button ended a scroll with 4px to spare, which on a screen with
   * a home indicator is no clearance at all. */
  --bar: calc(104px + env(safe-area-inset-bottom));
}

/* The same room with the lights on. Telegram does not tell us which theme it
 * handed us, but the OS does — and since the palette above is ours, this is the
 * whole of the light mode rather than a patch over one.
 *
 * Two things do not simply invert. The mustard is a mid-tone on near-white:
 * fine as a 12px block of colour, too weak as the 13px text it is mostly used
 * for, so it darkens rather than lightening. And the shadows come down hard —
 * the same black that reads as depth on a dark panel reads as grime on a white
 * one, so the light scheme leans on the tint of the surface instead. */
@media (prefers-color-scheme: light) {
  :root {
    /* Three steps, not two. --paper is pure #fff by requirement, and a card
     * that was ALSO pure #fff left the quoted tweet nested inside it invisible
     * — the sharp separation the black box gives for free in the dark scheme
     * had no equivalent here. So the card steps back to an off-white and the
     * page steps back again behind it, which puts the quote at the top of the
     * stack where the dark scheme puts it at the bottom. */
    --bg:        #e8ecf2;
    --bg-deep:   #dfe4ec;
    --bg-soft:   #f7f9fc;
    --paper:     #ffffff;
    --bg-soft-2: #eef1f6;
    --fg:        #2b3140;
    --fg-dim:    #6f7788;
    --link:      #2a7fc0;
    --accent:    #17a89d;
    --accent-fg: #ffffff;
    --signal:    #9a7118;
    --danger:    #c25f57;
    --chip-on-bg: #f2e3bd;
    --chip-on-fg: #5c440f;
    --hairline:  color-mix(in srgb, var(--fg) 12%, transparent);
    --shadow:    rgba(28, 38, 54, .12);
    --shadow-2:  rgba(28, 38, 54, .07);
    --lift:      inset 0 1px 0 #fff;
  }
}

* { box-sizing: border-box; }

/* The whole app hides things with the `hidden` attribute, and the UA rule for
 * it (display:none) loses to ANY author `display` — so every flex/grid element
 * here would stay on screen while the code believed it was gone. This is the
 * one rule holding that together; without it the spinner, the toast and the
 * sheets are all visible at once. */
[hidden] { display: none !important; }

body {
  margin: 0;
  padding-bottom: var(--bar);
  background: var(--bg);
  color: var(--fg);
  /* 1.6 rather than 1.45. Every block of prose on the screen inherits this, and
   * loosening the leading is most of what separates a dense panel from a page
   * that lets you breathe. */
  font: 400 var(--t5)/1.6 var(--ui);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overscroll-behavior-y: contain;
}

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

.rail {
  position: sticky; top: 0; z-index: 5;
  padding-top: env(safe-area-inset-top);
  background: color-mix(in srgb, var(--bg-deep) 86%, transparent);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  /* No border. The rail is darker than the page and casts a shadow down onto
   * it, which says "this floats above" without drawing a line to say it. */
  border-bottom: 0;
  border-radius: 0 0 var(--r-xl) var(--r-xl);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .16);
}

/* minmax(0, 1fr), not 1fr: a bare 1fr refuses to shrink below its content's
 * min-content width, so on a 320px screen the four labels pushed the whole
 * PAGE 42px wider than the viewport and every screen scrolled sideways. */
/* The gap and the side padding are as tight as they are because the pills spend
 * the width the old flat tabs did not have to: at gap:6 / padding:12 the labels
 * ellipsed to "Publi…" and "Histo…" on a 390px screen, which is the one thing a
 * tab bar may not do. Measured, not guessed. */
.tabs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
        gap: var(--s1); padding: var(--s3) var(--s3) var(--s4); }

/* Each tab is its own pill, not a word in a strip. They were four labels
 * sharing one surface, distinguished by a faint wash behind the selected one;
 * as objects with their own edge and their own shadow they read as four things
 * you can press, which is what they are. */
.tab {
  appearance: none; border: 0; cursor: pointer;
  min-height: var(--tap);
  display: flex; align-items: center; justify-content: center; gap: var(--s1);
  padding: var(--s3) var(--s2); border-radius: var(--r-full);
  font: 500 var(--t2)/1 var(--ui);
  color: var(--fg-dim);
  background: var(--bg-soft);
  box-shadow: var(--e0), var(--lift);
  position: relative;
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease),
              transform .1s var(--ease);
}
/* The turquoise is the only saturated fill in the chrome, and it lands on the
 * one control that answers "where am I".
 *
 * It used to throw a tinted glow, on the theory that a lit object should look
 * like it is lighting something. On a phone that reads as neon — the tab hums
 * against a screen where nothing else does. A flat fill and the same neutral
 * elevation every other raised surface gets says the same thing quietly. */
.tab[aria-selected="true"] {
  color: var(--accent-fg); background: var(--accent);
  box-shadow: var(--e1), var(--lift);
}
.tab:active { transform: scale(.94); }
@media (hover: hover) {
  .tab:hover:not([aria-selected="true"]) { background: var(--bg-soft-2); color: var(--fg); }
}
.tab__face { font-size: var(--t4); flex: 0 0 auto; }
.tab__name { min-width: 0; overflow: hidden; text-overflow: ellipsis;
             white-space: nowrap; }

/* iPhone SE and friends. Tightening the label rather than dropping it: an
 * icon-only tab bar is a guessing game, and four labels do fit at 320 once the
 * type and the padding come down a step. */
@media (max-width: 350px) {
  .tabs { gap: var(--s0); padding: var(--s2) var(--s2) var(--s3); }
  .tab { font-size: var(--t1); gap: var(--s0); padding: var(--s2) var(--s1); }
  .tab__face { font-size: var(--t3); }
}
/* Pinned to the corner, not sitting in the row. Inline, the count was a third
 * element competing for a quarter of a 375px screen, and it pushed "Publish"
 * and "History" into an ellipsis. A badge is an annotation on the tab; putting
 * it in the flow made it an equal. */
.tab__badge {
  position: absolute; top: -2px; right: var(--s0);
  min-width: 17px; padding: 1px var(--s1); border-radius: var(--r-xs);
  /* var(--bg), not a fixed brown: the mustard darkens in the light scheme, so a
     hardcoded dark text turned the badge into dark-on-dark there */
  background: var(--signal); color: var(--bg);
  font: 700 10px/1.5 var(--mono); font-variant-numeric: tabular-nums;
  text-align: center;
  /* the ring is the rail behind it, so the badge reads as sitting ON the pill
     rather than being welded to its corner */
  box-shadow: 0 0 0 2px var(--bg-deep), var(--e0);
}

/* queue meta: seq, deck position, time left.
 *
 * This line is entirely the machine talking — a serial number, a position in a
 * deck, a countdown. It should be legible and then get out of the way, so
 * nothing in it is full-strength white and everything in it is given room. */
.meta {
  display: flex; align-items: center; gap: var(--s3);
  padding: 0 var(--s5) var(--s5);
}
.meta__seq {
  font: 600 var(--t3)/1 var(--mono); font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, var(--signal) 82%, var(--fg-dim));
  letter-spacing: .02em;
  margin-right: auto;
}
.meta__nav {
  appearance: none; cursor: pointer;
  width: 36px; height: 34px; border-radius: var(--r-full);
  border: 0; background: var(--raise); color: var(--fg-dim);
  font-size: var(--t6); line-height: 1; padding: 0;
  transition: transform var(--fast) var(--ease),
              background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.meta__nav:active { transform: scale(.9); background: var(--raise-2); color: var(--fg); }
.meta__nav[disabled] { opacity: .3; pointer-events: none; }
.meta__pos {
  appearance: none; cursor: pointer;
  border: 0; background: var(--raise); color: var(--fg-dim);
  border-radius: var(--r-full); padding: var(--s3) var(--s5); min-height: 34px;
  font: 400 var(--t2)/1 var(--mono); font-variant-numeric: tabular-nums;
  transition: transform var(--fast) var(--ease), color var(--fast) var(--ease);
}
.meta__pos:active { transform: scale(.95); color: var(--fg); }
.meta__ttl {
  margin-left: auto; margin-right: var(--s1);
  font: 500 var(--t2)/1 var(--mono); font-variant-numeric: tabular-nums;
  color: var(--fg-dim);
}
.meta__ttl[data-soon] { color: var(--danger); }
/* Neither of these may break across two lines: "1 /" over "1" and "3h" over
 * "left" is what the extra padding bought on a 320px screen. */
.meta__pos, .meta__ttl { white-space: nowrap; }
/* + is the only thing in this row that ADDS something, so it is the only one
   with a surface of its own under it */
#add {
  font-size: var(--t7); width: 40px; height: 36px;
  background: var(--bg-soft); color: var(--fg);
  box-shadow: var(--e0), var(--lift);
}
#add:active { background: var(--bg-soft-2); }

/* iPhone SE. The generous spacing above is right at 390 and does not fit at
 * 320 — six controls in one row, and the row is the only place in the app where
 * everything is load-bearing. */
@media (max-width: 350px) {
  .meta { gap: var(--s2); padding: 0 var(--s4) var(--s4); }
  .meta__pos { padding: var(--s3) var(--s4); }
  #add { width: 36px; }
}

/* ── banner: a state the whole app is in ──────────────────────────────── */

.banner {
  margin: 0; padding: var(--s4) var(--s5);
  background: color-mix(in srgb, var(--danger) 16%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--danger) 40%, transparent);
  color: var(--fg);
  font: 500 var(--t3)/1.4 var(--ui);
}

/* New arrivals. Green, not red: this one is an invitation, and it shares the
   .banner box with a failure state that must stay distinguishable at a glance. */
.banner--fresh {
  display: block; width: 100%; text-align: inherit;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border: 0; border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  color: var(--fg);
  cursor: pointer;
  animation: rise var(--slow) var(--ease);
}

/* ── the tweet: X's voice ─────────────────────────────────────────────── */

.view { padding: var(--s6) var(--s5) var(--s8); max-width: 680px; margin: 0 auto; }

.card { animation: rise var(--slow) var(--ease); }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }

/* The card is the screen. Everything else on Queue is chrome around this one
 * object, so it gets the largest radius in the app, the most padding, and a
 * lighter surface than the page — the three things that together make a
 * rectangle stop reading as a region and start reading as a card you are
 * holding. The border is gone; a lit top edge does that job without a line. */
.tweet {
  background: var(--paper);
  border: 0;
  border-radius: var(--r-2xl);
  /* A black card on a lighter page is the one surface here that sits BELOW the
   * background rather than above it, so a drop shadow cannot describe its edge
   * — it would be dark on dark. A full inset rim does instead: the card reads as
   * cut into the page, which is exactly what it is. The shadow stays for the
   * light scheme, where white-on-grey still casts one. */
  box-shadow: var(--e1), inset 0 0 0 1px color-mix(in srgb, var(--fg) 9%, transparent);
  padding: var(--s6);
}

.tweet__head { display: flex; align-items: center; gap: var(--s4); }

/* We never stored avatar URLs, so a deterministic monogram — honest, and it
 * reads as a choice rather than a broken image. */
.avatar {
  /* aspect-ratio + a fixed width, so the circle stays a circle when the handle
     beside it is long enough to squeeze a flex item */
  flex: 0 0 auto; width: 42px; height: 42px; aspect-ratio: 1;
  border-radius: 50%;
  display: grid; place-items: center;
  font: 600 var(--t6)/1 var(--ui);
  /* not #fff on a mid-tone fill: a warm off-white sits INSIDE the colour
     instead of punching a hole through it */
  color: #fdfaf4;
  background: var(--accent);
  box-shadow: var(--e0), inset 0 1px 0 rgba(255, 255, 255, .18);
}
.avatar--sm { width: 24px; height: 24px; font-size: var(--t2); }

.tweet__who { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; }
.tweet__handle {
  font-weight: 600; font-size: var(--t5); letter-spacing: -.01em;
  color: color-mix(in srgb, var(--fg) 92%, var(--fg-dim));
}
.tweet__meta {
  font: 400 var(--t2)/1.3 var(--mono);
  font-variant-numeric: tabular-nums;
  color: var(--fg-dim);
}

.tweet__out {
  margin-left: auto; color: var(--fg-dim); text-decoration: none;
  font-size: var(--t6); padding: var(--s4); border-radius: var(--r-full);
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.tweet__out:hover, .tweet__out:active { color: var(--link); background: var(--raise); }

/* ── T: translate this, to Hebrew and back ────────────────────────────────
 *
 * The same object in three places — on the tweet, on the verdict, and on every
 * draft — because it does the same thing in all three and a reader should not
 * have to learn it twice. A frosted square rather than a pill or a word: it is
 * the only control that sits INSIDE somebody else's writing, so it has to read
 * as a mark on the surface rather than a button the app put there.
 *
 * The fill is --raise (6% of the text colour), so it takes its colour from
 * whatever it is laid on — the black paper of a tweet card, the mustard wash of
 * the verdict, the lighter panel of a draft — instead of carrying one of its
 * own into all three. */
.tbtn {
  position: relative;                /* for the tap halo below */
  flex: 0 0 auto;
  width: 26px; height: 26px;
  display: grid; place-items: center;
  font: 600 var(--t3)/1 var(--ui);
  color: var(--fg-dim);
  background: var(--raise);
  border: 1px solid var(--hairline);
  border-radius: var(--r-xs);
  cursor: pointer;
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease),
              border-color var(--fast) var(--ease);
}
/* [hidden] is a UA display:none, and the display:grid above would beat it —
   which is how a button with nothing to translate stays on screen. */
.tbtn[hidden] { display: none; }

/* 26px is a mark, not a tap target: the halo takes the hit area out to 42px
   without growing the square. -8px rather than more, because in the tweet head
   the ↗ sits 12px away and its own padding starts there — any wider and the T
   would be stealing presses meant for "open on X". */
.tbtn::after { content: ""; position: absolute; inset: -8px; }

.tbtn:hover, .tbtn:active {
  color: var(--fg); background: var(--raise-2);
}
/* showing Hebrew — the same accent the app uses for "this is on" */
.tbtn[data-on] {
  color: var(--accent-fg); background: var(--accent); border-color: transparent;
}
/* in flight. No spinner: the press answers in a second or two, and a second
   moving part on a card that already has one would be noise. */
.tbtn[data-busy] { opacity: .5; cursor: progress; }

/* In the head the T and the ↗ travel together on the right, so the auto margin
   moves to whichever comes first. */
.tweet__head .tbtn { margin-left: auto; }
.tweet__head .tbtn + .tweet__out { margin-left: 0; }
/* In the verdict row HOT already claims the right edge; the T follows it, and
   takes the edge itself on the (usual) card where there is no HOT. */
.verdict__hot[hidden] + .tbtn { margin-left: auto; }

/* A tweet is the one thing here worth reading rather than scanning: a step up
 * in size and a full 1.6 of leading, in a white warm enough not to glare. */
.tweet__text {
  margin: var(--s5) 0 0;
  font-size: calc(var(--read) + 1px); line-height: 1.6;
  color: var(--fg);
  white-space: pre-wrap; overflow-wrap: anywhere;
}

.tweet__media { display: grid; gap: var(--s2); margin-top: var(--s5); }
.tweet__media:empty { display: none; }
.tweet__media img {
  width: 100%; max-height: 42vh; object-fit: cover;
  border-radius: var(--r-xl); border: 0;
  background: var(--raise); display: block;
  cursor: zoom-in;
}
/* Tall comparison memes are this user's own medium, and cover() cuts exactly
 * the half that carries the argument. One tap shows the whole frame. */
.tweet__media--open img { max-height: none; object-fit: contain; cursor: zoom-out; }
.tweet__media--pair { grid-template-columns: 1fr 1fr; }
.tweet__media--pair.tweet__media--open { grid-template-columns: 1fr; }
.tweet__vid { position: relative; }
/* A label over someone else's picture, so: smaller, the UI face rather than the
 * machine's monospace, a pill, and frosted instead of a black slab. It should
 * be readable when looked for and invisible when not. */
.tweet__vid::after {
  content: "▶ video — open on X";
  position: absolute; inset: auto var(--s3) var(--s3) auto;
  font: 500 10px/1.2 var(--ui); letter-spacing: .01em;
  background: rgba(18, 20, 26, .52); color: rgba(255, 255, 255, .88);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  padding: var(--s1) var(--s3); border-radius: var(--r-full);
}

/* One row of numbers, breathing. The icons are stroked SVG (see icon() in
 * app.js) rather than the emoji that were here: an emoji font renders ♥ and 👁
 * in two different weights, two different colours and two different eras, which
 * is four glyphs that agree with nothing including each other. */
.tweet__metrics {
  display: flex; flex-wrap: wrap; gap: var(--s7); margin-top: var(--s5);
  font: 400 var(--t3)/1 var(--mono);
  font-variant-numeric: tabular-nums;
  color: var(--fg-dim);
}
.tweet__metrics .metric { gap: var(--s3); }
.tweet__metrics .ico { opacity: .75; }
/* four numbers on one line at 320 too — wrapped, they read as two rows of
   unrelated figures rather than one row of context */
@media (max-width: 350px) { .tweet__metrics { gap: var(--s5); } }

/* ── the verdict strip: the signature ─────────────────────────────────── */

/* Still marked by the signal colour, but the hard 2px rule down the left is now
 * a soft edge on a rounded wash: it is an aside about the tweet, not a warning
 * label stuck to it. */
.verdict {
  margin-top: var(--s5); padding: var(--s5);
  border-left: 3px solid color-mix(in srgb, var(--signal) 70%, transparent);
  background: color-mix(in srgb, var(--signal) 9%, transparent);
  border-radius: var(--r-sm) var(--r-xl) var(--r-xl) var(--r-sm);
}

.verdict__row { display: flex; align-items: center; gap: var(--s4); }

.verdict__score {
  font: 600 var(--t3)/1 var(--mono);
  font-variant-numeric: tabular-nums;
  color: var(--signal);
}

/* Ten segments with the pass threshold marked. A bare number tells you the
 * score; this tells you where the line is — which is the thing actually being
 * tuned week to week. The same shape is reused for any bounded measure with a
 * limit (see .gauge--wide on the board): inventing a second meter beside the
 * app's one distinctive component would have weakened both. */
.gauge { display: flex; gap: var(--s0); }
.gauge i {
  width: 7px; height: var(--s4); border-radius: var(--s0);
  background: color-mix(in srgb, var(--fg) 14%, transparent);
  transition: background var(--fast) var(--ease);
}
.gauge i[data-on] { background: var(--signal); }
.gauge i[data-edge] { box-shadow: inset -1px 0 0 var(--fg-dim); }
.gauge i[data-warn] { background: var(--danger); }
.gauge--wide { gap: 3px; }
.gauge--wide i { flex: 1; width: auto; height: var(--s3); border-radius: var(--s0); }

.verdict__hot {
  margin-left: auto;
  font: 600 var(--t1)/1 var(--mono); letter-spacing: .08em;
  color: var(--danger);
  border: 1px solid var(--danger);
  padding: 3px var(--s2); border-radius: var(--s1);
}

.verdict__why {
  margin: var(--s3) 0 0; font-size: var(--t3); line-height: 1.45;
  color: var(--fg-dim);
}

/* ── drafts ───────────────────────────────────────────────────────────── */

.drafts { display: grid; gap: var(--s4); margin-top: var(--s4); }

.draft {
  background: var(--bg-soft);
  border: 0;
  border-radius: var(--r-2xl);
  box-shadow: var(--e1), var(--lift);
  padding: var(--s6);
}
.draft__top {
  display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s3);
  font: 500 var(--t1)/1 var(--mono); letter-spacing: .04em;
  color: var(--fg-dim); text-transform: uppercase;
}
.draft__n { color: var(--signal); font-weight: 700; }
.draft__body { font-size: var(--read); line-height: 1.45; white-space: pre-wrap;
               overflow-wrap: anywhere; }
.draft__media {
  margin-top: var(--s4); padding: var(--s4); border-radius: var(--r-sm);
  background: var(--raise);
  font: 400 var(--t2)/1.5 var(--mono);
  color: var(--fg-dim); white-space: pre-wrap; overflow-wrap: anywhere;
}
/* why this register was chosen — an aside about the option, so it sits below
 * the prompt and reads lighter than either. With two image prompts offered in
 * two different media, this line IS the choice: nobody picks between them by
 * reading eighty words of generator instructions twice. */
.draft__why {
  margin: var(--s3) 0 0; font-size: var(--t2); line-height: 1.45;
  color: var(--fg-dim); font-style: italic;
}

.draft__count {
  margin-left: auto; font-variant-numeric: tabular-nums;
}
.draft__count[data-over] { color: var(--danger); }

.draft__row { display: flex; gap: var(--s3); margin-top: var(--s4); }

/* "Write my own reply" is the alternative to the whole machine, and it used to
 * look like a footnote under it. Full width, tall enough to be a destination,
 * and the softest surface in the view. */
.card__own { margin-top: var(--s5); display: flex; }
.card__own .btn {
  width: 100%; padding: var(--s5) var(--s6); min-height: 58px;
  border-radius: var(--r-2xl);
  background: var(--bg-soft-2); color: var(--fg);
  font-size: var(--t5);
  box-shadow: var(--e1), var(--lift);
}
.card__own .btn:active { transform: scale(.985); }
/* it carries .btn--ghost, whose :hover would otherwise strip the fill and the
   shadow straight back off again — same specificity, and it is declared later */
@media (hover: hover) {
  .card__own .btn:hover {
    background: color-mix(in srgb, var(--fg) 14%, var(--bg-soft));
    box-shadow: var(--e1), var(--lift);
  }
}

/* ── working / failed ─────────────────────────────────────────────────── */

.working {
  display: flex; align-items: center; gap: var(--s4);
  margin-top: var(--s5); padding: var(--s5) var(--s6);
  border: 1px dashed var(--hairline); border-radius: var(--r-2xl);
  font: 400 var(--t3)/1.4 var(--mono); color: var(--fg-dim);
}
.working__dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--signal);
  animation: pulse 1.1s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: .25; transform: scale(.8); } }

.failed {
  margin-top: var(--s5); padding: var(--s5);
  border-left: 3px solid color-mix(in srgb, var(--danger) 70%, transparent);
  background: color-mix(in srgb, var(--danger) 11%, transparent);
  border-radius: var(--r-sm) var(--r-xl) var(--r-xl) var(--r-sm);
}
.failed__what { margin: 0; font: 500 var(--t3)/1.4 var(--ui); }
.failed__hint { margin: var(--s2) 0 0; font-size: var(--t2); color: var(--fg-dim); }

/* ── the publish kit ──────────────────────────────────────────────────── */

/* Where an approval ends: X posting is not wired, so this is the handoff.
 * It gets the signal colour and a solid left edge — it is the one screen that
 * asks you to go do something outside the app. */
.kit {
  border-left: 3px solid var(--signal);
  background: var(--bg-soft);
  border-radius: 0 var(--r-lg) var(--r-lg) 0;
  box-shadow: var(--e1);
  padding: var(--s5);
  margin-bottom: var(--s4);
  animation: rise var(--slow) var(--ease);
}

.kit__head {
  display: flex; align-items: baseline; gap: var(--s3);
  font: 600 var(--t2)/1.3 var(--mono); letter-spacing: .05em;
  text-transform: uppercase; color: var(--signal);
  margin-bottom: var(--s4);
}
.kit__when { margin-left: auto; color: var(--fg-dim); text-transform: none;
             letter-spacing: 0; font-weight: 400; }

.kit__block { margin-bottom: var(--s4); }

.kit__label {
  display: flex; align-items: center; gap: var(--s4); margin-bottom: var(--s2);
  font: 500 var(--t1)/1 var(--mono); letter-spacing: .04em;
  text-transform: uppercase; color: var(--fg-dim);
}

.kit__copy {
  appearance: none; cursor: pointer; margin-left: auto;
  border: 1px solid var(--hairline); background: none; color: var(--fg);
  border-radius: var(--r-xs); padding: var(--s3) var(--s4);
  font: 500 var(--t1)/1 var(--mono); letter-spacing: .03em;
  transition: transform var(--fast) var(--ease);
}
.kit__copy:active { transform: scale(.95); }
.kit__copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.kit__body {
  margin: 0; padding: var(--s4);
  background: var(--raise); border-radius: var(--r-sm);
  font-size: var(--read); line-height: 1.45;
  white-space: pre-wrap; overflow-wrap: anywhere;
  -webkit-user-select: text; user-select: text;
}
.kit__body--mono { font: 400 var(--t2)/1.55 var(--mono); color: var(--fg-dim); }

/* The attached picture. Capped by height rather than width: a tall portrait
   photo at 100% width pushes the "Reply under @…" button off the screen, and a
   kit whose action you have to hunt for is the thing this tab exists to fix. */
.kit__imagelink { display: block; border-radius: var(--r-sm); overflow: hidden;
                  background: var(--raise); }
.kit__image {
  display: block; width: 100%; max-height: 60vh;
  object-fit: contain; background: var(--raise);
}

.kit__link { display: flex; gap: var(--s3); margin-top: var(--s4); }
.kit__link .editor__input { flex: 1; }
/* the destructive action sits apart and below, never beside the one you came
   here to use */
.kit__foot { margin-top: var(--s4); padding-top: var(--s4);
             border-top: 1px solid var(--hairline); }
.kit__foot .btn { width: 100%; }

/* ── editors ──────────────────────────────────────────────────────────── */

.editor { margin-top: var(--s4); }
.editor__label {
  display: block; margin: 0 0 var(--s2);
  font: 500 var(--t1)/1 var(--mono); letter-spacing: .05em;
  text-transform: uppercase; color: var(--fg-dim);
}
.editor__hint { text-transform: none; letter-spacing: 0; opacity: .7; }

.editor__text, .editor__input {
  width: 100%; padding: var(--s5);
  background: var(--bg-soft); color: var(--fg);
  border: 0; border-radius: var(--r-xl);
  box-shadow: inset 0 0 0 1px var(--hairline);
  font: 400 var(--read)/1.6 var(--ui);
  resize: vertical;
  transition: box-shadow var(--fast) var(--ease);
}
.editor__text--tall { min-height: 150px; font-family: var(--mono); font-size: var(--t3); }
.editor__text:focus, .editor__input:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
  box-shadow: none;
}
.editor__text + .editor__label { margin-top: var(--s4); }
.editor__from { margin: var(--s3) 0 0; font: 400 var(--t2)/1 var(--mono);
                color: var(--fg-dim); }

/* ── a rendered image, under the prompt that produced it ──────────────── */

.shot { margin: var(--s3) 0 0; }
.shot__link { display: block; border-radius: var(--r-sm); overflow: hidden; }
.shot__img {
  display: block; width: 100%; max-height: 50vh;
  object-fit: contain; background: var(--raise);
}
/* Deliberately quiet: the wait is a minute, and something pulsing for a minute
   next to two prompts you are meant to be reading is an irritant. */
.shot__wait {
  margin: 0; padding: var(--s5); text-align: center;
  background: var(--raise); border-radius: var(--r-sm);
  font: 400 var(--t2)/1.4 var(--mono); color: var(--fg-dim);
}
/* The specific reason, never "failed". The prompt above it is still copyable,
   which is the whole fallback. */
.shot__error {
  margin: 0; padding: var(--s4) var(--s5);
  background: color-mix(in srgb, var(--danger) 12%, var(--raise));
  border-radius: var(--r-sm);
  font: 400 var(--t2)/1.45 var(--mono); color: var(--danger);
}

/* ── attaching a picture ──────────────────────────────────────────────── */

.upload { margin-bottom: var(--s4); }
.upload .btn--ghost {
  width: 100%; box-shadow: inset 0 0 0 1px var(--hairline);
  border-radius: var(--r-xl); padding: var(--s5);
}
.upload__preview { margin-top: var(--s3); }
.upload__img {
  display: block; width: 100%; max-height: 40vh; object-fit: contain;
  background: var(--raise); border-radius: var(--r-sm);
}
.upload__remove {
  appearance: none; cursor: pointer; width: 100%; margin-top: var(--s2);
  border: 0; background: none; color: var(--fg-dim);
  padding: var(--s3); font: 500 var(--t1)/1 var(--mono);
  letter-spacing: .04em; text-transform: uppercase;
}
.upload__remove:active { opacity: .6; }
/* The one line that says WHY an upload was refused. Muted for the success case
   ("Attached · 1200×900") and coloured only for a refusal, so a failure is not
   something you have to read carefully to notice. */
.upload__note { margin: var(--s3) 0 0; font: 400 var(--t2)/1.4 var(--mono);
                color: var(--fg-dim); }
.upload__note--bad { color: var(--danger); }
.editor__actions { display: flex; gap: var(--s3); margin-top: var(--s4); }

.reasons { display: flex; flex-wrap: wrap; gap: var(--s2); margin-bottom: var(--s4); }

/* ── buttons ──────────────────────────────────────────────────────────── */

/* Stroked icons, one weight, one join. Sized in em so an icon inside a button
 * scales with that button's type instead of needing its own number. */
.ico {
  flex: 0 0 auto; width: 1.25em; height: 1.25em; display: block;
  overflow: visible;
}
.ico--lg { width: 1.5em; height: 1.5em; }

.btn {
  appearance: none; cursor: pointer;
  border: 0;
  background: var(--bg-soft); color: var(--fg);
  border-radius: var(--r-xl); padding: var(--s4) var(--s5);
  font: 500 var(--t4)/1.2 var(--ui);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s3);
  box-shadow: var(--e0), var(--lift);
  transition: transform .1s var(--ease), filter var(--fast) var(--ease),
              background var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease);
}
.btn:active { transform: scale(.96); box-shadow: var(--e0); }
@media (hover: hover) {
  .btn:hover { background: var(--bg-soft-2); box-shadow: var(--e1), var(--lift); }
}
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn[disabled] { opacity: .45; pointer-events: none; }

.btn--go {
  background: var(--accent); color: var(--accent-fg);
  flex: 1;
  box-shadow: var(--e0), 0 4px 14px color-mix(in srgb, var(--accent) 34%, transparent);
}
@media (hover: hover) {
  .btn--go:hover { background: color-mix(in srgb, var(--accent) 88%, #fff); }
}
/* the publish hand-off is an <a> styled as a button — it must not read as prose */
a.btn { text-decoration: none; }
/* Coral, not alarm red, and it carries a wash of its own colour rather than an
 * outline: rejecting a tweet is an ordinary move, not a warning. */
.btn--no {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 14%, var(--bg-soft));
}
@media (hover: hover) {
  .btn--no:hover { background: color-mix(in srgb, var(--danger) 22%, var(--bg-soft)); }
}
.btn--ghost { background: none; box-shadow: none; }
@media (hover: hover) {
  .btn--ghost:hover { background: var(--raise); box-shadow: none; }
}
.btn--wide { width: 100%; margin-top: var(--s4); }
.btn--small { padding: var(--s4); font-size: var(--t3); border-radius: var(--r-lg); }

.btn kbd {
  font: 500 var(--t1)/1 var(--mono); color: var(--fg-dim);
  border: 1px solid var(--hairline); border-radius: 3px;
  padding: var(--s0) var(--s1);
}
/* A touch device has no keyboard to hint at. */
@media (pointer: coarse) { .btn kbd { display: none; } }

/* ── action bar: fixed in the thumb zone ──────────────────────────────── */

/* The bar is a shelf the buttons sit on, not a strip they are cut into: it has
 * its own rounded top edge, its own darker material, and a shadow thrown UP
 * onto the content it overlaps. */
.actions {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 6;
  display: grid; gap: var(--s3);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, .8fr) minmax(0, .8fr);
  padding: var(--s5) var(--s5) calc(var(--s5) + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--bg-deep) 88%, transparent);
  backdrop-filter: saturate(180%) blur(18px);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  border-top: 0;
  border-radius: var(--r-2xl) var(--r-2xl) 0 0;
  box-shadow: 0 -8px 28px rgba(0, 0, 0, .22), var(--lift);
}

/* Every action is a pill with its own weight. They were four flat rectangles
 * with an emoji in each; the padding and the shadow are what make them feel
 * like things to press rather than places to tap. */
.actions .btn {
  padding: var(--s4) var(--s3); min-height: 52px;
  border-radius: var(--r-full);
  background: var(--bg-soft-2);
  box-shadow: var(--e0), var(--lift);
}
.actions .btn:active { transform: scale(.94); }
.actions .btn--no {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 16%, var(--bg-soft-2));
}
.btn__face {
  font-size: var(--t5);
  display: inline-flex; align-items: center; gap: var(--s2);
}
/* the option count as a small mustard chip rather than a digit in the label:
   the pen says what the button does, the chip says how many */
.btn__num {
  min-width: 19px; padding: 2px var(--s1); border-radius: var(--r-xs);
  background: color-mix(in srgb, var(--signal) 88%, transparent); color: var(--bg);
  font: 700 var(--t2)/1.25 var(--mono); font-variant-numeric: tabular-nums;
  text-align: center;
}

/* ── sheets ───────────────────────────────────────────────────────────── */

.sheet { position: fixed; inset: 0; z-index: 20; display: flex; align-items: flex-end; }
.sheet__scrim { position: absolute; inset: 0; background: rgba(0,0,0,.5);
                animation: fade var(--fast) var(--ease); }
@keyframes fade { from { opacity: 0; } }
.sheet__panel {
  position: relative; width: 100%; max-height: 82vh;
  display: flex; flex-direction: column;
  background: var(--bg); border-radius: var(--r-lg) var(--r-lg) 0 0;
  border-top: 1px solid var(--hairline);
  box-shadow: var(--e2);
  animation: slideup var(--slow) var(--ease);
}
@keyframes slideup { from { transform: translateY(14%); } }
.sheet__head {
  display: flex; align-items: center; gap: var(--s4);
  padding: var(--s5) var(--s5) var(--s4);
  font: 600 var(--t3)/1 var(--mono); letter-spacing: .04em;
  text-transform: uppercase; color: var(--fg-dim);
  border-bottom: 1px solid var(--hairline);
}
.sheet__close {
  appearance: none; cursor: pointer; margin-left: auto;
  width: 34px; height: 34px; border-radius: var(--r-sm);
  border: 1px solid var(--hairline); background: none; color: var(--fg);
  font-size: var(--t4); line-height: 1;
}
.sheet__body {
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: var(--s4) var(--s5) calc(var(--s5) + env(safe-area-inset-bottom));
}

/* one row of the queue overview */
.peek {
  display: flex; align-items: baseline; gap: var(--s4); width: 100%;
  text-align: left; cursor: pointer;
  background: none; border: 0; border-bottom: 1px solid var(--hairline);
  color: var(--fg); padding: var(--s4) var(--s0);
}
.peek[aria-current="true"] { background: var(--raise); }
.peek__seq { font: 600 var(--t2)/1.3 var(--mono); color: var(--signal); }
.peek__who { font: 500 var(--t2)/1.3 var(--ui); color: var(--fg-dim);
             white-space: nowrap; }
.peek__text {
  flex: 1; min-width: 0; font-size: var(--t3); line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.peek__ttl { font: 400 var(--t1)/1.3 var(--mono); color: var(--fg-dim); }
.peek__ttl[data-soon] { color: var(--danger); }

/* ── lists: history + publish ─────────────────────────────────────────── */

/* A physical switch, not two buttons that happen to touch. The track is sunk
 * into the page — darker than the surface it sits on, with an inset shadow —
 * and the selected half is a raised pill riding in it. That is the whole
 * metaphor, and it is why the track is darker rather than lighter: a groove you
 * can see is what makes the thing in it look movable. */
.seg {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s1);
  padding: var(--s1); margin-bottom: var(--s5);
  background: var(--bg-deep); border: 0;
  border-radius: var(--r-full);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, .22);
}
.seg__btn {
  appearance: none; cursor: pointer; border: 0; background: none;
  color: var(--fg-dim); border-radius: var(--r-full); min-height: 40px;
  font: 500 var(--t3)/1 var(--ui);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease);
}
.seg__btn[aria-selected="true"] {
  background: var(--bg-soft); color: var(--fg);
  box-shadow: var(--e0), var(--lift);
}
.seg__btn:active { transform: none; }        /* the pill slides; it does not dip */

.controls { display: grid; gap: var(--s2); margin-bottom: var(--s4); }
.filter { display: flex; flex-wrap: wrap; gap: var(--s2); }
/* Off-state chips carry no shadow and almost no fill. There are three of them
 * above every screen of the feed and they are touched about once a week; giving
 * them the same raised treatment as a control you actually press made the top
 * of the History tab louder than the replies underneath it. */
.chip {
  appearance: none; cursor: pointer;
  border: 0;
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  color: var(--fg-dim);
  border-radius: var(--r-full); padding: var(--s3) var(--s5); min-height: 38px;
  font: 500 var(--t2)/1 var(--mono); font-variant-numeric: tabular-nums;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease),
              transform .1s var(--ease);
}
.chip:active { transform: scale(.94); }
@media (hover: hover) {
  .chip:hover { background: color-mix(in srgb, var(--fg) 10%, transparent);
                color: var(--fg); }
}
/* Pastel, not the full signal mustard. The chip is a sort order, not a warning
 * — it only has to look chosen, and at full saturation it was the brightest
 * thing on a screen whose subject is the replies below it. */
.chip--on {
  color: var(--chip-on-fg); background: var(--chip-on-bg);
  border-color: transparent;
}
@media (hover: hover) {
  .chip--on:hover { color: var(--chip-on-fg); background: var(--chip-on-bg); }
}

/* One row of History→Decisions, and the Board's attention list. Same material
 * as .post, one size down: these are records of a decision rather than the work
 * itself, so they stay quieter without becoming a different kind of object. */
.hit {
  border: 0; border-radius: var(--r-xl);
  background: var(--bg-soft); box-shadow: var(--e0), var(--lift);
  padding: var(--s5); margin-bottom: var(--s4);
}
.hit__top {
  display: flex; align-items: baseline; gap: var(--s4);
  margin-bottom: var(--s4);
  font: 500 var(--t2)/1.3 var(--mono); font-variant-numeric: tabular-nums;
}
.hit__likes { color: var(--signal); font-weight: 700; font-size: var(--t5);
              flex: 0 0 auto; }
/* the metric run is the only part allowed to lose characters, so the badge and
 * the outbound link stay on the first line instead of wrapping onto their own */
.hit__rest { color: var(--fg-dim); min-width: 0; flex: 1 1 auto;
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the one control on a feed card, so it gets a target worth aiming at and an
   answer when you hit it */
.hit__out {
  flex: 0 0 auto; color: var(--fg-dim); text-decoration: none;
  padding: var(--s2) var(--s3); border-radius: var(--r-full);
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease),
              transform .1s var(--ease);
}
.hit__out:hover { color: var(--link); background: var(--raise); }
.hit__out:active { transform: scale(.9); color: var(--link); }
.hit__said { font-size: var(--read); line-height: 1.6; color: var(--fg);
             white-space: pre-wrap; overflow-wrap: anywhere; }
/* the tweet a decision was about. Not the full .quote treatment — it is a line
   of context, not a nested card — but the same soft edge and room to breathe. */
.hit__to {
  margin-top: var(--s4); padding: var(--s3) var(--s4);
  border-left: 2px solid color-mix(in srgb, var(--fg) 14%, transparent);
  border-radius: var(--r-xs) var(--r) var(--r) var(--r-xs);
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  font-size: var(--t3); line-height: 1.55;
  color: var(--fg-dim); overflow-wrap: anywhere;
}
.hit__row { display: flex; gap: var(--s3); margin-top: var(--s5); flex-wrap: wrap; }

.chip--end { margin-left: auto; }
.chip__dot {
  display: inline-block; width: var(--s2); height: var(--s2);
  border-radius: 50%; background: var(--signal); margin-left: var(--s2);
  vertical-align: 1px;
}
.chip--on .chip__dot { background: var(--bg); }

.sheet__group {
  margin: var(--s5) 0 var(--s3);
  font: 600 var(--t1)/1 var(--mono); letter-spacing: .07em;
  text-transform: uppercase; color: var(--fg-dim);
}
.sheet__group:first-child { margin-top: 0; }

/* ── the feed: a published reply, shaped like a reply ─────────────────── */

/* This is what History→Published shows. The row it replaces led with eighty
 * words of monospace image-generator prompt and demoted the tweet being
 * answered to a dim left border — a debugging view of a reply rather than a
 * reply. Here the reply reads first, its picture second, the tweet it answers
 * third as a real quoted card, and every trace of how it was made is folded
 * away at the bottom. */
.post {
  border: 0; border-radius: var(--r-2xl);
  background: var(--bg-soft); box-shadow: var(--e1), var(--lift);
  padding: var(--s6); margin-bottom: var(--s5);
  animation: rise var(--slow) var(--ease);
}
/* the card has no border to colour any more, so the mark is a ring in the
   shadow stack — same look, and it survives the border going away */
.post--best {
  box-shadow: var(--e1), var(--lift),
              0 0 0 1.5px color-mix(in srgb, var(--signal) 45%, transparent);
}

/* the board's trophy shelf: the reply and its numbers, nothing else */
.post--compact { padding: var(--s4); }
.post--compact .post__media img { max-height: 26vh; }
.post--compact .post__metrics { margin-top: var(--s4); padding-top: var(--s3); }

.post__head { display: flex; align-items: center; gap: var(--s4);
              margin-bottom: var(--s5); }
.post__who { display: flex; flex-direction: column; gap: var(--s1); min-width: 0;
             margin-right: auto; }
.post__me {
  font: 600 var(--t4)/1.2 var(--ui);
  color: color-mix(in srgb, var(--fg) 92%, var(--fg-dim));
}
.post__when {
  font: 400 var(--t2)/1.3 var(--mono); font-variant-numeric: tabular-nums;
  color: var(--fg-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.post__said {
  margin: 0; font-size: var(--read); line-height: 1.6;
  color: var(--fg);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.post__media { margin-top: var(--s4); }
.post__media img { max-height: 52vh; }
.post__nomedia {
  margin: var(--s4) 0 0; padding: var(--s4);
  border: 1px dashed var(--hairline); border-radius: var(--r-sm);
  font: 400 var(--t2)/1.3 var(--mono); color: var(--fg-dim); text-align: center;
}

/* The tweet being answered — a card inside a card, the way X nests a quote.
 *
 * Pure black in the dark scheme and pure white in the light one, the same
 * --paper the Queue card sits on. Two reasons it has to be this and not a tint:
 * it is the one place in the feed where somebody else's words sit inside ours,
 * and the contrast is what tells them apart at a glance while scrolling. The
 * radius is one rung below the card containing it, because a nested corner that
 * matches its parent's reads as a mistake rather than a nesting. */
.quote {
  margin-top: var(--s5); padding: var(--s5);
  border: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
  border-radius: var(--r-xl);
  background: var(--paper);
}
.quote__head { display: flex; align-items: center; gap: var(--s3);
               margin-bottom: var(--s4); }
.quote__who { font: 600 var(--t3)/1 var(--ui); color: var(--fg-dim); }
.quote__text {
  margin: 0; font-size: var(--t4); line-height: 1.6;
  color: color-mix(in srgb, var(--fg) 78%, var(--fg-dim));
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.quote__media { margin-top: var(--s4); }
.quote__media img { max-height: 28vh; border-radius: var(--r); }

.post__metrics {
  display: flex; flex-wrap: wrap; gap: var(--s6);
  margin-top: var(--s5); padding-top: var(--s5);
  border-top: 1px solid var(--hairline);
  font: 500 var(--t3)/1 var(--mono); font-variant-numeric: tabular-nums;
  color: var(--fg-dim);
}
.metric { display: inline-flex; align-items: center; gap: var(--s2); }
.metric__i { font-size: var(--t2); opacity: .8; }
/* likes is the number he actually ranks by; the other three are context */
.metric--likes { color: var(--signal); font-weight: 700; font-size: var(--t5); }
.metric--likes .metric__i { font-size: var(--t3); opacity: 1; }

/* how the reply was made: seq, decision, the generator prompt, what Opus had
 * proposed. All calibration signal, none of it the reply. */
.prov { margin-top: var(--s4); }
.prov__head {
  cursor: pointer; list-style: none;
  font: 400 var(--t1)/1.4 var(--mono); letter-spacing: .03em;
  color: var(--fg-dim); padding: var(--s2) 0;
}
.prov__head::-webkit-details-marker { display: none; }
.prov__head::before { content: "▸ "; }
.prov[open] .prov__head::before { content: "▾ "; }
.prov__label {
  margin: var(--s4) 0 var(--s2);
  font: 500 var(--t1)/1 var(--mono); letter-spacing: .04em;
  text-transform: uppercase; color: var(--fg-dim);
}
.prov__body {
  margin: 0; font-size: var(--t3); line-height: 1.45; color: var(--fg-dim);
  overflow-wrap: anywhere;
}

/* the type badge: an image reply is a different act from a written one */
.tag {
  border: 1px solid var(--hairline); border-radius: var(--r-xs);
  padding: var(--s1) var(--s2); font: 500 var(--t1)/1.4 var(--mono);
  letter-spacing: .03em; color: var(--fg-dim);
}
.tag--image { color: var(--signal); border-color: color-mix(in srgb, var(--signal) 45%, transparent); }
.tag--bad { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
.tag--best { color: var(--bg); background: var(--signal); border-color: transparent;
             font-weight: 700; }

/* ── board ────────────────────────────────────────────────────────────── */

.panel {
  border: 0; border-radius: var(--r-2xl);
  background: var(--bg-soft); box-shadow: var(--e1), var(--lift);
  padding: var(--s6); margin-bottom: var(--s5);
}
/* Quieter than it was: a section label is scaffolding, and at 600 weight in
 * near-white it was competing with the numbers it introduces. */
.panel__head {
  font: 500 10px/1 var(--mono); letter-spacing: .12em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--fg-dim) 88%, transparent);
  margin-bottom: var(--s5);
}
.stat { display: flex; align-items: center; gap: var(--s4);
        padding: var(--s3) 0;
        font: 400 var(--t3)/1.5 var(--ui); }
.stat__k { color: var(--fg-dim); }
.stat__v { margin-left: auto; font: 500 var(--t3)/1.4 var(--mono);
           font-variant-numeric: tabular-nums; }
/* pastel, not alarm: a warning here means "look at this today", never "stop" */
.stat__v[data-warn] {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 15%, transparent);
  padding: var(--s1) var(--s3); border-radius: var(--r-xs);
}

/* The one stat you can open. It reaches the replies waiting on a picture from
   you — the job the Publish tab used to do before the bot published on its own
   (2026-09-03). Marked with a chevron rather than a button face: it is still a
   reading first, and only sometimes an errand. */
.stat--tap { cursor: pointer; }
.stat--tap .stat__k::after { content: ' ›'; color: var(--fg-dim); }
.stat--tap:hover .stat__k, .stat--tap:focus-visible .stat__k { color: var(--fg); }
.stat--tap:focus-visible { outline: 2px solid var(--accent);
                           outline-offset: 2px; border-radius: var(--r-xs); }

/* a state, not a measurement — so it gets a chip and the UI face */
.pill {
  padding: var(--s2) var(--s4); border-radius: var(--r-full);
  font: 500 var(--t2)/1.2 var(--ui); letter-spacing: 0;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: var(--fg-dim);
}
.pill--ok {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: color-mix(in srgb, var(--accent) 82%, var(--fg));
}
.pill--warn {
  background: color-mix(in srgb, var(--danger) 16%, transparent);
  color: var(--danger);
}
.pill--idle {
  background: color-mix(in srgb, var(--signal) 15%, transparent);
  color: var(--signal);
}

/* the daily budget: a level in a vessel, so one continuous pill */
.meter {
  height: 10px; border-radius: var(--r-full);
  background: color-mix(in srgb, var(--fg) 9%, transparent);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .18);
  overflow: hidden; margin: var(--s2) 0 var(--s4);
}
.meter__fill {
  display: block; height: 100%; border-radius: var(--r-full);
  background: linear-gradient(90deg,
              color-mix(in srgb, var(--signal) 72%, transparent),
              var(--signal));
  transition: width var(--slow) var(--ease);
}
.meter__fill[data-warn] {
  background: linear-gradient(90deg,
              color-mix(in srgb, var(--danger) 68%, transparent),
              var(--danger));
}

.panel--in { animation: rise var(--slow) var(--ease) backwards; }
.panel--flush { padding-bottom: var(--s3); }
.panel--flush .post { box-shadow: none; background: color-mix(in srgb, var(--bg) 55%, transparent); }

/* the hero: the one number the week is judged on, at a size that says so */
.hero { display: flex; align-items: baseline; gap: var(--s4); }
.hero__n {
  font: 300 var(--t-hero)/1 var(--mono); font-variant-numeric: tabular-nums;
  letter-spacing: -.02em; color: var(--fg);
}
.hero__side { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }
.hero__unit { font: 500 var(--t4)/1 var(--ui); color: var(--fg-dim); }
.hero__delta {
  font: 500 var(--t2)/1 var(--mono); font-variant-numeric: tabular-nums;
  color: var(--signal);
}
.hero__delta[data-down] { color: var(--fg-dim); }
/* the reply count, demoted: effort, standing next to result */
.hero__sub {
  margin: var(--s4) 0 0; font-size: var(--t3); line-height: 1.5;
  color: var(--fg-dim);
}

/* Likes per week, as a line — one point per finished week.
 *
 * This and .bars sit on the same screen and are not two ways of drawing the
 * same thing. A line is for a level, which is what likes are: the value carries
 * from week to week and the shape between two points means something. Bars are
 * for a count of separate acts, where the gap between two weeks means nothing
 * and a week of zero has to look like zero rather than like a valley.
 */
.chart { margin-top: var(--s6); }
.chart__svg { display: block; width: 100%; overflow: visible; }
.chart__line {
  fill: none; stroke: var(--signal); stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
  /* the stroke must not stretch with preserveAspectRatio: none */
  vector-effect: non-scaling-stroke;
}
.chart__area { stroke: none; }
.chart__stop--top { stop-color: var(--signal); stop-opacity: .30; }
.chart__stop--bottom { stop-color: var(--signal); stop-opacity: 0; }
.chart__base {
  stroke: color-mix(in srgb, var(--fg) 12%, transparent); stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.chart__dot {
  fill: var(--signal);
  stroke: var(--bg-soft); stroke-width: 2.5;
  vector-effect: non-scaling-stroke;
}
.chart__axis {
  display: flex; justify-content: space-between; margin-top: var(--s3);
  font: 400 var(--t1)/1 var(--mono); color: var(--fg-dim);
}

/* Replies posted per week, as bars — every one of them a finished week.
 *
 * Two shapes have stood here. Fourteen daily bars of replies posted, then eight
 * weeks of likes as a curve. The curve was right for likes: a level carries
 * from week to week and the question is which way it is going. This is a count
 * of discrete acts again, and for a count the quiet weeks are the point — a bar
 * of zero is a week that produced nothing and looks like it, where a curve
 * would have drawn a graceful dip.
 *
 * Eight columns across a 375px phone leaves about 38px each, which is what sets
 * every size below: 11px numerals above the bars, 10px labels under them, and a
 * month name only on the label where the month turns over.
 */
.bars {
  display: flex; align-items: flex-end; gap: var(--s1);
  margin-top: var(--s6);
}
.bars__col {
  /* 1 1 0 with min-width:0 — the columns must SHRINK to fit eight across, and
   * a flex item's automatic minimum size would otherwise hold them at their
   * label width and push the last one out of the panel */
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: var(--s2);
}
.bars__n {
  font: 500 var(--t1)/1 var(--mono); font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, var(--fg-dim) 85%, transparent);
}
.bars__n[data-last] { color: var(--fg); }
.bars__track {
  /* max-width, because the columns divide whatever width they are given: on a
   * phone that is 34px each and on a desktop panel it is over 100px, at which
   * point a bar is wider than it is tall and stops reading as a bar at all.
   * The columns stay evenly spread; only the bar inside each one is capped. */
  width: 100%; max-width: 48px; height: 104px;
  display: flex; align-items: flex-end;
  border-radius: var(--r-xs);
  /* 4%, not 6: the track is a scale, not a second series. At 6 the unfilled
   * part of a short week read as a light grey block of its own, and eight of
   * them across a panel looked like the chart was mostly full of something. */
  background: color-mix(in srgb, var(--fg) 4%, transparent);
}
.bars__fill {
  display: block; width: 100%;
  border-radius: var(--r-xs);
  /* densest at the foot and fading upward — the opposite way round dissolves
   * the bottom edge into the track, and a bar that is vague about where it
   * meets the baseline is vague about its own value */
  background: linear-gradient(180deg,
              color-mix(in srgb, var(--signal) 34%, transparent),
              color-mix(in srgb, var(--signal) 66%, transparent));
}
/* the most recent finished week, at full strength: it is the one the others
 * are being read against, and on a chart with no axis the eye needs telling
 * which end is now */
.bars__fill[data-last] {
  background: linear-gradient(180deg,
              color-mix(in srgb, var(--signal) 62%, transparent),
              var(--signal));
}
.bars__wk {
  /* 9.5px is not a step on the type ladder, and it is here on purpose: at 10px
   * the one label that names a month ("Jun 22") measures 36px against a 34.4px
   * column on a 375px screen and bleeds into the gap on both sides. */
  font: 400 9.5px/1 var(--mono); color: var(--fg-dim);
  white-space: nowrap;
}

/* the per-day averages under the week and month totals: the same murmur as
 * .note, tucked up against the row it belongs to instead of floating between
 * two of them */
.cost__note {
  margin: calc(var(--s1) * -1) 0 var(--s4);
  font: 400 var(--t1)/1.5 var(--mono);
  color: color-mix(in srgb, var(--fg-dim) 80%, transparent);
}

.chart__note {
  margin: var(--s3) 0 0; font: 400 var(--t1)/1.5 var(--mono);
  color: color-mix(in srgb, var(--fg-dim) 80%, transparent);
}

/* ── misc ─────────────────────────────────────────────────────────────── */

.empty, .note {
  color: var(--fg-dim); font-size: var(--t3); line-height: 1.55;
  margin: 0 0 var(--s4);
}
.empty { text-align: center; padding: var(--s8) var(--s6); }
.empty .btn { margin-top: var(--s5); }

.fold { margin-top: var(--s7); border-top: 1px solid var(--hairline);
        padding-top: var(--s5); }
.fold__head { cursor: pointer; font: 500 var(--t3)/1 var(--ui); color: var(--fg-dim);
              padding: var(--s3) 0; }

.result { margin-top: var(--s4); font: 400 var(--t2)/1.55 var(--mono); }
.result b { color: var(--signal); font-weight: 600; }
.result s { color: var(--danger); text-decoration: none; }

/* boot skeleton: a blank screen is indistinguishable from a broken one */
.skel { border: 1px solid var(--hairline); border-radius: var(--r-lg);
        background: var(--bg-soft); padding: var(--s5); }
.skel i, .skel u {
  display: block; border-radius: var(--r-xs); background: var(--raise);
  animation: shimmer 1.3s ease-in-out infinite;
}
.skel u { width: 38px; height: 38px; border-radius: 50%; margin-bottom: var(--s4); }
.skel i { height: var(--s4); margin-bottom: var(--s3); }
.skel i:nth-child(3) { width: 92%; }
.skel i:nth-child(4) { width: 64%; }
@keyframes shimmer { 50% { opacity: .45; } }

.toast {
  position: fixed; left: 50%; bottom: calc(var(--bar) + var(--s5));
  transform: translateX(-50%); z-index: 30;
  max-width: calc(100% - var(--s8));
  display: flex; align-items: center; gap: var(--s4);
  background: var(--fg); color: var(--bg);
  padding: var(--s4) var(--s5); border-radius: var(--r-full);
  box-shadow: var(--e2);
  font: 500 var(--t3)/1.3 var(--ui); text-align: center;
  animation: rise var(--fast) var(--ease);
}
.toast[data-bad] { background: var(--danger); color: #fff; }
.toast__undo {
  appearance: none; cursor: pointer; background: none;
  border: 1px solid currentColor; border-radius: var(--r-full);
  color: inherit; padding: var(--s2) var(--s4); font: 600 var(--t2)/1 var(--ui);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ── the platform door ───────────────────────────────────────────────────
 *
 * Added when the app stopped being the only one on this server: "/" is now a
 * picker, and both apps need a way back to it. Purely additive — two new
 * selectors, no existing rule changed — and it only takes effect on a rail that
 * opts in with `rail--home`, so nothing that does not carry the class moves by
 * a pixel.
 *
 * A modifier rather than editing `.rail` itself, because the base rail is a
 * block whose two children stack; turning it into a flex row unconditionally
 * would relayout a screen that is in production and did not ask to change.
 */
.rail--home {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  padding-inline-start: var(--s2);
}

/* The tab strip takes the rest of the row; min-width lets it shrink past its
 * content, which is what keeps four labels on a 320px screen (same reasoning as
 * the minmax(0, 1fr) note above). */
.rail--home > .tabs { flex: 1 1 auto; min-width: 0; }

/* The queue meta row is a second line, not a third column. */
.rail--home > .meta { flex-basis: 100%; }

/* Small, quiet, always in the same place — it is a door, not a control. */
.backhome {
  display: grid;
  place-items: center;
  flex: none;
  width: 34px;
  height: var(--tap);
  border-radius: var(--r-full);
  color: var(--fg-dim);
  text-decoration: none;
  font-size: var(--t7);
  line-height: 1;
}

.backhome:active { background: var(--raise); }

@media (max-width: 350px) {
  .rail--home { gap: var(--s0); padding-inline-start: var(--s1); }
  .backhome { width: 26px; font-size: var(--t6); }
}

/* The door to Settings, mirroring .backhome at the other end of the rail: same
 * width, same quiet, so the row reads as [out] · tabs · [configure] rather than
 * as one control and one afterthought. Deliberately NOT a fifth tab — the strip
 * is a grid of four and the note above it explains why a fifth would not fit. */
.railcog {
  appearance: none; border: 0; cursor: pointer;
  display: grid;
  place-items: center;
  flex: none;
  width: 34px;
  height: var(--tap);
  border-radius: var(--r-full);
  background: none;
  color: var(--fg-dim);
  font-size: var(--t6);
  line-height: 1;
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease),
              transform var(--fast) var(--ease);
}
.railcog[aria-pressed="true"] { color: var(--accent); }
/* it turns when pressed. The one flourish on this screen, and it is the right
   object for it — a cog is the thing that reads as turnable. */
.railcog:active { background: var(--raise); transform: rotate(35deg); }
@media (hover: hover) { .railcog:hover { color: var(--fg); } }
.railcog:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 350px) {
  .railcog { width: 26px; font-size: var(--t5); }
}

/* ── settings: the watchlist ──────────────────────────────────────────── */

/* How many things are in this list is the first question the screen answers,
 * so the count sits in the heading instead of being counted by eye. */
.panel__count {
  margin-inline-start: var(--s3);
  padding: 1px var(--s2);
  border-radius: var(--r-xs);
  background: var(--raise);
  color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

/* Field and button on one line, ABOVE the list: adding is the reason to open
 * this screen, and putting it under a dozen rows would bury it. */
.watchadd { display: flex; gap: var(--s3); align-items: stretch; }
.watchadd .editor__input { flex: 1 1 auto; min-width: 0; margin: 0; }
/* .btn--go is flex:1 by default (it shares a row with Cancel in the editor);
   here it must stay exactly as wide as its label. */
.watchadd .btn--go { flex: 0 0 auto; }

.note--tight { margin: var(--s3) 0 0; font-size: var(--t2); }
.panel .empty { padding: var(--s6) 0 var(--s1); text-align: start; }

.watchset { margin-top: var(--s5); }
/* An account is a thing with attributes — hostile, added here — so it gets a
   line of its own, separated by a hairline rather than by a gap. */
.watchset--accounts { display: grid; }
/* A keyword is one word and there are a dozen; a column of one-word rows would
   be mostly empty space, so they wrap as tags. */
.watchset--keywords { display: flex; flex-wrap: wrap; gap: var(--s2); }

.watchrow {
  display: flex; align-items: center; gap: var(--s4);
  min-height: var(--tap);
  padding: var(--s2) 0;
}
.watchrow + .watchrow { border-top: 1px solid var(--hairline); }
.watchrow__who {
  display: flex; align-items: center; gap: var(--s3);
  flex: 1 1 auto; min-width: 0; flex-wrap: wrap;
}
.watchrow__name {
  font: 500 var(--t4)/1.3 var(--ui);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  min-width: 0;
}
.watchrow__tags { display: inline-flex; gap: var(--s2); flex: 0 0 auto; }

/* Provenance, murmured. It matters at the moment you go looking for it and
 * never before, so it is the quietest thing in the row. */
.tag {
  padding: 1px var(--s2); border-radius: var(--r-xs);
  background: var(--raise); color: var(--fg-dim);
  font: 500 var(--t1)/1.6 var(--mono); letter-spacing: .03em;
  white-space: nowrap;
}
/* Not a warning — a job. These are the accounts Opus is told to argue with. */
.tag--hostile {
  background: color-mix(in srgb, var(--danger) 16%, transparent);
  color: var(--danger);
}

.kw {
  display: inline-flex; align-items: center; gap: var(--s1);
  min-height: 40px;
  padding-inline: var(--s4) var(--s1);
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  font: 500 var(--t3)/1.2 var(--ui);
}
/* Added from the phone: a tinted rim rather than a different fill, so the set
   still reads as one material and the difference is legible without shouting. */
.kw[data-added] {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 34%, transparent);
}
.kw__word { overflow-wrap: anywhere; }

.watchx {
  appearance: none; border: 0; cursor: pointer; background: none;
  position: relative;
  flex: 0 0 auto;
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 50%;
  color: var(--fg-dim);
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease),
              transform .1s var(--ease);
}
/* The visible circle is 34px because a row of 46px buttons reads as a toolbar
   rather than a list; the target underneath is the full 46. Rows are exactly
   one --tap tall, so two neighbours meet and never overlap. */
.watchrow .watchx::after { content: ''; position: absolute; inset: -6px; }
.watchx:active { transform: scale(.88); }
@media (hover: hover) {
  .watchx:hover {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 14%, transparent);
  }
}
.watchx:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* What this screen took OFF config.yaml. Struck through because that is what it
 * is — an entry that still exists in the file and is being ignored — and
 * pressing it puts it back. */
.dropped {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2);
  margin-top: var(--s5); padding-top: var(--s4);
  border-top: 1px solid var(--hairline);
}
.dropped__label {
  flex: 1 0 100%;
  font: 500 var(--t1)/1.4 var(--mono); letter-spacing: .04em;
  text-transform: uppercase; color: var(--fg-dim);
}
.dropped .chip {
  min-height: 34px; padding: var(--s2) var(--s4);
  text-decoration: line-through;
  text-decoration-color: color-mix(in srgb, var(--fg-dim) 60%, transparent);
}
