/* 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,
                            and the status chips (their 7px survivor was
                            FOLDED here on the felt-difference pass)
            --r-md   10px   buttons, tiles, inner boxes, the toast
            --r-lg   12px   previews, stat tiles, inner boxes
            --r-xl   16px   cards (the 14px shell was FOLDED here on the
                            felt-difference pass) and mid surfaces
            --r-2xl  20px   the anchor card and the sheet panel's shoulders:
                            the deeper the surface, the rounder the corner
            --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)

   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 · --sp-7 28 ·
            --sp-8 32. Gaps and block rhythm ride the tokens. Folded on the
            felt-difference pass: the card's 13px bottom pad → --sp-4, the
            row's 9px/2px pad → --sp-3/--sp-1. The 7px column gaps and the
            optical trims inside .wday/.post stay literal on purpose: they
            are tuned to their component, not to the grid, and renaming
            them would pretend otherwise.

   TYPE     four tiers, felt-difference pass: DISPLAY (--fs-display 27,
            --ls-display) for the brand name; TITLES (--fs-title1 19 for
            the anchor, --fs-title2 17 for standard cards, both tracked
            negative); NUMERALS (--fs-num-hero 28, --fs-num-brand 26,
            --ls-num — one quantitative grammar, tabular, in the display
            ink); LABEL (--fs-label 11, --ls-label open / --ls-label-tight
            boxed) and MICRO (--fs-micro 10, --ls-micro) as the two small
            mono registers. Line heights: --lh-tight display+numerals,
            --lh-title card titles, --lh-read long-form.

   ELEVATION  rebuilt on the felt-difference pass into a four-step ladder.
            In LIGHT, surfaces float on layered shadows: --elev-low (the
            recessive account cards, barely off the page), --elev-card
            (standard, three layers), --elev-anchor (the one-per-screen
            bloom on Today), --elev-sheet (the panel's upward bloom).
            Controls carry their own grade: --elev-chip on small chrome
            (chips, pills, tabs, the scheme toggle), --elev-btn on the
            acts, --elev-btn-press for the pressed state, and
            --elev-approve — the app's ONLY glow — on the approval alone.
            In DARK, drop shadows die on near-black, so the ladder is
            surface steps plus INSET TOP-LIGHTS (0.04 card → 0.07 anchor →
            0.08 sheet) and the approval keeps a licensed orange glow.
            Inner boxes keep their hairlines in both schemes: one float
            per surface — a card that floats holds ruled content, never
            floating content. Dividers step down to --track; control
            borders and the brandbar/offline frames keep --line. */

: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-2xl: 20px;
  --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;
  --sp-7: 28px;
  --sp-8: 32px;
  /* The type tiers (see the scale note): structural, scheme-blind. */
  --fs-display: 27px;
  --ls-display: -0.025em;
  --fs-title1: 19px;
  --ls-title1: -0.02em;
  --fs-title2: 17px;
  --ls-title2: -0.015em;
  --fs-num-hero: 28px;
  --fs-num-brand: 26px;
  --ls-num: -0.02em;
  --fs-label: 11px;
  --ls-label: 0.16em;
  --ls-label-tight: 0.08em;
  --fs-micro: 10px;
  --ls-micro: 0.12em;
  --lh-tight: 1.12;
  --lh-title: 1.22;
  --lh-read: 1.65;
  /* Display ink: headings and hero numerals sit one step above body text.
     In light the two are the same near-black; in dark the display lifts to
     pure white — tonal elevation applied to type. */
  --text-display: #0B0B0C;
  /* The divider grade, one step under --line: rules BETWEEN rows are
     furniture, not frames. Control borders and the brandbar keep --line. */
  --track: rgba(127, 127, 130, 0.26);
  /* The elevation ladder (light): layered floats, low → card → anchor →
     sheet. Dark overrides every one of these — see the dark blocks. */
  --elev-low: 0 1px 2px rgba(11, 11, 12, 0.04), 0 2px 6px rgba(11, 11, 12, 0.04);
  --elev-card: 0 1px 1px rgba(11, 11, 12, 0.05), 0 4px 10px rgba(11, 11, 12, 0.06), 0 12px 28px rgba(11, 11, 12, 0.07);
  --elev-anchor: 0 1px 2px rgba(11, 11, 12, 0.06), 0 8px 18px rgba(11, 11, 12, 0.09), 0 24px 48px rgba(11, 11, 12, 0.10);
  --elev-sheet: 0 -8px 24px rgba(11, 11, 12, 0.10), 0 -24px 64px rgba(11, 11, 12, 0.14);
  /* The controls' own physics: pressed metal for the acts, a soft lip for
     the small chrome, and ONE glow in the whole app — the approval's. */
  --elev-btn: inset 0 1px 0 rgba(255, 255, 255, 0.22), inset 0 -1px 0 rgba(11, 11, 12, 0.08), 0 1px 2px rgba(11, 11, 12, 0.10), 0 4px 14px rgba(11, 11, 12, 0.16);
  --elev-btn-press: inset 0 2px 4px rgba(11, 11, 12, 0.16);
  --elev-chip: inset 0 1px 0 rgba(255, 255, 255, 0.60), 0 1px 2px rgba(11, 11, 12, 0.07);
  --elev-approve: inset 0 1px 0 rgba(255, 255, 255, 0.30), inset 0 -1px 0 rgba(0, 0, 0, 0.15), 0 2px 6px rgba(255, 90, 31, 0.30), 0 8px 24px rgba(255, 90, 31, 0.22);
  /* The toast's float, in the same layered system as the cards. */
  --elev-1: 0 4px 16px rgba(0, 0, 0, 0.16), 0 12px 32px rgba(0, 0, 0, 0.14);
  /* The sheet's scrim — darker in dark, where the page behind must die back
     further for the panel's tonal step to read. */
  --scrim: rgba(11, 11, 12, 0.52);
  --card-line: transparent;
}

/* 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;
    /* The sheet steps a full surface above --raised, so the panel reads as
       a place and --card boxes on it regain their contrast. */
    --sheet: #1E1E22;
    --text-display: #FFFFFF;
    --track: rgba(163, 163, 168, 0.16);
    /* Dark: drop shadows die on near-black, so the ladder is INSET
       TOP-LIGHTS on the tonal steps — 0.04 card, 0.07 anchor, 0.08 sheet —
       plus the hairline. Buttons stay inset-only; the approval carries the
       app's one licensed glow. */
    --elev-low: none;
    --elev-card: inset 0 1px 0 rgba(255, 255, 255, 0.04);
    --elev-anchor: inset 0 1px 0 rgba(255, 255, 255, 0.07);
    --elev-sheet: inset 0 1px 0 rgba(255, 255, 255, 0.08);
    --elev-btn: inset 0 1px 0 rgba(255, 255, 255, 0.12), inset 0 -1px 0 rgba(0, 0, 0, 0.35);
    --elev-btn-press: inset 0 2px 4px rgba(0, 0, 0, 0.45);
    --elev-chip: inset 0 1px 0 rgba(255, 255, 255, 0.05);
    --elev-approve: inset 0 1px 0 rgba(255, 255, 255, 0.28), inset 0 -1px 0 rgba(0, 0, 0, 0.25), 0 0 18px rgba(255, 90, 31, 0.30);
    --elev-1: none;
    --scrim: rgba(0, 0, 0, 0.66);
    --card-line: var(--line);
  }
}

/* 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: #1E1E22;
  --text-display: #FFFFFF;
  --track: rgba(163, 163, 168, 0.16);
  /* Dark: inset top-lights on the tonal steps — see the media block above. */
  --elev-low: none;
  --elev-card: inset 0 1px 0 rgba(255, 255, 255, 0.04);
  --elev-anchor: inset 0 1px 0 rgba(255, 255, 255, 0.07);
  --elev-sheet: inset 0 1px 0 rgba(255, 255, 255, 0.08);
  --elev-btn: inset 0 1px 0 rgba(255, 255, 255, 0.12), inset 0 -1px 0 rgba(0, 0, 0, 0.35);
  --elev-btn-press: inset 0 2px 4px rgba(0, 0, 0, 0.45);
  --elev-chip: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  --elev-approve: inset 0 1px 0 rgba(255, 255, 255, 0.28), inset 0 -1px 0 rgba(0, 0, 0, 0.25), 0 0 18px rgba(255, 90, 31, 0.30);
  --elev-1: none;
  --scrim: rgba(0, 0, 0, 0.66);
  --card-line: var(--line);
}

* { 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: var(--sp-6) var(--sp-4) 18px;
  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: 32px; height: 32px; margin-top: 3px; }

.brand-name {
  font-family: var(--font-heading);
  /* The DISPLAY tier: --fs-display/--ls-display/--lh-tight, the masthead's
     own tokens. Negative tracking on every heading is half of what makes
     dense screens read calm; the display ink lifts it one step in dark. */
  font-size: var(--fs-display);
  font-weight: 700;
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  color: var(--text-display);
  overflow-wrap: break-word;
}

.brand-mark {
  margin-top: 6px;
  font-family: var(--font-mono);
  /* 10px is the label voice's floor, app-wide. 9px tracked caps was below
     anyone's comfortable reading size for a string that names the product.
     The 0.22em tracking is the micro tier's ONE exception — wordmark
     identity, wider than --ls-micro on purpose. */
  font-size: 10px;
  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: 30px;
  min-width: 60px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--raised);
  box-shadow: var(--elev-chip);
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: var(--ls-micro);
  text-transform: uppercase;
  color: var(--hint);
  transition: transform 0.12s ease;
}
.scheme::before { content: ""; position: absolute; inset: -7px; }
.scheme:active { transform: scale(0.96); }

/* The two chrome controls — reload and scheme — ride one row at the top of
   the side stack; the usage meter keeps its place beneath them. */
.brand-ctrls {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* The reload control: an icon button that speaks the scheme pill's idiom —
   same raised chip, same border, same 44px tap target grown by a ::before
   pad so the visible chrome stays compact. The glyph rides currentColor. */
.reload {
  position: relative;
  margin-top: 2px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 30px;
  min-width: 30px;
  padding: 0 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--raised);
  box-shadow: var(--elev-chip);
  color: var(--hint);
  transition: transform 0.12s ease;
}
.reload::before { content: ""; position: absolute; inset: -7px; }
.reload:active { transform: scale(0.96); }
.reload[disabled] { cursor: default; opacity: 0.75; }
.reload-ic { width: 18px; height: 18px; display: block; flex: none; }

/* In flight: the ring-arrow spins one turn every 900ms, linear and endless,
   until the fetch settles. */
.reload--spin .reload-ic {
  animation: reload-spin 0.9s linear infinite;
  transform-origin: 50% 50%;
}
@keyframes reload-spin { to { transform: rotate(360deg); } }

/* A failed reload never fakes freshness: the data already on screen stays,
   and the control goes amber so it never reads as fresh. Amber darkens for
   light, brightens for dark — the app's own scheme tokens. */
.reload--fail { color: #b26a00; border-color: #b26a00; }
@media (prefers-color-scheme: dark) {
  :root:not([data-scheme="light"]) .reload--fail { color: #fab219; border-color: #fab219; }
}
:root[data-scheme="dark"] .reload--fail { color: #fab219; border-color: #fab219; }

/* 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: var(--fs-micro);
  font-weight: 600;
  letter-spacing: var(--ls-micro);
  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(--track); 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-3);
  margin-top: var(--sp-5);
}
.bstat { flex: 1 1 0; min-width: 0; }
.bstat .bn {
  display: block;
  font-family: var(--font-heading);
  font-size: var(--fs-num-brand);
  font-weight: 700;
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-num);
  color: var(--text-display);
  font-variant-numeric: tabular-nums;
}
.bstat .bt {
  display: block;
  margin-top: 3px;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: var(--ls-micro);
  text-transform: uppercase;
  color: var(--hint);
}

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

.main {
  flex: 1;
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: 20px 16px 48px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

.card {
  background: var(--card);
  border: 1px solid var(--card-line);
  border-radius: var(--r-xl);
  padding: var(--sp-5) 18px var(--sp-4);
  box-shadow: var(--elev-card);
}

.card-h {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
}
/* Card titles read as titles. The first build set every one in the 11px
   mono label voice, which made the register wallpaper: used on twenty jobs
   it marked none of them, and nine identically-voiced cards gave the page
   no hierarchy at all. Now the voice is spent by REGISTER (see the register
   block below): work cards carry a readable 17px negative-tracked title in
   the display ink, account cards keep the quiet 11px mono label, and the
   queue — the reason the app is opened — carries the largest title on the
   page at 19px. */
.card-h h2 {
  font-family: var(--font-heading);
  font-size: var(--fs-title2);
  font-weight: 600;
  letter-spacing: var(--ls-title2);
  line-height: var(--lh-title);
  color: var(--text-display);
}
.card-sub { margin-left: auto; font-size: 13px; color: var(--hint); font-variant-numeric: tabular-nums; }

/* ---- the three registers ----------------------------------------------
   One container recipe for every surface was the page's biggest tell: the
   login, the queue and the "Connected" ledger all wore the same box, so
   nothing led. Same flat identity, three weights now:

     ANCHOR    #card-today — the queue, the one card the founder came for.
               The raised surface and the largest title. One per screen.
     STANDARD  week, results, digest (and the portal's onboarding asks) —
               the card shell as it always was, titled in the ink.
     RECESSIVE outreach, pipeline, knowledge, credits, connected — account
               state, glanced at, never competing with the work. They keep
               the small mono caption as their whole voice.

   Hierarchy from surface, type AND height now: since the felt-difference
   pass the three registers sit at three physical heights on the elevation
   ladder — --elev-low recessive, --elev-card standard, --elev-anchor the
   queue — in both schemes. No colour, no new chrome. */
/* The anchor's frame is a step darker than the shared hairline: on paper,
   raised-white against card-white is a delta the eye cannot bank on, and a
   focal point that only exists in the tokens is not a focal point. Still a
   1px rule — the register speaks through surface, frame and title, never
   through a shadow or a colour. */
#card-today {
  background: var(--raised);
  /* In dark the anchor's frame stays a step darker than the hairline; in
     light --card-line is transparent and the mix of a transparent base is
     still transparent-ish enough that the FLOAT does the anchoring. */
  border-color: color-mix(in srgb, var(--hint) 55%, var(--card-line));
  padding: 22px 18px 18px;
  border-radius: var(--r-2xl);
  box-shadow: var(--elev-anchor);
}
#card-today .card-h h2 {
  font-size: var(--fs-title1);
  letter-spacing: var(--ls-title1);
  font-weight: 700;
}

#card-outreach .card-h h2,
#card-pipeline .card-h h2,
#card-knowledge .card-h h2,
#card-credits .card-h h2,
#card-connected .card-h h2 {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--hint);
}

/* The recessive register's height: barely off the page, in both schemes. */
#card-outreach, #card-pipeline, #card-knowledge, #card-credits,
#card-connected { box-shadow: var(--elev-low); }

.badge {
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  /* 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: 700;
  line-height: 20px;
  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: var(--fs-label); font-weight: 600;
     text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--hint);
     margin: var(--sp-1h) 0 var(--sp-2h); }
.h + .h, .list + .h { margin-top: var(--sp-6); }

/* Honest empty states: what the card is for, and what will fill it. */
.empty { padding: var(--sp-4) var(--sp-1) var(--sp-3); }
.empty .big { font-size: 16px; font-weight: 500; 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-3h);
  padding: 9px var(--sp-3);
  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: var(--fs-label);
  font-weight: 600;
  letter-spacing: var(--ls-label);
  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 POINT is the agent tapping the founder on the shoulder — the identity's
   own device, not the accent-stripe habit every generated card reaches for.
   Same rationed orange, now in the mark's own grammar. */
.digest-stale {
  font-size: 13px; color: var(--hint); margin-bottom: 10px; padding: 8px 0;
}
.digest-stale::before {
  content: "";
  display: inline-block;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--accent);
  margin-right: 7px;
  vertical-align: 1px;
}

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

/* Receipts: the morning block. Past tense, quiet. */
.receipts {
  border-top: 1px solid var(--track);
  padding-top: var(--sp-3);
  margin-top: var(--sp-4);
}
.receipt { display: flex; align-items: baseline; gap: var(--sp-2h); padding: 6px 0; font-size: 14px; }
.receipt .at { color: var(--hint); font-family: var(--font-mono); font-size: 12px;
               letter-spacing: 0.02em; 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: 60px;
  padding: var(--sp-3) var(--sp-1);
  border-bottom: 1px solid var(--track);
  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;
  font-weight: 500;
  letter-spacing: -0.005em;
  line-height: 1.35;
  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: 4px; line-height: 1.35; }
.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: 4px; }

.wday {
  flex: 1 1 0;
  min-width: 0;
  min-height: 68px;
  border-radius: var(--r-md);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding: 8px 0 6px;
  gap: 3px;
  color: var(--hint);
}
.wday .dow { font-family: var(--font-mono); font-size: var(--fs-micro); font-weight: 600;
             text-transform: uppercase; letter-spacing: var(--ls-micro); }
.wday .num { font-size: 17px; font-weight: 600; color: var(--text);
             font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }

/* 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: 7px;
  align-items: center;
  justify-content: center;
}
.dot { width: 6px; height: 6px; 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: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: var(--r-pill);
  font-size: 11px;
  font-weight: 600;
  line-height: 17px;
  text-align: center;
  color: var(--button-text);
  background: var(--button);
}
.wday .pend:empty { background: none; }

/* Today wears a quiet hairline ring; it sits BEFORE .sel so a selected
   today reads as selected — the ring recolours, never doubles. */
.wday.today { box-shadow: inset 0 0 0 1px var(--line); }
/* The selection ring rides --accent-text, not raw --accent: the full orange
   is 2.8–3.1:1 on the light surfaces, under the 3:1 non-text floor. */
.wday.sel { background: var(--raised); box-shadow: var(--elev-card), inset 0 0 0 2px var(--accent-text); }
.wday.sel .dow { color: var(--accent-text); }
.wday.today .num { color: var(--accent-text); }

/* Selected day's schedule under the strip */
.postlist { margin-top: var(--sp-3); border-top: 1px solid var(--track); padding-top: var(--sp-2h); }
.post {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2h);
  padding: 6px 2px;
  font-size: 14px;
}
.post .at { color: var(--hint); font-family: var(--font-mono); font-size: 12px;
            letter-spacing: 0.02em; 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: 11px 2px;
  border-bottom: 1px solid var(--track);
}
.tile:last-child { border-bottom: 0; }
.tile .tmid { flex: 1; min-width: 0; }
.tile .tname {
  font-size: 15px;
  font-weight: 500;
  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: var(--fs-label);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--ls-label-tight);
  line-height: 1.4;
  padding: 4px 9px;
  border-radius: var(--r-sm);
  background: var(--raised);
  border: 1px solid var(--line);
  box-shadow: var(--elev-chip);
  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-lg);
  padding: var(--sp-3h) var(--sp-3h) var(--sp-3);
}
.stat .n { font-family: var(--font-heading); font-size: var(--fs-num-hero); font-weight: 700;
           letter-spacing: var(--ls-num); line-height: var(--lh-tight);
           color: var(--text-display); font-variant-numeric: tabular-nums; }
.stat .lbl { margin-top: 3px; font-family: var(--font-mono); font-size: var(--fs-micro);
             font-weight: 600; letter-spacing: var(--ls-micro); text-transform: uppercase;
             color: var(--hint); }
.stagechips { display: flex; flex-wrap: wrap; gap: var(--sp-1h); margin-top: var(--sp-3); }
.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: 9px 14px; border-radius: var(--r-pill);
  border: 1px solid var(--accent); background: var(--accent);
  color: var(--accent-ink); cursor: pointer;
  box-shadow: var(--elev-chip);
  transition: transform 0.12s ease;
  -webkit-tap-highlight-color: transparent;
}
/* The ::before pad grows the 33px control to a 45px target without growing
   the chrome — the same trick the scheme pill uses. */
.connect-btn::before { content: ""; position: absolute; inset: -6px 0; }
.connect-btn:active { transform: scale(0.97); }
.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: 11px 0; border-bottom: 1px solid var(--track);
}
.know:last-child { border-bottom: 0; }
.know-what { flex: 1; min-width: 0; }
.know-what b { display: block; font-size: 15px; 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; box-shadow: none; }
.know-state.warn { color: var(--text); }
.know-state.gap { color: var(--destructive-text); border-color: currentColor; }

.know-needs {
  margin-top: 12px; padding: var(--sp-3) var(--sp-3h); border-radius: var(--r-lg);
  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(--track); }
.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: var(--sp-3) var(--sp-3h); border-radius: var(--r-lg);
  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(--track); }
.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: 7px 12px; border-radius: var(--r-pill);
  border: 1px solid var(--track); background: var(--raised); color: var(--text);
  box-shadow: var(--elev-chip);
  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: 8px 14px; border-radius: var(--r-pill);
  border: 1px solid var(--track); background: var(--raised); color: var(--muted);
  box-shadow: var(--elev-chip);
  transition: transform 0.12s ease;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
/* 31px tall drawn, 45px to a thumb — the scheme pill's own ::before trick. */
.chart-tab::before { content: ""; position: absolute; inset: -7px 0; }
.chart-tab:active { transform: scale(0.97); }
.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: 10px; font-family: var(--font-mono); }
/* 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.05; }
.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: var(--fs-title2); font-weight: 700; color: var(--text);
                   font-variant-numeric: tabular-nums; letter-spacing: var(--ls-title2); }
.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(--track);
  font-size: 13px; color: var(--muted);
}

.md { font-size: 15px; line-height: var(--lh-read); 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;
  letter-spacing: var(--ls-title2); color: var(--text-display); }
.md .md-h1 { font-size: var(--fs-title2); }
.md .md-h2 { font-size: 16px; }
.md .md-h3 { font-size: 15px; font-weight: 600; }
.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: var(--fs-label);
  letter-spacing: var(--ls-label-tight); 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: 9px 2px;
  border-bottom: 1px solid var(--track);
  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: var(--scrim);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  animation: fade 0.3s 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-2xl) var(--r-2xl) 0 0;
  box-shadow: var(--elev-sheet);
  animation: rise 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
/* The grabber: the panel is a flex column, so this pseudo is its first row.
   Inert — no pointer target, just the sheet saying what it is. */
.panel::before {
  content: "";
  flex: 0 0 auto;
  align-self: center;
  width: 36px;
  height: 4px;
  margin: 10px 0 2px;
  border-radius: var(--r-pill);
  background: var(--line);
}
@keyframes rise { from { transform: translateY(56px); opacity: 0.4; } }

/* 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; }
  .btn, .chart-tab, .scheme, .reload, .connect-btn, .clash-btn { transition: none; }
  /* The reload still needs a visible "in flight" — a spin would be motion, so
     the ring-arrow pulses its opacity 1 → 0.5 → 1 instead. */
  .reload--spin .reload-ic { animation: reload-pulse 0.9s ease-in-out infinite; }
  @keyframes reload-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
}

.panel-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2h);
  /* The grabber above supplies the top air. */
  padding: var(--sp-2) var(--sp-5) var(--sp-3);
}
.panel-head .dim { flex: 1; font-size: 13px; }
/* On the sheet, --card and --sheet sit one step apart in light and the chip
   was vanishing into its own ground; the hairline gives it an edge on both
   surfaces without giving it weight. */
.panel-head .chip { background: var(--card); border: 1px solid var(--line); }

.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);
  background: var(--card);
  border: 1px solid var(--track);
  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: var(--sp-2) var(--sp-5) var(--sp-6);
}

/* The draft itself: a comfortable read. */
.draft { max-width: 34em; margin: 0 auto; font-size: 17px; line-height: var(--lh-read); }
.draft p { margin: 0 0 1em; overflow-wrap: break-word; }
.draft .sec { font-family: var(--font-mono); font-size: var(--fs-label);
              font-weight: 600; text-transform: uppercase;
              letter-spacing: var(--ls-label); 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: var(--sp-3) var(--sp-3h); border-radius: var(--r-md);
  background: var(--card); color: var(--hint); font-size: 14px;
}

.panel-foot {
  padding: var(--sp-3h) var(--sp-5) calc(var(--sp-4) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--track);
}
.panel-foot:empty { display: none; }

.btn {
  display: block;
  width: 100%;
  min-height: 52px;
  border-radius: var(--r-lg);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.01em;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
/* The press: a machined part travels. One pixel down, and the primary's
   shadow inverts to the pressed inset. */
.btn:active:not([disabled]) { transform: translateY(1px); }
.btn.primary:active:not([disabled]) { box-shadow: var(--elev-btn-press); }
/* A filled control is never flat: an inset top light and a soft drop give
   it the pressed-metal reading the reference system calls "no flat fills".
   Quiet, ack and danger stay as they were — one physical primary per card. */
.btn.primary { background: var(--button); color: var(--button-text); box-shadow: var(--elev-btn); }
/* THE APPROVAL WEARS THE ACCENT, AND ONLY THE APPROVAL. Sign-in, onboarding
   and retry ride quieter registers; this is the tap that publishes under the
   founder's name, so it is the one accent-filled act in the app — and it is
   scheme-STABLE: the fixed orange with the identity's ink (6.3:1), identical
   in light and dark, because the gravest act should not change its face with
   the wallpaper. The two hexes are the identity's two fixed colours.
   It also carries the app's ONLY glow (--elev-approve) and the only
   heavyweight button label — the gravest act reads heaviest. */
.approve-card .btn.primary {
  background: #FF5A1F; color: #0B0B0C;
  min-height: 54px;
  box-shadow: var(--elev-approve);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.btn.quiet { color: var(--destructive-text); }
.btn.danger { background: var(--destructive); color: #fff; box-shadow: var(--elev-btn); }
.btn[disabled] { opacity: 0.55; }
.btn + .btn { margin-top: 10px; }

/* 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-3);
  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(--raised);
  color: var(--text);
  border: 1px solid var(--line);
  box-shadow: var(--elev-chip);
  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: 12px; left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  max-width: 90%;
  padding: var(--sp-2h) var(--sp-4);
  border-radius: var(--r-lg);
  background: var(--card);
  color: var(--text);
  font-size: 14px;
  font-weight: 500;
  /* Transparent in light, where --elev-1's layered float carries the depth;
     a hairline in dark, where --elev-1 is none — the toast stops vanishing. */
  border: 1px solid var(--card-line);
  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%); } }

/* The day's calls: who is on it, behind a tap.
   The list is deliberately plain — these are other people's addresses, not
   product chrome, and dressing them up invites screenshots. */
.callguests { margin-top: 4px; }
.callguests > summary {
  cursor: pointer; font-size: 12px; color: var(--hint);
  list-style: none; padding: 2px 0;
}
.callguests > summary::-webkit-details-marker { display: none; }
.callguests > summary::before { content: "\25B8 "; display: inline-block; transition: transform .15s; }
.callguests[open] > summary::before { transform: rotate(90deg); }
.callguests ul.guests { margin: 4px 0 2px; padding-left: 14px; list-style: none; }
.callguests ul.guests li {
  font-size: 12px; color: var(--hint); line-height: 1.5;
  overflow-wrap: anywhere;
}

/* The provider's own incident, named before the founder has to wonder. Warm,
   not alarming: the studio is fine, the weather is not. */
.upstream-notice {
  margin: 0 0 12px;
  padding: 10px 12px;
  border-radius: 10px;
  font-size: 13px;
  line-height: 1.45;
  background: color-mix(in srgb, var(--warn, #b45309) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn, #b45309) 35%, transparent);
  color: var(--ink, inherit);
}

/* ---- tidying the list: Remove, Edit title, Clear (4.4) ----------------
   Housekeeping, so it speaks in the quiet register: body type, the hint ink,
   no accent (the orange stays the approval's alone), and every target at
   least 44px tall. Nothing here is destructive in colour either: a removed
   card keeps its history and comes back with Undo. */
.card-clear {
  margin-left: auto;
  min-height: 44px;
  min-width: 44px;
  padding: 0 var(--sp-2);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  color: var(--hint);
  border-radius: var(--r-md);
}
.card-clear[aria-expanded="true"] { color: var(--text); background: var(--card); }

.clear-panel {
  margin: 0 0 var(--sp-4);
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--card);
}
.clear-title { font-family: var(--font-body); font-size: 15px; font-weight: 600;
  color: var(--text); margin-bottom: var(--sp-2); }
.clear-choices { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.clear-choice {
  min-height: 44px;
  padding: 0 var(--sp-3h);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--raised);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
}
.clear-choice.quiet { background: transparent; border-color: transparent; color: var(--hint); }
.clear-sub { margin-top: var(--sp-2); font-size: 13px; line-height: 1.4; color: var(--hint); }

.receipt .row-remove {
  align-self: center;
  flex: none;
  min-height: 44px;
  padding: 0 var(--sp-2);
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--hint);
  border-radius: var(--r-md);
}

.tidy-row {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}
.tidy-btn {
  min-height: 44px;
  padding: 0 var(--sp-3);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  color: var(--hint);
  border-radius: var(--r-md);
}
.tidy-btn[disabled] { opacity: 0.55; }

.title-input {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--card);
  color: var(--text);
  font: inherit;
}
.title-input:focus { outline: none; border-color: var(--accent-text); }

/* A toast that carries one action (Undo). */
.toast:has(.toast-act) { display: flex; align-items: center; gap: var(--sp-3); padding-right: var(--sp-1); }
.toast .toast-act {
  min-height: 44px;
  padding: 0 var(--sp-3);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  border-radius: var(--r-md);
  text-decoration: underline;
  text-underline-offset: 3px;
}
