/* Ops Studio mini app — the command center.
   The app chrome is ALWAYS the Ops Studio identity (black #0B0B0C, paper,
   orange #FF5A1F as a point of accent, never a wash) — standardized, the
   same on every studio. A client's brand tokens dress their DELIVERABLES
   (cards, docs); they never repaint this app. Nothing here is hardcoded
   to content: type scales with the system font, cards grow with what
   fills them.

   Which of the two palettes below applies is decided by app.js and
   stamped as data-scheme on :root — an explicit founder choice first,
   the device (tg.colorScheme / prefers-color-scheme) in AUTO. In AUTO
   inside Telegram, app.js also paints Telegram's stock theme parameters
   onto :root inline, exactly the layer this app always had.

   --font-mono is the app's label voice — small uppercase wide-tracked
   labels, the identity's "small mono caption" register. --font-heading
   and --font-body exist so the identity's type reads from one place. */

/* ---- the scales ----------------------------------------------------------
   shadcn's discipline without its code: radius, spacing and elevation each
   draw from ONE named scale, defined on :root below. This layer is a RENAME
   of the values the app already shipped — nothing renders differently.

   RADIUS   --r-hair 2px    hairline rounds: rules, meter tracks
            --r-xs   6px    the sheet's kind chip
            --r-sm   8px    chips, tables, code blocks, week-strip badges
            --r-md   10px   buttons, tiles, inner boxes, the toast
            --r-lg   12px   cards and previews
            --r-xl   16px   the sheet panel's shoulders
            --r-pill 999px  pills and full rounds (the 9px badge and the
                            22px close were half-of-height rounds already;
                            they are renamed to the pill they always were)
            One survivor sits off the scale: the status chip's 7px, kept
            exact because folding it to 8 would move pixels.

   SPACING  a 4px base with the half-steps this app's rhythm really uses:
            --sp-1 4 · --sp-1h 6 · --sp-2 8 · --sp-2h 10 · --sp-3 12 ·
            --sp-3h 14 · --sp-4 16 · --sp-5 20 · --sp-6 24.
            Gaps and block rhythm ride the tokens. One-off optical trims
            (a 13px card bottom, a 9px row pad, a 7px column gap) stay
            literal on purpose: they are tuned to their component, not to
            the grid, and renaming them would pretend otherwise.

   ELEVATION  the app is flat by design — cards are drawn with borders and
            the sheet is a plane, not a float. The one thing that hovers is
            the toast: --elev-1. Nothing else may cast a shadow. */

:root {
  --bg: #F4F4F2;
  --text: #0B0B0C;
  --hint: #5D5D59;
  --link: #FF5A1F;
  --button: #1A1A1E;
  --button-text: #F4F4F2;
  --card: #FAFAF8;
  --destructive: #D03A2A;
  /* A mid grey WITH alpha: the same value must rule quietly on paper and on
     near-black, and only a composite does both. */
  --line: rgba(127, 127, 130, 0.42);
  --ok: #1E8A50;
  --accent: var(--link);
  /* Secondary text: a detail line under a bullet, a caption under a chart.
     It was used in two places before it existed anywhere, so both quietly
     inherited body colour and read as loud as the sentence above them. It is
     the same tone as --hint by design — one meaning, one value, and it
     follows the theme through --hint rather than repeating a hex code. */
  --muted: var(--hint);
  --raised: #FFFFFF;
  /* Accent and status colours AS SMALL TEXT on the light surfaces. The full
     #FF5A1F reads at 2.8–3.1:1 against paper — fine as a graphic point
     (dots, rules, the chart line), never as 9–15px text. Dark already swaps
     its text orange to the pale #FF8A5E for exactly this reason; these are
     the light scheme's equivalents, same hues taken darker until every
     surface (#FFFFFF, #FAFAF8, #F4F4F2) clears 4.5:1. */
  --accent-text: #B8420A;
  --ok-text: #187446;
  --destructive-text: #C43526;
  /* Ink ON an accent-filled control (selected chart tab, Connect Google).
     The identity's black on the orange: 6.3:1. Fixed in both schemes —
     dark fills those controls with the pale orange, where it reads 8.5:1. */
  --accent-ink: #0B0B0C;
  /* The sheet's own surface. The page colour in light (the scrim darkens
     the page behind it); raised in dark, or panel and scrimmed page are
     the same near-black and the sheet reads as a page replacement. */
  --sheet: var(--bg);
  --font-body: -apple-system, system-ui, "Segoe UI", Roboto, sans-serif;
  --font-heading: var(--font-body);
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "Segoe UI Mono",
               Menlo, Consolas, monospace;
  /* The scales (see the note above). Structural, not thematic: the scheme
     machinery never touches these. */
  --r-hair: 2px;
  --r-xs: 6px;
  --r-sm: 8px;
  --r-md: 10px;
  --r-lg: 12px;
  --r-xl: 16px;
  --r-pill: 999px;
  --sp-1: 4px;
  --sp-1h: 6px;
  --sp-2: 8px;
  --sp-2h: 10px;
  --sp-3: 12px;
  --sp-3h: 14px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --elev-1: 0 2px 10px rgba(0, 0, 0, 0.25);
}

/* The dark palette rides the device UNLESS the founder explicitly chose
   light — the :not() is that precedence, and it also covers the first
   frame before app.js has stamped anything. */
@media (prefers-color-scheme: dark) {
  :root:not([data-scheme="light"]) {
    --bg: #0B0B0C;
    --text: #F4F4F2;
    --hint: #A2A29E;
    --link: #FF8A5E;
    --button: #FF5A1F;
    --button-text: #0B0B0C;
    --card: #131316;
    --destructive: #E07F75;
    --ok: #56A77C;
    --raised: #1A1A1E;
    --accent-text: #FF8A5E;
    --ok-text: #56A77C;
    --destructive-text: #E07F75;
    --sheet: #1A1A1E;
  }
}

/* The stamped verdict: dark whatever the device says. SAME VALUES as the
   media block above — a change there lands here too. (Light needs no twin:
   with data-scheme="light" the media block stands down and the :root
   defaults are already the light palette.) */
:root[data-scheme="dark"] {
  --bg: #0B0B0C;
  --text: #F4F4F2;
  --hint: #A2A29E;
  --link: #FF8A5E;
  --button: #FF5A1F;
  --button-text: #0B0B0C;
  --card: #131316;
  --destructive: #E07F75;
  --ok: #56A77C;
  --raised: #1A1A1E;
  --accent-text: #FF8A5E;
  --ok-text: #56A77C;
  --destructive-text: #E07F75;
  --sheet: #1A1A1E;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* The hidden attribute must always win, even over display:flex below. */
[hidden] { display: none !important; }

html, body {
  background: var(--bg);
  color: var(--text);
  font: 16px/1.45 -apple-system, system-ui, "Segoe UI", Roboto, sans-serif;
  font-family: var(--font-body);
  -webkit-text-size-adjust: 100%;
}

body {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  overscroll-behavior-y: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* The one focus treatment, the identity's own: the accent as a point.
   The text-grade orange, so the ring clears 3:1 on the light surfaces too. */
:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }

.dim { color: var(--hint); }

/* ---- top: offline notice + identity strip ---- */

.offline {
  position: sticky;
  top: 0;
  z-index: 5;
  padding: 6px 16px;
  font-size: 13px;
  color: var(--hint);
  background: var(--card);
  border-bottom: 1px solid var(--line);
  text-align: center;
}

/* The identity strip: the Ops Studio mark (an open ring, a filled point),
   the client's name in their own heading face, the wordmark as a small mono
   label, the usage meter as a slim branded bar, and three at-a-glance
   numbers. Everything sizes from its content — a long name wraps, missing
   stats show an en dash. */

.brandbar {
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: 16px 16px 12px;
  border-bottom: 1px solid var(--line);
}

.brand-row {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3h);
}

.brand-id { flex: 1; min-width: 0; }

/* The mark. The ring rides currentColor so a client brand's ink recolours
   it with everything else; the point stays #FF5A1F in the SVG itself — the
   one fixed colour the identity allows ("the point is the agent"). */
.brand-sigil { flex: 0 0 auto; width: 28px; height: 28px; margin-top: 2px; }

.brand-name {
  font-family: var(--font-heading);
  font-size: 23px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: 0.01em;
  overflow-wrap: break-word;
}

.brand-mark {
  margin-top: 4px;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--hint);
}

/* The strip's right-hand column: the scheme control, then the usage
   meters beneath it. A column, because an inline pill stole the width
   the title needs — the identity block keeps its full measure. */
.brand-side {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 7px;
}
.brand-side .usage { padding-top: 0; }

/* The scheme control: chrome, so it speaks the label voice. One pill,
   tap to cycle AUTO → LIGHT → DARK. The visible pill stays quiet and
   compact; the ::before pad grows the touch target to ~44px without
   growing the chrome. */
.scheme {
  position: relative;
  margin-top: 2px;
  min-height: 26px;
  min-width: 56px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--hint);
}
.scheme::before { content: ""; position: absolute; inset: -9px; }

/* Usage, present but never alarming: slim accent bars, top right, absent
   entirely when the server sends null. */
.usage {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 5px;
  padding-top: 5px;
  font-size: 11px;
  color: var(--hint);
}
/* The meters' noun — chrome, so it speaks the label voice. Without it the
   bars read as a nameless fuel gauge in the app's best position. */
.usage .ulabel {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.meter { display: flex; align-items: center; gap: var(--sp-1h); }
.meter .pct { font-variant-numeric: tabular-nums; min-width: 3ch; text-align: right; }
.meter .track {
  width: 56px; height: 3px; border-radius: var(--r-hair);
  background: var(--line); overflow: hidden;
}
/* display:block, or the span stays inline, its width is ignored and the
   meter reads as an empty track — the accent never appeared at all. */
.meter .fill { display: block; height: 100%; background: var(--accent); border-radius: var(--r-hair); }
/* An old sample says when it was measured — quiet, never a warning. */
.usage .asof { font-size: 10px; opacity: 0.75; }

.brand-stats {
  display: flex;
  gap: var(--sp-2h);
  margin-top: 14px;
}
.bstat { flex: 1 1 0; min-width: 0; }
.bstat .bn {
  display: block;
  font-family: var(--font-heading);
  font-size: 21px;
  font-weight: 700;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
}
.bstat .bt {
  display: block;
  margin-top: 1px;
  font-size: 11px;
  color: var(--hint);
}

/* ---- the card column ---- */

.main {
  flex: 1;
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: 14px 12px 24px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3h);
}

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 16px 16px 13px;
}

.card-h {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  margin-bottom: 12px;
}
/* Card titles are the app's chrome, not the client's content, so they wear
   the identity's label voice: small mono uppercase, wide-tracked. The
   client's own faces stay on the name, the numbers and the prose. */
.card-h h2 {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--hint);
}
.card-sub { margin-left: auto; font-size: 12px; color: var(--hint); }

.badge {
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  /* 9px on an 18px height was always the full round; named as what it is. */
  border-radius: var(--r-pill);
  font-size: 12px;
  font-weight: 600;
  line-height: 18px;
  text-align: center;
  color: var(--button-text);
  background: var(--button);
}

/* Section headings inside a card — same label voice as the card titles. */
.h { font-family: var(--font-mono); font-size: 11px; font-weight: 600;
     text-transform: uppercase; letter-spacing: 0.18em; color: var(--hint);
     margin: 4px 0 8px; }
.h + .h, .list + .h { margin-top: 16px; }

/* Honest empty states: what the card is for, and what will fill it. */
.empty { padding: 10px 2px 8px; }
.empty .big { font-size: 15px; color: var(--text); margin-bottom: 3px; }
.empty .sub { font-size: 13px; color: var(--hint); }

/* The agent strip: the studio's visible state, at the top of Today. Quiet
   chrome — the label voice, a raised row — with the identity's point as its
   mark ("the point is the agent"). It states only what the receipts record;
   app.js keeps it honest, this only dresses it. */
.agent-strip {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-3);
  padding: 7px var(--sp-2h);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--raised);
  font-size: 13px;
  color: var(--hint);
}
.agent-dot {
  flex: 0 0 auto;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--accent);
}
.agent-label {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.agent-text { min-width: 0; overflow-wrap: break-word; }
.agent-text b { color: var(--text); font-weight: 600; }

/* An old digest has to announce itself, not sit there looking like today's.
   The accent rule is the agent tapping the founder on the shoulder — one of
   the few places the orange is allowed to mean "look here". */
.digest-stale {
  font-size: 13px; color: var(--hint); margin-bottom: 10px; padding: 8px 10px;
  border-left: 2px solid var(--accent); border-radius: var(--r-hair);
}

.centre { text-align: center; color: var(--hint); padding: 24px 12px; }
.centre .big { font-size: 17px; color: var(--text); margin-bottom: 6px; }

/* Receipts: the morning block. Past tense, quiet. */
.receipts {
  border-top: 1px solid var(--line);
  padding-top: 8px;
  margin-top: 12px;
}
.receipt { display: flex; gap: var(--sp-2h); padding: 4px 0; font-size: 14px; }
.receipt .at { color: var(--hint); font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.receipt .what { flex: 1; min-width: 0; overflow-wrap: break-word; }
.receipt .what small { display: block; color: var(--hint); font-size: 13px; }

/* Item rows */
.list { display: flex; flex-direction: column; }

.row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: 56px;
  padding: 9px 2px;
  border-bottom: 1px solid var(--line);
  text-align: left;
}
.row:last-child { border-bottom: 0; }
.row:active { background: var(--line); }

.row .mid { flex: 1; min-width: 0; }
.row .sum {
  font-size: 16px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.row .meta { display: block; font-size: 13px; color: var(--hint); margin-top: 3px; }
.row .kindlab { color: var(--accent-text); font-weight: 600; }
.row .go { color: var(--hint); font-size: 20px; flex: 0 0 auto; }

.row.done { opacity: 0.55; }

/* A decided row settles out of the queue — motion recipe two of three
   (transitions.dev): the movement narrates "this is handled", then the
   refetched list redraws the truth. app.js skips the class entirely under
   prefers-reduced-motion; the media block below is the belt to that brace. */
.row.settling {
  animation: settle 0.28s ease forwards;
  pointer-events: none;
}
@keyframes settle { to { opacity: 0; transform: translateY(-6px); } }
.row .mark { flex: 0 0 auto; font-size: 15px; }
.mark.approved, .mark.sent { color: var(--ok-text); }
.mark.rejected, .mark.failed { color: var(--destructive-text); }
/* A read note is neither a success nor a warning; it settles into the
   decided list in the quiet voice of a fact. */
.mark.seen { color: var(--hint); }

.chip {
  flex: 0 0 auto;
  font-size: 12px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: var(--r-xs);
  background: var(--card);
  color: var(--accent-text);
  white-space: nowrap;
}

/* ---- week strip: seven equal cells, never a horizontal scroll ---- */

.weekstrip { display: flex; gap: 3px; }

.wday {
  flex: 1 1 0;
  min-width: 0;
  min-height: 62px;
  border-radius: var(--r-md);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding: 6px 0 5px;
  gap: 2px;
  color: var(--hint);
}
.wday .dow { font-family: var(--font-mono); font-size: 9px;
             text-transform: uppercase; letter-spacing: 0.1em; }
.wday .num { font-size: 15px; font-weight: 600; color: var(--text); }

/* Post dots: one per scheduled post, coloured by state. Reserved height so
   quiet days align with busy ones. */
.wday .dots {
  display: flex;
  gap: 3px;
  height: 6px;
  align-items: center;
  justify-content: center;
}
.dot { width: 5px; height: 5px; border-radius: 50%; background: var(--hint); }
.dot.sent { background: var(--ok); }
.dot.queued { background: var(--link); }
.dot.failed, .dot.cancelled { background: var(--destructive); }

/* Pending-review badge under the dots. Reserved height so days align. */
.wday .pend {
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--r-sm);
  font-size: 11px;
  font-weight: 600;
  line-height: 16px;
  text-align: center;
  color: var(--button-text);
  background: var(--button);
}
.wday .pend:empty { background: none; }

.wday.sel { background: var(--raised); box-shadow: inset 0 0 0 1px var(--accent); }
.wday.sel .dow { color: var(--accent-text); }
.wday.today .num { color: var(--accent-text); }

/* Selected day's schedule under the strip */
.postlist { margin-top: 10px; border-top: 1px solid var(--line); padding-top: 6px; }
.post {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2h);
  padding: 6px 2px;
  font-size: 14px;
}
.post .at { color: var(--hint); font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.post .title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pstate.sent { color: var(--ok-text); }
.pstate.queued { color: var(--accent-text); }
.pstate.failed, .pstate.cancelled { color: var(--destructive-text); }

/* ---- outreach tiles ---- */

.tiles { display: flex; flex-direction: column; }
.tile {
  display: flex;
  align-items: center;
  gap: var(--sp-2h);
  padding: 9px 2px;
  border-bottom: 1px solid var(--line);
}
.tile:last-child { border-bottom: 0; }
.tile .tmid { flex: 1; min-width: 0; }
.tile .tname {
  font-size: 15px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tile .tcounts { font-size: 13px; color: var(--hint); margin-top: 2px; }
/* THE status chip — one recipe for every state word at the end of a row.
   Campaigns (.pill), posts (.pstate) and knowledge (.know-state) each grew
   their own dialect for the same job; they now all wear the identity's
   small mono label, and only the colour carries the state. */
.pill, .pstate, .know-state {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 1.4;
  padding: 3px 8px;
  /* The one radius off the scale (see the scale note): folding 7 to 8 would
     move pixels, and the scale pass may not. Fold on the next visual pass. */
  border-radius: 7px;
  background: var(--raised);
  border: 1px solid var(--line);
  color: var(--hint);
  white-space: nowrap;
}
.pill.active { color: var(--ok-text); }
.pill.paused { color: var(--accent-text); }
.pill.failed, .pill.canceled { color: var(--destructive-text); }

/* ---- pipeline stat tiles: content-adaptive, side by side ---- */

.stats { display: flex; gap: var(--sp-2h); }
.stat {
  flex: 1 1 0;
  min-width: 0;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 11px 12px 9px;
}
.stat .n { font-family: var(--font-heading); font-size: 24px; font-weight: 700;
           font-variant-numeric: tabular-nums; }
.stat .lbl { font-size: 12px; color: var(--hint); margin-top: 1px; }
.stagechips { display: flex; flex-wrap: wrap; gap: var(--sp-1h); margin-top: 10px; }
.stagechip {
  font-size: 12px;
  color: var(--hint);
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 3px 8px;
}
.stagechip b { color: var(--text); font-variant-numeric: tabular-nums; }
.pipeline-sub { font-size: 13px; color: var(--hint); margin-top: 10px; }

/* ---- digest: a comfortable read that collapses when long ---- */

/* The one button a founder taps to connect their calendar. It sits in the
   Connected card's own row rhythm, so it reads as part of the list rather
   than an advert. */
.connect-btn {
  position: relative;
  font: inherit; font-size: 13px; line-height: 1; font-weight: 600;
  padding: 7px 12px; border-radius: var(--r-pill);
  border: 1px solid var(--accent); background: var(--accent);
  color: var(--accent-ink); cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* The ::before pad grows the 30px control to a 44px target without growing
   the chrome — the same trick the scheme pill uses. */
.connect-btn::before { content: ""; position: absolute; inset: -7px 0; }
.connect-btn[disabled] { opacity: 0.6; cursor: default; }
.connnote { font-size: 12px; color: var(--muted); margin-top: 6px; }

/* ---- what your studio knows: the founder's own brain, gaps included ---- */

.knows { display: flex; flex-direction: column; }
.know {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 9px 0; border-bottom: 1px solid var(--line);
}
.know:last-child { border-bottom: 0; }
.know-what { flex: 1; min-width: 0; }
.know-what b { display: block; font-size: 14px; font-weight: 600; color: var(--text); }
.know-what small { display: block; font-size: 12px; color: var(--muted); margin-top: 1px; }
/* .know-state shares the status-chip recipe (see .pill). Held is quiet on
   purpose: a founder should scan for what is missing, and a row of green
   ticks is the fastest way to make the gaps invisible. */
.know-state.ok { color: var(--ok-text); border-color: transparent; background: none; }
.know-state.warn { color: var(--text); }
.know-state.gap { color: var(--destructive-text); border-color: currentColor; }

.know-needs {
  margin-top: 12px; padding: 10px 12px; border-radius: var(--r-md);
  background: var(--raised); border: 1px solid var(--line);
}
.know-needs > b { display: block; font-size: 14px; margin-bottom: 6px; }
.know-need { padding: 5px 0; border-top: 1px solid var(--line); }
.know-need:first-of-type { border-top: 0; }
.know-need span { display: block; font-size: 13px; color: var(--text); }
.know-need small { display: block; font-size: 13px; color: var(--muted); margin-top: 1px; }

.know-clash {
  margin-top: 12px; padding: 10px 12px; border-radius: var(--r-md);
  background: var(--raised); border: 1px solid var(--line);
}
.know-clash b { display: block; font-size: 14px; margin-bottom: 4px; }
.know-clash small { display: block; font-size: 13px; color: var(--muted); margin-top: 3px; }
/* The hub claim, said once; every fact that disagrees is a row beneath it,
   answered where it stands. Quiet buttons on purpose: this is the founder's
   own housekeeping, not an approval, and nothing here celebrates. */
.clash-hub { font-size: 13px; color: var(--text); margin-top: 2px; }
.clash-row { padding: 8px 0 2px; margin-top: 8px; border-top: 1px solid var(--line); }
.clash-acts { display: flex; flex-wrap: wrap; gap: var(--sp-1h); margin-top: 7px; }
.clash-btn {
  position: relative;
  font: inherit; font-size: 12px; line-height: 1;
  padding: 6px 10px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: transparent; color: var(--text);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
/* Drawn small, 44px to a thumb — the chart tab's own ::before trick. */
.clash-btn::before { content: ""; position: absolute; inset: -8px 0; }
.clash-btn:disabled { opacity: 0.5; cursor: default; }

/* ---- what your posts did: one curve, a window to choose, one caption ----
   The stretch is deliberate: preserveAspectRatio="none" on the svg lets the
   plot fill any phone's width at a fixed comfortable height, and nothing in
   here is sized in points-per-post, so two posts and two hundred both fit. */

.chart-tabs { display: flex; gap: var(--sp-1h); margin: 0 0 12px; }
.chart-tab {
  position: relative;
  font: inherit; font-size: 13px; line-height: 1;
  padding: 7px 12px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: transparent; color: var(--muted);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
/* 30px tall drawn, 44px to a thumb — the scheme pill's own ::before trick. */
.chart-tab::before { content: ""; position: absolute; inset: -7px 0; }
.chart-tab.on {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
  font-weight: 600;
}

.chart { display: block; width: 100%; height: 150px; overflow: visible; }
.chart-grid { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.chart-axis { fill: var(--hint); font-size: 11px; font-family: var(--font-body); }
/* The identity's rule: the accent is a point, never a wash. The area under
   the curve is kept to a breath of tint so the line and its points carry it. */
.chart-area { fill: var(--accent); opacity: 0.08; }
.chart-line {
  stroke: var(--accent); stroke-width: 2; stroke-linejoin: round;
  stroke-linecap: round; vector-effect: non-scaling-stroke;
}
.chart-dot {
  fill: var(--card); stroke: var(--accent); stroke-width: 2;
  vector-effect: non-scaling-stroke;
}
.chart-dot.on { fill: var(--accent); }
.chart-hit { cursor: pointer; }

/* The caption holds a fixed two lines whatever is selected, so tapping along
   the curve moves the point and never the page under the founder's thumb. */
.chart-caption { margin: 12px 0 0; min-height: 2.7em; }
.chart-caption b { display: block; font-size: 14px; font-weight: 700; color: var(--text); }
.chart-caption small { display: block; font-size: 13px; color: var(--muted); margin-top: 2px; }
.chart-foot {
  margin: 10px 0 0; padding-top: 10px; border-top: 1px solid var(--line);
  font-size: 13px; color: var(--muted);
}

.md { font-size: 15px; line-height: 1.55; overflow-wrap: break-word; }
.md p, .md ul { margin: 0 0 0.8em; }
.md ul { padding-left: 1.2em; }
.md a { color: var(--accent-text); text-decoration: none; }
.md .md-h1, .md .md-h2, .md .md-h3 { font-family: var(--font-heading);
  font-weight: 700; margin: 0.9em 0 0.35em; }
.md .md-h1 { font-size: 17px; }
.md .md-h2 { font-size: 16px; }
.md .md-h3 { font-size: 15px; }
.md > :first-child { margin-top: 0; }

/* An expandable bullet. A founder reads this on a phone: one line each, and
   the detail behind a line only when they ask for it. The marker has to look
   tappable without a caption saying so, and the tap target is the whole line
   rather than the triangle, because a thumb is not a mouse pointer. */
.md details > summary {
  list-style: none; cursor: pointer; position: relative; padding-right: 1.1em;
}
.md details > summary::-webkit-details-marker { display: none; }
.md details > summary::after {
  content: '⌄'; position: absolute; right: 0; top: -0.15em;
  color: var(--muted); font-size: 13px; transition: transform 0.15s ease;
}
.md details[open] > summary::after { transform: rotate(180deg); }
.md .md-detail {
  margin: 0.35em 0 0.2em; padding-left: 0.7em;
  border-left: 2px solid var(--line);
  color: var(--muted); font-size: 14px;
}

/* Tables. A lead list is 25 rows across six columns and the phone is 390px
   wide, so the table keeps its own width and scrolls sideways inside its box
   rather than crushing every column into two words per line. The first column
   stays put, because a row you have scrolled sideways is unreadable once the
   name is gone. */
.md-tablewrap {
  margin: 0 0 0.9em; overflow-x: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line); border-radius: var(--r-sm);
}
/* The scroll affordance: the digest's bottom-fade pattern, turned on its
   side. The shell wraps the scroller so the fade can sit still while the
   table moves; app.js keeps .scrollable honest (only while there is more
   table to the right), so a table that fits shows no fade at all. */
.md-tableshell { position: relative; margin: 0 0 0.9em; }
.md-tableshell .md-tablewrap { margin: 0; }
.md-tableshell::after {
  content: "";
  position: absolute;
  top: 1px; bottom: 1px; right: 1px;
  width: 40px;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  background: linear-gradient(to right, transparent, var(--card));
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.md-tableshell.scrollable::after { opacity: 1; }
.md-table { border-collapse: collapse; font-size: 13px; line-height: 1.35; min-width: 100%; }
.md-table th, .md-table td {
  padding: 8px 10px; text-align: left; white-space: nowrap;
  border-bottom: 1px solid var(--line);
}
.md-table th {
  font-family: var(--font-mono); font-weight: 600; font-size: 11px;
  letter-spacing: .06em; text-transform: uppercase; color: var(--hint);
  position: sticky; top: 0; background: var(--raised);
}
.md-table tbody tr:last-child td { border-bottom: 0; }
.md-table th:first-child, .md-table td:first-child {
  position: sticky; left: 0; background: var(--raised);
}
.md-table td:first-child { font-variant-numeric: tabular-nums; color: var(--hint); }
.md-table a { color: var(--accent-text); text-decoration: none; }

.md-clip { max-height: 340px; overflow: hidden; position: relative; }
.md-clip::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 56px;
  background: linear-gradient(to bottom, transparent, var(--card));
}
.md-more {
  display: block;
  width: 100%;
  padding: 8px 0 2px;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent-text);
  text-align: center;
}

/* ---- connected: quiet trust rows ---- */

.conn { display: flex; flex-direction: column; }
.connrow {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  padding: 7px 2px;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
}
.connrow:last-child { border-bottom: 0; }
.connrow .k { flex: 0 0 auto; color: var(--hint); }
.connrow .v {
  flex: 1;
  min-width: 0;
  text-align: right;
  overflow-wrap: break-word;
  font-variant-numeric: tabular-nums;
}
.connrow .v.unset { color: var(--hint); }

/* ---- item sheet ---- */

.sheet { position: fixed; inset: 0; z-index: 20; }

/* The scrim fades in as the panel rises — one gesture, not two arrivals. */
.scrim {
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.45);
  animation: fade 0.22s ease-out;
}
@keyframes fade { from { opacity: 0; } }

.panel {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  max-height: 94%;
  max-height: 94dvh;
  display: flex;
  flex-direction: column;
  background: var(--sheet);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  animation: rise 0.22s ease-out;
}
@keyframes rise { from { transform: translateY(24px); opacity: 0.6; } }

/* Every motion in the app stands down together. The settle also stands
   down in app.js (the class is never added), and the shimmer's animation
   lives on a pseudo-element that only exists outside this preference. */
@media (prefers-reduced-motion: reduce) {
  .panel, .scrim, .row.settling { animation: none; }
}

.panel-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2h);
  padding: 14px 16px 10px;
}
.panel-head .dim { flex: 1; font-size: 13px; }
.panel-head .chip { background: var(--card); }

.close {
  width: 44px; height: 44px;
  margin: -10px -12px -10px 0;
  /* 22px on a 44px square was always the full round; named as what it is. */
  border-radius: var(--r-pill);
  font-size: 17px;
  color: var(--hint);
  display: flex; align-items: center; justify-content: center;
}

.panel-body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 4px 20px 16px;
}

/* The draft itself: a comfortable read. */
.draft { max-width: 34em; margin: 0 auto; font-size: 17px; line-height: 1.6; }
.draft p { margin: 0 0 1em; overflow-wrap: break-word; }
.draft .sec { font-family: var(--font-mono); font-size: 11px;
              font-weight: 600; text-transform: uppercase;
              letter-spacing: 0.18em; color: var(--hint); margin: 18px 0 6px; }
.draft .sec:first-child { margin-top: 2px; }
.draft ul { margin: 0 0 1em 1.2em; }
.draft pre {
  font: 13px/1.5 ui-monospace, Menlo, Consolas, monospace;
  background: var(--card); border-radius: var(--r-sm); padding: 10px 12px;
  overflow-x: auto; margin-bottom: 1em; white-space: pre-wrap;
}

/* Preview artifact: the thing under review, so it gets the room. The frame
 * height chases the viewport rather than a fixed number, and the theme
 * variables keep it native in light and dark alike. */
.preview { margin: 0 auto 16px; max-width: 40em; }
.preview-holder { position: relative; }
.preview-frame {
  display: block; width: 100%; height: min(62vh, 560px);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: #fff; /* an artifact styles itself; a dark page behind a light
                       magnet reads as a rendering bug, so the canvas is fixed */
}
.preview-img {
  display: block; max-width: 100%; height: auto; margin: 0 auto;
  border: 1px solid var(--line); border-radius: var(--r-lg);
}
.preview .centre { padding: 24px 0; }
.preview .btn { max-width: 160px; margin: 10px auto 0; }

.decided-note {
  max-width: 34em; margin: 0 auto 14px;
  padding: 10px 14px; border-radius: var(--r-md);
  background: var(--card); color: var(--hint); font-size: 14px;
}

.panel-foot {
  padding: 10px 16px calc(12px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
}
.panel-foot:empty { display: none; }

.btn {
  display: block;
  width: 100%;
  min-height: 48px;
  border-radius: var(--r-md);
  font-size: 16px;
  font-weight: 600;
}
.btn.primary { background: var(--button); color: var(--button-text); }
.btn.quiet { color: var(--destructive-text); }
.btn.danger { background: var(--destructive); color: #fff; }
.btn[disabled] { opacity: 0.55; }
.btn + .btn { margin-top: 8px; }

/* The approval card: the consequence stated before the tap, the primary
   action weighted, the rejection visibly second. The reject keeps its full
   44px+ hit height and its warning colour — smaller in voice, never in
   target — and the danger styling stays on the confirm step where it
   belongs (renderWhy), so the reflex tap here commits nothing. */
.approve-card .approve-means {
  max-width: 34em;
  margin: 0 auto var(--sp-2h);
  font-size: 13px;
  line-height: 1.45;
  color: var(--hint);
  text-align: center;
}
.btn.approve-reject {
  width: auto;
  min-height: 44px;
  margin: 0 auto;
  padding: 0 var(--sp-4);
  font-size: 14px;
  font-weight: 500;
}
.btn + .btn.approve-reject { margin-top: 2px; }

/* A note's one action. Quiet on purpose: reading is not approving, so the
   button carries none of the approval's weight — the app's own ink on a
   hairline, no filled primary, no warning colour. Both palettes come from
   the same vars, so light and dark need no second recipe. */
.btn.ack {
  background: var(--card);
  color: var(--text);
  border: 1px solid var(--line);
  font-weight: 500;
}

/* Reject reason */
.why label { display: block; font-size: 15px; margin-bottom: 2px; }
.why .sub { font-size: 13px; color: var(--hint); margin-bottom: 8px; }
.why textarea {
  width: 100%;
  min-height: 84px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--card);
  color: var(--text);
  font: inherit;
  resize: vertical;
}
.why textarea:focus { outline: none; border-color: var(--accent-text); }
.why .acts { display: flex; gap: var(--sp-2); margin-top: 10px; }
.why .acts .btn { flex: 1; margin: 0; }

/* ---- toast ---- */

.toast {
  position: fixed;
  top: 10px; left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  max-width: 90%;
  padding: 8px 16px;
  border-radius: var(--r-md);
  background: var(--card);
  color: var(--text);
  font-size: 14px;
  box-shadow: var(--elev-1);
}

/* Loading dots on rows and views */
.loading { color: var(--hint); padding: 20px 0; text-align: center; }

/* The skeleton: motion recipe three (transitions.dev) — ghost lines where
   content will land, for the cache-first first paint. The line is the
   theme's own rule colour; the shimmer is a sweep of the card surface, and
   it exists only outside prefers-reduced-motion — inside it, the ghost
   lines stand still, which still beats a bare word. */
.skeleton {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2h);
  padding: var(--sp-2h) 2px var(--sp-2);
}
.sk-line {
  display: block;
  height: 12px;
  border-radius: var(--r-xs);
  background: var(--line);
  position: relative;
  overflow: hidden;
}
.sk-line:nth-child(3n + 1) { width: 82%; }
.sk-line:nth-child(3n + 2) { width: 58%; }
.sk-line:nth-child(3n) { width: 70%; }
@media (prefers-reduced-motion: no-preference) {
  .sk-line::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, var(--card), transparent);
    opacity: 0.7;
    transform: translateX(-100%);
    animation: shimmer 1.4s ease infinite;
  }
}
@keyframes shimmer { to { transform: translateX(100%); } }
