/* Report Console — refined-utilitarian skin (2026-06-11 visual refresh).
   One plain CSS file, no build step. Inter is self-hosted (no CDN — Tailscale-only app).
   Every color is a token so the dark variant below only remaps :root. */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;             /* variable font — one latin file covers all weights */
  font-display: swap;
  src: url("fonts/inter-var-latin.260c81a4759b.woff2") format("woff2");
}

:root {
  --brand:#1e3a5f;                  /* navy identity (header) */
  --brand-ink:#dbe6f4;              /* text on navy */
  --accent:#2563eb; --accent-strong:#1d4ed8;
  --ink:#0f172a; --soft:#5d6b80;  /* 5.09:1 on --bg, 4.94 on --code-bg (AA off-card too) */
  --bg:#f6f8fb; --surface:#ffffff; --line:#e2e8f0;
  /* graphics tokens (badges, rails, borders) … */
  --ok:#16a34a; --warn:#d97706; --bad:#dc2626;
  /* …and darker -ink twins for SMALL TEXT — the plain tokens fail WCAG AA on white */
  --ok-ink:#15803d; --warn-ink:#b45309; --bad-ink:#b91c1c;
  --tint-accent:#eff6ff; --tint-ok:#f0fdf4; --tint-warn:#fffbeb; --tint-bad:#fef2f2;
  --code-bg:#f1f5f9; --press:#e2e8f0;
  /* Radius scale (named tiers, values = the ones already in use → no visual change):
     xs inline code chips · sm form controls/nav · md default panels · lg cards/pills. */
  --radius-xs:.25rem; --radius-sm:.35rem; --radius:.45rem; --radius-lg:.6rem;
  --shadow:0 1px 2px rgba(15,23,42,.06), 0 1px 3px rgba(15,23,42,.08);
  /* Attention accent (amber) — needs-you / stale / aging. Distinct from --accent (action)
     and --bad (fail/destructive). The -ink twin clears AA for small text on white/tints. */
  --attn-ink:#b45309; --attn-tint:#fffbeb;
  /* Type scale (documents the recurring sizes; values match what's in use). */
  --text-xs:.75rem; --text-sm:.85rem; --text-base:1rem; --text-lg:1.15rem; --text-xl:1.5rem;
  /* Job-log block (a dark console look in BOTH themes — deliberately not remapped). */
  --joblog-bg:#0b1426; --joblog-ink:#dbe6f4;
  --sidebar-w:15.5rem;
}

* { box-sizing:border-box; }
/* 16px base: anything smaller in an input makes iOS Safari auto-zoom on focus. */
body { margin:0; font:16px/1.55 Inter, system-ui, sans-serif; color:var(--ink);
       background:var(--bg); -webkit-text-size-adjust:100%; }
h1 { font-size:1.55rem; font-weight:700; letter-spacing:-.015em; }
h2 { font-size:1.1rem; font-weight:650; letter-spacing:-.01em; color:var(--ink); }

/* Micro-interactions: one shared 150ms rhythm, fully disabled on reduced motion. */
a, button, .badge, .btn-primary {
  transition: background-color .15s ease, color .15s ease,
              border-color .15s ease, box-shadow .15s ease; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration:.01ms !important; animation:none !important; }
}

/* Inline SVG icons (sprite in _icon_sprite.html). Stroke/fill via currentColor so they theme
   for free and inherit link/button color. Decorative — labels live on the surrounding element. */
.icon { width:1.15em; height:1.15em; flex:none; fill:none; stroke:currentColor;
        stroke-width:2; stroke-linecap:round; stroke-linejoin:round;
        vertical-align:-.18em; }
header strong .icon { width:1.3rem; height:1.3rem; vertical-align:-.24em; color:var(--brand-ink); }

header { display:flex; flex-wrap:wrap; justify-content:space-between; align-items:center;
         gap:.4rem 1rem; padding:.6rem max(1rem, env(safe-area-inset-right))
         .6rem max(1rem, env(safe-area-inset-left));
         background:var(--brand); color:#fff; }
header strong { font-weight:650; letter-spacing:-.01em; }
nav { display:flex; flex-wrap:wrap; gap:.2rem .4rem; align-items:center; }
/* 44px touch targets for nav links and all buttons (Apple HIG minimum). */
nav a { color:var(--brand-ink); text-decoration:none; padding:.6rem .5rem;
        min-height:44px; display:inline-flex; align-items:center; gap:.45rem;
        border-radius:var(--radius-sm); }
nav a:hover { background:rgba(255,255,255,.08); color:#fff; }
nav a:active { background:rgba(255,255,255,.16); color:#fff; }
nav a.active { background:rgba(255,255,255,.14); color:#fff;
        box-shadow:inset 0 -2px 0 #fff; }
/* Log out stays a POST form (CSRF) but reads as just another nav link. */
header form.inline button { background:transparent; border:none;
        color:var(--brand-ink); border-radius:var(--radius-sm);
        display:inline-flex; align-items:center; gap:.45rem; }
header form.inline button:hover { background:rgba(255,255,255,.08); color:#fff; }
form.inline { display:inline; }
/* The header title doubles as a home link — inherit the navy-header colors, no underline. */
header strong .brand-link { color:inherit; text-decoration:none; display:inline-flex;
        align-items:center; gap:.4rem; border-radius:var(--radius-sm); }

/* Keyboard skip-link: parked off-screen until focused, then pinned above the chrome so a
   keyboard user can jump past the nav. Snaps into place under reduced motion (transition off). */
.skip-link { position:fixed; left:.5rem; top:-4rem; z-index:1000;
        background:var(--surface); color:var(--accent-strong);
        border:1px solid var(--accent); border-radius:var(--radius);
        padding:.5rem .9rem; font-weight:600; text-decoration:none;
        transition:top .15s ease; }
.skip-link:focus { top:.5rem; }
main:focus { outline:none; }   /* programmatic target after skipping — suppress the full-page ring */

main { max-width:60rem; margin:0 auto;
       padding:1rem max(1rem, env(safe-area-inset-right))
       max(1.5rem, env(safe-area-inset-bottom)) max(1rem, env(safe-area-inset-left)); }
main a { color:var(--accent); }
main a:hover { color:var(--accent-strong); }

/* ── App shell: top bar on phones, left sidebar on desktop (≥1024px). ONE <nav>, reflowed by
   CSS — never duplicated (the tests assert a single aria-current="page" per page). ─────────── */
@media (min-width:1024px) {
  body { display:grid; grid-template-columns:var(--sidebar-w) 1fr; align-items:start; }
  header { position:sticky; top:0; height:100dvh; overflow-y:auto;
           flex-direction:column; flex-wrap:nowrap; align-items:stretch;
           justify-content:flex-start; gap:1rem; padding:1rem .8rem; }
  header strong { font-size:1.05rem; padding:.2rem .4rem; letter-spacing:-.01em; }
  nav { flex-direction:column; flex-wrap:nowrap; align-items:stretch; gap:.15rem; width:100%; }
  nav a { width:100%; }
  nav a.active { box-shadow:inset 3px 0 0 #fff; }   /* left indicator suits a vertical rail */
  .nav-logout { margin-top:auto; }                  /* pin log out to the rail's bottom */
  .nav-logout button { width:100%; justify-content:flex-start; }
  main { grid-column:2; }
}

button, input, select, textarea { font:inherit; }   /* keeps inputs at 16px — no iOS zoom */
button { min-height:44px; padding:.5rem 1rem; border:1px solid var(--line);
         border-radius:var(--radius); background:var(--surface); color:var(--ink);
         cursor:pointer; touch-action:manipulation; }
button:hover { border-color:var(--soft); }
button:active { background:var(--press); }
button[type="submit"] { background:var(--accent); border-color:var(--accent); color:#fff;
         font-weight:600; }
button[type="submit"]:hover { background:var(--accent-strong); border-color:var(--accent-strong); }
button[type="submit"]:active { background:var(--accent-strong); }
button[disabled] { opacity:.5; cursor:not-allowed; }
button.danger { background:var(--bad); border-color:var(--bad); color:#fff; }
button.danger:hover, button.danger:active { background:var(--bad-ink); border-color:var(--bad-ink); }
.danger-zone { border:1px solid var(--bad); border-radius:var(--radius); padding:.4rem .8rem; }

/* Keyboard users get the same affordance pointer users get from :hover/:active. */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible {
  outline:2px solid var(--accent); outline-offset:2px; }
/* The accent ring is only 2.23:1 against the navy header — use white there (11.5:1). */
header a:focus-visible, header button:focus-visible { outline-color:#fff; }

input:not([type="checkbox"]) { min-height:44px; padding:.45rem .6rem; width:100%;
        max-width:22rem; border:1px solid var(--line); border-radius:var(--radius-sm);
        background:var(--surface); color:var(--ink); }
input[type="checkbox"] { width:1.2rem; height:1.2rem; accent-color:var(--accent); }
/* The label IS the tap target for a 19px checkbox — keep it at the 44px minimum. */
label:has(input[type="checkbox"]) { min-height:44px; }
select { min-height:44px; padding:.45rem .6rem; border:1px solid var(--line);
        border-radius:var(--radius-sm); background:var(--surface); color:var(--ink); }
label { display:inline-flex; flex-wrap:wrap; align-items:center; gap:.4rem;
        padding:.25rem 0; }

/* Tables scroll inside their wrapper instead of stretching the page sideways. */
/* position:relative contains absolutely-positioned .vh headers (else they escape the
   clip and stretch the page sideways on mobile). */
.table-wrap { overflow-x:auto; -webkit-overflow-scrolling:touch; position:relative;
              background:var(--surface); border:1px solid var(--line);
              border-radius:var(--radius); }
table { border-collapse:collapse; width:100%; font-variant-numeric:tabular-nums; }
th { font-size:var(--text-xs); font-weight:600; text-transform:uppercase; letter-spacing:.05em;
     color:var(--soft); }
th, td { text-align:left; padding:.55rem .7rem; border-bottom:1px solid var(--line);
         overflow-wrap:break-word; }
tr:last-child td { border-bottom:none; }
td:first-child { white-space:nowrap; }
/* Long tables (history ledger, audit, experiments, attention): pin the header row to the
   top of the viewport so the column labels stay readable while scrolling. --surface bg keeps
   rows from showing through (and themes for free in dark mode). */
.table-wrap thead th { position:sticky; top:0; z-index:1; background:var(--surface); }

.badge { padding:.12rem .55rem; border-radius:var(--radius-lg); font-size:.8rem; font-weight:600;
         color:#fff; white-space:nowrap; }
.badge.generated { background:var(--soft); }
.badge.approved { background:var(--ok-ink); }      /* -ink shades: white text needs 4.5:1 */
.badge.approved_stale { background:var(--bad); }
.badge.posted { background:var(--accent); }
.badge.missing { background:var(--warn-ink); }
.badge.in-progress { background:var(--warn-ink); }  /* J3: recap for the still-running week */
.chip-pass { color:var(--ok-ink); font-weight:600; }
.chip-fail { color:var(--bad-ink); font-weight:600; }
.mention { color:var(--accent); font-weight:600; }
.error, li.error { color:var(--bad-ink); }
.empty-state { color:var(--soft); font-style:italic; }

.messages { list-style:none; padding:0; margin:0 0 .3rem; }
.messages li { display:flex; align-items:center; gap:.5rem; padding:.4rem .4rem .4rem .7rem;
               border-radius:var(--radius); margin:.3rem 0;
               border:1px solid var(--line); background:var(--surface);
               animation:msg-in .2s ease-out; }
.messages li > .icon { flex:none; }                 /* status icon (alert / check) */
.messages .msg-text { flex:1 1 auto; min-width:0; } /* take the row; close button stays put */
/* Dismiss button: ghost, inherits the message's semantic color, 44px hit target. */
.messages .msg-close { flex:none; min-height:44px; min-width:44px; padding:0;
                       display:inline-flex; align-items:center; justify-content:center;
                       background:transparent; border:1px solid transparent; border-radius:var(--radius-sm);
                       color:inherit; opacity:.7; cursor:pointer; }
.messages .msg-close:hover { opacity:1; background:color-mix(in srgb, currentColor 12%, transparent); }
.messages .msg-close .icon { width:1rem; height:1rem; }
/* Leaving = fade + collapse before JS removes the node (skipped under reduced motion). */
.messages li.msg--leaving { opacity:0; transform:translateY(-.3rem);
                            transition:opacity .18s ease, transform .18s ease; }
@keyframes msg-in { from { opacity:0; transform:translateY(-.4rem); } to { opacity:1; transform:none; } }
.messages li.success { background:var(--tint-ok); border-color:var(--ok);
                       color:var(--ok-ink); }
.messages li.error   { background:var(--tint-bad); border-color:var(--bad);
                       color:var(--bad-ink); }
.messages li.warning { background:var(--tint-warn); border-color:var(--warn);
                       color:var(--warn-ink); }
.messages li.info    { background:var(--tint-accent); border-color:var(--accent);
                       color:var(--accent-strong); }

/* ── Cards (shared base: dashboard hero, action cards) ─────────────────────── */
.card { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-lg);
        padding:.9rem 1.1rem; box-shadow:var(--shadow); }
.card.danger-zone { border-color:var(--bad); }   /* .card's border must not mute it */
.card h2 { margin:.2rem 0 .4rem; }
.cards { display:grid; grid-template-columns:1fr 1fr; gap:1rem; align-items:start; }
.cards > div { display:grid; gap:1rem; align-content:start; }
.action-desc { color:var(--soft); font-size:.9rem; margin:.2rem 0 .7rem; }
details > summary { min-height:44px; display:flex; align-items:center; gap:.3rem;
        cursor:pointer; color:var(--soft); font-weight:600; }
details[open] > summary { color:var(--ink); }
.vh { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0);
      white-space:nowrap; }

.badge.job-queued { background:var(--soft); }
.badge.job-running { background:var(--accent); }
.badge.job-done { background:var(--ok-ink); }
.badge.job-failed { background:var(--bad); }

/* ════════════════════════════════════════════════════════════════════════════
   Command-center dashboard (redesign, Option A). All token-driven → themes for free.
   ════════════════════════════════════════════════════════════════════════════ */

/* ── Status command strip ──────────────────────────────────────────────────── */
.cmd-strip { display:flex; align-items:center; flex-wrap:wrap; gap:.55rem .9rem;
             background:var(--surface); border:1px solid var(--line);
             border-radius:var(--radius); box-shadow:var(--shadow);
             padding:.55rem .8rem; margin:.2rem 0 1rem; }
.cmd-lead { display:flex; flex-direction:column; line-height:1.15; }
.cmd-eyebrow { font-size:.7rem; font-weight:600; text-transform:uppercase; letter-spacing:.05em;
               color:var(--soft); }
.cmd-day { font-size:var(--text-base); font-weight:700; letter-spacing:-.01em; color:var(--ink);
           text-decoration:none; font-variant-numeric:tabular-nums; min-height:44px;
           display:inline-flex; align-items:center; }
.cmd-day:hover { color:var(--accent); }

.pipe { display:flex; align-items:center; gap:0; list-style:none; margin:0; padding:0;
        flex:1 1 auto; min-width:0; justify-content:center; }
.pipe-step { display:flex; align-items:center; gap:.35rem; position:relative; padding:0 .15rem; }
.pipe-step + .pipe-step { margin-left:1.2rem; }
.pipe-step + .pipe-step::before { content:""; position:absolute; left:-1.2rem; top:50%;
        width:1.2rem; height:2px; transform:translateY(-50%); background:var(--line); }
.pipe-step.is-done + .pipe-step::before { background:var(--ok-ink); }
.pipe-dot { width:.62rem; height:.62rem; border-radius:50%; background:var(--line); flex:none; }
.pipe-step.is-done .pipe-dot { background:var(--ok-ink); }
.pipe-step.is-now  .pipe-dot { background:var(--accent); box-shadow:0 0 0 3px var(--tint-accent); }
.pipe-lbl { font-size:.78rem; font-weight:600; letter-spacing:-.01em; color:var(--soft);
            white-space:nowrap; }
.pipe-step.is-done .pipe-lbl { color:var(--ok-ink); }
.pipe-step.is-now  .pipe-lbl { color:var(--accent-strong); }

/* J1: a hand-edited draft that FAILs the gate would ship unchecked via Copy — the strip's
   actionable warning (attention amber, distinct from action blue and fail red). */
.cmd-warn { display:flex; align-items:center; gap:.5rem; flex-wrap:wrap;
            background:var(--attn-tint); border:1px solid var(--attn-ink);
            color:var(--attn-ink); padding:.5rem .9rem; border-radius:var(--radius);
            margin:-.4rem 0 1rem; font-size:var(--text-sm); font-weight:600; }
.cmd-warn a { color:inherit; text-decoration:underline; }

.tally { display:flex; align-items:center; gap:.35rem; font-size:.78rem; font-weight:600;
         white-space:nowrap; background:var(--code-bg); border-radius:var(--radius-lg); padding:.25rem .6rem;
         font-variant-numeric:tabular-nums; }
.tally-pass { color:var(--ok-ink); }
.tally-fail { color:var(--bad-ink); }
.tally-sep  { color:var(--soft); }

/* ── Artifact status grid: Standup · Daily digest · Weekly recap · Monthly (2×2) ── */
.status-grid { display:grid; grid-template-columns:repeat(2, 1fr); gap:1rem;
               margin:0 0 1.1rem; align-items:start; }
.art-card { display:flex; flex-direction:column; gap:.35rem;
            background:var(--surface); border:1px solid var(--line);
            border-left:4px solid var(--soft); border-radius:var(--radius-lg);
            padding:.75rem .85rem; box-shadow:var(--shadow); }
.art-card--approved       { border-left-color:var(--ok); }
.art-card--approved_stale { border-left-color:var(--bad); }
.art-card--posted         { border-left-color:var(--accent); }
.art-card--generated      { border-left-color:var(--soft); }
.art-head { display:flex; align-items:center; justify-content:space-between; gap:.5rem; }
.art-kind { display:inline-flex; align-items:center; gap:.35rem; font-weight:650;
            font-size:.9rem; color:var(--ink); }
.art-kind .icon { color:var(--soft); }
.art-date { font-size:var(--text-lg); font-weight:700; letter-spacing:-.01em; color:var(--ink);
            text-decoration:none; font-variant-numeric:tabular-nums; min-height:44px;
            display:inline-flex; align-items:center; }
.art-date:hover { color:var(--accent); }
.art-card .provenance { margin:.1rem 0 .2rem; }
.art-card > .badge { align-self:flex-start; } /* a badge row must not stretch the card's width */
.art-act { display:flex; align-items:center; gap:.7rem; margin-top:auto; padding-top:.2rem; }
.art-act .btn-primary, .art-act .btn-tonal { font-size:.88rem; padding:.35rem .8rem; }
.art-act .btn-primary .icon, .art-act .btn-tonal .icon { margin-left:.2rem; }

/* ── Structured-view entry cards (Experiments · Stuck & aging) ──────────────── */
.entry-grid { display:grid; grid-template-columns:1fr 1fr; gap:1rem; margin:0 0 1.3rem; }
.entry-card { display:flex; align-items:center; gap:.8rem; text-decoration:none;
              background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-lg);
              padding:.7rem .9rem; box-shadow:var(--shadow); color:var(--ink);
              min-height:44px; }
.entry-card:hover { border-color:var(--soft); }
.entry-icon { display:inline-flex; }
.entry-icon .icon { width:1.6rem; height:1.6rem; color:var(--accent); }
.entry-body { display:flex; flex-direction:column; line-height:1.15; flex:1 1 auto; min-width:0; }
.entry-n { font-size:var(--text-xl); font-weight:700; letter-spacing:-.02em;
           font-variant-numeric:tabular-nums; }
.entry-lbl { font-size:var(--text-sm); color:var(--soft); }
.attn-text { color:var(--attn-ink); font-weight:600; }
.empty-inline { color:var(--soft); font-style:italic; }   /* muted "no current snapshot" note */
.entry-go { color:var(--soft); flex:none; }

/* J4-v — "what's DUE but ungenerated" prompt on the weekly/monthly cards (attention amber,
   not alarm red — it's a nudge, not a failure). */
.due-hint { display:flex; align-items:center; gap:.35rem; flex-wrap:wrap; margin:.3rem 0 0;
            font-size:var(--text-sm); color:var(--attn-ink); }
.due-hint .icon { width:1em; height:1em; flex:none; }
.due-hint a { color:var(--attn-ink); text-decoration:underline; font-weight:600; }

/* Structured-view tables (Experiments / Stuck & aging): aging rows tinted amber, numeric cols. */
tr.row-attn td { background:var(--attn-tint); }
td.cell-num, th.cell-num { white-space:nowrap; font-variant-numeric:tabular-nums; }
/* Let the long name column wrap (override the global td:first-child nowrap) and cap its width so
   the numeric columns stay on-screen without horizontal scroll. */
table.data-table td:first-child { white-space:normal; min-width:13rem; max-width:24rem;
                                  overflow-wrap:anywhere; }

/* J6 — themed "not generated yet" page. */
.missing-card { max-width:34rem; }
.missing-card h1 { margin:0 0 .4rem; }
.missing-card h1 .sub { color:var(--soft); font-weight:500; font-size:.75em; }
.missing-card .missing-lead { color:var(--ink); margin:.2rem 0 1rem; }
.missing-card .btn-primary { margin:.2rem 0; }
.missing-card .quiet-link { justify-content:flex-start; padding-left:0; margin-top:.6rem; }

.recent-more { margin:.4rem 0 0; }
.recent-more > summary { font-size:.9rem; }
.recent-more h2 { font-size:1rem; margin:.6rem 0 .3rem; }

/* Generation-history list (as-generated page) — one row per snapshot run for a date. */
.gen-list { list-style:none; padding:0; margin:.3rem 0 0; }
.gen-list li { display:flex; flex-wrap:wrap; align-items:center; gap:.2rem .5rem;
               padding:.35rem 0; border-bottom:1px solid var(--line); }
.gen-list li:last-child { border-bottom:0; }
.gen-list li.is-current { background:var(--tint-accent); border-radius:var(--radius-sm);
                          padding-left:.5rem; padding-right:.5rem; }
.gen-list a { min-height:44px; display:inline-flex; align-items:center; font-weight:600;
              text-decoration:none; font-variant-numeric:tabular-nums; }

/* ── Today-First hero (dashboard) ──────────────────────────────────────────── */
.hero { background:var(--surface); border:1px solid var(--line);
        border-left:4px solid var(--soft); border-radius:var(--radius-lg);
        padding:.9rem 1.1rem; margin:.8rem 0 1.3rem; box-shadow:var(--shadow); }
.hero--approved        { border-left-color:var(--ok);     background:var(--tint-ok); }
.hero--approved_stale  { border-left-color:var(--bad);    background:var(--tint-bad); }
.hero--posted          { border-left-color:var(--accent); background:var(--tint-accent); }
.hero--missing         { border-left-color:var(--warn);   background:var(--tint-warn); }
.hero-head { display:flex; flex-wrap:wrap; align-items:center; gap:.5rem .7rem; }
.hero-date { font-size:1.3rem; font-weight:700; letter-spacing:-.01em;
             color:var(--ink); text-decoration:none; min-height:44px;
             display:inline-flex; align-items:center; }
.hero-date:hover { color:var(--accent); }
.hero-actions { display:flex; align-items:center; gap:.8rem; margin:.5rem 0 .1rem; }
.btn-primary { display:inline-flex; align-items:center; min-height:44px;
               padding:.5rem 1.1rem; background:var(--accent); color:#fff;
               border-radius:var(--radius); text-decoration:none; font-weight:600; }
.btn-primary:hover { background:var(--accent-strong); }
.btn-primary:active { background:var(--accent-strong); }
.quiet-link { color:var(--soft); min-height:44px; min-width:44px; display:inline-flex;
              align-items:center; justify-content:center; padding:.6rem .4rem; }
.row-date a { display:inline-flex; min-height:44px; align-items:center;
              text-decoration:none; font-weight:600; color:var(--accent); }
.row-date .gen-hint { display:block; white-space:normal; }
.digest-list { list-style:none; padding:0; }
.digest-list a { display:inline-flex; min-height:44px; align-items:center;
                 padding:.4rem .2rem; }

/* Single full-width editor column (the live editor renders inline, so no side preview).
   min-width:0 stops long unbreakable validator tokens from widening the page on phones. */
.editor-wrap { max-width:58rem; min-width:0; }
textarea { width:100%; border:1px solid var(--line); border-radius:var(--radius-sm);
           background:var(--surface); color:var(--ink); }
textarea#editor { min-height:30rem; font:16px/1.5 ui-monospace, monospace; }
.preview { border:1px solid var(--line); border-radius:var(--radius); padding:0 1rem;
           background:var(--surface);
           overflow:auto; overflow-wrap:break-word; word-break:break-word; }
.preview a { overflow-wrap:anywhere; }
/* Standalone (view-only) preview: cap the prose measure near the 65–75ch band. */
.preview-solo { max-width:46rem; }

/* Inline live-preview markdown editor (CodeMirror 6, vendored bundle). Themed via the app
   tokens so the dark variant maps for free. The .cm-lp-* classes come from our live-preview
   plugin (editor-src/src/index.js): it hides the markdown markers off the active line and
   renders bold/headings/links in place — but the document stays literal markdown. */
/* Toolbar (formatting + Write/Markdown toggle) sits flush on top of the editor and stays
   pinned while you scroll a long draft, so formatting + save state are always reachable. */
.md-toolbar { display:flex; align-items:center; justify-content:space-between; gap:.5rem;
              flex-wrap:wrap; padding:.3rem .4rem; background:var(--code-bg);
              border:1px solid var(--line); border-bottom:0; border-radius:var(--radius-sm) var(--radius-sm) 0 0;
              position:sticky; top:0; z-index:5; }
.md-tools { display:flex; align-items:center; gap:.1rem; }
.md-right { display:flex; align-items:center; gap:.6rem; }
.md-right .save-status { font-size:.8rem; }
.md-btn { min-height:0; height:2rem; width:2.2rem; padding:0; margin:0;
          display:inline-flex; align-items:center; justify-content:center;
          background:transparent; border:1px solid transparent; border-radius:var(--radius-sm);
          color:var(--ink); cursor:pointer; touch-action:manipulation; }
.md-btn:hover { background:var(--press); }
.md-btn:focus-visible { outline:2px solid var(--accent); outline-offset:1px; }
.md-btn svg { width:18px; height:18px; }
.md-sep { width:1px; height:1.3rem; background:var(--line); margin:0 .3rem; }
.seg-toggle { display:inline-flex; border:1px solid var(--line); border-radius:var(--radius);
              overflow:hidden; background:var(--surface); }
.seg-opt { min-height:0; height:1.9rem; padding:0 .8rem; margin:0; border:0;
           background:transparent; color:var(--soft); font-weight:600; font-size:.82rem;
           cursor:pointer; touch-action:manipulation; }
.seg-opt + .seg-opt { border-left:1px solid var(--line); }
.seg-opt:hover { background:var(--press); }
.seg-opt:focus-visible { outline:2px solid var(--accent); outline-offset:-2px; }
.seg-opt[aria-pressed="true"] { background:var(--accent); color:#fff; }
/* Touch devices (iPhone via Tailscale): meet the 44px minimum tap target. */
@media (pointer: coarse) {
  .md-btn { height:44px; width:44px; }
  .md-btn svg { width:20px; height:20px; }
  .seg-opt { height:44px; padding:0 1rem; }
}
/* Restore-unsaved-draft banner (local autosave). */
.draft-restore { display:flex; align-items:center; gap:.6rem; flex-wrap:wrap;
                 background:var(--tint-accent); border:1px solid var(--accent);
                 color:var(--ink); padding:.5rem .8rem; border-radius:var(--radius);
                 margin:.6rem 0; font-size:.9rem; }
.draft-restore button { min-height:0; height:2rem; padding:0 .8rem; font-size:var(--text-sm); }
.cm-editor { border:1px solid var(--line); border-radius:var(--radius-sm);
             background:var(--surface); color:var(--ink); }
/* In the edit pane the toolbar sits flush on top, so square the editor's top corners
   (the hidden <textarea> sits between them in the DOM, so an adjacent selector won't do). */
.editor-wrap .cm-editor { border-top-left-radius:0; border-top-right-radius:0; }
.cm-editor.cm-focused { outline:2px solid var(--accent); outline-offset:-1px; }
/* No max-height: the editor grows with content so the PAGE scrolls as one (no nested
   scroll region). The sticky toolbar keeps tools in reach. */
.cm-editor .cm-scroller { font:16px/1.7 Inter, system-ui, sans-serif; min-height:30rem; }
/* Markdown (raw) mode reads better monospace — alignment of `- `, `·`, links, code. */
.cm-editor.cm-raw .cm-scroller { font-family:ui-monospace, monospace; }
.cm-editor .cm-content { padding:.7rem .4rem; caret-color:var(--ink); }
.cm-editor .cm-cursor { border-left-color:var(--ink); }
.cm-editor .cm-activeLine { background:color-mix(in srgb, var(--accent) 7%, transparent); }
.cm-editor .cm-selectionBackground,
.cm-editor.cm-focused .cm-selectionBackground { background:var(--press); }
/* Inline-rendered markdown (markers hidden off the active line). */
.cm-lp-h1 { font-size:1.5em;  font-weight:700; line-height:1.3; }
.cm-lp-h2 { font-size:1.25em; font-weight:700; line-height:1.3; }
.cm-lp-h3 { font-size:1.12em; font-weight:700; }
.cm-lp-h4, .cm-lp-h5, .cm-lp-h6 { font-weight:700; }
.cm-lp-strong { font-weight:700; }
.cm-lp-em { font-style:italic; }
.cm-lp-strike { text-decoration:line-through; color:var(--soft); }
.cm-lp-code { font-family:ui-monospace, monospace; background:var(--code-bg);
              border-radius:var(--radius-xs); padding:0 .2rem; }
.cm-lp-link { color:var(--accent); text-decoration:underline; cursor:text; }
.cm-lp-mention { color:var(--accent); font-weight:600; } /* <@U…> rendered as @Nick (mirrors .mention) */

/* ── Report editor hierarchy (draft / digest pages) ─────────────────────────
   Two tiers: report-level actions (Copy / Regenerate) up top, edit-level
   actions (Save / Approve) demoted below the editor. Button weight encodes
   rank: filled primary → tonal → neutral → ghost. Tier classes are
   container-scoped (specificity 0,2,0) so they outrank the global
   button[type=submit] accent rule in BOTH light and dark themes. */
.report-head { display:flex; align-items:flex-start; justify-content:space-between;
               gap:1rem; flex-wrap:wrap; }
.report-head h1 { margin:0; }
.report-head .sub { color:var(--soft); font-weight:500; }
.report-head .provenance { margin:.3rem 0 0; }
.status-pills { display:flex; gap:.4rem; align-items:center; padding-top:.35rem; }

.report-actions { display:flex; align-items:center; gap:.6rem; flex-wrap:wrap;
                  margin:.9rem 0 .5rem; }
.report-actions .hint { color:var(--soft); font-size:var(--text-sm); }
.report-actions .btn-tonal { background:var(--tint-accent); border-color:var(--accent);
                             color:var(--accent-strong); font-weight:600; }
.report-actions .btn-tonal:hover { background:var(--accent); border-color:var(--accent); color:#fff; }
.report-actions .btn-tonal:disabled { opacity:.5; cursor:not-allowed; }

.edit-controls { display:flex; align-items:center; gap:.6rem; flex-wrap:wrap;
                 margin-top:.8rem; padding-top:.8rem; border-top:1px solid var(--line); }
.edit-controls .btn-neutral { background:var(--surface); border-color:var(--line);
                              color:var(--ink); font-weight:500; }
.edit-controls .btn-neutral:hover { background:var(--press); }
.edit-controls .approve-wrap { margin-left:auto; display:flex; align-items:center; gap:.6rem; }
.edit-controls .approve-hint { color:var(--soft); font-size:.78rem; max-width:18rem; text-align:right; }
.edit-controls .btn-ghost { background:transparent; border-color:var(--line);
                            color:var(--soft); font-weight:500; }
.edit-controls .btn-ghost:hover { border-color:var(--soft); color:var(--ink); }
.edit-controls .btn-ghost:disabled { opacity:.5; cursor:not-allowed; }

pre.joblog { background:var(--joblog-bg); color:var(--joblog-ink); padding:1rem;
             border-radius:var(--radius); max-height:28rem; overflow:auto; font-size:13px; }
.stale-banner { background:var(--tint-bad); border:1px solid var(--bad);
                color:var(--bad-ink); padding:.6rem 1rem; border-radius:var(--radius);
                margin:.6rem 0; }
/* Links inside the alert inherit its ink + underline, so the recovery action reads as a link
   without a clashing blue inside the red banner. */
.stale-banner a { color:inherit; text-decoration:underline; font-weight:600; }
.provenance { color:var(--soft); font-size:var(--text-sm); margin:.2rem 0 .8rem; }
.provenance code, .gen-hint code, .hero code { background:var(--code-bg);
                padding:.05rem .3rem; border-radius:var(--radius-xs); }
.edited-badge { color:var(--warn-ink); font-weight:600; margin-left:.3rem;
                white-space:nowrap; }   /* keep "✎ edited" glued to its glyph at phone widths */
.gen-hint { color:var(--soft); font-size:.8rem; white-space:nowrap; }
p.gen-hint { white-space:normal; }  /* prose hints (diff legend) must wrap at 320px */
ul.nested { margin:.1rem 0 0; padding-left:1.4rem; list-style:circle; }
ul li { padding:.15rem 0; }

/* Validation issues render as a bordered panel (it's a live region draft.js rewrites). */
ul#issues { list-style:none; margin:.6rem 0 0; padding:0; }
ul#issues:has(li) { border:1px solid var(--line); border-radius:var(--radius);
                    padding:.5rem .8rem; background:var(--surface); }
ul#issues li.error { padding:.15rem 0; overflow-wrap:anywhere; }
.save-status { color:var(--ok-ink); font-weight:600; }
.save-status.unsaved { color:var(--warn-ink); }
ul#issues.stale { opacity:.55; }   /* live-validate unreachable → results are stale */
.approve-status { background:var(--tint-accent); border:1px solid var(--accent);
                  color:var(--accent-strong); padding:.6rem 1rem;
                  border-radius:var(--radius); margin:.6rem 0; }
.approve-status .job-head { display:flex; align-items:center; gap:.4rem; }
.approve-status .icon { color:var(--accent-strong); }
/* Indeterminate progress bar for a running approve/regenerate job (no real %; just
   "working"). Transform-only sliver; reduced motion (below) freezes it to a full track. */
.job-progress { position:relative; height:3px; margin-top:.5rem; overflow:hidden;
                border-radius:var(--radius-xs);
                background:color-mix(in srgb, var(--accent) 22%, transparent); }
.job-progress::after { content:""; position:absolute; inset:0 auto 0 0; width:40%;
                       border-radius:inherit; background:var(--accent);
                       animation:job-slide 1.4s ease-in-out infinite; }
@keyframes job-slide { 0% { transform:translateX(-110%); } 100% { transform:translateX(360%); } }

/* Inline async spinner (Save / Approve / Copy / Regenerate). Transform-only, so the
   global reduced-motion rule leaves a static partial ring — a visible busy mark, never a
   frozen-looking blank. Sized in em so it tracks the button's text. */
.spinner { display:inline-block; width:.85em; height:.85em; margin-right:.45em;
           vertical-align:-.1em; border:2px solid currentColor; border-top-color:transparent;
           border-radius:50%; animation:spin .6s linear infinite; }
@keyframes spin { to { transform:rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .job-progress::after { width:100%; }   /* static full track instead of a moving sliver */
}

form.login { display:grid; gap:.35rem; }
form.login label { padding-bottom:0; font-weight:600; }
form.login input { max-width:none; margin-bottom:.5rem; }
form.login .btn-primary { width:100%; justify-content:center; border:none;
        cursor:pointer; margin-top:.3rem; }
.login-card { max-width:24rem; margin:8vh auto 0; }
.login-card h1 { margin-top:.2rem; }
.login-card > .btn-primary { width:100%; justify-content:center; }

/* ── Audit log: action badges, filter bar, pager ───────────────────────────── */
.badge.audit-save    { background:var(--accent); }
.badge.audit-approve { background:var(--ok-ink); }
.badge.audit-job     { background:var(--warn-ink); }
.badge.audit-cancel  { background:var(--bad); }
.badge.audit-login, .badge.audit-other { background:var(--soft); }
.filter-bar { display:flex; flex-wrap:wrap; align-items:end; gap:.6rem 1rem;
        margin:.4rem 0 1rem; }
.filter-bar label { display:grid; gap:.2rem; font-weight:600; font-size:var(--text-sm);
        color:var(--soft); }
.pager { display:flex; align-items:center; gap:1rem; margin:.8rem 0; }
.pager a { display:inline-flex; min-height:44px; align-items:center;
        text-decoration:none; font-weight:600; }

/* ── Archive: listings, name filter, breadcrumb ────────────────────────────── */
.breadcrumb { color:var(--soft); font-size:.9rem; margin:.4rem 0 0; }
.breadcrumb a { text-decoration:none; }
.file-list { list-style:none; padding:0; margin:.2rem 0 0; }
.file-list a { display:inline-flex; min-height:44px; align-items:center;
        text-decoration:none; overflow-wrap:anywhere; }
.archive-filter input { max-width:28rem; }

/* ── Generated snapshot: view toggle + diff ────────────────────────────────── */
.view-toggle { display:inline-flex; border:1px solid var(--line); border-radius:var(--radius-lg);
        overflow:hidden; margin:.9rem 0 .7rem; background:var(--surface); }
.view-toggle a { display:inline-flex; min-height:44px; align-items:center;
        padding:.5rem 1rem; text-decoration:none; color:var(--soft); font-weight:600; }
.view-toggle a + a { border-left:1px solid var(--line); }
.view-toggle a.active { background:var(--accent); color:#fff; }
.diff { border:1px solid var(--line); border-radius:var(--radius);
        background:var(--surface); overflow-x:auto;
        font:13px/1.6 ui-monospace, monospace; }
.diff-line { padding:.05rem .8rem; white-space:pre-wrap; overflow-wrap:anywhere; }
.diff-add  { background:var(--tint-ok);  color:var(--ok-ink); }
.diff-del  { background:var(--tint-bad); color:var(--bad-ink); }
.diff-hunk { background:var(--code-bg);  color:var(--soft); }
.diff-key  { padding:.05rem .45rem; border-radius:var(--radius-xs); font-weight:600; }

/* ── Dark variant: token remap only (used at night/early morning) ─────────── */
@media (prefers-color-scheme: dark) {
  :root {
    --brand:#152a47; --brand-ink:#c6d6ea;
    --accent:#5b93f5; --accent-strong:#8ab2f8;
    --ink:#e2e8f0; --soft:#97a4b6;
    --bg:#0b1220; --surface:#151e31; --line:#2b3a52;
    --ok:#22c55e; --warn:#f59e0b; --bad:#ef4444;
    --ok-ink:#5fd58a; --warn-ink:#f1b25b; --bad-ink:#f58c8c;
    --tint-accent:#16243d; --tint-ok:#12281b; --tint-warn:#2a2008; --tint-bad:#2c1414;
    --code-bg:#22304a; --press:#23314c;
    --shadow:0 1px 2px rgba(0,0,0,.4);
    /* Attention amber, lightened for dark surfaces (mirrors --warn-ink's dark treatment). */
    --attn-ink:#f1b25b; --attn-tint:#2a2008;
  }
  /* Mid-tone ok/warn/bad tokens need DARK text in dark mode (white fails on them).
     The slate badge needs WHITE (6.31:1 — dark ink was 2.93); the blue is lightened
     to #5d8bf0 so its dark ink clears AA (5.73:1, was 4.41 on #3b76e8). */
  .badge.approved { background:var(--ok); color:#06270f; }
  .badge.missing { background:var(--warn); color:#2a1c02; }
  .badge.in-progress { background:var(--warn); color:#2a1c02; }
  .badge.approved_stale { background:var(--bad); color:#2d0707; }
  .badge.posted { background:#5d8bf0; color:#061129; }
  .badge.generated { background:#536174; color:#fff; }
  .badge.job-queued { background:#536174; color:#fff; }
  .badge.job-running { background:#5d8bf0; color:#061129; }
  .badge.job-done { background:var(--ok); color:#06270f; }
  .badge.job-failed { background:var(--bad); color:#2d0707; }
  .badge.audit-save { background:#5d8bf0; color:#061129; }
  .badge.audit-approve { background:var(--ok); color:#06270f; }
  .badge.audit-job { background:var(--warn); color:#2a1c02; }
  .badge.audit-cancel { background:var(--bad); color:#2d0707; }
  .badge.audit-login, .badge.audit-other { background:#536174; color:#fff; }
  .view-toggle a.active { background:#2f6ae0; color:#fff; }
  button[type="submit"], .btn-primary { background:#2f6ae0; border-color:#2f6ae0; color:#fff; }
  button[type="submit"]:hover, .btn-primary:hover { background:#4a80ea; border-color:#4a80ea; }
  button.danger { background:#c93535; border-color:#c93535; color:#fff; }
}

/* Phones + small tablets: stack the side-by-side grids, shrink the editor. */
@media (max-width: 900px) {
  .cards { grid-template-columns:1fr; }
  .status-grid { grid-template-columns:1fr; }
  .entry-grid { grid-template-columns:1fr; }
  textarea#editor { min-height:18rem; }
  .cm-editor .cm-scroller { min-height:18rem; }
  .preview { max-height:24rem; }
  main { padding-top:.75rem; }
  h1 { font-size:1.4rem; }
  .view-toggle { display:grid; grid-template-columns:1fr 1fr; width:100%; }
  .view-toggle a { justify-content:center; text-align:center; padding:.5rem .4rem; }
}

/* ── Print: weekly/monthly reports are management-shareable documents — print the CONTENT,
   not the app chrome (J17). On edit pages the hidden server render (#render-cache) becomes
   the printed body in place of the editor. */
@media print {
  header, .skip-link, .messages, .report-actions, .edit-controls, .md-toolbar,
  ul#issues, #issues-summary, .stale-banner, .cmd-warn, .approve-status,
  .draft-restore, .recent-more, .entry-grid, .filter-bar, .pager { display:none !important; }
  body { display:block; background:#fff; }
  main { max-width:none; padding:0; }
  .editor-wrap { display:none; }
  #render-cache[hidden] { display:block; }
  .preview, .preview-solo { max-width:none; max-height:none; overflow:visible;
                            border:none; padding:0; }
}
