/* Operational Command Center — ops-console theme.
   Palette matches mikesailab.com: bg #060606, borders #27272a, text #f4f4f5.
   Status colours are deliberately NOT the site accent — on this page colour
   carries meaning, so sky-blue is reserved for "informational", never "good". */

:root {
  --bg: #060606;
  --bg-2: #0b0b0d;
  --panel: rgba(24, 24, 27, 0.42);
  --panel-2: rgba(24, 24, 27, 0.7);
  --border: rgba(39, 39, 42, 0.75);
  --border-2: #3f3f46;
  --fg: #f4f4f5;
  --fg-2: #d4d4d8;
  --fg-3: #a1a1aa;
  --fg-4: #71717a;
  --accent: #38bdf8;

  --ok: #34d399;          /* emerald-400 */
  --ok-bg: rgba(52, 211, 153, 0.12);
  --warn: #fbbf24;        /* amber-400 */
  --warn-bg: rgba(251, 191, 36, 0.12);
  --fail: #fb7185;        /* rose-400 */
  --fail-bg: rgba(251, 113, 133, 0.13);
  --unchecked: #a78bfa;   /* violet-400 — "we do not know" must not read as ok */
  --unchecked-bg: rgba(167, 139, 250, 0.12);

  --radius: 12px;
  --radius-sm: 8px;
  --font: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "Cascadia Code", Consolas, monospace;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: 130px; }

body {
  margin: 0;
  background:
    radial-gradient(1100px 520px at 50% -12%, rgba(56, 189, 248, 0.06), transparent 70%),
    var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-size: 14.5px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
body[data-boot="pending"] #main,
body[data-boot="pending"] .kpi-rail { opacity: 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
code, .mono { font-family: var(--mono); font-size: 0.92em; }
h2, h3 { margin: 0; font-weight: 650; letter-spacing: -0.01em; }
::selection { background: rgba(56, 189, 248, 0.3); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* ══ Top bar ══════════════════════════════════════════════════════════ */

#topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 14px;
  padding: 10px 18px;
  background: rgba(6, 6, 6, 0.88);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.topbar-spacer { flex: 1 1 auto; }

.brand { display: flex; align-items: center; gap: 10px; color: var(--fg); flex: 0 0 auto; }
.brand:hover { text-decoration: none; }
.brand svg { width: 34px; height: 34px; padding: 6px; border: 1px solid var(--border); border-radius: 9px; color: var(--accent); }
.brand strong { display: block; font-size: 14px; font-weight: 700; }
.brand small { display: block; color: var(--fg-4); font-size: 11px; line-height: 1.2; }

/* Verdict pill — the one thing readable from across the room. */
.verdict {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 16px 7px 12px;
  border: 1px solid var(--border-2);
  border-radius: 999px;
  font-weight: 700; font-size: 13.5px; letter-spacing: 0.04em; text-transform: uppercase;
}
.verdict-dot { width: 10px; height: 10px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.verdict-counts { font-weight: 500; font-size: 12px; letter-spacing: 0; text-transform: none; color: var(--fg-3); }
.verdict--ok        { color: var(--ok);        border-color: rgba(52,211,153,.45);  background: var(--ok-bg); }
.verdict--warn      { color: var(--warn);      border-color: rgba(251,191,36,.45);  background: var(--warn-bg); }
.verdict--fail      { color: var(--fail);      border-color: rgba(251,113,133,.5);  background: var(--fail-bg); }
.verdict--unchecked { color: var(--unchecked); border-color: rgba(167,139,250,.45); background: var(--unchecked-bg); }
.verdict--unknown   { color: var(--fg-3); }
.verdict--fail .verdict-dot { animation: pulse 1.4s ease-in-out infinite; }

@keyframes pulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.8); } }

/* Feed state — live vs snapshot, and how old. */
.feed-state {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 12px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--panel);
  font-size: 12px; color: var(--fg-3); white-space: nowrap;
}
.feed-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--fg-4); flex: 0 0 auto; }
.feed-label { font-weight: 650; letter-spacing: .05em; text-transform: uppercase; font-size: 10.5px; }
.feed-age { font-family: var(--mono); font-size: 11px; }
.feed-state--live     { border-color: rgba(52,211,153,.4); color: var(--ok); }
.feed-state--live .feed-dot { background: var(--ok); animation: pulse 2.4s ease-in-out infinite; }
.feed-state--snapshot { border-color: rgba(251,191,36,.35); color: var(--warn); }
.feed-state--snapshot .feed-dot { background: var(--warn); }
.feed-state--stale    { border-color: rgba(251,113,133,.5); color: var(--fail); }
.feed-state--stale .feed-dot { background: var(--fail); animation: pulse 1s ease-in-out infinite; }
.feed-state--loading .feed-dot { background: var(--accent); animation: pulse 1s linear infinite; }
/* The endpoint was asked and did not usefully answer. Amber: the document on
   screen may still be recent, but nothing is confirming it any more. */
.feed-state--noanswer { border-color: rgba(251,191,36,.45); color: var(--warn); }
.feed-state--noanswer .feed-dot { background: var(--warn); animation: pulse 1.4s ease-in-out infinite; }
/* This DEVICE has no network, so the endpoint was never asked. Violet, the
   page's `unchecked` colour, because that is precisely the claim being made:
   not verified. An offline laptop is no evidence at all about the box, and
   colouring it as a fault would blame the fleet for a dead wifi. It does not
   pulse — nothing is in progress. */
.feed-state--offline  { border-color: rgba(167,139,250,.45); color: var(--unchecked); }
.feed-state--offline .feed-dot { background: var(--unchecked); }
.feed-fails {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .02em;
  padding: 1px 6px; border-radius: 999px;
  border: 1px solid currentColor; opacity: .85;
}

.topbar-actions { display: flex; gap: 6px; flex: 0 0 auto; }
.tb-btn {
  display: grid; place-items: center;
  width: 34px; height: 34px; padding: 0;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); color: var(--fg-3); cursor: pointer;
  transition: color .12s, border-color .12s, background .12s;
}
.tb-btn:hover { color: var(--fg); border-color: var(--border-2); background: var(--panel-2); }
.tb-btn svg { width: 17px; height: 17px; }
.tb-btn[aria-pressed="true"] { color: var(--accent); border-color: rgba(56,189,248,.45); }
#soundBtn .ic-off { display: none; }
#soundBtn[aria-pressed="false"] .ic-on { display: none; }
#soundBtn[aria-pressed="false"] .ic-off { display: block; }
#refreshBtn.spinning svg { animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ══ Banner ═══════════════════════════════════════════════════════════ */

.banner {
  margin: 0; padding: 11px 20px;
  font-size: 13px; font-weight: 500; line-height: 1.5;
  border-bottom: 1px solid var(--border);
}
.banner > span + span { margin-left: .5em; }
.banner--hidden { display: none; }
.banner--warn { background: var(--warn-bg); color: var(--warn); border-bottom-color: rgba(251,191,36,.3); }
.banner--fail { background: var(--fail-bg); color: var(--fail); border-bottom-color: rgba(251,113,133,.35); }
.banner--info { background: rgba(56,189,248,.09); color: var(--accent); }
.banner b { font-weight: 700; }
.banner a { color: inherit; text-decoration: underline; }

/* ══ KPI rail ═════════════════════════════════════════════════════════ */

.kpi-rail {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(178px, 1fr));
  padding: 18px 20px 4px;
  max-width: 1680px; margin: 0 auto;
  transition: opacity .25s;
}
.kpi {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel); padding: 12px 14px;
  display: flex; flex-direction: column; gap: 2px; min-height: 92px;
  position: relative; overflow: hidden;
}
.kpi::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 3px; background: var(--fg-4); }
.kpi--ok::before   { background: var(--ok); }
.kpi--warn::before { background: var(--warn); }
.kpi--fail::before { background: var(--fail); }
.kpi--info::before { background: var(--accent); }
.kpi-label { font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--fg-4); font-weight: 650; }
.kpi-value { font-size: 27px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.15; font-variant-numeric: tabular-nums; }
.kpi-value small { font-size: 15px; color: var(--fg-4); font-weight: 600; }
.kpi-note { font-size: 11.5px; color: var(--fg-3); line-height: 1.35; }
.kpi--ok .kpi-value   { color: var(--ok); }
.kpi--warn .kpi-value { color: var(--warn); }
.kpi--fail .kpi-value { color: var(--fail); }

/* ══ Section nav ══════════════════════════════════════════════════════ */

.section-nav {
  position: sticky; top: 55px; z-index: 30;
  display: flex; gap: 4px; overflow-x: auto;
  padding: 12px 20px 10px;
  max-width: 1680px; margin: 0 auto;
  background: rgba(6, 6, 6, 0.94);
  backdrop-filter: blur(8px);
  box-shadow: 0 10px 12px -10px rgba(6, 6, 6, 0.95);
  scrollbar-width: none;
}
.section-nav::-webkit-scrollbar { display: none; }
.section-nav a {
  padding: 5px 11px; border-radius: 999px; font-size: 12.5px; white-space: nowrap;
  color: var(--fg-3); border: 1px solid transparent;
}
.section-nav a:hover { color: var(--fg); background: var(--panel); text-decoration: none; }
.section-nav a.on { color: var(--fg); background: var(--panel-2); border-color: var(--border); }
.section-nav a[data-bad="1"]::after { content: ""; display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: var(--fail); margin-left: 6px; vertical-align: middle; }
.section-nav a[data-bad="2"]::after { content: ""; display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: var(--warn); margin-left: 6px; vertical-align: middle; }

/* ══ Cards ════════════════════════════════════════════════════════════ */

#main { max-width: 1680px; margin: 0 auto; padding: 4px 20px 80px; transition: opacity .25s; }

.card {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel); margin-bottom: 18px; overflow: hidden;
}
/* The head reserves a right-hand column for the collapse toggle, so the button
   sits at a fixed x across all thirteen cards rather than trailing each title. */
.card-head {
  padding: 16px 18px 14px; border-bottom: 1px solid var(--border);
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start;
  column-gap: 12px;
}
.card-head h2 { font-size: 16px; grid-column: 1; }
.card-head > .card-toggle { grid-column: 2; grid-row: 1; }
.card-head > :not(h2):not(.card-toggle) { grid-column: 1; }
.card-sub { margin: 5px 0 0; font-size: 12.5px; color: var(--fg-3); max-width: 92ch; }
.card-sub code { color: var(--fg-2); }
.card-tools { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 12px; }

/* ── Collapse / expand ─────────────────────────────────────────────────────
   Collapsed = title + summary. Everything else in the card is hidden, which
   includes the body sibling of .card-head, the description and the tools. */
.card-toggle {
  width: 28px; height: 28px; flex: 0 0 auto; display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); color: var(--fg-4); cursor: pointer; padding: 0;
  transition: color .12s, border-color .12s;
}
.card-toggle:hover { color: var(--fg-2); border-color: var(--border-2); }
.card-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.card-toggle svg { width: 15px; height: 15px; transition: transform .16s ease; }
/* Chevron points down when open (press to close), right when closed. */
.card--collapsed .card-toggle svg { transform: rotate(-90deg); }

.card--collapsed > .card-head { border-bottom: 0; }
.card--collapsed > :not(.card-head) { display: none; }
.card--collapsed .card-sub,
.card--collapsed .card-tools { display: none; }

/* The summary is the collapsed card's whole content, so it only shows there —
   expanded, the section's own data says all this and more. */
.card-summary { display: none; margin-top: 6px; flex-wrap: wrap; gap: 8px; align-items: baseline; }
.card--collapsed .card-summary { display: flex; }
.card-summary-tally {
  font-size: 10.5px; letter-spacing: .05em; text-transform: uppercase; font-weight: 700;
  border: 1px solid currentColor; border-radius: 4px; padding: 1px 6px;
}
.card-summary-tally--ok { color: var(--ok); }
.card-summary-tally--warn { color: var(--warn); }
.card-summary-tally--fail { color: var(--fail); }
.card-summary-tally--unchecked { color: var(--unchecked); }
.card-summary-extra { font-size: 12px; color: var(--fg-4); }

@media (prefers-reduced-motion: reduce) {
  .card-toggle svg { transition: none; }
}

.mini-btn {
  padding: 5px 11px; font-size: 12px; font-family: inherit;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); color: var(--fg-2); cursor: pointer;
  transition: border-color .12s, color .12s;
}
.mini-btn:hover { border-color: var(--border-2); color: var(--fg); text-decoration: none; }
.mini-btn--primary { background: var(--accent); color: #061018; border-color: var(--accent); font-weight: 650; }
.mini-input {
  padding: 5px 10px; font-size: 12.5px; font-family: inherit;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-2); color: var(--fg); min-width: 170px;
}
.chk { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--fg-3); cursor: pointer; }
.chk input { accent-color: var(--accent); }

.seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.seg button {
  padding: 5px 11px; font-size: 12px; font-family: inherit;
  background: transparent; color: var(--fg-4); border: 0; cursor: pointer; white-space: nowrap;
}
.seg button + button { border-left: 1px solid var(--border); }
.seg button:hover { color: var(--fg-2); }
.seg button.on { background: var(--panel-2); color: var(--fg); }

/* ══ Generic check rows ═══════════════════════════════════════════════ */

.rows { display: flex; flex-direction: column; }
.row {
  display: grid; grid-template-columns: 84px minmax(150px, 230px) 1fr;
  gap: 14px; align-items: baseline;
  padding: 11px 18px; border-bottom: 1px solid var(--border);
}
.row:last-child { border-bottom: 0; }
.row:hover { background: rgba(255,255,255,.017); }
.row-name { font-family: var(--mono); font-size: 12.5px; color: var(--fg-2); word-break: break-word; }
.row-detail { font-size: 12.8px; color: var(--fg-3); }
.row-detail strong { color: var(--fg-2); font-weight: 600; }
.row-meta { display: block; font-size: 11.5px; color: var(--fg-4); margin-top: 3px; }

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: 999px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  border: 1px solid currentColor; white-space: nowrap;
}
.pill--ok        { color: var(--ok);        background: var(--ok-bg); }
.pill--warn      { color: var(--warn);      background: var(--warn-bg); }
.pill--fail      { color: var(--fail);      background: var(--fail-bg); }
.pill--unchecked { color: var(--unchecked); background: var(--unchecked-bg); }
.pill--info      { color: var(--accent);    background: rgba(56,189,248,.1); }
.pill--mute      { color: var(--fg-4);      background: transparent; }

/* Remedy block — the paste-ready fix. */
.remedy {
  margin-top: 8px; display: flex; gap: 8px; align-items: flex-start;
  padding: 8px 10px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg-2);
}
.remedy-text { flex: 1 1 auto; font-family: var(--mono); font-size: 11.5px; color: var(--fg-2); white-space: pre-wrap; word-break: break-word; }
.remedy-label { display: block; font-family: var(--font); font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: var(--fg-4); margin-bottom: 4px; font-weight: 650; }
.copy-btn {
  flex: 0 0 auto; padding: 4px 9px; font-size: 11px; font-family: inherit;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--panel); color: var(--fg-3); cursor: pointer;
}
.copy-btn:hover { color: var(--fg); border-color: var(--border-2); }
.copy-btn.done { color: var(--ok); border-color: rgba(52,211,153,.45); }

/* ══ Alerts ═══════════════════════════════════════════════════════════ */

.alert-list { display: flex; flex-direction: column; }
.alert {
  display: grid; grid-template-columns: 86px 1fr auto; gap: 14px;
  padding: 13px 18px; border-bottom: 1px solid var(--border);
  align-items: start;
}
.alert:last-child { border-bottom: 0; }
.alert--fail { background: linear-gradient(90deg, rgba(251,113,133,.06), transparent 42%); }
.alert--warn { background: linear-gradient(90deg, rgba(251,191,36,.05), transparent 42%); }
.alert--acked { opacity: .42; }
.alert-name { font-family: var(--mono); font-size: 12.5px; color: var(--fg); font-weight: 600; }
.alert-area { font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-4); margin-bottom: 2px; }
.alert-detail { font-size: 13px; color: var(--fg-2); }
.alert-actions { display: flex; gap: 6px; align-items: center; }
.alert-human { color: var(--fail); font-weight: 650; }
.alert-auto { color: var(--accent); }

/* Alert timestamps. Muted by default and never coloured by status: the row's
   own background already carries the verdict, and a red timestamp would read as
   a second, competing severity signal. The one exception is the ack stamp,
   which is emerald because "a human has seen this" IS a good state. */
.alert-times {
  display: flex; flex-wrap: wrap; gap: 6px 14px;
  margin-top: 6px; font-size: 11.5px; color: var(--fg-4);
}
.alert-time { display: inline-flex; align-items: baseline; gap: 5px; }
.alert-time-label {
  font-size: 10px; letter-spacing: .07em; text-transform: uppercase; color: var(--fg-4);
}
.alert-time b { font-family: var(--mono); font-size: 11.5px; font-weight: 600; color: var(--fg-3); }
.alert-time-age { color: var(--fg-4); }
/* The probe's own evidence outranks the browser's observation, so it is the one
   drawn at full strength — see the comment on alertTimes() in script.js. */
.alert-time--evidence b { color: var(--fg-2); }
.alert-time--ack b { color: var(--ok); }
.alert-time--refire b { color: var(--warn); }
.alert-time--unknown b { font-style: italic; font-weight: 500; color: var(--fg-4); }

.empty {
  padding: 30px 18px; text-align: center; color: var(--fg-4); font-size: 13px;
}
.empty strong { display: block; color: var(--ok); font-size: 15px; margin-bottom: 4px; font-weight: 650; }

/* ══ Cost board ═══════════════════════════════════════════════════════ */

.cost-body { display: grid; grid-template-columns: repeat(auto-fit, minmax(310px, 1fr)); gap: 0; }
.cost-panel { padding: 16px 18px; border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.cost-panel:last-child { border-right: 0; }
.cost-panel h3 { font-size: 12.5px; letter-spacing: .05em; text-transform: uppercase; color: var(--fg-4); margin-bottom: 10px; }
.cost-big { font-size: 30px; font-weight: 700; letter-spacing: -0.025em; font-variant-numeric: tabular-nums; line-height: 1.1; }
.cost-big small { font-size: 14px; color: var(--fg-4); font-weight: 600; letter-spacing: 0; }
.cost-note { font-size: 12px; color: var(--fg-3); margin-top: 6px; }

/* Artifact stat strip. Deliberately NOT reusing .kpi — the top rail is the
   fleet's headline verdict, and a second rail with the same weight inside a
   card would read as a competing one. Same tokens, quieter type. */
.stat-strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  border-bottom: 1px solid var(--border);
}
.stat-cell { padding: 13px 18px; border-right: 1px solid var(--border); }
.stat-cell:last-child { border-right: 0; }
.stat-label { font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--fg-4); font-weight: 650; }
.stat-value { font-size: 22px; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; line-height: 1.2; margin-top: 2px; }
.stat-note { font-size: 11.5px; color: var(--fg-3); margin-top: 2px; line-height: 1.35; }

/* Split bar: free vs billed. */
.splitbar { display: flex; height: 12px; border-radius: 999px; overflow: hidden; background: var(--bg-2); margin: 12px 0 8px; border: 1px solid var(--border); }
.splitbar span { display: block; height: 100%; }
.splitbar .free   { background: var(--ok); }
.splitbar .billed { background: var(--fail); }
.splitbar .none   { background: var(--fg-4); }
.split-legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 11.5px; color: var(--fg-3); }
.split-legend i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 5px; vertical-align: middle; font-style: normal; }

/* Meter: quota gauges. */
.meter { height: 9px; border-radius: 999px; background: var(--bg-2); border: 1px solid var(--border); overflow: hidden; margin: 10px 0 6px; }
.meter > span { display: block; height: 100%; background: var(--ok); transition: width .4s; }
.meter--warn > span { background: var(--warn); }
.meter--fail > span { background: var(--fail); }

/* Capacity meter: the same gauge, plus the two thresholds that matter. The cap
   is not the number that stops a deploy — the build guard below it is — so both
   are drawn on the track rather than left in prose. */
.cap-meter { position: relative; height: 12px; margin: 12px 0 8px; overflow: visible; border-radius: 999px; }
.cap-meter > span { border-radius: 999px; }
.cap-tick {
  position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px;
  border-radius: 1px; pointer-events: auto;
}
.cap-tick--warn  { background: var(--warn); opacity: .75; }
.cap-tick--guard { background: var(--fail); opacity: .8; }

/* Inline share bar inside a table cell — a column of numbers hides a dominant
   row; a column of bars does not. */
.cap-bar {
  display: inline-block; width: 46px; height: 5px; margin-right: 7px;
  vertical-align: middle; border-radius: 999px; background: var(--bg-2);
  border: 1px solid var(--border); overflow: hidden;
}
.cap-bar i { display: block; height: 100%; background: var(--fg-4); }

.mini-table { width: 100%; border-collapse: collapse; font-size: 12.2px; margin-top: 10px; }
.mini-table th { text-align: left; font-weight: 650; font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--fg-4); padding: 5px 8px 5px 0; border-bottom: 1px solid var(--border); }
.mini-table td { padding: 5px 8px 5px 0; border-bottom: 1px solid var(--border); color: var(--fg-2); vertical-align: baseline; }
.mini-table tr:last-child td { border-bottom: 0; }
.mini-table .num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--mono); font-size: 11.5px; padding-right: 0; }
.mini-table .slug { font-family: var(--mono); font-size: 11.5px; }
.t-bad { color: var(--fail); }
.t-good { color: var(--ok); }
.t-warn { color: var(--warn); }
.t-mute { color: var(--fg-4); }
/* Accent = informational, never "good". A duration trend is an observation the
   page made about data the probe shipped, not a verdict the probe reached. */
.t-info { color: var(--accent); }
/* Violet = "we did not verify this", which is a different claim from "fine". */
.t-uncheck { color: var(--unchecked); }

/* ══ Subscriptions ════════════════════════════════════════════════════ */

.sub-panel .cost-big { margin-top: 2px; }
.sub-head { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }
.sub-head h3 { margin-bottom: 0; }
.sub-vendor {
  font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--fg-4); margin-bottom: 8px;
}

/* The anti-misreading callout on the Claude panel. It sits between a dollar
   figure and three quota meters, and without it the eye reads "spent out of a
   budget" — which is precisely the claim that cannot be made for a Max plan. */
.sub-nocap {
  font-size: 11.5px; line-height: 1.45; color: var(--fg-3);
  border-left: 2px solid var(--unchecked);
  background: var(--unchecked-bg);
  padding: 7px 9px; border-radius: 0 5px 5px 0; margin: 10px 0 4px;
}

/* Shown while the incremental meter is still walking its backlog: the numbers
   below it are real but PARTIAL, so they get a warning frame rather than the
   ordinary muted note treatment. */
.sub-priming {
  font-size: 11.5px; line-height: 1.45; color: var(--warn);
  border: 1px dashed var(--warn); border-radius: 6px;
  padding: 7px 9px; margin-top: 10px;
}

.sub-spark { display: flex; align-items: center; gap: 8px; color: var(--accent); margin: 10px 0 2px; }
.sub-spark-label { font-size: 10.5px; color: var(--fg-4); }

/* ══ Timeline ═════════════════════════════════════════════════════════ */

#timelineBody { padding: 16px 18px; overflow-x: auto; }
.tl {
  --gutter: 186px;                 /* fixed label column, so a bar never sits under a slug */
  position: relative; min-width: 940px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-2); padding: 26px 0 0;
}
.tl-hours { position: relative; height: 20px; margin-left: var(--gutter); border-bottom: 1px solid var(--border); }
.tl-hour { position: absolute; top: 0; font-size: 10px; color: var(--fg-4); font-family: var(--mono); transform: translateX(-50%); }
.tl-grid { position: absolute; inset: 26px 0 0 var(--gutter); pointer-events: none; }
.tl-gridline { position: absolute; top: 0; bottom: 0; width: 1px; background: rgba(63,63,70,.35); }
.tl-now { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--accent); box-shadow: 0 0 10px rgba(56,189,248,.8); z-index: 3; }
.tl-now::after {
  content: "now"; position: absolute; top: -20px; left: 50%; transform: translateX(-50%);
  font-size: 9.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--accent); font-weight: 700;
}
.tl-lane {
  display: grid; grid-template-columns: var(--gutter) 1fr;
  height: 24px; border-bottom: 1px solid rgba(39,39,42,.5);
}
.tl-lane:last-child { border-bottom: 0; }
.tl-lane:nth-child(even) { background: rgba(255,255,255,.012); }
.tl-lane-plot { position: relative; }   /* every bar/marker positions inside HERE */
.tl-lane-label {
  align-self: center; padding: 0 10px;
  font-family: var(--mono); font-size: 10.5px; color: var(--fg-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  border-right: 1px solid var(--border);
}
.tl-lane--fail .tl-lane-label { color: var(--fail); }
.tl-lane--warn .tl-lane-label { color: var(--warn); }
.tl-run {
  position: absolute; top: 5px; height: 14px; border-radius: 3px; min-width: 3px;
  background: var(--ok); opacity: .85; cursor: pointer; z-index: 1;
}
.tl-run--fail { background: var(--fail); }
.tl-run--soft { background: var(--warn); }
.tl-run--live { background: var(--accent); animation: pulse 1.2s ease-in-out infinite; }
.tl-fire {
  position: absolute; top: 6px; width: 0; height: 0; z-index: 1;
  border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 7px solid var(--fg-4);
}
.tl-fire--missed { border-bottom-color: var(--fail); }
.tl-legend { display: flex; gap: 16px; flex-wrap: wrap; font-size: 11.5px; color: var(--fg-3); margin-top: 12px; }

/* ══ Job grid ═════════════════════════════════════════════════════════ */

.job-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 10px; padding: 16px 18px; }
.job {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-2); padding: 11px 12px; text-align: left;
  display: flex; flex-direction: column; gap: 6px;
  position: relative; overflow: hidden;
  font: inherit; color: inherit; width: 100%;
  transition: border-color .12s, transform .12s;
}
.job:hover { border-color: var(--border-2); transform: translateY(-1px); }

/* The tile is a div, not a button, so it can legally contain the runbook link.
   This stretched button is the tile's click target: it sits behind the content
   and covers the whole card. Content above it is pointer-transparent, so a click
   anywhere still opens the drawer — except on .job-runbook, which opts back in. */
.job-hit {
  position: absolute; inset: 0; z-index: 1;
  border: 0; background: transparent; padding: 0; margin: 0;
  width: 100%; cursor: pointer; font: inherit; color: inherit;
}
.job-hit:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; border-radius: var(--radius-sm); }
.job > *:not(.job-hit) { position: relative; z-index: 2; pointer-events: none; }
/* Must out-specify the :not() rule above — `.job > *:not(.job-hit)` scores 0,2,0
   and a bare `.job-runbook` scores 0,1,0, so the plain class loses and the link
   inherits pointer-events:none, i.e. it renders perfectly and cannot be clicked.
   Caught in the browser; a visual check alone would have shipped it. */
/* The runbook link and the pause button now share a footer row, so they sit one
   level deeper than they used to. That matters more than it looks: `.job >
   *:not(.job-hit)` above sets pointer-events:none on the WRAPPER, and children
   inherit it — the same trap the comment above describes, one nesting level down.
   Each real control therefore re-arms itself explicitly, and the selectors below
   score 0,3,0 so they beat the 0,2,0 :not() rule on specificity rather than on
   source order. Both were verified clickable in the browser; neither is provable
   by looking at the rendering. */
.job > .job-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-top: 2px;
}
.job .job-foot a.job-runbook,
.job .job-foot .pause-ctl,
.job .job-foot .pause-ctl * { pointer-events: auto; z-index: 3; }
.job .job-foot a.job-runbook {
  font-size: 10px; letter-spacing: .05em; text-transform: uppercase; font-weight: 700;
  color: var(--fg-4); border-bottom: 1px dotted currentColor; margin-top: 1px;
}
.job-runbook:hover { color: var(--accent); }
/* On touch the link is a small target on an otherwise card-sized button, so a
   near-miss opens the drawer instead — the wrong action, and an easy one to
   blame on the link "not working". Pad it out on coarse pointers only; on a mouse
   the 17px line is fine and a 40px block would just push the tile taller. */
@media (pointer: coarse) {
  .job .job-foot a.job-runbook { padding: 9px 4px; margin: -7px 0 -5px; }
}
.job::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 3px; background: var(--ok); }
.job--warn::before { background: var(--warn); }
.job--fail::before { background: var(--fail); }
.job--running::before { background: var(--accent); animation: pulse 1.4s ease-in-out infinite; }
/* Paused is a STATE, not a verdict, so it does not take one of the status
   colours — a paused job is neither healthy nor broken, it is switched off. It
   gets slate and a dimmed tile, and it deliberately overrides the status stripe:
   a job you turned off yourself should not keep shouting red at you for the
   fires it is no longer meant to answer. */
.job--paused { opacity: .62; }
.job--paused::before { background: var(--fg-4) !important; animation: none !important; }
.job--paused:hover { opacity: .85; }
.job-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.job-slug { font-family: var(--mono); font-size: 12.5px; font-weight: 600; color: var(--fg); word-break: break-all; }
.job-cron { font-family: var(--mono); font-size: 10.5px; color: var(--fg-4); }
.job-line { display: flex; justify-content: space-between; gap: 8px; font-size: 11.5px; color: var(--fg-3); }
.job-line b { color: var(--fg-2); font-weight: 600; font-variant-numeric: tabular-nums; }
.job-notes { font-size: 11.5px; color: var(--fg-4); line-height: 1.35; }
.job--fail .job-notes { color: var(--fail); }
.job--warn .job-notes { color: var(--warn); }
.job-tags { display: flex; gap: 5px; flex-wrap: wrap; }
.tag {
  font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
  padding: 1.5px 6px; border-radius: 4px; border: 1px solid currentColor;
}
.tag--fail { color: var(--fail); }
.tag--warn { color: var(--warn); }
.tag--live { color: var(--accent); }
.tag--mute { color: var(--fg-4); }
/* "It broke and then fixed itself." Emerald would claim the job is good right now,
   which is a stronger statement than the probe made — this is history, so it is
   drawn like history: present, legible, and not competing for attention. */
.tag--healed { color: var(--fg-4); border-style: dashed; }
/* Accent, deliberately NOT amber: a job that is slowing down is still whatever the
   probe said it was. Amber here would make the page and the weekly report two
   systems grading the same fleet. */
.tag--trend { color: var(--accent); }
/* Solid rather than outlined, because this is the one tag that describes a
   deliberate human decision rather than something the fleet did on its own. */
.tag--paused { color: var(--fg-2); background: rgba(113,113,122,.28); border-color: var(--fg-4); }

/* ══ Pause / resume control ═══════════════════════════════════════════ */

.pause-ctl { display: inline-flex; align-items: center; gap: 7px; }
.pause-btn { padding: 3px 10px; font-size: 11px; white-space: nowrap; }
/* The button says what clicking DOES, so on a paused item it reads "Resume" —
   the on-state colour marks the item as switched off, not the button as active. */
.pause-btn--on { color: var(--fg); border-color: var(--border-2); background: var(--panel-2); }
.pause-btn:disabled { opacity: .55; cursor: progress; }
/* No control endpoint configured: this button copies a command instead of
   pausing anything, and is drawn as the different affordance it is. */
.pause-btn--manual { border-style: dashed; color: var(--fg-4); }
.pause-btn--manual:hover { color: var(--accent); }
.pause-ctl--stalled .pause-btn { color: var(--warn); border-color: rgba(251,191,36,.45); }
.pause-since { font-size: 10.5px; color: var(--fg-4); white-space: nowrap; }
/* Accent, not amber: waiting for the box to confirm is the system working as
   designed, not a fault. It only becomes amber once it has waited too long — and
   that state is .pause-ctl--stalled, which colours the button. */
.pause-ctl--pausing .pause-waiting,
.pause-ctl--resuming .pause-waiting { color: var(--accent); }
.pause-ctl--stalled .pause-waiting { color: var(--warn); }
/* On a job tile the control sits in a flex row with the runbook link; let the
   explanation wrap under it rather than stretch the tile. */
.job .job-foot .pause-ctl { flex-wrap: wrap; justify-content: flex-end; }
.job .job-foot .pause-waiting { white-space: normal; text-align: right; }

/* ══ Duration sparkline ═══════════════════════════════════════════════ */

.job-line--spark { align-items: center; }
.spark-wrap { display: inline-flex; align-items: center; gap: 7px; color: var(--fg-4); }
.spark-wrap--up { color: var(--accent); }
.spark-wrap b { color: var(--fg-2); font-weight: 600; font-variant-numeric: tabular-nums; }
.spark { display: block; overflow: visible; }
.spark-wrap--big { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; margin: 6px 0 10px; }
.spark-wrap--big .spark { width: 100%; height: 42px; }
.spark-cap { font-size: 10.5px; color: var(--fg-4); }

/* ══ Slowest movers strip ═════════════════════════════════════════════ */

.movers {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  padding: 10px 18px 0;
}
.movers-lead {
  font-size: 9.5px; letter-spacing: .08em; text-transform: uppercase;
  font-weight: 700; color: var(--accent); cursor: help;
}
.mover {
  display: inline-flex; align-items: baseline; gap: 7px; cursor: pointer;
  font: inherit; text-align: left;
  border: 1px solid rgba(56,189,248,.32); border-radius: 999px;
  background: rgba(56,189,248,.07); padding: 3px 11px;
  transition: border-color .12s, background .12s;
}
.mover:hover { border-color: var(--accent); background: rgba(56,189,248,.13); }
.mover-slug { font-family: var(--mono); font-size: 11.5px; color: var(--fg); }
.mover-delta { font-size: 11.5px; font-weight: 700; color: var(--accent); font-variant-numeric: tabular-nums; }
.mover-time { font-size: 10.5px; color: var(--fg-4); font-variant-numeric: tabular-nums; }
.movers-note { font-size: 11px; color: var(--fg-4); flex-basis: 100%; }

/* ══ Drawer doc links ═════════════════════════════════════════════════ */

.drawer-links { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 4px; }
.btn-link {
  font-size: 11.5px; padding: 5px 11px; border-radius: 6px;
  border: 1px solid var(--border-2); color: var(--fg-2); background: var(--bg-2);
}
.btn-link:hover { border-color: var(--accent); color: var(--accent); }

/* ══ Drawer ═══════════════════════════════════════════════════════════ */

.drawer-scrim { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 50; backdrop-filter: blur(2px); }
.drawer {
  position: fixed; inset: 0 0 0 auto; width: min(560px, 100vw); z-index: 51;
  background: var(--bg-2); border-left: 1px solid var(--border-2);
  display: flex; flex-direction: column; overflow: hidden;
  box-shadow: -20px 0 60px rgba(0,0,0,.5);
}
.drawer[hidden] { display: none; }
.drawer-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 16px 18px; border-bottom: 1px solid var(--border); }
.drawer-head h3 { font-family: var(--mono); font-size: 15px; word-break: break-all; }
.drawer-sub { margin: 4px 0 0; font-size: 12px; color: var(--fg-3); }
.drawer-body { padding: 16px 18px 40px; overflow-y: auto; flex: 1 1 auto; }
.drawer-body h4 { margin: 18px 0 8px; font-size: 11px; letter-spacing: .09em; text-transform: uppercase; color: var(--fg-4); font-weight: 650; }
.drawer-body h4:first-child { margin-top: 0; }
.kv { display: grid; grid-template-columns: 132px 1fr; gap: 4px 12px; font-size: 12.5px; }
.kv dt { color: var(--fg-4); }
.kv dd { margin: 0; color: var(--fg-2); font-family: var(--mono); font-size: 11.8px; word-break: break-word; }
.note-list { margin: 0; padding-left: 16px; font-size: 12.5px; color: var(--fg-2); }
.note-list li { margin-bottom: 5px; }

/* ══ Settings dialog ══════════════════════════════════════════════════ */

.settings {
  border: 1px solid var(--border-2); border-radius: var(--radius);
  background: var(--bg-2); color: var(--fg); padding: 0;
  width: min(520px, calc(100vw - 32px)); max-height: 86vh;
}
.settings::backdrop { background: rgba(0,0,0,.65); backdrop-filter: blur(2px); }
.settings form { padding: 20px; overflow-y: auto; max-height: 86vh; }
.settings h3 { font-size: 16px; margin-bottom: 14px; }
.settings fieldset { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px 14px 14px; margin: 0 0 14px; }
.settings legend { font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--fg-4); font-weight: 650; padding: 0 6px; }
.settings .hint { font-size: 11.8px; color: var(--fg-4); margin: 0 0 10px; line-height: 1.45; }
.field { display: grid; grid-template-columns: 1fr; gap: 4px; margin-bottom: 10px; }
.field > span { font-size: 12px; color: var(--fg-3); }
.field input, .field select {
  padding: 7px 10px; font-size: 13px; font-family: inherit;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg); color: var(--fg); width: 100%;
}
.field input[type="url"] { font-family: var(--mono); font-size: 12px; }
.settings menu { display: flex; justify-content: flex-end; gap: 8px; padding: 0; margin: 4px 0 0; }

/* ══ Toast ════════════════════════════════════════════════════════════ */

.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translate(-50%, 16px);
  padding: 9px 16px; border-radius: 999px;
  background: var(--panel-2); border: 1px solid var(--border-2); color: var(--fg);
  font-size: 12.5px; z-index: 60; opacity: 0; pointer-events: none;
  transition: opacity .18s, transform .18s; backdrop-filter: blur(8px);
}
.toast.on { opacity: 1; transform: translate(-50%, 0); }

/* ══ Footer ═══════════════════════════════════════════════════════════ */

#footer { color: var(--fg-4); font-size: 11.8px; padding: 8px 2px 0; max-width: 92ch; }
#footer p { margin: 0 0 5px; }
#footer code { color: var(--fg-3); }

/* ══ Wallboard mode ═══════════════════════════════════════════════════ */

body.wallboard { font-size: 17px; }
body.wallboard .section-nav,
body.wallboard #footer,
body.wallboard #sec-drift,
body.wallboard .card-sub,
body.wallboard .card-tools { display: none; }
body.wallboard .verdict { font-size: 19px; padding: 11px 24px 11px 18px; }
body.wallboard .kpi-value { font-size: 40px; }
body.wallboard .kpi { min-height: 120px; }
body.wallboard .kpi-rail { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
body.wallboard .job-grid { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
body.wallboard #sec-jobs .job--ok { display: none; }

/* ══ Schedule ═════════════════════════════════════════════════════════ */

/* Seven columns, one per weekday, mirroring the /schedule/ calendar so the two
   read as the same object. auto-fit rather than a fixed 7: below ~1100px the
   week wraps to 3-4 columns instead of producing seven unreadable slivers, and
   each column keeps its day label so a wrapped grid is still legible. */
.sched-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(212px, 1fr));
  gap: 14px; padding: 16px 18px;
}
.sched-day {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-2); display: flex; flex-direction: column; min-width: 0;
  /* The day head is a filled bar; without this its background squares off the
     column's top corners. */
  overflow: hidden;
}
/* Today gets the accent — informational, never a status colour. Which day it is
   is a fact about the clock, not a verdict about the fleet. */
.sched-day--today { border-color: rgba(56,189,248,.45); }
/* The head is a filled bar rather than a hairline-separated label. Two jobs: it
   names the day loudly enough to find while scrolling, and it draws the seam
   between one day and the next — which a 1px border at 10.5px did not. Sky, for
   the same reason today is sky: a weekday is information, not a verdict, so it
   must not borrow emerald/amber/rose from the status palette. */
/* Also the fold toggle for its column, which is why it is a <button>: the whole
   bar is the hit target, so folding a day on a phone does not mean hitting a
   12px chevron. Reset the UA button styling — it must still read as a bar. */
.sched-day-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 10px 12px; border: 0; border-bottom: 1px solid rgba(56,189,248,.22);
  background: rgba(56,189,248,.10);
  width: 100%; text-align: left; font: inherit; color: inherit;
  cursor: pointer; transition: background .14s;
}
.sched-day-head:hover { background: rgba(56,189,248,.19); }
.sched-day--today .sched-day-head {
  background: rgba(56,189,248,.22); border-bottom-color: rgba(56,189,248,.45);
}
.sched-day--today .sched-day-head:hover { background: rgba(56,189,248,.30); }
.sched-day-headright { display: flex; align-items: center; gap: 7px; flex: 0 0 auto; }
.sched-day-chev {
  width: 14px; height: 14px; color: #7dd3fc; flex: 0 0 auto;
  transition: transform .16s;
}
/* Closed: the head and its count stay, the tiles go. The column then costs one
   row of height, which is the whole point of folding it. */
.sched-day--closed .sched-day-body { display: none; }
.sched-day--closed .sched-day-chev { transform: rotate(-90deg); }
.sched-day--closed .sched-day-head { border-bottom-color: transparent; }
/* Grid items stretch to their row's height by default, so without this a folded
   column would keep the height of the tallest open day beside it — an empty box
   instead of a fold. Only the closed one opts out; the open columns still line
   up level with each other. */
.sched-day--closed { align-self: start; }
.sched-day-name {
  font-size: 12.5px; letter-spacing: .09em; text-transform: uppercase;
  font-weight: 700; color: #bae6fd;
}
.sched-day--today .sched-day-name { color: var(--accent); }
.sched-day-count { font-family: var(--mono); font-size: 12px; color: #7dd3fc; }
.sched-empty { padding: 14px 10px; font-size: 11.5px; color: var(--fg-4); text-align: center; }

.sched-item {
  padding: 8px 10px 9px; border-bottom: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 5px; min-width: 0;
  position: relative;
}
.sched-item:last-child { border-bottom: 0; }
/* The colour bar carries the automation's OWN identity colour from the TOML —
   the same one the calendar page uses — so a job is recognisable across both
   surfaces. It is deliberately not a status colour; status is the chip below. */
.sched-item::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 3px; background: var(--chip);
}
.sched-item--paused { opacity: .55; }
.sched-item--paused:hover { opacity: .85; }
.sched-item-head { display: flex; align-items: baseline; gap: 7px; min-width: 0; }
.sched-time {
  font-family: var(--mono); font-size: 11px; color: var(--fg-4);
  font-variant-numeric: tabular-nums; flex: 0 0 auto;
}
.sched-name {
  font-size: 12px; font-weight: 600; color: var(--fg-2);
  overflow-wrap: anywhere; min-width: 0;
}
a.sched-name:hover { color: var(--accent); }
/* Two rows, split by whether their contents are facts or targets: .sched-meta
   holds the read-only chips, .sched-actions holds every link and button.
   .sched-meta stays small text; .sched-actions is a row of real buttons at every
   width. That button bar started life inside the phone block, on the theory that
   a mouse can hit a 9.5px link and a thumb cannot — true, but it made the two
   surfaces disagree about what the tile IS, and the desktop tile was the worse
   of the two: three differently-shaped scraps of text with no edges. The bar is
   the tile's control surface, so it is drawn as one here; the phone block below
   only grows it.

   Colour is per control, not per state: engine = sky, runbook = purple, pause =
   rose, resume = emerald. The two status colours are borrowed deliberately —
   pause/resume is the one pair where red-means-stop and green-means-go is
   stronger than this page's usual rule that emerald/rose belong to the probe.
   The verdict chips live one band up in .sched-meta and keep their own colours;
   nothing in this row ever claims a verdict. */
.sched-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; }
.sched-actions {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  align-items: start; gap: 6px; margin-top: 1px;
}
.sched-actions .sched-engine,
.sched-actions .sched-doc,
.sched-actions .pause-btn {
  min-height: 34px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--btn-bd); border-radius: var(--radius-sm);
  background: var(--btn-bg); color: var(--btn);
  font-size: 10px; letter-spacing: .04em; text-transform: uppercase; font-weight: 700;
  padding: 0 5px; text-align: center; white-space: normal; line-height: 1.2;
  transition: background-color .12s, border-color .12s, color .12s;
}
/* "AGENT-RUNNER" is the one label too long for a third of a narrow day column —
   it breaks at its hyphen onto two lines. Tracking and padding are tuned to give
   it the best chance of fitting on one; where it still does not, the wrap stands
   rather than the label dropping to an illegible 8px. It costs nothing:
   min-height already holds the row, so two lines make the button, the tile and
   the column no taller. Deliberately NOT white-space: nowrap — for some future
   engine name, wrapping is the graceful failure and overflow is not. */
.sched-actions .sched-engine {
  --btn: #7dd3fc; --btn-bd: rgba(56,189,248,.42); --btn-bg: rgba(56,189,248,.10);
  letter-spacing: 0; padding: 0 3px;
}
/* Purple, not violet-400: --unchecked owns violet, and a runbook link is not a
   claim that anything is unchecked. Far enough apart at button size. */
.sched-actions .sched-doc {
  --btn: #d8b4fe; --btn-bd: rgba(192,132,252,.42); --btn-bg: rgba(192,132,252,.10);
  /* Beats both the dotted-underline base rule and the (pointer: coarse) padding
     further up, which treat this link as a piece of text; here it is a button
     and does its own spacing. */
  margin: 0;
}
.sched-actions .pause-btn {
  --btn: #fda4af; --btn-bd: rgba(251,113,133,.45); --btn-bg: rgba(251,113,133,.11);
  width: 100%; padding: 0 5px;
}
/* The button says what clicking DOES — so green appears on a paused item, where
   the only move left is to start it again. */
.sched-actions .pause-btn--on {
  --btn: #6ee7b7; --btn-bd: rgba(52,211,153,.45); --btn-bg: rgba(52,211,153,.11);
}
/* Waiting too long for the box to confirm is neither stop nor go. */
.sched-actions .pause-ctl--stalled .pause-btn {
  --btn: var(--warn); --btn-bd: rgba(251,191,36,.5); --btn-bg: rgba(251,191,36,.11);
}
/* No control endpoint: this one copies a command instead of pausing anything.
   Same colour, dashed edge — the affordance differs, the meaning does not. */
.sched-actions .pause-btn--manual { border-style: dashed; }
.sched-actions .pause-btn:disabled { opacity: .6; cursor: progress; }
.sched-actions .sched-engine:hover,
.sched-actions .sched-doc:hover,
.sched-actions .pause-btn:hover:not(:disabled) {
  color: #fff; border-color: var(--btn); background: var(--btn-bg);
  filter: brightness(1.35); text-decoration: none;
}
.sched-actions .sched-engine:active,
.sched-actions .sched-doc:active,
.sched-actions .pause-btn:active:not(:disabled) { filter: brightness(.9); }
/* The pause control is a button plus an optional caption ("paused 3h ago",
   "sent · awaiting the box"). Stack them so the caption sits under its own
   button instead of stealing width from it, and let it push only its own grid
   cell taller — which is what align-items: start on the row above buys. */
.sched-actions .pause-ctl {
  display: flex; flex-direction: column; align-items: stretch; gap: 4px; min-width: 0;
}
.sched-actions .pause-since {
  font-size: 9.5px; line-height: 1.3; text-align: center; white-space: normal;
}
.sched-slug {
  font-family: var(--mono); font-size: 10px; color: var(--fg-4);
  overflow-wrap: anywhere;
}
.sched-engine {
  font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
  color: var(--fg-4); border: 1px solid currentColor; border-radius: 4px; padding: 0 4px;
  white-space: nowrap;
}
a.sched-engine:hover { color: var(--accent); }
.sched-status {
  font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
  border: 1px solid currentColor; border-radius: 4px; padding: 0 4px;
}
/* Sized and cased like the status chips beside it so the meta row reads as one
   strip, but drawn as a link (dotted underline, no border box) — a chip claims a
   verdict about the job, and this one only goes somewhere. */
.sched-doc {
  font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
  color: var(--fg-4); border-bottom: 1px dotted currentColor; line-height: 1.5;
}
.sched-doc:hover { color: var(--accent); }
/* Same reason as the job tiles': on a touch screen a 9.5px link inside a dense
   day column is a near-impossible target. Grow the hit area on coarse pointers
   only, with negative margins so the column does not get taller. */
@media (pointer: coarse) {
  .sched-doc { padding: 8px 4px; margin: -8px -2px; }
}
.sched-status--ok { color: var(--ok); }
.sched-status--warn { color: var(--warn); }
.sched-status--fail { color: var(--fail); }
.sched-status--unchecked { color: var(--unchecked); }
.sched-status--live { color: var(--accent); }

.sched-strip-head { border-top: 1px solid var(--border); }
.sched-strip {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 10px; padding: 0 18px 16px;
}
.sched-strip .sched-item {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-2); overflow: hidden;
}
.sched-form {
  display: block; padding: 9px 11px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg-2);
  font-size: 12px; font-weight: 600; color: var(--fg-2);
  border-left: 3px solid var(--chip);
}
.sched-form:hover { border-color: var(--border-2); color: var(--fg); }
.sched-note { padding: 0 18px 16px; font-size: 11.5px; color: var(--fg-4); }
.sched-note code { font-size: 11px; }

/* Jump-to-day. Off by default: on a wide screen all seven columns are already in
   view, so a control that scrolls to one of them would be a button that does
   nothing visible. The mobile block below is what turns it on. */
.sched-jump { display: none; }
.sched-jump-btn {
  flex: 1 1 auto; min-width: 42px; min-height: 42px;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-2); color: var(--fg-3); cursor: pointer;
  font: inherit; font-size: 11.5px; font-weight: 650;
  letter-spacing: .06em; text-transform: uppercase;
}
.sched-jump-btn--today { border-color: rgba(56,189,248,.45); color: var(--accent); }
.sched-jump-n { font-family: var(--mono); font-size: 10.5px; font-weight: 400; color: var(--fg-4); }
.sched-jump-btn--today .sched-jump-n { color: var(--accent); }

.drawer-state { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }

/* ══ Responsive ═══════════════════════════════════════════════════════ */

@media (max-width: 880px) {
  #topbar { flex-wrap: wrap; gap: 10px; }
  .topbar-spacer { display: none; }
  .verdict { order: 3; width: 100%; justify-content: center; }
  .brand small { display: none; }
  .row { grid-template-columns: 78px 1fr; }
  .row-detail { grid-column: 1 / -1; }
  .alert { grid-template-columns: 78px 1fr; }
  .alert-actions { grid-column: 1 / -1; }
  .cost-panel { border-right: 0; }
  .kv { grid-template-columns: 1fr; gap: 1px 0; }
  .kv dt { margin-top: 8px; }
  html { scroll-padding-top: 180px; }
  .section-nav { top: 0; position: relative; }
}

/* ── Schedule on a phone ───────────────────────────────────────────────
   The seven-column week is right for a desktop and wrong for a thumb. Squeezed
   to one column the tile kept its desktop proportions: four separate targets —
   name, engine, runbook, pause — packed into 87px of height at 9.5–12px, with
   5px between them. Everything was legible and almost nothing was hittable,
   which is why the retired /schedule/ page (one full-width pill per automation,
   one link, ~40px tall) still felt better on a phone than its replacement.

   So below 760px the tile is re-cut into three bands rather than shrunk:

     1. the title — one full-width link, 46px tall, opening the output README.
        The most-wanted target on the tile, so it gets the most area.
     2. the chips — slug and probe verdict. Read-only, so still small.
     3. the buttons — engine, runbook, pause, as equal columns 44px tall.
        Real borders, not text: a control you can see the edges of.

   Ordering, not just sizing, is the point. Bands 1 and 3 are the only things
   you can tap, and nothing tappable sits within 8px of another.

   This deliberately trades height for reach — a tile goes ~87px → ~135px, and
   the week gets about a third longer. The jump-to-day strip above the grid is
   the compensation, and it is why that control only exists at this width. */
@media (max-width: 760px) {
  /* A grid, not a wrapped flex row: seven labels of unequal width ("SUN 11" vs
     "SAT 7") wrap into a ragged two-line block that reads as a mistake. Equal
     columns wrap tidily into 4 + 3. */
  .sched-jump {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(64px, 1fr));
    gap: 6px; padding: 14px 12px 0;
  }
  /* Wider gutters than the desktop grid gets. Stacked, the days are seven boxes
     down one column with nothing but the gap between them, so the gap is the
     only thing saying "this is Wednesday's last job, not Thursday's first". */
  .sched-grid { grid-template-columns: 1fr; gap: 22px; padding: 12px; }
  .sched-strip { grid-template-columns: 1fr; padding: 0 12px 14px; }
  /* Clears the 187px sticky topbar. html{scroll-padding-top:180px} does most of
     it; this is the remainder plus a little air. */
  .sched-day { scroll-margin-top: 14px; }
  .sched-day-head { padding: 13px 14px; }
  .sched-day-name { font-size: 14px; letter-spacing: .07em; }
  .sched-day-count { font-size: 13px; }

  /* The tile stops padding itself — each band pads its own row instead, so a
     row can span the full width and still leave the 3px identity bar visible. */
  .sched-item { padding: 0; gap: 0; }
  .sched-item-head { align-items: stretch; gap: 0; }
  .sched-time { display: flex; align-items: center; padding: 0 9px 0 13px; font-size: 12px; }
  .sched-name {
    flex: 1; display: flex; align-items: center; min-height: 46px;
    padding: 9px 12px 9px 0; font-size: 14px; line-height: 1.3; color: var(--fg);
  }
  /* A tile whose name is a plain span (a job that emits no artifact) keeps the
     same height so the column does not go ragged, but must not look tappable. */
  span.sched-name { color: var(--fg-2); font-weight: 600; }
  a.sched-name:active { background: var(--panel-2); }

  .sched-meta { padding: 0 12px 9px 13px; gap: 6px; }
  .sched-slug { font-size: 11px; }
  .sched-status { font-size: 10px; padding: 1px 5px; }
  /* An engine chip lands here only when it links nowhere — keep it a label. */
  .sched-meta .sched-engine { font-size: 10px; padding: 1px 5px; }

  /* The bar itself is defined up in the base rules — three equal columns, real
     borders, one colour per control. What the phone adds is reach: 44px instead
     of 34, a wider gap so no two targets sit within 8px of each other, and the
     tile's own padding, which .sched-item gave up above so each band can span
     the full width. */
  .sched-actions { gap: 8px; padding: 0 12px 12px 13px; }
  .sched-actions .sched-engine,
  .sched-actions .sched-doc,
  .sched-actions .pause-btn { min-height: 44px; font-size: 11px; padding: 0 6px; }
  .sched-actions .sched-engine { font-size: 10px; padding: 0 3px; }
  /* Compact tiles never build one, and on a stacked strip tile the caption is
     the least of what the row is for. */
  .sched-actions .pause-since { display: none; }

  .sched-form { padding: 14px 12px; font-size: 13.5px; min-height: 44px; }
  .sched-note { padding: 0 12px 16px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
  html { scroll-behavior: auto; }
}

/* Print — a health report you can hand to someone. */
@media print {
  :root { --bg: #fff; --fg: #111; --fg-2: #333; --fg-3: #555; --fg-4: #777; --panel: #fff; --border: #ccc; }
  body { background: #fff; color: #111; }
  #topbar, .section-nav, .card-tools, .topbar-actions, .drawer, .drawer-scrim, .toast { display: none !important; }
  .card { break-inside: avoid; border-color: #ccc; }
}
