/* ── tokens ─────────────────────────────────────────────────────────── */
/* Redesigned 2026-07-26 (operator's call): a modern product surface —
   white cards on a cool light ground, generous radii, soft elevation, and a
   single brand accent for the primary action and the active place. The
   ok/work/hold/stop state hues stay exactly as they were, so status still
   reads the same across every view; only the chrome changed. */
:root {
  /* ground + surfaces: cool near-white, hue ~255 */
  --app-bg:     oklch(0.972 0.004 255);   /* the ground the cards sit on */
  --paper:      oklch(0.999 0.001 255);   /* card / control surface (white) */
  --paper-sunk: oklch(0.968 0.004 255);   /* inset wells, page ground */
  --paper-band: oklch(0.958 0.006 255);   /* grouped-row headers */
  --rule:       oklch(0.916 0.006 255);
  --rule-soft:  oklch(0.945 0.005 255);
  --rule-hard:  oklch(0.872 0.008 255);

  /* Text is NEUTRAL grey, not blue-grey: at chroma 0.02/hue 265 the helper
     text read as a link. Chroma is now ~0.005 so only the accent is coloured.
     Levels are set by contrast on white, not by taste:
       ink   0.24 → ~13:1   primary text
       ink-2 0.44 → ~6:1    secondary text
       ink-3 0.52 → ~4.7:1  helper/meta — still passes 4.5:1
       ink-4 0.68 → decorative ONLY (placeholder, disabled, never content) */
  --ink:    oklch(0.240 0.005 260);
  --ink-2:  oklch(0.440 0.005 260);
  --ink-3:  oklch(0.520 0.004 260);
  --ink-4:  oklch(0.680 0.004 260);

  /* brand accent: the primary action, the active nav, focus. One hue only. */
  --accent:      oklch(0.560 0.190 275);
  --accent-strong: oklch(0.500 0.195 275);
  --accent-ink:  oklch(0.470 0.170 275);
  --accent-bg:   oklch(0.960 0.030 275);
  --on-accent:   oklch(0.995 0.005 275);

  /* the only saturated colour that means status: ok/work/hold/stop */
  --st-ok:   oklch(0.58 0.150 150); --st-ok-bg:   oklch(0.958 0.040 150); --st-ok-ink:   oklch(0.40 0.11 150);
  --st-work: oklch(0.56 0.150 262); --st-work-bg: oklch(0.958 0.035 262); --st-work-ink: oklch(0.42 0.12 262);
  --st-hold: oklch(0.72 0.150 72);  --st-hold-bg: oklch(0.960 0.050 82);  --st-hold-ink: oklch(0.45 0.115 62);
  --st-stop: oklch(0.575 0.205 27); --st-stop-bg: oklch(0.960 0.035 27);  --st-stop-ink: oklch(0.44 0.155 27);
  --st-idle: oklch(0.74 0.010 255); --st-idle-bg: oklch(0.950 0.005 255); --st-idle-ink: oklch(0.46 0.014 265);

  /* platform brand colours — used only on the connect/reconnect surface, as
     identity, never as app state (the state rule still owns the rest) */
  --pl-instagram: oklch(0.60 0.20 12);
  --pl-threads:   oklch(0.30 0.01 265);
  --pl-youtube:   oklch(0.58 0.22 25);
  --pl-tiktok:    oklch(0.32 0.02 265);

  --r-s: 7px; --r-m: 11px; --r-l: 16px; --r-pill: 999px;
  --ease: cubic-bezier(0.2, 0.9, 0.3, 1);
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  /* ── type: five sizes, four weights, and hierarchy carried by BOTH ──
     The pages read flat because a section heading (680) and a field label
     (650) were nearly the same weight. They are now three steps apart. */
  --t-xs: 0.786rem;  /* 11px   counters, codes, timestamps */
  --t-sm: 0.893rem;  /* 12.5px field labels, controls, helper text */
  --t-md: 1rem;      /* 14px   body — captions, names, what you read */
  --t-lg: 1.286rem;  /* 18px   section headings */
  --t-xl: 1.714rem;  /* 24px   page title / sign-in */

  --w-regular:  440;
  --w-medium:   540;  /* field labels — quiet, not shouty */
  --w-semibold: 620;  /* emphasis inside prose, buttons */
  --w-bold:     720;  /* section headings — visibly heavier than a label */

  /* ── spacing: one scale, no arbitrary values ── */
  --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px; --s-5: 24px; --s-6: 32px;

  /* ── form controls: one shared geometry for every input/select/textarea ── */
  --ctl-h: 38px;
  --ctl-px: 11px;
  --ctl-radius: var(--r-m);
  --ctl-border: var(--rule-hard);
  --ctl-bg: var(--paper);
  /* the page's content column */
  --page-max: 1300px;

  /* ── surfaces that are not a flat token elsewhere ── */
  --topbar-bg: oklch(0.999 0.001 255 / 0.85);   /* translucent bar */
  --frame-ring: oklch(0.24 0.005 260 / 0.10);   /* hairline inside a thumbnail */
  --backdrop: oklch(0.25 0.02 80 / 0.42);       /* behind a dialog */
  --tip-key: oklch(0.80 0.010 95);              /* dim label inside a chart tooltip */
  --attn-rule: oklch(0.87 0.05 82);             /* divider between attention rows */
  --attn-rule-stop: oklch(0.87 0.04 27);
  --attn-why: oklch(0.44 0.08 62);              /* the explaining half of an alert */
  --attn-why-stop: oklch(0.45 0.11 27);
  /* the select chevron is a data URI, so its stroke cannot be currentColor */
  --chev: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1.5 6 6.5l5-5' fill='none' stroke='%236b6b70' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/></svg>");

  /* soft, layered elevation — cards lift a little, floating things lift more */
  --shadow-sm: 0 1px 2px oklch(0.30 0.03 265 / 0.05), 0 1px 3px oklch(0.30 0.03 265 / 0.07);
  --shadow-md: 0 4px 12px oklch(0.30 0.03 265 / 0.08), 0 2px 4px oklch(0.30 0.03 265 / 0.05);
  --lift: 0 12px 34px oklch(0.28 0.04 265 / 0.16), 0 3px 8px oklch(0.28 0.04 265 / 0.10);
  --z-dialog: 40; --z-dock: 50; --z-toast: 60;
}

/* ── night mode ───────────────────────────────────────────────────────────
   Only tokens change. Every surface, ink and state colour in the app already
   comes from the block above, so the whole product turns over here rather
   than being restyled page by page.

   Levels are chosen for contrast on the dark card (L 0.235), not by eye:
     ink   0.96 → ~14:1   ink-2 0.80 → ~8.8:1   ink-3 0.70 → ~6.2:1
     ink-4 0.55 → decorative only (placeholder/disabled), never content.
   The accent LIGHTENS and its text goes dark: a 0.56-lightness violet with
   white text only reaches ~3.9:1, which fails, whereas a light chip with dark
   text is both legible and the usual dark-mode convention. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --app-bg:     oklch(0.185 0.008 265);
  --paper:      oklch(0.235 0.009 265);
  --paper-sunk: oklch(0.205 0.008 265);
  --paper-band: oklch(0.275 0.010 265);
  --rule:       oklch(0.320 0.011 265);
  --rule-soft:  oklch(0.275 0.010 265);
  --rule-hard:  oklch(0.405 0.013 265);

  --ink:   oklch(0.960 0.004 260);
  --ink-2: oklch(0.800 0.005 260);
  --ink-3: oklch(0.700 0.005 260);
  --ink-4: oklch(0.550 0.006 260);

  --accent:        oklch(0.720 0.150 275);
  --accent-strong: oklch(0.790 0.145 275);
  --accent-ink:    oklch(0.800 0.130 275);
  --accent-bg:     oklch(0.300 0.060 275);
  --on-accent:     oklch(0.180 0.030 275);

  --st-ok:   oklch(0.70 0.150 150); --st-ok-bg:   oklch(0.285 0.050 150); --st-ok-ink:   oklch(0.840 0.130 150);
  --st-work: oklch(0.70 0.140 262); --st-work-bg: oklch(0.285 0.050 262); --st-work-ink: oklch(0.840 0.110 262);
  --st-hold: oklch(0.78 0.140 72);  --st-hold-bg: oklch(0.300 0.055 72);  --st-hold-ink: oklch(0.870 0.120 82);
  --st-stop: oklch(0.68 0.190 27);  --st-stop-bg: oklch(0.300 0.075 27);  --st-stop-ink: oklch(0.840 0.130 27);
  --st-idle: oklch(0.50 0.010 265); --st-idle-bg: oklch(0.285 0.008 265); --st-idle-ink: oklch(0.760 0.008 265);

  /* the two dark platform marks would vanish on a dark card */
  --pl-threads: oklch(0.92 0.005 265);
  --pl-tiktok:  oklch(0.92 0.008 265);

  --topbar-bg:  oklch(0.235 0.009 265 / 0.86);
  --frame-ring: oklch(0.98 0.004 260 / 0.10);
  --backdrop:   oklch(0.12 0.015 265 / 0.62);
  --tip-key:    oklch(0.62 0.010 265);
  --attn-rule:      oklch(0.40 0.055 82);
  --attn-rule-stop: oklch(0.40 0.070 27);
  --attn-why:       oklch(0.78 0.090 62);
  --attn-why-stop:  oklch(0.78 0.110 27);
  --chev: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23a7a7b0' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/></svg>");

  /* shadows do almost nothing on a dark ground; depth comes from the border,
     so they only deepen rather than pretending to lift */
  --shadow-sm: 0 1px 2px oklch(0.08 0.01 265 / 0.40);
  --shadow-md: 0 4px 12px oklch(0.08 0.01 265 / 0.45);
  --lift:      0 14px 38px oklch(0.06 0.01 265 / 0.60), 0 3px 10px oklch(0.06 0.01 265 / 0.45);
}
:root[data-theme="light"] { color-scheme: light; }

* { box-sizing: border-box; }
/* 87.5% = 14px root, so the rem values in the scale above mean what they say */
html { -webkit-text-size-adjust: 100%; font-size: 87.5%; }
body {
  margin: 0;
  background: var(--app-bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-md);
  line-height: 1.55;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3 { margin: 0; font-weight: var(--w-bold); letter-spacing: -0.018em; text-wrap: balance; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--accent-ink); text-underline-offset: 2px; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 3px; }

/* An explicit `display` in author CSS beats the `hidden` attribute, which is
   only `display: none` in the UA sheet. That is why the repeat counter and the
   language tabs showed while still marked hidden. */
[hidden] { display: none !important; }

/* ── form controls ────────────────────────────────────────────────────────
   One geometry for every field, so the page speaks a single visual language.
   `.line` is the shared class already used across the app; textarea.line was
   missing from it, which is why "First comment" alone had a dark UA border. */
input.line, select.line, textarea.line, .ctl {
  background: var(--ctl-bg); color: var(--ink);
  border: 1px solid var(--ctl-border); border-radius: var(--ctl-radius);
  padding: 0 var(--ctl-px); min-height: var(--ctl-h);
  font-size: var(--t-sm); width: 100%;
  transition: border-color 120ms var(--ease), box-shadow 120ms var(--ease);
}
/* a textarea is multi-line, so it needs real vertical padding and no fixed height */
textarea.line { padding: 9px var(--ctl-px); min-height: 0; line-height: 1.55; resize: vertical; }
input.line:hover, select.line:hover, textarea.line:hover { border-color: var(--ink-4); }
input.line:focus-visible, select.line:focus-visible, textarea.line:focus-visible,
textarea.caption:focus-visible {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}
input.line:disabled, select.line:disabled, textarea.line:disabled {
  background: var(--paper-sunk); color: var(--ink-4); cursor: not-allowed;
  border-color: var(--rule);
}
::placeholder { color: var(--ink-4); opacity: 1; }

/* Native selects and date inputs carried the OS look next to our own pills.
   The arrow is an inline SVG data URI — no dependency, no request. */
select.line {
  appearance: none; -webkit-appearance: none;
  padding-right: 32px;
  background-image: var(--chev);
  background-repeat: no-repeat;
  background-position: right 11px center;
}
/* the native calendar button: same ink as the rest of the field, and a target */
input[type="datetime-local"].line, input[type="date"].line { padding-right: 8px; }
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="date"]::-webkit-calendar-picker-indicator {
  opacity: 0.55; cursor: pointer; padding: 4px; border-radius: var(--r-s);
}
input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; background: var(--paper-sunk); }
.num, .mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* ── top bar ─────────────────────────────────────────────────────────── */
.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: stretch; gap: 18px;
  padding-inline: max(clamp(14px, 2.4vw, 28px), calc((100% - var(--page-max)) / 2));
  height: 58px;
  background: var(--topbar-bg); backdrop-filter: saturate(1.4) blur(8px);
  border-bottom: 1px solid var(--rule);
}
.mark {
  display: flex; align-items: center; gap: 1px; flex: none;
  font-weight: var(--w-bold); letter-spacing: -0.03em; font-size: var(--t-lg);
}
/* the wordmark's one spot of brand colour */
.mark i { font-style: normal; color: var(--accent); font-weight: var(--w-semibold); }

/* ── view nav ────────────────────────────────────────────────────────── */
/* Six places, named, always in the same order, with the current one held by a
   2px ink rule sitting on the topbar's own border. This is the only chrome in
   the app that says where you are, so it is ink and never a colour — except
   the state dot, which is the same ok/work/hold/stop vocabulary as everywhere
   else and is the one thing allowed to shout across views.

   The nav takes the row's slack, so the tally and session stay pinned right
   without two competing auto margins deciding it between them. */
.views { display: flex; align-items: center; gap: 2px; flex: 1 1 auto; min-width: 0; margin-left: 4px; }
/* the active place is now an accent pill rather than an ink underline —
   softer, and the one bit of chrome that says where you are */
.views button {
  border: 0; background: none; cursor: pointer; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 13px; border-radius: var(--r-pill);
  font-size: var(--t-sm); font-weight: var(--w-semibold); color: var(--ink-3);
  transition: color 120ms var(--ease), background 120ms var(--ease);
}
.views button:hover { color: var(--ink); background: var(--paper-sunk); }
.views button.on { color: var(--accent-ink); background: var(--accent-bg); font-weight: var(--w-semibold); }
.views button[hidden] { display: none; }
.vdot { width: 6px; height: 6px; border-radius: 50%; background: var(--st-idle); flex: none; }
.vdot.hold { background: var(--st-hold); }
.vdot.stop { background: var(--st-stop); }
.vdot.work { background: var(--st-work); animation: breathe 1.6s ease-in-out infinite; }
.vcount { font-family: var(--mono); font-size: var(--t-xs); font-weight: var(--w-semibold); color: var(--ink-3); }
.views button.on .vcount { color: var(--ink-2); }

.tally { display: flex; align-items: center; gap: 14px; font-size: var(--t-sm); color: var(--ink-2); flex: none; }
.tally b { font-family: var(--mono); font-weight: var(--w-semibold); color: var(--ink); }
.tally .lit { color: var(--st-hold-ink); }
/* Today's YouTube upload budget: a measurement, so mono ink — not a state.
   It only takes colour when it is actually spent, and the bar is a hairline
   rule filling up rather than a progress widget. */
.quota { display: flex; align-items: center; gap: 7px; font-size: var(--t-xs); color: var(--ink-2); flex: none; }
.quota b { font-family: var(--mono); font-weight: var(--w-semibold); font-variant-numeric: tabular-nums; color: var(--ink); }
.quota .bar { width: 34px; height: 3px; background: var(--rule); border-radius: 2px; overflow: hidden; }
.quota .bar i { display: block; height: 100%; background: var(--ink-3); }
.quota.spent { color: var(--st-hold-ink); }
.quota.spent b { color: var(--st-hold-ink); }
.quota.spent .bar i { background: var(--st-hold-ink); }
.session { display: flex; align-items: center; gap: 12px; font-size: var(--t-sm); color: var(--ink-2); flex: none; }
.who { font-weight: var(--w-semibold); color: var(--ink); max-width: 22ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.heart { display: inline-flex; align-items: center; gap: 6px; }
.heart::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--st-idle); flex: none;
}
.heart.up::before { background: var(--st-ok); }
.heart.down::before { background: var(--st-stop); }

/* ── buttons ─────────────────────────────────────────────────────────── */
.btn {
  border: 1px solid var(--rule-hard); background: var(--paper); color: var(--ink);
  border-radius: var(--r-m); padding: 7px 14px; font-size: var(--t-sm); font-weight: var(--w-semibold);
  cursor: pointer; box-shadow: var(--shadow-sm);
  transition: border-color 120ms var(--ease), background 120ms var(--ease), box-shadow 120ms var(--ease);
}
.btn:hover { border-color: var(--ink-3); box-shadow: var(--shadow-md); }
/* Anchors carrying .btn (download links) must not read as links among buttons */
a.btn { text-decoration: none; display: inline-block; }
.btn:disabled { color: var(--ink-3); border-color: var(--rule); background: var(--paper-sunk); box-shadow: none; cursor: not-allowed; }
/* .btn-ink is the primary action everywhere — now the brand accent */
.btn-ink { background: var(--accent); border-color: transparent; color: var(--on-accent); box-shadow: var(--shadow-sm); }
.btn-ink:hover { background: var(--accent-strong); border-color: transparent; box-shadow: var(--shadow-md); }
.btn-ink:disabled { background: var(--paper-sunk); border-color: var(--rule); color: var(--ink-3); box-shadow: none; }
.btn-sm { padding: 4px 10px; font-size: var(--t-xs); border-radius: var(--r-s); }
.icon-btn { padding: 4px 8px; font-size: var(--t-md); line-height: 1.1; }
.btn-quiet { border-color: transparent; background: none; color: var(--ink-2); box-shadow: none; }
.btn-quiet:hover { border-color: var(--rule-hard); color: var(--ink); box-shadow: none; }
.btn-stop:hover { border-color: var(--st-stop); color: var(--st-stop-ink); }

/* ── status stamp ────────────────────────────────────────────────────── */
.stamp {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  font-size: var(--t-xs); font-weight: var(--w-semibold); letter-spacing: 0.01em;
  padding: 2px 8px 2px 6px; border-radius: 3px;
  background: var(--st-idle-bg); color: var(--st-idle-ink); white-space: nowrap;
}
.stamp::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--st-idle); flex: none; }
.stamp.ok   { background: var(--st-ok-bg);   color: var(--st-ok-ink); }   .stamp.ok::before   { background: var(--st-ok); }
.stamp.work { background: var(--st-work-bg); color: var(--st-work-ink); } .stamp.work::before { background: var(--st-work); animation: breathe 1.6s ease-in-out infinite; }
.stamp.hold { background: var(--st-hold-bg); color: var(--st-hold-ink); } .stamp.hold::before { background: var(--st-hold); }
.stamp.stop { background: var(--st-stop-bg); color: var(--st-stop-ink); } .stamp.stop::before { background: var(--st-stop); }
@keyframes breathe { 50% { opacity: 0.2; } }

/* the one authored motion: a line in flight ticks along its rule */
.ticking { position: relative; }
.ticking::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: linear-gradient(90deg, transparent 0%, var(--st-work) 18%, transparent 36%);
  background-size: 260% 100%;
  animation: tick 2.6s linear infinite;
}
@keyframes tick { from { background-position: 120% 0; } to { background-position: -160% 0; } }

/* ── attention strip ─────────────────────────────────────────────────── */
.attention { border-bottom: 1px solid var(--rule-hard); background: var(--st-hold-bg); }
.attention:empty { display: none; }
.attn-row {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding-block: 10px;
  padding-inline: max(clamp(14px, 2.4vw, 28px), calc((100% - var(--page-max)) / 2));
  color: var(--st-hold-ink); font-size: var(--t-sm);
}
.attn-row + .attn-row { border-top: 1px solid var(--attn-rule); }
.attn-row .why { color: var(--attn-why); }
.attn-row .btn { margin-left: auto; border-color: var(--st-hold-ink); color: var(--st-hold-ink); background: transparent; }
.attn-row .btn:hover { background: var(--st-hold-ink); color: var(--paper); }
.attn-row .attn-dismiss { margin-left: 0; border-color: transparent; color: var(--ink-3); }
.attn-row .attn-dismiss:hover { background: none; border-color: var(--rule-hard); color: var(--ink); }
/* a failed line is a different state, so it carries the different colour */
.attn-row.stop { background: var(--st-stop-bg); color: var(--st-stop-ink); border-top-color: var(--attn-rule-stop); }
.attn-row.stop .why { color: var(--attn-why-stop); }
.attn-row.stop .btn { border-color: var(--st-stop-ink); color: var(--st-stop-ink); }
.attn-row.stop .btn:hover { background: var(--st-stop-ink); color: var(--paper); }

/* ── board ───────────────────────────────────────────────────────────── */
/* The rail and the work are now cards floating on the ground, with air
   between them — the modern layout the redesign asks for. */
.board {
  display: grid; grid-template-columns: 300px minmax(0, 1fr);
  align-items: start; gap: var(--s-4);
  /* one centred content column, capped, with the gutter never below 14px */
  max-width: var(--page-max); margin-inline: auto;
  padding: var(--s-4) clamp(14px, 2.4vw, 28px) 96px;
}
.rail {
  position: sticky; top: 74px; align-self: start;
  max-height: calc(100vh - 92px); overflow-y: auto;
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: var(--r-l); box-shadow: var(--shadow-sm);
  padding-bottom: 10px;
}
.stack { display: grid; gap: 18px; min-width: 0; }

/* each view is its own card */
.sec {
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: var(--r-l); box-shadow: var(--shadow-sm);
  padding: 0 clamp(14px, 1.6vw, 22px) 20px;
}
.sec[hidden] { display: none; }
/* the head holds the section's own controls, so it rides along at the top of a
   long scroll instead of being something you scroll back up to find */
.sec-head {
  position: sticky; top: 58px; z-index: 6; background: var(--paper);
  display: flex; align-items: baseline; gap: 10px;
  padding: 16px 0 12px; margin-bottom: 4px; border-bottom: 1px solid var(--rule);
  border-radius: var(--r-l) var(--r-l) 0 0;
}
.sec-head h2 { font-size: var(--t-lg); flex: none; }
.sec-head .note { font-size: var(--t-sm); color: var(--ink-3); flex: 1 1 auto; min-width: 0; }
/* the controls never shrink: letting the note grow (so the heading stopped
   wrapping) squeezed the Statistics filter until two of its four buttons were
   clipped away by .seg's overflow:hidden */
.sec-head .end { margin-left: auto; display: flex; align-items: center; gap: var(--s-2); flex: none; }
/* two sections on one view (bulk under compose) each keep their own card */
.sec + .sec { margin-top: 0; }

/* where-did-it-go: a row jumped to from another view names itself once, in
   grey — a wayfinding affordance, not a colour, so the state rule holds */
@keyframes findme { from { background: var(--paper-band); } to { background: transparent; } }
.flash { animation: findme 1600ms var(--ease) 1; }

/* ── rail: language bands ────────────────────────────────────────────── */
.rail-head {
  display: flex; align-items: baseline; gap: 8px;
  padding: 14px 14px 8px; font-size: var(--t-sm); color: var(--ink-3);
}
.rail-head h2 { font-size: var(--t-sm); color: var(--ink); letter-spacing: 0.02em; }
.rail-head .btn { margin-left: auto; align-self: center; }
.band + .band { margin-top: 2px; }
.band-head {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 14px; background: var(--paper-band);
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
  position: sticky; top: 0; z-index: 2;
}
.code {
  font-family: var(--mono); font-size: var(--t-xs); font-weight: var(--w-bold); letter-spacing: 0.06em;
  border: 1px solid var(--rule-hard); border-radius: 3px; padding: 1px 5px; color: var(--ink-2);
  background: var(--paper); flex: none;
}
.band-head .endonym { font-weight: var(--w-semibold); font-size: var(--t-sm); }
.band-head .count { margin-left: auto; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); }

/* wraps: the plain-language status stamps ("Reconnect needed") are wider than
   the old one-word ones, and a 300px rail cannot fit name + stamp + button on
   one line without crushing the name */
.acct { display: flex; align-items: center; flex-wrap: wrap; gap: 9px 8px; padding: 9px 14px; }
.acct .id { flex: 1 1 auto; }
.acct .right { flex-wrap: wrap; }
.acct + .acct { border-top: 1px solid var(--rule-soft); }
.acct .face {
  width: 26px; height: 26px; border-radius: 50%; flex: none; object-fit: cover;
  border: 1px solid var(--rule-hard); background: var(--paper-band);
  display: grid; place-items: center; font-size: var(--t-xs); font-weight: var(--w-bold); color: var(--ink-2);
}
.acct .id { min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
.acct .name { font-weight: var(--w-semibold); font-size: var(--t-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct .sub { font-size: var(--t-xs); color: var(--ink-3); display: flex; gap: 5px; align-items: baseline; }
.acct .right { margin-left: auto; display: flex; align-items: center; gap: 6px; flex: none; }
.acct .edit { opacity: 0; transition: opacity 120ms var(--ease); }
.acct:hover .edit, .acct:focus-within .edit { opacity: 1; }

.acct-edit { padding: var(--s-3) 14px; background: var(--paper-sunk); border-top: 1px solid var(--rule-soft); display: grid; gap: var(--s-2); }
.acct-edit .row { display: flex; gap: 8px; }
.acct-edit input, .acct-edit select {
  border: 1px solid var(--rule-hard); border-radius: var(--r-s); background: var(--paper);
  padding: 5px 8px; font-size: var(--t-sm); min-width: 0; width: 100%;
}
.acct-edit .hint { font-size: var(--t-xs); color: var(--ink-3); }

/* ── connect / onboarding ────────────────────────────────────────────── */
/* Branded "Continue with X" buttons, an explicit statement of what each
   connection grants, and links to the policies — the surface a platform
   reviewer lands on, and a newcomer's first step. Driven by a registry in
   JS so a fifth platform is one entry, not a rewrite. */
.connect { padding: 14px; display: grid; gap: 9px; border-top: 1px solid var(--rule); margin-top: 6px; }
.connect .lbl { font-size: var(--t-xs); font-weight: var(--w-bold); letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-3); }
.connect .intro { font-size: var(--t-sm); color: var(--ink-2); line-height: 1.5; }
.connect .intro b { color: var(--ink); font-weight: var(--w-bold); }
.connect .links { display: grid; gap: 8px; }
.plat-btn {
  display: flex; align-items: center; gap: 10px; text-decoration: none;
  border: 1px solid var(--rule-hard); border-radius: var(--r-m); background: var(--paper);
  padding: 10px 12px; font-size: var(--t-sm); font-weight: var(--w-semibold); color: var(--ink);
  box-shadow: var(--shadow-sm);
  transition: border-color 120ms var(--ease), box-shadow 120ms var(--ease), transform 120ms var(--ease);
}
.plat-btn:hover { border-color: var(--plat, var(--ink-3)); box-shadow: var(--shadow-md); transform: translateY(-1px); }
.plat-btn .logo { width: 22px; height: 22px; flex: none; display: grid; place-items: center; color: var(--plat, var(--ink)); }
.plat-btn .logo svg { width: 100%; height: 100%; display: block; }
.plat-btn .tail { margin-left: auto; color: var(--ink-3); font-weight: var(--w-regular); font-size: var(--t-xs); }
.plat-btn.added .tail { color: var(--st-ok-ink); }
.connect .disclosure { font-size: var(--t-xs); color: var(--ink-3); line-height: 1.55; }
.connect .disclosure a { color: var(--accent-ink); font-weight: var(--w-semibold); }

/* ── composer ────────────────────────────────────────────────────────── */
.composer { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(280px, 1fr); gap: 0; align-items: start; }
.composer .pane { padding: var(--s-4) 0; min-width: 0; }
/* the right column is short and the left is long, which left a dead zone under
   the publish button — it now travels with the scroll instead */
.composer .pane.right {
  padding-left: var(--s-5); margin-left: var(--s-5); border-left: 1px solid var(--rule);
  position: sticky; top: 74px;
}
/* prose has a comfortable measure; a 1100px-wide caption box does not */
textarea.caption { max-width: 680px; }
.genbar, #capNote, #autoNote { max-width: 680px; }

/* a label + control + helper text, stacked on the spacing scale */
.ffield { display: grid; gap: var(--s-2); grid-template-columns: minmax(0, 1fr); }
.ffield + .ffield { margin-top: var(--s-4); }

/* ── per-platform block ──────────────────────────────────────────────── */
.psec {
  border: 1px solid var(--rule); border-radius: var(--r-m);
  background: var(--paper); overflow: hidden; margin-top: var(--s-5);
}
.psec-head {
  display: flex; align-items: center; gap: var(--s-2); width: 100%;
  padding: var(--s-3); border: 0; background: var(--paper-sunk); cursor: pointer;
  text-align: left; border-bottom: 1px solid var(--rule);
}
.psec-head:hover { background: var(--paper-band); }
.psec-logo { width: 18px; height: 18px; flex: none; color: var(--plat, var(--ink)); }
.psec-logo svg { width: 100%; height: 100%; display: block; }
.psec-name { font-size: var(--t-md); font-weight: var(--w-bold); letter-spacing: -0.01em; }
.psec-sub { font-size: var(--t-sm); color: var(--ink-3); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.psec-chev { width: 12px; height: 8px; flex: none; margin-left: auto; color: var(--ink-3); transition: transform 140ms var(--ease); }
.psec-head[aria-expanded="false"] .psec-chev { transform: rotate(-90deg); }
.psec-head[aria-expanded="false"] { border-bottom-color: transparent; }
.psec-body { padding: var(--s-4) var(--s-3) var(--s-4); }
/* minmax(0,1fr) and not the implicit `auto` track: an auto track is sized by its
   widest child's MIN-content, so the media strip (eight 84px chips that refuse to
   shrink) stretched this grid to ~720px and painted over the accounts and the
   schedule in the right pane — and pushed the whole page sideways on a phone.
   The column now takes its width from the pane, and a wide child scrolls or wraps
   inside it. Same reason on .ffield and #ttBody. */
.fieldset { display: grid; gap: var(--s-2); grid-template-columns: minmax(0, 1fr); }
.fieldset + .fieldset { margin-top: var(--s-5); }
.flabel {
  font-size: var(--t-sm); font-weight: var(--w-medium); color: var(--ink-2);
  display: flex; align-items: baseline; gap: var(--s-2);
}
.flabel .meter { margin-left: auto; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); font-weight: var(--w-regular); }
.flabel .meter.over { color: var(--st-stop-ink); font-weight: var(--w-bold); }
.fieldset .hint { font-size: var(--t-sm); color: var(--ink-3); line-height: 1.5; }

textarea.caption {
  border: 1px solid var(--rule-hard); border-radius: var(--r-m); background: var(--paper);
  padding: 11px 12px; min-height: 124px; resize: vertical; font-size: var(--t-md); line-height: 1.55;
  width: 100%;
}
textarea.caption::placeholder { color: var(--ink-3); }
/* one tab per language once the generator has run — each holds its own copy */
.langtabs { display: flex; gap: 4px; flex-wrap: wrap; }
.langtabs button {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--rule-hard); border-bottom-color: transparent;
  background: var(--paper-sunk); color: var(--ink-2);
  border-radius: var(--r-s) var(--r-s) 0 0; padding: 4px 11px;
  font-size: var(--t-xs); font-weight: var(--w-semibold); cursor: pointer; position: relative; top: 1px;
}
.langtabs button.on { background: var(--paper); color: var(--ink); border-color: var(--rule-hard); }
.langtabs button .edited { width: 5px; height: 5px; border-radius: 50%; background: var(--st-work); }
.langtabs + textarea.caption { border-top-left-radius: 0; }

.genbar { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.gennote { font-size: var(--t-xs); color: var(--ink-3); }
.gennote.bad { color: var(--st-stop-ink); }
.gennote.busy::after {
  content: ""; display: inline-block; width: 5px; height: 5px; margin-left: 6px;
  border-radius: 50%; background: var(--st-work); animation: breathe 1.2s ease-in-out infinite;
}
.tags { display: flex; flex-wrap: wrap; gap: 4px; }
.tag-chip {
  font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-2);
  border: 1px solid var(--rule); border-radius: 3px; padding: 1px 6px; background: var(--paper);
}

/* (the shared control geometry now lives with the tokens, near the top) */

.langpicks { display: flex; flex-wrap: wrap; gap: 5px; }
.langpick {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  border: 1px solid var(--rule-hard); border-radius: 999px; background: var(--paper);
  padding: 3px 10px 3px 7px; font-size: var(--t-sm); font-weight: var(--w-semibold); color: var(--ink-2);
  transition: border-color 120ms var(--ease), color 120ms var(--ease), background 120ms var(--ease);
}
.langpick:hover:not(:disabled) { border-color: var(--ink); color: var(--ink); }
.langpick:disabled { opacity: 0.45; cursor: not-allowed; }
.langpick.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.langpick .n { font-family: var(--mono); font-size: var(--t-xs); opacity: 0.75; }

/* the same auto-track trap as .fieldset: a row is checkbox + handle + platform,
   and the platform badge does not shrink, so an auto column was sized by the
   longest handle and the names ran out through the side of the card */
.picks { display: grid; grid-template-columns: minmax(0, 1fr); gap: 3px; max-height: 208px; overflow-y: auto; overflow-x: hidden; }
.pick {
  display: flex; align-items: center; gap: var(--s-2); padding: var(--s-2);
  border-radius: var(--r-s); cursor: pointer; min-width: 0;
}
.pick:hover { background: var(--paper-sunk); }
.pick input { accent-color: var(--accent); width: 16px; height: 16px; flex: none; }
.pick .name {
  font-size: var(--t-sm); font-weight: var(--w-semibold); color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto;
}
/* was margin-left:auto, which threw the badges to the far edge and left a
   ~750px gap in the middle of every row */
.pick .meta {
  font-size: var(--t-xs); color: var(--ink-3);
  display: flex; gap: var(--s-1); align-items: center; flex: none;
}
.pick.off { opacity: 0.5; }

.mixed {
  font-size: var(--t-xs); color: var(--st-hold-ink); background: var(--st-hold-bg);
  border-radius: var(--r-s); padding: 6px 9px;
}

/* Pre-flight: what a platform will refuse, said before the post is written
   rather than when the scheduler gets to it. `stop` and not `hold` — these
   targets cannot publish as they stand, so the colour has to mean that. */
.preflight {
  display: grid; gap: 4px; margin-bottom: var(--s-3);
  font-size: var(--t-xs); color: var(--st-stop-ink); background: var(--st-stop-bg);
  border-radius: var(--r-s); padding: 8px 10px;
}
.preflight-row b { font-weight: var(--w-medium); }

/* TikTok direct-post controls: privacy has no default, interactions default
   off, and a creator-disabled interaction is greyed out (TikTok UX rules). */
#ttBody, #ttDirect { display: grid; gap: var(--s-3); grid-template-columns: minmax(0, 1fr); }
/* a grid item stretches, which left the segment border running on past its two
   buttons — it takes exactly the width it needs instead */
#ttMode { justify-self: start; max-width: 100%; }
.tt-toggles { display: flex; flex-wrap: wrap; gap: var(--s-4); }
.tt-check { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-sm); color: var(--ink-2); cursor: pointer; }
.tt-check input { accent-color: var(--accent); width: 15px; height: 15px; flex: none; }
.tt-check.off { opacity: 0.42; cursor: not-allowed; }
#ttWho { color: var(--ink-3); font-weight: var(--w-regular); }
#ttConsent a { color: var(--accent-ink); font-weight: var(--w-semibold); }

/* media chooser */
/* The chips never shrink, so the row is always wider than the column: it has to
   be a scroller, and it has to be one you can SEE is a scroller — hence a real
   thin scrollbar and snap points rather than a silently clipped row. */
.strip {
  display: flex; gap: 7px; padding-bottom: 5px;
  max-width: 100%; overflow-x: auto; overflow-y: hidden;
  scrollbar-width: thin; scrollbar-color: var(--rule-hard) transparent;
  scroll-snap-type: x proximity; overscroll-behavior-x: contain;
}
.strip::-webkit-scrollbar { height: 6px; }
.strip::-webkit-scrollbar-thumb { background: var(--rule-hard); border-radius: 3px; }
.strip::-webkit-scrollbar-track { background: transparent; }
.strip > * { scroll-snap-align: start; }
/* A frame that CONTAINS the picture: the images were the loudest colour on
   the page because nothing bounded them. Uniform square, cover-cropped, an
   inner hairline so the artwork sits inside the frame, and a selected state
   that reads as selected (accent ring + tick) not a 1px border change. */
.chip {
  flex: none; width: 84px; border: 1px solid var(--rule); border-radius: var(--r-m);
  background: var(--paper); padding: 0; cursor: pointer; overflow: hidden; text-align: left;
  box-shadow: var(--shadow-sm); position: relative;
  transition: border-color 120ms var(--ease), box-shadow 120ms var(--ease), transform 120ms var(--ease);
}
.chip:hover { border-color: var(--ink-4); box-shadow: var(--shadow-md); transform: translateY(-1px); }
.chip.on { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-bg); }
.chip .thumb::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 0 1px var(--frame-ring);
}
.chip.on .cap { background: var(--accent-bg); color: var(--accent-ink); font-weight: var(--w-semibold); }
.chip.on::after {
  content: "✓"; position: absolute; top: 4px; right: 4px; z-index: 2;
  width: 17px; height: 17px; border-radius: 50%;
  background: var(--accent); color: var(--on-accent);
  font-size: 11px; line-height: 17px; text-align: center; font-weight: var(--w-bold);
}
.chip .thumb { aspect-ratio: 1; background: var(--paper-band); display: grid; place-items: center; overflow: hidden; position: relative; }
.chip .thumb :is(img, video) { width: 100%; height: 100%; object-fit: cover; display: block; }
.chip .thumb .glyph { font-size: var(--t-md); color: var(--ink-3); }
.chip .cap {
  font-size: var(--t-xs); padding: var(--s-1) 5px; color: var(--ink-2); border-top: 1px solid var(--rule-soft);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chip.more { display: grid; place-items: center; aspect-ratio: 1; width: 74px; font-size: var(--t-xs); font-weight: var(--w-semibold); color: var(--ink-2); }

/* three buttons that cannot shrink plus a filename: in a narrow column they have
   to fall to their own line instead of running out over the right pane */
.chosen {
  display: flex; align-items: center; gap: 9px; padding: 7px 9px;
  border: 1px solid var(--rule-hard); border-radius: var(--r-m); background: var(--paper);
  flex-wrap: wrap; row-gap: 8px;
}
.chosen .thumb { width: 40px; height: 40px; border-radius: var(--r-s); overflow: hidden; background: var(--paper-band); flex: none; }
.chosen .thumb :is(img, video) { width: 100%; height: 100%; object-fit: cover; display: block; }
.chosen .what { min-width: 0; flex: 1 1 150px; line-height: 1.35; }
.chosen .what b { display: block; font-size: var(--t-sm); font-weight: var(--w-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chosen .what span { font-size: var(--t-xs); color: var(--ink-3); font-family: var(--mono); }
/* the three actions are one group, so they wrap together and keep their auto
   margin as a group — separately, each wrapped button right-aligned its own line */
.chosen .acts { display: flex; align-items: center; gap: 6px; flex: none; margin-left: auto; }
.chosen .btn { flex: none; }

.go { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--rule); }
.go .when { font-size: var(--t-xs); color: var(--ink-3); }
.go .btn-ink { flex: 1; padding: 9px 16px; }

/* scheduler: the common times as one press, the exact field underneath */
.whenbar { display: flex; flex-wrap: wrap; gap: 5px; }
.whenbar button {
  border: 1px solid var(--rule-hard); background: var(--paper); color: var(--ink-2);
  border-radius: 999px; padding: 3px 11px; font-size: var(--t-xs); font-weight: var(--w-semibold); cursor: pointer;
  transition: border-color 120ms var(--ease), color 120ms var(--ease), background 120ms var(--ease);
}
.whenbar button:hover { border-color: var(--ink); color: var(--ink); }
.whenbar button.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.whenread { font-size: var(--t-xs); color: var(--ink-3); line-height: 1.45; }
.whenread b { color: var(--ink); font-weight: var(--w-semibold); }
.whenread .clash { color: var(--st-hold-ink); }
.whenread .past { color: var(--st-stop-ink); }

/* ── library ─────────────────────────────────────────────────────────── */
.libbar { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; padding: var(--s-3) 0; }
.libbar input.search { max-width: 220px; }
/* flex:none matters — .seg clips with overflow:hidden, so any shrinking
   silently amputates its last buttons rather than making them narrower */
.seg { display: flex; flex: none; border: 1px solid var(--rule-hard); border-radius: var(--r-s); overflow: hidden; }
.seg button { flex: none; white-space: nowrap; }
.seg button {
  border: 0; background: var(--paper); color: var(--ink-2); padding: 5px 11px;
  font-size: var(--t-sm); font-weight: var(--w-semibold); cursor: pointer;
}
.seg button + button { border-left: 1px solid var(--rule-hard); }
.seg button.on { background: var(--accent); color: var(--on-accent); }

.drop {
  display: block; border: 1.5px dashed var(--rule-hard); border-radius: var(--r-m);
  padding: var(--s-5) var(--s-4); text-align: center; color: var(--ink-3); font-size: var(--t-sm); cursor: pointer;
  background: var(--paper); transition: border-color 120ms var(--ease), background 120ms var(--ease);
}
.drop:hover, .drop.over { border-color: var(--accent); color: var(--ink-2); background: var(--accent-bg); border-style: solid; }
.drop b { color: var(--ink); font-weight: var(--w-semibold); }
/* clipped, not display:none — the input must stay in the tab order */
.drop input { position: absolute; width: 1px; height: 1px; opacity: 0; clip-path: inset(50%); }
.drop:focus-within { border-color: var(--ink); border-style: solid; }
.upline { font-family: var(--mono); font-size: var(--t-xs); color: var(--st-work-ink); min-height: 1.2em; margin-top: 6px; }

.shelf { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(172px, 1fr)); margin-top: 14px; }
.item {
  border: 1px solid var(--rule); border-radius: var(--r-m); background: var(--paper);
  overflow: hidden; display: flex; flex-direction: column; box-shadow: var(--shadow-sm);
  transition: box-shadow 140ms var(--ease), transform 140ms var(--ease), border-color 140ms var(--ease);
}
.item:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); border-color: var(--rule-hard); }
.item.gone { opacity: 0.55; }
.item .thumb { aspect-ratio: 4/3; background: var(--paper-band); display: grid; place-items: center; overflow: hidden; }
/* the artwork sits inside the frame instead of running to its edge */
.item .thumb::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 0 1px var(--frame-ring);
}
.item .thumb :is(img, video) { width: 100%; height: 100%; object-fit: cover; display: block; }
.item .body { padding: var(--s-2) var(--s-3) var(--s-3); display: grid; gap: var(--s-2); }
/* the name reads as text until you touch it, then it is an ordinary field */
.item input.rename {
  border: 1px solid transparent; border-radius: var(--r-s); background: none;
  padding: var(--s-1) 5px; font-size: var(--t-sm); font-weight: var(--w-semibold);
  width: 100%; min-height: 0; color: var(--ink);
}
.item input.rename:hover { border-color: var(--rule-hard); }
.item input.rename:focus-visible {
  outline: none; border-color: var(--accent); background: var(--paper);
  box-shadow: 0 0 0 3px var(--accent-bg);
}
.item .facts { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; }
/* The date read out of the file's own name. The shelf is ordered by it, so it
   is the one fact on this row that earns emphasis — the rest (kind, duration,
   size) is reference material. */
.dated { color: var(--ink-2); font-weight: var(--w-medium); }
.item .facts .dated::before { content: '▸ '; color: var(--ink-4); }
.item select.lang {
  border: 1px solid var(--rule-hard); border-radius: var(--r-s); background: var(--ctl-bg);
  font-size: var(--t-xs); padding: 3px 22px 3px var(--s-2); color: var(--ink-2); max-width: 116px;
  appearance: none; -webkit-appearance: none;
  background-image: var(--chev);
  background-repeat: no-repeat; background-position: right 6px center;
}
.item select.lang:hover { border-color: var(--ink-4); }
.item select.lang.unset { color: var(--ink-3); border-style: dashed; }
/* the language a file is cut for, on the thumbnail where it is scannable */
.item .thumb { position: relative; }
.item .thumb .tag {
  position: absolute; top: 6px; left: 6px;
  font-family: var(--mono); font-size: var(--t-xs); font-weight: var(--w-bold); letter-spacing: 0.06em;
  background: var(--paper); border: 1px solid var(--rule-hard); border-radius: 3px; padding: 0 5px; color: var(--ink-2);
}
.chip .thumb { position: relative; }
.chip .thumb .tag {
  position: absolute; top: 3px; left: 3px;
  font-family: var(--mono); font-size: 0.65rem; font-weight: var(--w-bold);
  background: var(--paper); border: 1px solid var(--rule-hard); border-radius: 2px; padding: 0 3px; color: var(--ink-2);
}
.stripnote { font-size: var(--t-xs); color: var(--ink-3); margin-top: 5px; }
.linkish {
  border: 0; background: none; padding: 0; font: inherit; cursor: pointer;
  color: var(--accent-ink); font-weight: var(--w-semibold); text-decoration: underline; text-underline-offset: 2px;
}
.stripnote button {
  border: 0; background: none; padding: 0; font-size: inherit; font-weight: var(--w-semibold);
  color: var(--ink); cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
}
.item .acts { display: flex; gap: 5px; align-items: center; }
.item .acts .btn { padding: 2px 7px; font-size: var(--t-xs); }
.item .acts .btn:last-child { margin-left: auto; }

/* ── posts log ───────────────────────────────────────────────────────── */
/* the log reads down, not across: one column grid shared by the header and every line */
/* Every .line is its OWN grid container, so any content-dependent track makes
   the columns resolve differently per row and the status stamp lands at a
   different x on every line. Measured 2026-07-26: an `auto` last track took
   129.7px / 0px / 56.4px depending on which buttons a row had, which moved the
   status column by up to 46px. So every track here is content-independent —
   fixed px, or `minmax(0, fr)` so long text ellipsizes instead of widening the
   track. The actions column is a fixed width and right-aligned. */
.log { display: flex; flex-direction: column; --cols: 92px minmax(0, 1.05fr) 144px minmax(0, 1.9fr) 152px; }
/* One column-heading treatment for every ruled list AND the month grid, so
   the four lists that had four slightly different header styles now share one. */
.log-cols, .s-cols, .u-cols, .cal-dow {
  font-family: var(--mono); font-size: var(--t-xs); font-weight: var(--w-medium);
  text-transform: uppercase; letter-spacing: 0.07em; color: var(--ink-3);
  border-bottom: 1px solid var(--rule);
}
.log-cols {
  display: grid; grid-template-columns: var(--cols); gap: var(--s-3); align-items: center;
  padding: var(--s-2) 2px var(--s-2) 14px;
}
/* a heavier rule closes each entry, the way a printed log closes a group */
.entry { border-bottom: 1px solid var(--rule-hard); }
.entry-head { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; padding: var(--s-3) 2px var(--s-2); }
/* the frame it went out with: a post is recognised by its picture long before
   its name, and the log is a list of things you made */
.entry-head .cover {
  width: 34px; height: 34px; border-radius: var(--r-s); flex: none; overflow: hidden;
  border: 1px solid var(--rule); background: var(--paper-band); display: block;
}
.entry-head .cover :is(img, video) { width: 100%; height: 100%; object-fit: cover; display: block; }
/* the shape of a published post's audience, at the size of a word */
.line .spark { display: flex; align-items: center; gap: 7px; min-width: 0; }
.line .spark svg { flex: none; }
.line .spark .n { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); }
.line .spark .spark-line { fill: none; stroke: var(--ink-3); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
.line .spark .spark-dot { fill: var(--ink); }
.entry-head .title { font-weight: var(--w-semibold); font-size: var(--t-md); }
.entry-head input.title-edit { max-width: 280px; }
.entry-head .when { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); }
.entry-head .acts { margin-left: auto; display: flex; gap: var(--s-2); align-items: center; }
.entry .said {
  padding: 0 2px var(--s-3); color: var(--ink-2); font-size: var(--t-md); max-width: 72ch;
  overflow-wrap: anywhere; white-space: pre-wrap;
}
.reschedule { display: flex; gap: 7px; align-items: center; flex-wrap: wrap; padding: 0 2px 11px; }
.reschedule input { max-width: 220px; }

.line {
  display: grid; grid-template-columns: var(--cols); gap: 10px; align-items: center;
  padding: var(--s-3) 2px var(--s-3) 14px; border-top: 1px solid var(--rule-soft); font-size: var(--t-sm);
}
/* nowrap without a clip is what let 'Jul 24, 09:33 PM' run into the channel
   column; the cell now truncates instead of overlapping its neighbour */
.line .at-time { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.line .chan { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.line .chan .plat { font-weight: var(--w-semibold); flex: none; }
.line .chan .at { color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.line .say { color: var(--st-stop-ink); font-size: var(--t-sm); overflow-wrap: anywhere; min-width: 0; }
/* a wait is not a failure: same sentence slot, ordinary ink */
.line .say.wait { color: var(--ink-2); }
.line .say.hold { color: var(--st-hold-ink); }
.line .tail { display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
.line .tail a { font-size: var(--t-sm); font-weight: var(--w-semibold); }

/* the cells are nowrap mono numbers, so on a narrow screen the table is wider
   than the page: it scrolls inside itself, and the page never scrolls sideways */
.readout { border-top: 1px solid var(--rule-soft); background: var(--paper-sunk); padding: var(--s-3) 14px; overflow-x: auto; }
.readout table { border-collapse: collapse; font-size: var(--t-sm); width: 100%; font-family: var(--mono); }
.readout th, .readout td { text-align: left; padding: 3px 14px 3px 0; border-bottom: 1px solid var(--rule-soft); white-space: nowrap; }
.readout th { color: var(--ink-3); font-weight: var(--w-semibold); }
.readout .none { color: var(--ink-3); font-size: var(--t-sm); font-family: system-ui, sans-serif; }

/* ── empty & skeleton ────────────────────────────────────────────────── */
.nothing { padding: 22px 2px; color: var(--ink-3); font-size: var(--t-sm); }
.nothing b { color: var(--ink); font-weight: var(--w-semibold); display: block; margin-bottom: 3px; }
.bone { background: var(--paper-band); border-radius: var(--r-s); position: relative; overflow: hidden; }
.bone::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--paper), transparent);
  animation: sweep 1.3s var(--ease) infinite;
}
@keyframes sweep { from { translate: -100% 0; } to { translate: 100% 0; } }

/* ── dialogs, sign-in, toast ─────────────────────────────────────────── */
dialog { border: 1px solid var(--rule-hard); border-radius: var(--r-l); padding: 0; background: var(--paper); color: var(--ink); box-shadow: var(--lift); }
dialog::backdrop { background: var(--backdrop); }
.dlg-head { display: flex; align-items: center; gap: 10px; padding: 13px 16px; border-bottom: 1px solid var(--rule); }
.dlg-head h3 { font-size: var(--t-md); }
.dlg-head .btn { margin-left: auto; }
.dlg-body { padding: 14px 16px; }

#picker { width: min(880px, 94vw); }
#picker .dlg-body { max-height: min(66vh, 560px); overflow-y: auto; }
.picker-grid { display: grid; gap: 9px; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); }
.pcard { border: 1px solid var(--rule); border-radius: var(--r-m); overflow: hidden; background: var(--paper); cursor: pointer; padding: 0; text-align: left; }
.pcard:hover { border-color: var(--ink); }
.pcard.on { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.pcard .thumb { aspect-ratio: 1; background: var(--paper-band); display: grid; place-items: center; overflow: hidden; position: relative; }
.pcard .thumb .tag {
  position: absolute; top: 5px; left: 5px;
  font-family: var(--mono); font-size: var(--t-xs); font-weight: var(--w-bold);
  background: var(--paper); border: 1px solid var(--rule-hard); border-radius: 3px; padding: 0 4px; color: var(--ink-2);
}
.pcard .thumb :is(img, video) { width: 100%; height: 100%; object-fit: cover; display: block; }
.pcard .cap { padding: 5px 7px; font-size: var(--t-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pcard .sub { padding: 0 7px 6px; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); }

.signin {
  position: fixed; inset: 0; z-index: var(--z-dialog); display: none;
  align-items: center; justify-content: center; background: var(--paper-sunk);
}
.signin.show { display: flex; }
.signin form {
  width: min(348px, 90vw); background: var(--paper); border: 1px solid var(--rule-hard);
  border-radius: var(--r-l); padding: 24px; display: grid; gap: 12px; box-shadow: var(--lift);
}
.signin .mark { font-size: var(--t-xl); margin-bottom: 2px; }
.signin label { display: grid; gap: var(--s-2); font-size: var(--t-sm); font-weight: var(--w-medium); color: var(--ink-2); }
.bad { color: var(--st-stop-ink); font-size: var(--t-sm); min-height: 1.2em; }

#pw { width: min(360px, 92vw); }
#pw form { display: grid; gap: 11px; }
#pw label { display: grid; gap: var(--s-2); font-size: var(--t-sm); font-weight: var(--w-medium); color: var(--ink-2); }
.dlg-acts { display: flex; gap: 8px; justify-content: flex-end; }

.toast {
  position: fixed; left: 50%; bottom: 20px; translate: -50% 10px; z-index: var(--z-toast);
  background: var(--ink); color: var(--paper); border-radius: var(--r-m);
  padding: 9px 16px; font-size: var(--t-sm); font-weight: var(--w-medium);
  opacity: 0; pointer-events: none; box-shadow: var(--lift);
  transition: opacity 180ms var(--ease), translate 180ms var(--ease);
  max-width: min(520px, 92vw);
}
.toast.show { opacity: 1; translate: -50% 0; }
.toast.bad { background: var(--st-stop-ink); }

/* ── calendar ─────────────────────────────────────────────────────────── */
/* A month as a ruled grid, not a card wall: same paper, same 1px rules, the
   status stamp doing the colour work. Dragging an entry to another cell is the
   whole point — the reschedule endpoint already existed, this makes it a wrist
   movement instead of a form. */
/* the month controls ride in the sticky section head, so paging months never
   means scrolling back up to find the arrows */
#calBody { padding-top: 10px; }
.cal-title { font-weight: var(--w-bold); font-size: var(--t-md); min-width: 11ch; text-align: center; }
.sec-head .btn { padding: 3px 9px; }
.cal-dow { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal-dow span { padding: var(--s-1) var(--s-2); }
.cal-grid {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  border-left: 1px solid var(--rule-soft);
}
.cal-day {
  min-height: 92px; padding: var(--s-1) 5px var(--s-2); border-right: 1px solid var(--rule-soft);
  border-bottom: 1px solid var(--rule-soft); display: flex; flex-direction: column; gap: 3px;
}
.cal-day.other { background: var(--paper-sunk); }
.cal-day:not(.other):hover { background: var(--paper-sunk); }
.cal-day.today { box-shadow: inset 0 2px 0 var(--accent); }
.cal-day.drop { background: var(--st-work-bg); }
.cal-day .dnum {
  font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3);
  display: flex; align-items: baseline; gap: 5px;
}
.cal-day.today .dnum { color: var(--accent-ink); font-weight: var(--w-bold); }
.cal-day .dnum .add {
  margin-left: auto; opacity: 0; border: 0; background: none; cursor: pointer;
  font-size: var(--t-xs); color: var(--ink-3); padding: 0 2px;
}
.cal-day:hover .dnum .add { opacity: 1; }
.cal-day .dnum .add:hover { color: var(--ink); }
.cal-item {
  text-align: left; width: 100%; border: 1px solid var(--rule); border-radius: var(--r-s);
  background: var(--paper); padding: 3px 6px; cursor: grab; display: grid; gap: 1px;
  box-shadow: var(--shadow-sm); transition: border-color 120ms var(--ease), box-shadow 120ms var(--ease);
}
.cal-item:hover { border-color: var(--accent); box-shadow: var(--shadow-md); }
.cal-item.dragging { opacity: 0.4; }
.cal-item .t { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-2); font-weight: var(--w-semibold); }
/* one chip per language, carrying that language's worst state — the state
   vocabulary is the same ok/work/hold/stop used everywhere else */
.cal-langs { display: flex; flex-wrap: wrap; gap: 3px; }
.cal-lang {
  font-family: var(--mono); font-size: 9.5px; font-weight: var(--w-bold); letter-spacing: 0.04em;
  line-height: 1.5; padding: 0 4px; border-radius: 3px;
  background: var(--st-idle-bg); color: var(--st-idle-ink);
}
.cal-lang i { font-style: normal; opacity: 0.75; margin-left: 2px; }
.cal-lang.ok   { background: var(--st-ok-bg);   color: var(--st-ok-ink); }
.cal-lang.work { background: var(--st-work-bg); color: var(--st-work-ink); }
.cal-lang.hold { background: var(--st-hold-bg); color: var(--st-hold-ink); }
.cal-lang.stop { background: var(--st-stop-bg); color: var(--st-stop-ink); }
/* how many posts that day, when there is more than one */
.cal-count {
  font-family: var(--mono); font-size: var(--t-xs); font-weight: var(--w-bold);
  color: var(--ink-2); background: var(--paper-band); border-radius: 3px; padding: 0 4px;
}
/* the languages this day does NOT cover */
.cal-gap {
  display: block; margin-top: 1px; font-family: var(--mono); font-size: 9.5px;
  font-weight: var(--w-bold); letter-spacing: 0.04em; color: var(--st-hold-ink);
}
.cal-empty { padding: 14px 2px; color: var(--ink-3); font-size: var(--t-sm); }
@media (max-width: 820px) {
  /* a 7-column month is unreadable on a phone: it becomes a list of the days
     that actually have something on them */
  .cal-dow { display: none; }
  .cal-grid { grid-template-columns: 1fr; border-left: 0; }
  .cal-day { min-height: 0; border-right: 0; }
  .cal-day:not(.has) { display: none; }
  .cal-day.other { display: none; }
}

.repeat-row { display: grid; gap: var(--s-2); margin-top: var(--s-2); }
.repeat-row select { min-width: 0; }
.repeat-times {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-sm); color: var(--ink-2);
}
.repeat-times input { width: 92px; flex: none; }
.repeats {
  font-family: var(--mono); font-size: var(--t-xs); color: var(--st-work-ink);
  background: var(--st-work-bg); border-radius: 3px; padding: 0 5px;
}

.carousel-pick { display: flex; gap: 7px; flex-wrap: wrap; align-items: center; }
.cpitem { position: relative; width: 62px; }
.cpitem .thumb { display: block; width: 62px; height: 62px; border: 1px solid var(--rule-hard); border-radius: var(--r-s); overflow: hidden; background: var(--paper-band); }
.cpitem .thumb :is(img, video) { width: 100%; height: 100%; object-fit: cover; display: block; }
.cpitem .n {
  position: absolute; left: 3px; bottom: 3px; font-family: var(--mono); font-size: var(--t-xs);
  background: var(--paper); border: 1px solid var(--rule-hard); border-radius: 3px; padding: 0 3px; font-weight: var(--w-bold);
}
.cpitem .x {
  position: absolute; top: -6px; right: -6px; width: 18px; height: 18px; border-radius: 50%;
  border: 1px solid var(--rule-hard); background: var(--paper); cursor: pointer; line-height: 1; padding: 0;
  font-size: 12px; color: var(--ink-2);
}
.cpitem .x:hover { border-color: var(--st-stop); color: var(--st-stop-ink); }
.cpadd {
  width: 62px; height: 62px; border: 1px dashed var(--rule-hard); border-radius: var(--r-s);
  background: none; cursor: pointer; font-size: 18px; color: var(--ink-3);
}
.cpadd:hover { border-color: var(--ink); color: var(--ink); }
.pickcount { margin-left: auto; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); }

/* ── charts ───────────────────────────────────────────────────────────── */
/* Ink on paper, like everything else. The marks carry no hue at all, because
   colour here means state and nothing else (DESIGN.md) — so identity comes
   from position and a label, never from a legend of four colours competing
   with the amber strip. That is also, by luck of the same decision, the only
   encoding that survives every kind of colour blindness.

   Weight does the work instead: the line is 2px ink, its wash is the same ink
   at 8%, and the grid is one step off the paper, solid and hairline. */
.chart { margin: 0; position: relative; }
.chart svg { display: block; width: 100%; }
.chart svg:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: 2px; }
.c-grid { stroke: var(--rule-soft); stroke-width: 1; shape-rendering: crispEdges; }
.c-axis { stroke: var(--rule); stroke-width: 1; shape-rendering: crispEdges; }
.c-line { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.c-wash { fill: var(--ink); opacity: 0.08; stroke: none; }
/* the 2px surface ring: the end dot stays legible wherever the line puts it */
.c-dot { fill: var(--ink); stroke: var(--paper); stroke-width: 2; }
.c-tick { fill: var(--ink-3); font-family: var(--mono); font-size: 10px; font-variant-numeric: tabular-nums; }
.c-label { fill: var(--ink); font-family: var(--mono); font-size: 10px; font-weight: var(--w-medium); font-variant-numeric: tabular-nums; }
.c-hair { stroke: var(--ink-3); stroke-width: 1; shape-rendering: crispEdges; }
.c-hit { fill: transparent; cursor: crosshair; }

/* tooltips enhance, never gate: every value here is also in the table below */
.c-tip {
  position: absolute; z-index: 3; pointer-events: none; opacity: 0;
  background: var(--ink); color: var(--paper); border-radius: var(--r-s);
  padding: 5px 9px; font-size: var(--t-xs); line-height: 1.45; white-space: nowrap;
  box-shadow: var(--lift); transition: opacity 110ms var(--ease);
}
.c-tip.on { opacity: 1; }
.c-tip b { font-family: var(--mono); font-weight: var(--w-bold); font-variant-numeric: tabular-nums; }
.c-tip .k { color: var(--tip-key); }

.c-cap { display: flex; align-items: baseline; gap: 8px; padding: 0 0 8px; }
.c-cap h3 { font-size: var(--t-sm); font-weight: var(--w-bold); }
.c-cap .note { font-size: var(--t-xs); color: var(--ink-3); }
.c-cap .end { margin-left: auto; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); }

/* the hero: one per view, proportional figures — tabular would make it loose */
.hero { display: flex; align-items: flex-end; gap: var(--s-4); padding: var(--s-4) 0 var(--s-1); }
.hero b {
  font-size: 48px; line-height: 0.95; font-weight: var(--w-bold); letter-spacing: -0.03em;
  font-variant-numeric: proportional-nums;
}
.hero .of { font-size: var(--t-sm); color: var(--ink-3); padding-bottom: 5px; }
.hero .of b { font-size: var(--t-sm); font-weight: var(--w-semibold); color: var(--ink-2); letter-spacing: 0; }

/* stat tiles: label · value · sparkline */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(124px, 1fr)); border-top: 1px solid var(--rule); }
.tile { padding: var(--s-3); border-right: 1px solid var(--rule-soft); display: grid; gap: var(--s-1); min-width: 0; }
.tile:last-child { border-right: 0; }
.tile .k { font-size: var(--t-xs); color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.06em; }
.tile .v { font-family: var(--mono); font-size: var(--t-lg); font-weight: var(--w-semibold); font-variant-numeric: proportional-nums; }
.tile .v.none { color: var(--ink-3); }
.tile svg { height: 22px; }
.tile .spark-line { fill: none; stroke: var(--ink-3); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
.tile .spark-dot { fill: var(--ink); }

/* small multiples: one platform per panel, each on its own scale */
.panels { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 0; border-top: 1px solid var(--rule); }
.panel { padding: var(--s-3); border-right: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft); min-width: 0; }
.panel .who { display: flex; align-items: baseline; gap: 7px; }
.panel .who b { font-size: var(--t-sm); font-weight: var(--w-semibold); }
.panel .who .n { margin-left: auto; font-family: var(--mono); font-size: var(--t-sm); font-weight: var(--w-semibold); }
.panel .who .n.none { color: var(--ink-3); font-weight: var(--w-regular); }
.panel .sub { font-size: var(--t-xs); color: var(--ink-3); }
.panel .chart { margin-top: 6px; }

/* a refetch holds the old render rather than flashing a skeleton over it */
.restating { opacity: 0.45; transition: opacity 120ms var(--ease); }

/* ── numbers + bulk ───────────────────────────────────────────────────── */
.sum-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(112px, 1fr)); gap: 0; border-bottom: 1px solid var(--rule); }
.sum-tile { padding: var(--s-3); border-right: 1px solid var(--rule-soft); }
.sum-tile:last-child { border-right: 0; }
.sum-tile b { display: block; font-family: var(--mono); font-size: var(--t-lg); font-variant-numeric: tabular-nums; }
.sum-tile span { font-size: var(--t-xs); color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.06em; }
/* account · language · views · likes · comments · shares · bar — every track
   content-independent, so rows stay aligned whatever the names are */
.sum-rows { display: flex; flex-direction: column; --scols: minmax(110px, 1fr) 58px repeat(4, 78px) 70px; }
.s-cols, .s-row { display: grid; grid-template-columns: var(--scols); gap: var(--s-2); align-items: center; }
/* the metric headings sit over right-aligned numbers, so they align right too */
.s-cols > :nth-child(n+3) { justify-self: end; }
.s-row > :nth-child(2) { justify-self: start; }

/* a sortable column heading */
.s-sort {
  border: 0; background: none; padding: 2px 0; cursor: pointer;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
  display: inline-flex; align-items: center; gap: 3px; white-space: nowrap;
}
.s-sort:hover { color: var(--ink); }
.s-sort.on { color: var(--accent-ink); font-weight: var(--w-bold); }
.s-arrow { font-size: 8px; line-height: 1; }
/* the ranked column's figures lead the row */
.s-row .num.lead { color: var(--ink); font-weight: var(--w-semibold); }
.s-cols { padding: var(--s-2) 2px var(--s-2) var(--s-3); }
.s-row { padding: var(--s-3) 2px var(--s-3) var(--s-3); border-bottom: 1px solid var(--rule-soft); font-size: var(--t-sm); }
.s-row .num { font-family: var(--mono); font-size: var(--t-xs); text-align: right; font-variant-numeric: tabular-nums; }
.s-row .who { display: grid; gap: 1px; min-width: 0; }
.s-row .who b { font-weight: var(--w-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.s-row .who .hint { font-size: var(--t-xs); color: var(--ink-3); }
/* display:block matters — this is a <span>, and an inline box ignores width,
   so the proportion bar had never actually rendered */
.s-row .bar { display: block; height: 5px; background: var(--accent); border-radius: 3px; opacity: 0.8; }
.s-group { padding: 9px 12px; font-size: var(--t-xs); font-weight: var(--w-bold); text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-3); background: var(--paper-sunk); }
.bulk-text { width: 100%; font-family: var(--mono); font-size: var(--t-xs); margin-top: 10px; }
.bulk-drop { display: flex; align-items: center; gap: 9px; padding-top: 11px; }
.bulk-acts { display: flex; align-items: center; gap: 9px; margin-top: 9px; }
.b-row { display: grid; grid-template-columns: 44px 132px minmax(0, 1fr); gap: 9px; padding: 6px 2px 6px 12px; border-bottom: 1px solid var(--rule-soft); font-size: var(--t-sm); }
.b-row.bad { background: var(--st-stop-bg); }
.b-row .ln { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); }
.b-row .errs { color: var(--st-stop-ink); font-size: var(--t-xs); }
@media (max-width: 820px) {
  .s-cols { display: none; }
  .s-row { grid-template-columns: 1fr 1fr; gap: 4px; }
  .b-row { grid-template-columns: 1fr; }
}

/* ── admin ────────────────────────────────────────────────────────────── */
/* People, as a ruled list: same column discipline as the post log, so whoever
   can read one can read the other. Role is a control, not a badge — the thing
   you came here to change is the thing you can click. */
/* Same fix as .log: a `minmax(180px, …)` floor let a long name widen column 1,
   and the `auto` actions track varied with how many buttons a row showed — so
   role/state/owns sat at a different x on every person. All tracks are now
   content-independent; the name ellipsizes (it already had text-overflow) and
   the actions column is fixed, wrapping inside itself when it must. */
.users { display: flex; flex-direction: column; --ucols: minmax(0, 1.7fr) 132px 112px minmax(0, 0.9fr) 320px; }
.u-cols, .u-row { display: grid; grid-template-columns: var(--ucols); gap: 10px; align-items: center; }
.u-cols { padding: var(--s-2) 2px var(--s-2) var(--s-3); }
.u-row { padding: var(--s-3) 2px var(--s-3) var(--s-3); border-bottom: 1px solid var(--rule-soft); }
.u-row.you { background: var(--paper-sunk); }
.u-row.off .u-who b { color: var(--ink-3); text-decoration: line-through; }
.u-who { display: grid; gap: 1px; min-width: 0; }
/* the name line carries an optional "owner" badge, so it wraps rather than
   clipping the badge; the email line below keeps its ellipsis */
.u-who b {
  font-weight: var(--w-semibold); display: flex; align-items: center;
  gap: var(--s-2); flex-wrap: wrap; min-width: 0;
}
.u-who span { font-size: var(--t-xs); color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.u-owns { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-2); }
.u-acts { display: flex; gap: var(--s-2); justify-content: flex-end; flex-wrap: wrap; }
/* was a mono 3px-padded oddity; it is a select, so it looks like every select */
select.u-role { max-width: 128px; }
.u-add {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s-3); align-items: end; padding: var(--s-4) 2px var(--s-1) var(--s-3);
}
.u-add label { display: grid; gap: var(--s-2); font-size: var(--t-sm); font-weight: var(--w-medium); color: var(--ink-2); }
@media (max-width: 820px) {
  .u-cols { display: none; }
  .u-row { grid-template-columns: 1fr; gap: 6px; }
  .u-acts { justify-content: flex-start; }
}

/* ── activity dock ───────────────────────────────────────────────────── */
/* The machine talking back. Same ruled-rows-on-paper as the log above it,
   shrunk to a corner: one line per event, time and gap in mono, and the only
   colour is the level — warn is hold, error is stop. It floats, so it earns a
   shadow (dialog and toast are the other two). */
.dock {
  position: fixed; left: 14px; bottom: 14px; z-index: var(--z-dock);
  width: min(430px, calc(100vw - 28px));
  display: grid; justify-items: start; gap: 6px;
  /* it sits on top of the library, so it stays quiet until looked at */
  opacity: 0.62; transition: opacity 120ms var(--ease);
}
.dock:hover, .dock:focus-within, .dock[data-busy="1"] { opacity: 1; }
.dock-tab {
  display: inline-flex; align-items: center; gap: 8px; max-width: 100%;
  font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-2);
  background: var(--paper); border: 1px solid var(--rule-hard); border-radius: var(--r-m);
  padding: 5px 9px; cursor: pointer; box-shadow: var(--lift); text-align: left;
}
.dock-tab:hover { border-color: var(--ink); color: var(--ink); }
.dock-tab .msg { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dock-tab .unseen {
  font-weight: var(--w-bold); color: var(--ink); background: var(--paper-band);
  border: 1px solid var(--rule-hard); border-radius: 3px; padding: 0 4px;
}
.dock-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--st-idle); flex: none; }
.dock-dot.ok { background: var(--st-ok); }
.dock-dot.work { background: var(--st-work); }
.dock-dot.hold { background: var(--st-hold); }
.dock-dot.stop { background: var(--st-stop); }
.dock-panel {
  width: 100%; background: var(--paper); border: 1px solid var(--rule-hard);
  border-radius: var(--r-l); box-shadow: var(--lift); overflow: hidden;
}
.dock-head {
  display: flex; align-items: center; gap: 8px; padding: 7px 9px;
  border-bottom: 1px solid var(--rule); background: var(--paper-sunk);
}
.dock-head .title { font-size: var(--t-xs); font-weight: var(--w-bold); letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-2); }
.dock-head .grow { margin-left: auto; }
input.dock-filter {
  width: 116px; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink);
  background: var(--ctl-bg); border: 1px solid var(--ctl-border);
  border-radius: var(--r-s); padding: 3px var(--s-2);
}
input.dock-filter:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-bg);
}
.dock-conn { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); cursor: pointer; background: none; border: 0; padding: 0; }
.dock-conn.off { color: var(--st-stop-ink); }
.dock-rows {
  max-height: min(28vh, 210px); overflow-y: auto; overscroll-behavior: contain;
  font-family: var(--mono); font-size: var(--t-xs); font-variant-numeric: tabular-nums;
}
.drow { display: grid; grid-template-columns: 52px 40px 1fr; gap: 7px; padding: 3px 8px; border-top: 1px solid var(--rule-soft); }
.drow.pending .dmsg { color: var(--st-work-ink); }
.drow:first-child { border-top: 0; }
.drow .dt { color: var(--ink-3); }
/* the gap since the previous line — this column is the answer to "why is it
   slow", so it is ink rather than grey once the wait is a whole second */
.drow .dgap { color: var(--ink-2); text-align: right; }
.drow .dmsg { color: var(--ink); word-break: break-word; }
.drow .dfields { display: block; color: var(--ink-3); }
.drow.lv-warn .dmsg { color: var(--st-hold-ink); }
.drow.lv-stop { background: var(--st-stop-bg); }
.drow.lv-stop .dmsg { color: var(--st-stop-ink); font-weight: var(--w-semibold); }
.dock-foot { padding: 5px 9px; border-top: 1px solid var(--rule); background: var(--paper-sunk); }
.dock-empty { padding: 12px 9px; color: var(--ink-3); }
@media (max-width: 820px) { .dock { left: 10px; right: 10px; bottom: 10px; width: auto; } }

/* ── help / documentation ─────────────────────────────────────────────── */
/* Prose, not an instrument: this is the one view someone reads top to bottom,
   so the measure is capped and the rhythm is looser than the rest of the app. */
.doc { max-width: 74ch; padding-top: 4px; }
.doc h3 {
  font-size: var(--t-lg); margin: 30px 0 8px; padding-top: 14px;
  border-top: 1px solid var(--rule);
}
.doc h3:first-of-type { border-top: 0; padding-top: 0; margin-top: 14px; }
.doc h4 { font-size: var(--t-md); margin: 18px 0 5px; }
.doc p, .doc li { color: var(--ink-2); line-height: 1.65; }
.doc p { margin: 0 0 11px; }
.doc ul, .doc ol { margin: 0 0 12px; padding-left: 22px; }
.doc li { margin-bottom: 6px; }
.doc b, .doc strong { color: var(--ink); font-weight: var(--w-medium); }
.doc code {
  font-family: var(--mono); font-size: var(--t-sm); background: var(--paper-sunk);
  border: 1px solid var(--rule); border-radius: 5px; padding: 1px 5px; color: var(--ink);
}
/* the jump list: this page is long, so it opens with its own contents */
.doc-toc { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: var(--s-1) 0 var(--s-2); }
.doc-toc a {
  text-decoration: none; font-size: var(--t-sm); font-weight: var(--w-semibold); color: var(--ink-2);
  border: 1px solid var(--rule-hard); border-radius: var(--r-pill); padding: 5px 12px;
  background: var(--paper); box-shadow: var(--shadow-sm);
}
.doc-toc a:hover { border-color: var(--accent); color: var(--accent-ink); }
/* a callout for the things that will bite you if you skip them */
.doc-note {
  border: 1px solid var(--rule); border-left: 3px solid var(--accent);
  background: var(--paper-sunk); border-radius: var(--r-s);
  padding: var(--s-3) var(--s-4); margin: 0 0 var(--s-4);
}
.doc-note.warn { border-left-color: var(--st-hold); background: var(--st-hold-bg); }
.doc-note p:last-child { margin-bottom: 0; }
/* status glossary: the real chips, so what you read here is what you see there */
/* Two content-independent tracks, so every explanation starts at the same x
   however long the chip's label is — the same rule as the Posts and Team
   lists. A wrapping flex row put long text on its own line and looked ragged. */
.doc-states { display: grid; gap: 9px; margin: 2px 0 14px; }
.doc-state { display: grid; grid-template-columns: 152px minmax(0, 1fr); gap: 12px; align-items: baseline; }
.doc-state .stamp { justify-self: start; }
.doc-state span:last-child { color: var(--ink-2); font-size: var(--t-sm); min-width: 0; }
@media (max-width: 820px) {
  /* no room for two columns: the chip sits above its explanation */
  .doc-state { grid-template-columns: 1fr; gap: 3px; }
}

/* ── view polish (M4) ─────────────────────────────────────────────────── */
/* The ruled lists get the one affordance a modern surface expects and the
   dense original refused: a row lights up under the pointer. It is a hover
   wash on the paper, not a colour — the state rule still owns saturation.
   Rows carry it to their own edges via a negative-margin/positive-padding
   trick so the highlight reaches the section-card's padding, not just the
   text column. */
.line, .u-row, .s-row, .acct {
  transition: background 110ms var(--ease);
}
.line:hover,
.u-row:hover:not(.you),
.s-row:hover,
.acct:hover,
.b-row:hover {
  background: var(--paper-sunk);
}
/* the metric tiles and small-multiple panels answer the pointer too */
.tile, .sum-tile, .panel { transition: background 110ms var(--ease); }
.tile:hover, .sum-tile:hover, .panel:hover { background: var(--paper-sunk); }
/* a published log line's action links are the brand accent, like every link */
.line .tail a { color: var(--accent-ink); }

/* ── responsive ──────────────────────────────────────────────────────── */
/* 1100 and not 1000: below this the right track sits on its 280px floor and the
   left one is squeezed under 340px, which is where the account handles and the
   media row start fighting for space. One column is better than two bad ones. */
@media (max-width: 1100px) {
  .composer { grid-template-columns: 1fr; }
  /* one column: the right pane is now BELOW the left, so sticking it would
     pin the publish button over the fields it belongs to */
  .composer .pane.right {
    padding-left: 0; margin-left: 0; border-left: 0; border-top: 1px solid var(--rule);
    position: static;
  }
  textarea.caption, .genbar, #capNote, #autoNote { max-width: none; }
}
/* the nav outranks the tally: knowing where you can go beats a headcount.
   The quota goes with it — when it matters it is in the strip anyway. */
@media (max-width: 1120px) { .tally, .quota { display: none; } }
@media (max-width: 820px) {
  /* Reclaim the side padding a phone cannot spare, and let every card shrink:
     a grid item's default min-width is its content's min-content width, so
     without min-width:0 a wide control cluster inside a section pushed the
     whole page sideways (measured 2026-07-26: Statistics scrolled to 595px in
     a 390px viewport). */
  .board { grid-template-columns: 1fr; padding: 12px 10px 80px; gap: 12px; }
  .stack, .sec { min-width: 0; }
  .sec { padding-left: 12px; padding-right: 12px; }

  /* The section head is a heading + an explanation + that section's controls.
     On a phone those cannot share one line, and a nowrap flex row was what
     forced the page wider than the screen. They stack instead. */
  .sec-head { flex-wrap: wrap; row-gap: 8px; }
  .sec-head .end { margin-left: 0; flex-wrap: wrap; width: 100%; }
  /* a full-width select in a control cluster is what made the window filter
     185px wide on a 390px screen */
  .sec-head .end select.line, .sec-head .end input.line { width: auto; max-width: 100%; }
  /* four joined filters have to fit the screen, so they give up some padding */
  .seg button { padding: 8px 10px; }
  /* on a phone the rail is a wall in front of the thing you opened the app for,
     so the work comes first and the accounts sit under it */
  .rail {
    position: static; max-height: none; order: 2;
    border-right: 0; border-top: 1px solid var(--rule-hard); padding-top: 4px;
  }
  .band-head { position: static; }
  .stack { order: 1; padding-top: 4px; }
  /* One row could not hold the brand, six places and the session: the nav was
     squeezed to about 60px ("Pos…"), which is the one thing a phone must not
     lose. The bar becomes two rows — identity and session on top, the nav
     across the full width beneath it, where it can scroll sideways. */
  .topbar {
    gap: 8px 10px; padding: 6px 10px; height: auto; flex-wrap: wrap;
    align-items: center;
  }
  .views { order: 3; width: 100%; }
  .session { order: 2; margin-left: auto; }
  /* the nav takes the width it needs and scrolls sideways; nothing else may */
  .views { overflow-x: auto; scrollbar-width: none; }
  .views::-webkit-scrollbar { display: none; }
  /* the section heads stick under a taller bar now, so they cannot claim 58px */
  .sec-head, .rail { top: auto; position: static; }
  /* The nav is a pill now, not an underline: zeroing the vertical padding (as
     the old design did here) left a 20px-tall tap target. A thumb needs the
     height even though the row scrolls sideways. */
  .views button { padding: 8px 11px; }
  /* NOT hidden any more: the <i> used to hold the decorative "/log", but it
     now holds "post" — hiding it rendered the product name as "auto". */
  /* a phone has no room for the words: the dot already carries the state */
  .heart { font-size: 0; gap: 0; }
  .who { max-width: 12ch; }
  /* no room for columns on a phone: the line folds back into a stack */
  .log-cols { display: none; }
  .line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
  .line .tail { margin-left: auto; }

  /* ── what a phone changes about a control ──────────────────────────── */

  /* iOS Safari zooms the page whenever a focused field computes under 16px,
     and it does not zoom back out afterwards — so on a phone every tap into
     the caption, the sign-in, or a filter left the whole console magnified
     and scrolled sideways. 16px here is not a type-scale decision; it is the
     size at which the browser stops interfering. Only the phone pays it. */
  input, select, textarea,
  input.line, select.line, textarea.caption,
  .acct-edit input, .acct-edit select, select.u-role { font-size: 16px; }

  /* A 21px-tall button is a desk instrument's answer, not a thumb's. These
     are the two that sit side by side at the end of a log line — one of them
     deletes — so they get height and a wider gap between them. Density still
     wins everywhere text is only read. */
  .btn { min-height: 40px; padding: 8px 14px; }
  .btn-sm, .item .acts .btn, .sec-head .btn { min-height: 36px; padding: 6px 12px; font-size: var(--t-sm); }
  .line .tail { gap: 12px; }

  /* Measured 2026-07-26: a 15px checkbox and a 19px select are not tap
     targets. Everything a thumb has to hit gets real height — the checkbox
     itself grows, and its whole label row becomes the target so the words are
     tappable too, not just the box. */
  .tt-check input, .pick input { width: 22px; height: 22px; }
  .tt-check, .pick { min-height: 40px; }
  .langpick { min-height: 36px; }
  select.line, input.line, textarea.line, select.u-role { min-height: 40px; }
  .item select.lang, .item input.rename, .acct-edit input, .acct-edit select { min-height: 36px; }
  .cal-day .dnum .add { min-height: 32px; min-width: 32px; }
  /* the policy links sit in a sentence, so they get padding rather than height */
  .connect .disclosure a, #ttConsent a { display: inline-block; padding: 3px 0; }

  /* the dock floats over the end of the page, and on a phone the accounts
     rail is what ends it — without this its last rows sit under the tab */
  body:has(.dock:not([hidden])) .rail { padding-bottom: 92px; }
  body:has(.dock:not([hidden])) .board { padding-bottom: 116px; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ── dev instance ──────────────────────────────────────────────────────
   Only ever rendered by the private dev deployment (compose.dev.yml). It is
   the same build as production behind the same login, so the two tabs are
   otherwise indistinguishable — this is the thing that stops an edit landing
   in the wrong one. Sticky, because a banner you scroll past is a banner you
   stop seeing. */
.devbar {
  position: sticky; top: 0; z-index: 100;
  background: var(--st-stop); color: #fff;
  font-family: var(--mono); font-size: var(--t-xs); font-weight: var(--w-medium);
  letter-spacing: 0.06em; text-transform: uppercase;
  text-align: center; padding: 5px var(--s-3);
}
/* a hairline down the whole page, so it reads as dev even mid-scroll */
:root[data-deploy="dev"] body { border-left: 3px solid var(--st-stop); }
