/* Customs Launch Control.
   One stylesheet, no build step, no CDN font. The whole surface is built from
   four things: a near black ground, hairlines, one amber signal, and the four
   state colours (cleared, at risk, blocked, not evaluated). */

:root {
  --ground:   #0a0d10;
  --ground-2: #0d1116;
  --panel:    #11161c;
  --well:     #0c1014;
  --line:     #1c242c;
  --line-2:   #2a343e;
  --ink:      #e8eef4;
  --ink-2:    #9dafbe;
  --ink-3:    #63768a;
  --signal:   #ff9d2e;
  --signal-d: #b96b13;

  --cleared:  #2fd67f;
  --at_risk:  #ffbe3d;
  --blocked:  #ff4d5e;
  --error:    #8ea3b5;
  --pending:  #45596b;

  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  --wrap: 1280px;
  --radius: 3px;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background:
    radial-gradient(900px 420px at 78% -10%, rgba(255, 157, 46, 0.06), transparent 70%),
    radial-gradient(700px 380px at 8% 0%, rgba(60, 130, 190, 0.05), transparent 65%),
    var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
a:focus-visible, button:focus-visible, input:focus-visible, summary:focus-visible {
  outline: 1px solid var(--signal);
  outline-offset: 2px;
}

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 32px; }

/* ---------- micro typography ---------- */

.label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.20em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.mono { font-family: var(--mono); }

.rule-row {
  display: flex;
  align-items: center;
  gap: 16px;
  margin: 0 0 20px;
}
.rule-row::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--line-2), transparent);
  order: 1;
}
.rule-row .right { order: 2; }

/* ---------- top bar ---------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  border-bottom: 1px solid var(--line);
  background: rgba(10, 13, 16, 0.86);
  backdrop-filter: blur(10px);
}
.topbar .wrap {
  display: flex;
  align-items: center;
  gap: 20px;
  height: 58px;
}
.brand { display: flex; align-items: baseline; gap: 10px; }
.brand-mark {
  width: 11px; height: 11px;
  background: var(--signal);
  transform: rotate(45deg);
  box-shadow: 0 0 14px rgba(255, 157, 46, 0.55);
  align-self: center;
}
.brand-name {
  font-family: var(--mono);
  font-size: 14px;
  letter-spacing: 0.28em;
  font-weight: 600;
}
.brand-sub {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.24em;
  color: var(--ink-3);
}
.topbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 18px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--ink-3);
}
.topbar-right a:hover { color: var(--ink); }

/* ---------- run navigation ---------- */

/* --- wayfinding: breadcrumb, lifecycle strip, first-visit hints --- */
.crumb { display: flex; align-items: center; gap: 10px; padding: 8px 0 2px; }
.crumb-up { display: inline-flex; align-items: center; gap: 6px; font-size: 12px;
  color: var(--ink-2); text-decoration: none; }
.crumb-up:hover { color: var(--signal); }
.crumb-up .ic { width: 13px; height: 13px; }
.crumb-sep { color: var(--ink-3); font-size: 12px; }
.crumb-here { font-size: 12px; color: var(--ink); }
.crumb-asset { font-size: 12px; color: var(--ink-3); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.lifecycle { display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  padding: 7px 0 4px; }
.lc { display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.08em;
  text-transform: uppercase; text-decoration: none; color: var(--ink-3); }
.lc .ic { width: 12px; height: 12px; }
.lc-done { color: var(--ink-2); }
a.lc-done:hover, a.lc-current:hover { color: var(--signal); }
.lc-current { color: var(--g-yellow); }
.lc-sep { width: 14px; height: 1px; background: var(--line-2); flex: none; }
.lc-cta { display: inline-flex; align-items: center; gap: 10px; margin-left: auto;
  min-width: 0; }
.lc-why { font-size: 12px; color: var(--ink-2); }
@media (max-width: 900px) { .lc-cta { margin-left: 0; flex-basis: 100%; } .lc-why { display: none; } }
html[data-theme="studio"] .lc-current { color: #8a6d00; }

.tab-continue { margin-left: auto; color: var(--ink-3); }
.tab-continue .mono { font-size: 11px; }

.runnav {
  border-bottom: 1px solid var(--line);
  background: var(--ground-2);
}
.runnav .wrap {
  display: flex;
  align-items: center;
  gap: 4px;
  min-height: 46px;
  flex-wrap: wrap;
}
/* On a phone the tab rows stop wrapping and scroll.
   Seven console tabs and six run tabs wrapped into thirteen stacked rows,
   which is two screens of navigation above the thing being navigated to.
   One line each, swipeable, with the ends faded so it is obvious there is
   more -- and no visible scrollbar, because these are chrome. */
@media (max-width: 760px) {
  .runnav .wrap {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    -ms-overflow-style: none;
    mask-image: linear-gradient(90deg, transparent 0, #000 14px,
      #000 calc(100% - 22px), transparent);
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 14px,
      #000 calc(100% - 22px), transparent);
  }
  .runnav .wrap::-webkit-scrollbar { display: none; }
  .runnav .wrap > * { flex: 0 0 auto; }
  .runnav .wrap.crumb { flex-wrap: wrap; overflow: visible; mask-image: none;
    -webkit-mask-image: none; }
  .runnav .wrap.lifecycle { mask-image: none; -webkit-mask-image: none; }
}
.tab {
  font-family: var(--mono);
  font-size: 10.5px;
  /* Seven tabs. At 0.18em of tracking and 14px either side the row wrapped
     the moment My edits joined it, and a nav on two lines reads as two
     navs. */
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding: 14px 11px 13px;
  border-bottom: 2px solid transparent;
}
.tab:hover { color: var(--ink-2); }
.tab.on { color: var(--ink); border-bottom-color: var(--signal); }
.runnav-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 14px;
}
.runid {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-3);
  letter-spacing: 0.06em;
}

/* ---------- pills and chips ---------- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 4px 9px;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  color: var(--ink-2);
  white-space: nowrap;
}
.pill .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

.s-cleared { color: var(--cleared); border-color: rgba(47, 214, 127, 0.35); }
.s-at_risk { color: var(--at_risk); border-color: rgba(255, 190, 61, 0.35); }
.s-blocked { color: var(--blocked); border-color: rgba(255, 77, 94, 0.35); }
.s-error   { color: var(--error);   border-color: rgba(142, 163, 181, 0.35); }
.s-pending { color: var(--ink-3);   border-color: var(--line-2); }
.s-pending .dot { animation: blink 1.6s ease-in-out infinite; }

@keyframes blink { 0%, 100% { opacity: 0.25; } 50% { opacity: 1; } }

.chip {
  display: inline-block;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 3px 7px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  color: var(--ink-2);
}
.chip.legal   { color: #ff8f9b; border-color: rgba(255, 77, 94, 0.3); background: rgba(255, 77, 94, 0.07); }
.chip.policy  { color: var(--at_risk); border-color: rgba(255, 190, 61, 0.3); background: rgba(255, 190, 61, 0.07); }
.chip.offence { color: #c39bff; border-color: rgba(160, 120, 255, 0.32); background: rgba(160, 120, 255, 0.08); }
.chip.method  { color: var(--signal); border-color: rgba(255, 157, 46, 0.32); background: rgba(255, 157, 46, 0.07); }
.chip.sourced { color: var(--cleared); border-color: rgba(47, 214, 127, 0.3); }
.chip.unsourced { color: var(--ink-3); }
.chip.on { color: var(--ink); border-color: var(--line-2); background: rgba(255, 255, 255, 0.03); }

/* ---------- panels ---------- */

.panel {
  border: 1px solid var(--line);
  background: var(--panel);
  border-radius: var(--radius);
}
.panel-head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 18px;
  border-bottom: 1px solid var(--line);
}
.panel-head .label { color: var(--ink-2); }
.panel-body { padding: 18px; }

section { padding: 34px 0; border-top: 1px solid var(--line); }
section:first-of-type { border-top: none; }

/* ---------- the hero verdict ---------- */

.hero { padding: 38px 0 30px; border-top: none; }
.headline {
  margin: 0;
  /* Half what it was. Sixty pixels of "CLEARED FOR LAUNCH IN 4 OF 11
     MARKETS" filled a laptop screen with one sentence and pushed the
     markets it is about below the fold. */
  font-size: clamp(19px, 2.5vw, 30px);
  line-height: 1.02;
  letter-spacing: -0.025em;
  font-weight: 680;
  text-wrap: balance;
}
.headline .count { color: var(--signal); font-family: var(--mono); font-weight: 600; letter-spacing: -0.01em; }
.headline.go .count { color: var(--cleared); }
.subline {
  margin: 18px 0 0;
  max-width: 62ch;
  color: var(--ink-2);
  font-size: 16px;
}
.subline strong { color: var(--ink); font-weight: 600; }

.verdict-strip {
  display: flex;
  gap: 5px;
  margin-top: 26px;
  padding: 5px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--well);
}
.verdict-strip span {
  height: 7px;
  flex: 1;
  background: var(--pending);
  border-radius: 1px;
}
.verdict-strip .v-cleared { background: var(--cleared); }
.verdict-strip .v-at_risk { background: var(--at_risk); }
.verdict-strip .v-blocked { background: var(--blocked); }
.verdict-strip .v-error   { background: repeating-linear-gradient(45deg, var(--error) 0 3px, transparent 3px 6px); }
.verdict-strip .v-pending { animation: pulse-bar 2s ease-in-out infinite; }

@keyframes pulse-bar { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.85; } }

/* ---------- the asset strip ---------- */

.specs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  border: 1px solid var(--line);
  background: var(--panel);
  border-radius: var(--radius);
  margin-top: 26px;
}
.spec { padding: 13px 18px; border-left: 1px solid var(--line); }
.spec:first-child { border-left: none; }
.spec .label { display: block; margin-bottom: 6px; }
.spec .value {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--ink);
  overflow-wrap: anywhere;
}

/* ---------- market tiles ---------- */

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 14px;
}
.tile {
  position: relative;
  display: block;
  border: 1px solid var(--line);
  background: var(--panel);
  border-radius: var(--radius);
  padding: 20px 20px 16px;
  overflow: hidden;
  transition: border-color 0.18s ease, transform 0.18s ease, background 0.18s ease;
}
.tile::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--pending);
}
.tile:hover { border-color: var(--line-2); transform: translateY(-1px); }
.tile.t-cleared::before { background: var(--cleared); }
.tile.t-at_risk::before { background: var(--at_risk); }
.tile.t-blocked::before { background: var(--blocked); }
.tile.t-error::before   { background: repeating-linear-gradient(180deg, var(--error) 0 4px, transparent 4px 8px); }
.tile.t-pending::before { animation: pulse-bar 2s ease-in-out infinite; }
.tile.t-pending { background: linear-gradient(180deg, var(--panel), var(--ground-2)); }
.tile.t-blocked { background: linear-gradient(180deg, rgba(255, 77, 94, 0.05), var(--panel) 60%); }

.tile-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.tile-code {
  font-family: var(--mono);
  font-size: 30px;
  line-height: 1;
  letter-spacing: 0.06em;
  font-weight: 600;
}
.tile-name { margin-top: 8px; color: var(--ink-2); font-size: 13.5px; }
.tile-stats {
  margin-top: 18px;
  display: flex;
  gap: 18px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-3);
  letter-spacing: 0.06em;
}
.tile-stats b { color: var(--ink); font-weight: 600; }
.tile-go {
  margin-top: 16px;
  padding-top: 13px;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  display: flex;
  justify-content: space-between;
}
.tile:hover .tile-go { color: var(--signal); }

/* ---------- links out ---------- */

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 14px; }
.card {
  display: block;
  border: 1px solid var(--line);
  background: var(--panel);
  border-radius: var(--radius);
  padding: 22px;
  transition: border-color 0.18s ease, background 0.18s ease;
}
.card:hover { border-color: var(--signal-d); background: var(--ground-2); }
.card h3 { margin: 10px 0 6px; font-size: 17px; letter-spacing: -0.01em; }
.card p { margin: 0; color: var(--ink-2); font-size: 13.5px; }

/* ---------- grafana embeds ---------- */

.embed {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--well);
  overflow: hidden;
}
.embed iframe { display: block; width: 100%; border: 0; background: var(--well); }
.embed img { display: block; width: 100%; height: auto; background: var(--well); }
/* The lanes render is 1200x420 of Grafana PNG: without this it overflowed
   every viewport under ~1250px sideways, and its 6-20s first render shoved
   the page when it finally landed. aspect-ratio OVERRIDES a replaced
   element's natural ratio, so it must match the render exactly. */
.boardlanes { display: block; width: 100%; height: auto; aspect-ratio: 1200 / 420; background: var(--well); }
.embed.down .shade { padding: 40px; text-align: center; }
.embed-note { padding: 10px 16px; border-top: 1px solid var(--line); color: var(--ink-3); font-size: 12px; }
.embed-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
.embed-note a { white-space: nowrap; }

/* ---------- tables ---------- */

table.grid { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.grid th {
  text-align: left;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
  padding: 0 14px 10px;
  border-bottom: 1px solid var(--line);
}
table.grid td {
  padding: 14px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
table.grid tr:last-child td { border-bottom: none; }
table.grid tr:hover td { background: rgba(255, 255, 255, 0.015); }
td.tight { white-space: nowrap; }
.rule-id { font-family: var(--mono); font-size: 13px; color: var(--ink); letter-spacing: 0.04em; }
.rationale { color: var(--ink-2); font-size: 13px; margin-top: 6px; max-width: 54ch; }
.cite {
  color: var(--signal);
  font-size: 12.5px;
  text-decoration: underline;
  text-decoration-color: rgba(255, 157, 46, 0.32);
  text-underline-offset: 3px;
}
.cite:hover { text-decoration-color: var(--signal); }

.sev { display: flex; align-items: center; gap: 9px; }
.sev-track { width: 62px; height: 4px; background: var(--line); border-radius: 2px; overflow: hidden; }
.sev-fill { display: block; height: 100%; background: var(--at_risk); }
.sev-fill.hot { background: var(--blocked); }
.sev-fill.cool { background: var(--ink-3); }
.sev-num { font-family: var(--mono); font-size: 12px; color: var(--ink); }

/* ---------- the guard panel ---------- */

.guard {
  border: 1px solid rgba(160, 120, 255, 0.38);
  border-left: 4px solid #a078ff;
  background: linear-gradient(180deg, rgba(160, 120, 255, 0.10), rgba(160, 120, 255, 0.025) 55%);
  border-radius: var(--radius);
  padding: 24px;
}
.guard h2 {
  margin: 0;
  font-size: 19px;
  letter-spacing: -0.01em;
  display: flex;
  align-items: center;
  gap: 12px;
}
.guard .why { margin: 10px 0 22px; color: var(--ink-2); max-width: 74ch; font-size: 14px; }
.guard-item {
  border-top: 1px solid rgba(160, 120, 255, 0.22);
  padding: 18px 0 4px;
  display: grid;
  grid-template-columns: minmax(150px, 200px) 1fr;
  gap: 24px;
}
.guard-item .reason {
  font-family: var(--mono);
  font-size: 12.5px;
  color: #d5c2ff;
  letter-spacing: 0.02em;
}
.guard-item .rationale { margin-top: 8px; }
.decision {
  margin-top: 12px;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 9px 14px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.btn:hover { color: var(--ink); border-color: var(--ink-3); background: rgba(255, 255, 255, 0.03); }
.btn.go { color: #0a0d10; background: var(--signal); border-color: var(--signal); font-weight: 700; }
.btn.go:hover { background: #ffb257; color: #0a0d10; }
.btn[disabled] { opacity: 0.4; cursor: not-allowed; }
.btn[disabled]:hover { color: var(--ink-2); border-color: var(--line-2); background: transparent; }

/* ---------- upload form ---------- */

.launcher { display: grid; grid-template-columns: 1.25fr 1fr; gap: 0; }
.launcher > div { padding: 30px; }
.launcher > div + div { border-left: 1px solid var(--line); }

.drop {
  display: block;
  border: 1px dashed var(--line-2);
  border-radius: var(--radius);
  background: var(--well);
  padding: 30px;
  text-align: center;
  transition: border-color 0.18s ease, background 0.18s ease;
}
.drop:hover { border-color: var(--signal-d); }
.drop input[type=file] { display: none; }
.drop .pick {
  display: inline-block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 11px 18px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  cursor: pointer;
  color: var(--ink);
}
.drop .pick:hover { border-color: var(--signal); color: var(--signal); }
.drop .picked { display: block; margin-top: 14px; font-family: var(--mono); font-size: 12px; color: var(--ink-2); }
.limits { margin-top: 14px; color: var(--ink-3); font-size: 12px; font-family: var(--mono); letter-spacing: 0.04em; }

.markets { display: grid; gap: 10px; margin-top: 4px; }
.market-pick {
  display: flex;
  align-items: center;
  gap: 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 13px 15px;
  cursor: pointer;
  background: var(--well);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.market-pick:hover { border-color: var(--line-2); }
.market-pick input { accent-color: var(--signal); width: 15px; height: 15px; }
.market-pick .code { font-family: var(--mono); font-size: 15px; letter-spacing: 0.08em; min-width: 30px; }
.market-pick .name { color: var(--ink-2); font-size: 13px; }
.market-pick .regime { margin-left: auto; }
.market-pick:has(input:checked) { border-color: rgba(255, 157, 46, 0.4); background: rgba(255, 157, 46, 0.05); }

/* ---------- runs list ---------- */

.runlist { display: grid; gap: 8px; }
.runrow {
  display: grid;
  grid-template-columns: 220px 1fr 130px 220px;
  gap: 20px;
  align-items: center;
  border: 1px solid var(--line);
  background: var(--panel);
  border-radius: var(--radius);
  padding: 15px 18px;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.runrow:hover { border-color: var(--line-2); background: var(--ground-2); }
.runrow .id { font-family: var(--mono); font-size: 12px; color: var(--ink-2); }
.runrow .asset { font-size: 14px; overflow-wrap: anywhere; }
.runrow .when { font-family: var(--mono); font-size: 11.5px; color: var(--ink-3); white-space: nowrap; }
.runrow .flags { display: flex; gap: 6px; justify-content: flex-end; flex-wrap: wrap; }

/* ---------- empty states ---------- */

.empty {
  border: 1px dashed var(--line-2);
  border-radius: var(--radius);
  padding: 42px 30px;
  text-align: center;
  color: var(--ink-3);
  background: rgba(255, 255, 255, 0.012);
}
.empty .big {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.empty p { margin: 12px auto 0; max-width: 52ch; font-size: 13.5px; }

/* ---------- mission feed ---------- */

.feed {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--well);
  height: calc(100vh - 240px);
  min-height: 420px;
  overflow-y: auto;
  padding: 10px 0 24px;
  scrollbar-width: thin;
}
.line {
  display: grid;
  grid-template-columns: 88px 118px 1fr;
  gap: 14px;
  padding: 4px 20px;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.5;
  border-left: 2px solid transparent;
}
.line:hover { background: rgba(255, 255, 255, 0.02); }
.line .t { color: var(--ink-3); }
.line .m { color: var(--ink); overflow-wrap: anywhere; }
.line .a {
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 10.5px;
  align-self: start;
  color: var(--ink-2);
}
.line .a::before { content: "\25b8  "; }
.line.new { animation: land 0.5s ease; }
@keyframes land { from { background: rgba(255, 157, 46, 0.14); } to { background: transparent; } }

.a-pipeline      { color: #7f93a6 !important; }
.a-ingest        { color: #56c8e0 !important; }
.a-transcription { color: #4fb8a8 !important; }
.a-analyst       { color: #6ea8ff !important; }
.a-adjudicator   { color: var(--signal) !important; }
.a-guard         { color: #a078ff !important; }
.a-publisher     { color: var(--cleared) !important; }
.a-remediator    { color: #ff8f4d !important; }
.a-verifier      { color: #4de0b0 !important; }

.line.err { border-left-color: var(--blocked); background: rgba(255, 77, 94, 0.05); }
.line.err .m { color: #ffb3ba; }

.feedbar { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; }
.feedbar .live { color: var(--cleared); }
.feedbar .live.off { color: var(--ink-3); }

/* ---------- cutting room ---------- */

.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.pane { border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); overflow: hidden; }
.pane video { display: block; width: 100%; background: #000; aspect-ratio: 16 / 9; }
.pane .cap { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 11px 14px; border-bottom: 1px solid var(--line); }

.changes { display: grid; gap: 14px; }
.change { border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); }
.change-head { display: flex; align-items: center; gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--line); flex-wrap: wrap; }
.change-body { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line); }
.frame { background: var(--well); padding: 14px; }
.frame img { display: block; width: 100%; border-radius: 2px; border: 1px solid var(--line); }
.frame .label { display: block; margin-bottom: 10px; }
.change-why { padding: 16px 18px; color: var(--ink-2); font-size: 13.5px; border-top: 1px solid var(--line); }
.change-why .instruction { font-family: var(--mono); font-size: 12.5px; color: var(--ink-3); margin-top: 8px; overflow-wrap: anywhere; }

/* ---------- market room header ---------- */

.mkt-head { display: flex; align-items: flex-start; gap: 28px; flex-wrap: wrap; }
.mkt-code {
  font-family: var(--mono);
  font-size: clamp(48px, 8vw, 86px);
  line-height: 0.9;
  letter-spacing: 0.02em;
  font-weight: 600;
}
.mkt-meta { display: grid; gap: 10px; padding-top: 6px; }
.mkt-meta h1 { margin: 0; font-size: 26px; letter-spacing: -0.02em; font-weight: 650; }
.mkt-facts { display: flex; gap: 10px; flex-wrap: wrap; }

/* ---------- footer ---------- */

footer {
  border-top: 1px solid var(--line);
  margin-top: 60px;
  padding: 26px 0 46px;
}
footer .wrap { display: flex; gap: 18px; flex-wrap: wrap; justify-content: space-between; }
footer .label { color: #4b5c6c; }

/* ---------- responsive ---------- */

@media (max-width: 900px) {
  .wrap { padding: 0 20px; }
  .launcher, .pair, .change-body, .embed-grid { grid-template-columns: 1fr; }
  .launcher > div + div { border-left: none; border-top: 1px solid var(--line); }
  .runrow { grid-template-columns: 1fr; gap: 8px; }
  .runrow .flags { justify-content: flex-start; }
  .line { grid-template-columns: 80px 1fr; }
  .line .m { grid-column: 1 / -1; }
  .guard-item { grid-template-columns: 1fr; gap: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---------- evidence frames ---------- */

.evidence { display: block; margin-top: 12px; width: 168px; }
.evidence img {
  display: block;
  width: 100%;
  border: 1px solid var(--line-2);
  border-radius: 2px;
  transition: border-color 0.15s ease;
}
.evidence .label { display: block; margin-top: 6px; }
.evidence:hover img { border-color: var(--signal); }
.evidence:hover .label { color: var(--signal); }

/* ---------- style modes ----------
   Two modes, switched by data-theme on <html> and persisted per browser:
     (none)   MISSION  the dark launch-control room, the default
     studio   STUDIO   light, for daylight desks, projectors and print
   Both keep the same four state colours; only ground, ink and the signal's
   temperature move. Screening and Spectrum were removed on 2026-08-25:
   four house styles is three more than anyone switches between, and each
   one was a second place every new colour had to be defined. */

.theme-switch {
  display: inline-flex;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  overflow: hidden;
}
.theme-switch .tbtn {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 5px 9px;
  border: none;
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
}
.theme-switch .tbtn + .tbtn { border-left: 1px solid var(--line-2); }
.theme-switch .tbtn:hover { color: var(--ink); }
.theme-switch .tbtn.on { color: var(--ink); background: rgba(255, 157, 46, 0.12); }

html[data-theme="studio"] {
  --ground:   #f3f5f7;
  --ground-2: #eaedf1;
  --panel:    #ffffff;
  --well:     #eef1f4;
  --line:     #dbe2e8;
  --line-2:   #c3cdd7;
  --ink:      #17222c;
  --ink-2:    #485866;
  --ink-3:    #7e8fa0;
  --signal:   #d86f00;
  --signal-d: #a85800;
  --cleared:  #14874d;
  --at_risk:  #a97700;
  --blocked:  #cf3040;
  --error:    #64798c;
  --pending:  #8fa1b2;
}
html[data-theme="studio"] body {
  background:
    radial-gradient(900px 420px at 78% -10%, rgba(216, 111, 0, 0.05), transparent 70%),
    radial-gradient(700px 380px at 8% 0%, rgba(60, 130, 190, 0.05), transparent 65%),
    var(--ground);
  -webkit-font-smoothing: auto;
}
html[data-theme="studio"] .topbar { background: rgba(243, 245, 247, 0.86); }
html[data-theme="studio"] .brand-mark { box-shadow: 0 0 10px rgba(216, 111, 0, 0.35); }
html[data-theme="studio"] .btn.go { color: #ffffff; }
html[data-theme="studio"] .btn.go:hover { background: #b95f00; color: #ffffff; }
html[data-theme="studio"] .btn:hover { background: rgba(23, 34, 44, 0.04); }
html[data-theme="studio"] .chip.on { background: rgba(23, 34, 44, 0.05); color: var(--ink); }
html[data-theme="studio"] table.grid tr:hover td { background: rgba(23, 34, 44, 0.025); }
html[data-theme="studio"] .line:hover { background: rgba(23, 34, 44, 0.03); }
html[data-theme="studio"] .empty { background: rgba(23, 34, 44, 0.02); }
html[data-theme="studio"] .theme-switch .tbtn.on { background: rgba(216, 111, 0, 0.12); }
html[data-theme="studio"] .chip.legal { color: #b8303f; }
html[data-theme="studio"] .chip.offence { color: #6a3fd0; border-color: rgba(106, 63, 208, 0.35); background: rgba(106, 63, 208, 0.07); }
html[data-theme="studio"] .guard { border-color: rgba(106, 63, 208, 0.4); border-left-color: #6a3fd0; background: linear-gradient(180deg, rgba(106, 63, 208, 0.07), rgba(106, 63, 208, 0.015) 55%); }
html[data-theme="studio"] .guard-item { border-top-color: rgba(106, 63, 208, 0.22); }
html[data-theme="studio"] .guard-item .reason { color: #5a34b4; }
html[data-theme="studio"] .line.err .m { color: #a8323f; }
html[data-theme="studio"] .a-pipeline      { color: #5c7186 !important; }
html[data-theme="studio"] .a-ingest        { color: #0f7f96 !important; }
html[data-theme="studio"] .a-transcription { color: #0e7d6c !important; }
html[data-theme="studio"] .a-analyst       { color: #2a63c4 !important; }
html[data-theme="studio"] .a-guard         { color: #6a3fd0 !important; }
html[data-theme="studio"] .a-remediator    { color: #c25412 !important; }
html[data-theme="studio"] .a-verifier      { color: #0d8f6c !important; }
/* The two the list forgot: brand yellow on near-white is 1.6:1 -- the
   adjudicator badge simply vanished in the light theme -- and raw #34A853
   at 10px is not much better for the publisher. */
html[data-theme="studio"] .a-adjudicator   { color: #8a6d00 !important; }
html[data-theme="studio"] .a-publisher     { color: #0d7a44 !important; }
html[data-theme="studio"] .fix-why.caveat  { color: #8a6d00; }
html[data-theme="studio"] .pane video { background: #dde3e8; }

/* ---------- youtube link input ---------- */

.urlin {
  width: 100%;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--ink);
  background: var(--well);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  padding: 12px 14px;
  letter-spacing: 0.02em;
}
.urlin::placeholder { color: var(--ink-3); }
.urlin:focus { outline: none; border-color: var(--signal-d); }

/* ---------- iconography ----------
   One stroke language, drawn as <symbol>s in base.html (24 grid, 1.75
   stroke, square terminals) and referenced with <use>. Everything inherits
   currentColor so the state palette carries the meaning. */

.defs { position: absolute; width: 0; height: 0; overflow: hidden; }
.ic {
  width: 15px; height: 15px;
  flex: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.pill .ic { width: 13px; height: 13px; }
.chip .ic { width: 12px; height: 12px; vertical-align: -2px; margin-right: 5px; }
.line .a .ic { width: 13px; height: 13px; margin-right: 7px; vertical-align: -2.5px; }
.line .a::before { content: none; }
.guard h2 .ic { width: 20px; height: 20px; color: #c39bff; }
.tile-stats .ic-human { width: 12px; height: 12px; vertical-align: -2px; margin-right: 4px; color: #c39bff; }
.evidence .label .ic { width: 12px; height: 12px; vertical-align: -2.5px; margin-right: 5px; }
html[data-theme="studio"] .guard h2 .ic,
html[data-theme="studio"] .tile-stats .ic-human { color: #6a3fd0; }

/* ---------- timeline ---------- */

.tl { display: grid; gap: 10px; }
.tl-row { display: grid; grid-template-columns: 110px 1fr; gap: 14px; align-items: center; }
.tl-label { display: flex; align-items: center; gap: 9px; font-family: var(--mono); font-size: 14px; letter-spacing: 0.06em; }
.tl-label a:hover { color: var(--signal); }
.tl-label .ic { width: 13px; height: 13px; }
.tl-ruler { position: relative; height: 20px; }
.tick { position: absolute; top: 0; font-family: var(--mono); font-size: 9px; color: var(--ink-3); letter-spacing: 0.04em; transform: translateX(-50%); text-align: center; }
.tick i { display: block; width: 1px; height: 5px; background: var(--line-2); margin: 0 auto 2px; }
.tl-track { position: relative; height: 34px; background: var(--well); border: 1px solid var(--line); border-radius: 3px; }
.tl-clean { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; opacity: 0.55; }
.seg { position: absolute; top: 4px; bottom: 4px; border-radius: 2px; display: block; min-width: 7px; }
.sc-legal { background: rgba(255, 77, 94, 0.72); }
.sc-policy { background: rgba(255, 190, 61, 0.66); }
.sc-offence { background: rgba(160, 120, 255, 0.66); }
.sc-ok { background: rgba(47, 214, 127, 0.28); box-shadow: inset 0 0 0 1px rgba(47, 214, 127, 0.75); }
.seg:hover { z-index: 40; box-shadow: 0 0 0 1px var(--ink); }
.segcard {
  display: none;
  position: absolute;
  top: calc(100% + 12px);
  left: 0;
  width: 320px;
  background: var(--panel);
  border: 1px solid var(--line-2);
  border-radius: 3px;
  padding: 12px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55);
  cursor: default;
}
.segcard.flip { left: auto; right: 0; }
.seg:hover .segcard { display: block; }
.segcard img { display: block; width: 100%; border: 1px solid var(--line); border-radius: 2px; margin-bottom: 10px; }
.segcard-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.segcard-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.segcard-why { display: block; margin-top: 8px; color: var(--ink-2); font-size: 12.5px; line-height: 1.45; }
.tl-legend { display: flex; gap: 20px; margin-top: 18px; font-family: var(--mono); font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3); }
.tl-legend span { display: inline-flex; align-items: center; gap: 7px; }
.tl-legend .sw { width: 14px; height: 8px; border-radius: 2px; display: inline-block; }
html[data-theme="studio"] .segcard { box-shadow: 0 16px 40px rgba(23, 34, 44, 0.25); }

@media (max-width: 900px) {
  .tl-row { grid-template-columns: 64px 1fr; gap: 8px; }
  .segcard { width: 260px; }
}

/* ---------- findings: detail and list ----------
   One table, two densities. List keeps every column and the evidence
   thumbnail (shrunk, inline) and drops the prose, so a market with fifteen
   findings is one screen instead of five. */

.view-switch { display: inline-flex; border: 1px solid var(--line-2); border-radius: var(--radius); overflow: hidden; }
.view-switch .tbtn { font-family: var(--mono); font-size: 9px; letter-spacing: 0.14em; text-transform: uppercase; padding: 5px 10px; border: none; background: transparent; color: var(--ink-3); cursor: pointer; }
.view-switch .tbtn + .tbtn { border-left: 1px solid var(--line-2); }
.view-switch .tbtn:hover { color: var(--ink); }
.view-switch .tbtn.on { color: var(--ink); background: rgba(255, 157, 46, 0.12); }
html[data-theme="studio"] .view-switch .tbtn.on { background: rgba(216, 111, 0, 0.12); }

.as-list table.grid td { padding: 9px 14px; vertical-align: middle; }
.as-list .rationale,
.as-list td > div[style*="margin-top:8px"],
.as-list .mono[style*="margin-top:8px"] { display: none; }
.as-list .evidence {
  display: inline-block;
  width: 108px;
  margin: 0 0 0 14px;
  vertical-align: middle;
}
.as-list .evidence .label { display: none; }
.as-list .rule-id { display: inline-block; vertical-align: middle; }

/* ponytail: logo replaces the CSS diamond; conic-gradient rule above is dead
   for the mark but still styles nothing else, so it stays harmless. */
.brand-logo { height: 24px; width: auto; display: block; align-self: center; }
html[data-theme="studio"] .brand-logo { filter: none; }

/* ---------- the jurisdiction picker ----------
   Global, continental, national, channel. Nothing is preselected: choosing
   where an ad has to clear is the operator's decision, not a default. */

.levels { display: grid; gap: 8px; }
.level { border: 1px solid var(--line); border-radius: var(--radius); background: var(--well); }
.level > summary { display: flex; align-items: baseline; gap: 12px; padding: 11px 14px; cursor: pointer; list-style: none; }
.level > summary::-webkit-details-marker { display: none; }
.level > summary::before { content: "+"; font-family: var(--mono); color: var(--ink-3); }
.level[open] > summary::before { content: "-"; }
.lv-name { font-family: var(--mono); font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink); }
.lv-blurb { font-size: 12.5px; color: var(--ink-3); }
.lv-count { margin-left: auto; font-size: 11px; color: var(--ink-3); }
.level .markets { padding: 0 12px 12px; }
.level .fam { padding: 10px 14px 4px; }

/* ---------- icons speak in exactly four colours ----------
   Google Blue #4285F4, Red #EA4335, Yellow #FBBC05, Green #34A853, and
   nothing else. Text keeps the theme's ink; the marks carry the brand. The
   mapping is semantic, not decorative: green cleared, yellow at risk, red
   blocked, blue everything the system is telling you rather than judging. */

:root {
  --g-blue:   #4285F4;
  --g-red:    #EA4335;
  --g-yellow: #FBBC05;
  --g-green:  #34A853;
}

/* Icon colour lives in each symbol now (base.html): every mark is drawn
   in the four brand codes directly, so nothing here tints them. */

/* ---------- what a tile is of ----------
   A country tile carries its own outline, a channel tile its monogram.
   ponytail: monogram, not the broadcaster's logo. Eighty real channel logos
   are licensed assets; when they are uploaded they replace this block. */

.outline {
  position: absolute; right: 18px; top: 52%;
  transform: translateY(-50%);
  width: 66px; height: 66px;
  fill: none; stroke: currentColor; stroke-width: 1.1;
  stroke-linejoin: round;
  opacity: 0.28; pointer-events: none;
}
.tile.t-cleared .outline { color: var(--g-green); }
.tile.t-at_risk .outline { color: var(--g-yellow); }
.tile.t-blocked .outline { color: var(--g-red); }
.tile.t-pending .outline, .tile.t-error .outline { color: var(--g-blue); }
.chan-mark {
  position: absolute; right: 18px; top: 52%;
  transform: translateY(-50%);
  font-family: var(--mono); font-size: 13px; letter-spacing: 0.16em;
  padding: 6px 9px; border-radius: 2px;
  color: var(--g-blue);
  border: 1px solid currentColor;
  opacity: 0.75;
}

/* ---------- tiles: keep the stats clear of the mark ---------- */
.tile-stats { padding-right: 86px; }

/* ---------- mission feed, row level ----------
   The feed is the crew talking. Each line gets a rail it hangs off, an
   outlined badge in its agent's colour, and a lit left edge on hover, so a
   40-line run reads as a stream instead of a log dump. */

.feed { position: relative; }
.feed::before {
  content: "";
  position: absolute; left: 104px; top: 0; bottom: 0;
  width: 1px; background: var(--line);
}
.line {
  grid-template-columns: 74px 152px 1fr;
  gap: 16px;
  padding: 5px 20px;
  border-left: 2px solid transparent;
  align-items: baseline;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.line .t { font-size: 11px; letter-spacing: 0.02em; }
.line .a {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid currentColor;
  border-radius: 2px;
  padding: 2px 7px;
  font-size: 9px; letter-spacing: 0.16em;
  opacity: 0.9;
  position: relative; z-index: 1;
  background: var(--well);
}
.line .a .ic { margin-right: 0; }
.line:hover { background: rgba(255, 255, 255, 0.028); border-left-color: currentColor; }
.line:hover .a { opacity: 1; }
.line .m { font-size: 12.5px; }
.line.err { border-left-color: var(--g-red); background: rgba(234, 67, 53, 0.06); }
.line.new { animation: land 0.6s ease; }
html[data-theme="studio"] .line:hover { background: rgba(23, 34, 44, 0.04); }
html[data-theme="studio"] .line.err { background: rgba(207, 48, 64, 0.06); }

@media (max-width: 900px) { .feed::before { display: none; } }

/* Badges hug their label, and the crew speaks in the same four colours as
   the icons: blue is the system telling you what it did, yellow is judging,
   red is fixing, green is confirming. The mark says which agent; the colour
   says what kind of move it was. */
.feed::before { display: none; }
.line .a { justify-self: start; }
.a-pipeline, .a-ingest, .a-analyst, .a-guard   { color: var(--g-blue) !important; }
.a-adjudicator, .a-remediator                  { color: var(--g-yellow) !important; }
.a-transcription, .a-publisher, .a-verifier    { color: var(--g-green) !important; }
.a-remediator                                  { color: var(--g-red) !important; }

/* nav tabs wear their screen's mark */
.tab { display: inline-flex; align-items: center; gap: 7px; }
.tab .ic { width: 14px; height: 14px; opacity: 0.85; }
.tab.on .ic, .tab:hover .ic { opacity: 1; }

/* ---------- the library ---------- */
.lib { display: grid; gap: 8px; }
.libcard { border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); }
.libcard > summary { display: flex; align-items: center; gap: 16px; padding: 14px 18px; cursor: pointer; list-style: none; }
.libcard > summary::-webkit-details-marker { display: none; }
.dimic { width: 30px; height: 30px; flex: none; }
.libname { display: grid; gap: 3px; }
.libname .t { font-size: 15px; text-transform: capitalize; letter-spacing: -0.01em; }
.libname .b { font-size: 12.5px; color: var(--ink-3); }
.libcount { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.libcount .mono { font-size: 12px; color: var(--ink-2); min-width: 22px; text-align: right; }
.libcard table.grid { border-top: 1px solid var(--line); }
.libcard table.grid th { padding-top: 12px; }

/* ---------- mission feed: a row per stage ----------
   Consecutive events from one agent are one stage. The row carries its mark,
   its latest line and a bar: animated while the stage is live, solid once it
   has moved on. Click to open the individual events. */

.grp { position: relative; border-bottom: 1px solid var(--line); }
.grp:last-child { border-bottom: none; }
.grp > summary {
  display: grid;
  /* icon · the sentences · agent · count · clock. The text block is second
     in the markup now, so it takes the 1fr track -- it was landing in the
     118px column meant for the agent name and wrapping to a ribbon while
     half the row sat empty. */
  grid-template-columns: 18px 1fr 118px auto auto;
  gap: 14px; align-items: start;
  padding: 13px 20px 14px;
  cursor: pointer; list-style: none;
}
.grp > summary::-webkit-details-marker { display: none; }
.grp > summary:hover { background: rgba(255, 255, 255, 0.03); }
html[data-theme="studio"] .grp > summary:hover { background: rgba(23, 34, 44, 0.04); }
.grp .ga { font-family: var(--mono); font-size: 10px; letter-spacing: 0.16em;
  text-transform: uppercase; padding-top: 3px; }
.grp .gm { font-family: var(--mono); font-size: 12.5px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.grp[open] .gm { color: var(--ink-3); }
.grp .gc { font-size: 11px; color: var(--ink-3); padding-top: 3px; white-space: nowrap; }

.grp .gt { font-size: 11px; color: var(--ink-3); padding-top: 3px; }
.glines { padding: 2px 20px 14px 150px; }
.glines .line { grid-template-columns: 74px 1fr; padding: 3px 0; border-left: none; }
.glines .line:hover { background: none; }

.gbar { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; overflow: hidden; }
.gbar i { display: block; height: 100%; width: 100%; background: currentColor; opacity: 0.5; }
.grp.live .gbar i { width: 34%; animation: sweep 1.5s ease-in-out infinite; opacity: 1; }
.grp.err .gbar i { background: var(--g-red); opacity: 1; }
@keyframes sweep {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(330%); }
}

/* ---------- the fix picker ----------
   Remediation is two decisions an operator owns: what should change, and
   how much you are willing to spend changing it. Both are on the button. */

.fixer { display: block; position: relative; }
.fixer > summary { list-style: none; cursor: pointer; }
.fixer > summary::-webkit-details-marker { display: none; }
.fixer[open] > summary { border-color: var(--g-blue); color: var(--ink); }
.fixer > summary { display: inline-flex; align-items: center; gap: 6px; }
.fixer > summary::after { content: "\203A"; font-size: 17px; line-height: 1;
  transition: transform .15s ease; }
.fixer[open] > summary::after { transform: rotate(90deg); }

/* ---------- the panel, one row wide ----------
   It used to open inside the findings table's last cell, which is 12% of
   the table and sized for the word "open". Five priced methods with a
   sentence each could not fit: the panel was clipped at the table's right
   edge, the price column -- the one number the decision turns on -- fell
   off it entirely, and the rule cell opposite was squeezed until
   "AE-ALC-01" broke across three lines.

   So the panel is its own row spanning all eight columns, revealed by the
   <details> still sitting in that cell. :has() means no JavaScript is
   involved: with scripting off the disclosure works exactly as before. */
tr.fixrow > td { display: none; }
tr.frow:has(.fixer[open]) + tr.fixrow > td {
  display: table-cell; padding: 0 12px 16px;
}
.fixpanel {
  padding: 16px 18px 14px; text-align: left;
  border: 1px solid var(--g-blue); border-radius: var(--radius);
  background: var(--well);
  box-shadow: 0 10px 30px rgba(23, 34, 44, 0.08);
}
.fp-head { margin-bottom: 14px; }
.fp-title { font-size: 14px; color: var(--ink-2); }
.fp-title b { color: var(--ink); font-family: var(--mono); font-weight: 600; }
/* what and how, side by side: the pairing IS the decision. The intents
   are short labels and the methods carry prices, hence the 2:3 split. */
.fp-cols { display: grid; grid-template-columns: minmax(220px, 2fr) 3fr; gap: 12px 26px; }
.fp-col { min-width: 0; }
.fp-n {
  display: inline-grid; place-items: center; width: 17px; height: 17px;
  margin-right: 7px; border-radius: 50%; background: var(--g-blue);
  color: #fff; font-size: 10px; letter-spacing: 0;
}
/* The scope caveat, said once above the methods it applies to, instead of
   repeated verbatim under every one of them. */
.fp-caveat {
  display: flex; gap: 8px; margin: 0 0 8px; padding: 8px 10px;
  border: 1px solid rgba(251, 188, 5, 0.5); border-radius: var(--radius);
  background: rgba(251, 188, 5, 0.09);
  font-size: 12.5px; line-height: 1.45; color: var(--ink-2);
}
/* amber on the dark ground by default, darkened for the light one --
   #8a6d00 on the dark theme's panel is unreadable, which is the mistake
   the .fix-why.caveat rule above already had to correct for */
.fp-caveat .ic { flex: none; width: 15px; height: 15px; margin-top: 1px; color: #FBBC05; }
.fp-tag {
  margin-left: 8px; padding: 1px 6px; border-radius: 3px;
  background: rgba(251, 188, 5, 0.18); color: #FBBC05;
  font-family: var(--mono); font-size: 9.5px; font-weight: 500;
  letter-spacing: 0.06em; text-transform: uppercase; vertical-align: 1px;
}
html[data-theme="studio"] .fp-caveat .ic { color: #B07D00; }
html[data-theme="studio"] .fp-tag { color: #8a6d00; }
.fix-h { display: flex; align-items: center; margin: 0 0 8px; }
.fix-h + .fix-opt { margin-top: 0; }
.fix-opt {
  display: flex; align-items: center; gap: 10px; max-width: 100%;
  padding: 9px 11px; margin-bottom: 6px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); cursor: pointer; font-size: 13.5px;
}
.fix-opt:hover { border-color: var(--line-2); }
.fix-opt input { accent-color: var(--g-blue); }
/* The method's own mark. Five choices described in three lines each is a
   wall of prose; the glyph is what tells them apart at a glance. */
.fix-opt .ic-m { width: 22px; height: 22px; }
.fix-opt.off .ic-m { filter: grayscale(1); }
.fix-opt:has(input:checked) { border-color: var(--g-blue); background: rgba(66, 133, 244, 0.08); }
.fix-opt.off { opacity: 0.45; cursor: not-allowed; }
/* min-width: 0 or the grid child refuses to shrink and its text runs
   straight out of the panel. */
.fix-m { display: grid; gap: 2px; min-width: 0; }
.fix-m b { font-weight: 600; }
.fix-why { font-size: 12.5px; color: var(--ink-2); line-height: 1.4; }
/* Scope says this method is a poor fit. It is advice, not a refusal: the
   option stays selectable and the sentence says what to expect. */
.fix-why.caveat { color: #FBBC05; display: block; margin-top: 3px; }
/* the "that option cannot run" line under the picker, written on click */
.fix-refused { color: var(--blocked); font-size: 12px; margin: 10px 0 0; }
.fix-cost { margin-left: auto; text-align: right; display: grid; gap: 2px; white-space: nowrap; }
.fix-cost b { font-size: 13.5px; color: var(--ink); }
.fix-cost span { font-size: 11px; color: var(--ink-3); }
.fix-foot { display: flex; align-items: center; gap: 12px; margin-top: 14px;
  padding-top: 12px; border-top: 1px solid var(--line); }
.fix-foot .btn { margin-left: auto; }



/* nav on two rows: the screens, then the markets */
.runnav .wrap.screens { min-height: 44px; border-bottom: 1px solid var(--line); }
.runnav .wrap.markets-row { min-height: 42px; }
.runnav .wrap.markets-row .tab { padding: 11px 12px 10px; font-size: 10px; }

/* how structural a finding is */
.chip.sc-frame   { color: var(--g-green); border-color: rgba(52, 168, 83, 0.4); }
.chip.sc-segment { color: var(--g-blue); border-color: rgba(66, 133, 244, 0.4); }
.chip.sc-scene   { color: var(--g-yellow); border-color: rgba(251, 188, 5, 0.4); }
.chip.sc-concept { color: var(--g-red); border-color: rgba(234, 67, 53, 0.45); background: rgba(234, 67, 53, 0.08); }
/* the analyst's reading of this finding, at the top of the panel: full
   width now, so it is a paragraph rather than a clipped column */
.fix-verdict { margin: 6px 0 0; max-width: 104ch;
  font-size: 13px; color: var(--ink-2); line-height: 1.5; }

/* a row with work in flight */
.spin {
  display: inline-block; width: 9px; height: 9px; margin-right: 7px;
  border: 1.5px solid currentColor; border-right-color: transparent;
  border-radius: 50%; animation: spin 0.9s linear infinite;
  vertical-align: -1px;
}
@keyframes spin { to { transform: rotate(360deg); } }
.btn[data-busy] { color: var(--g-blue); border-color: var(--g-blue); opacity: 1; }

/* ---------- the archive: list or cards ---------- */
.runlist.as-rows .runrow { padding: 9px 16px; grid-template-columns: 200px 1fr 120px 300px; gap: 14px; }
.runlist.as-rows .runrow .flags { flex-wrap: nowrap; overflow: hidden; }
.runlist.as-rows .runrow .flags .pill { flex: none; }
.runlist:not(.as-rows) { grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); display: grid; gap: 12px; }
.runlist:not(.as-rows) .runrow { grid-template-columns: 1fr; gap: 10px; padding: 18px; align-items: start; }
.runlist:not(.as-rows) .runrow .flags { justify-content: flex-start; }

/* the picker's rows carry what they are: a country by its outline, a
   channel by the film mark, the layers above by their own */
.market-pick .ic.lvl { width: 17px; height: 17px; }
.market-pick .ic.lvl.country { fill: none; stroke: var(--g-blue); stroke-width: 2.4; opacity: 0.85; }
.market-pick:has(input:checked) .ic.lvl.country { stroke: var(--signal); opacity: 1; }

/* cleared to air, with things still on the table: not green, because green
   means nothing is open, and not yellow, because yellow means a policy
   finding is holding the market. Blue is the system telling you something. */
.s-noted { color: var(--g-blue); border-color: rgba(66, 133, 244, 0.42); }
.tile.t-noted::before { background: var(--g-blue); }
.tile.t-noted { background: linear-gradient(180deg, rgba(66, 133, 244, 0.05), var(--panel) 60%); }
.verdict-strip .v-noted { background: var(--g-blue); }
.tile.t-noted .outline { color: var(--g-blue); }

/* ---------- how far along a clearance is ---------- */
.progress { margin-top: 24px; }
.progress-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 8px; }
.progress-head .mono { font-size: 12px; color: var(--ink); }
.progress-track {
  height: 6px; border-radius: 3px; overflow: hidden;
  background: var(--well); border: 1px solid var(--line);
}
/* The bar the whole run is watched through, in the same four colours as
   everything else that is working -- and flowing, so a stage that sits at
   the same percentage for a minute (the analyst reads every shot, and that
   is most of the wall clock) still looks alive rather than stuck. The
   gradient is twice the bar's width and slides; the width transition is
   the actual progress. */
@keyframes spk-flow { to { background-position: -200% 0; } }
.progress-track span {
  display: block; height: 100%;
  background: linear-gradient(90deg,
    #4285F4, #EA4335, #FBBC05, #34A853, #4285F4);
  background-size: 200% 100%;
  animation: spk-flow 2.4s linear infinite;
  transition: width 0.6s ease;
}
/* A run that is over is not flowing anywhere. */
.progress-track span.done { animation: none;
  background: linear-gradient(90deg, var(--g-blue), var(--g-green)); }
@media (prefers-reduced-motion: reduce) {
  .progress-track span { animation: none; }
}

/* tile stats keep clear of the mark without stacking into a column */
.tile-stats { padding-right: 70px; gap: 14px; flex-wrap: wrap; }
.outline { width: 58px; height: 58px; right: 14px; }

/* ---------- frame board ----------
   The film as the analyst saw it: one still per observation, the sentence
   it wrote beside it, and what each market did with that sentence. */

.fb { display: grid; gap: 12px; }
/* the scenescroll: one opening still per scene, flagged ones badged, each
   an anchor jump to its card. Scrolls sideways inside its own strip. */
.scenescroll { display: flex; gap: 8px; overflow-x: auto; padding: 2px 2px 12px; }
.ss { position: relative; flex: none; width: 104px; display: grid; gap: 3px; text-decoration: none; }
.ss img, .ss-blank { width: 104px; height: 58px; object-fit: cover; display: block; border: 1px solid var(--line); border-radius: 2px; }
.ss-blank { display: grid; place-items: center; border-style: dashed; font-size: 9px; }
.ss.hit img { border-color: var(--g-yellow); box-shadow: 0 0 0 1px var(--g-yellow); }
.ss:hover img { border-color: var(--signal); box-shadow: 0 0 0 1px var(--signal); }
.ss-t { font-size: 10px; color: var(--ink-3); }
.ss-n { position: absolute; top: 3px; right: 3px; background: var(--blocked); color: #fff; font-size: 10px; line-height: 1.5; padding: 0 4px; border-radius: 2px; }
.sc { scroll-margin-top: 72px; }
/* scene header rows inside the market room's findings table */
.scene-row td { border-top: 2px solid var(--line-2); padding: 14px 14px 6px; }
.scene-row .label + .fb-time { margin: 0 10px; }
.scene-row { scroll-margin-top: 72px; }
/* scene cards: one per shot, opened and closed by its first and last kept
   frame, every observation of the shot inside. A three second smoking scene
   used to be a scroll of near-identical frames, one per observation. */
.sc {
  display: grid; grid-template-columns: 280px 1fr; gap: 16px;
  padding: 14px; border: 1px solid var(--line); border-radius: 4px;
  background: var(--panel);
}
.sc.hit { border-left: 3px solid var(--g-yellow); }
/* Opens and closes, side by side. Stacked, a scene card was as tall as two
   frames and the reading beside it floated in the middle of the whitespace;
   the pair is also how you actually read a shot -- what it started as, what
   it ended as. */
.sc-frames { display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
  align-content: start; }
.sc-frames:has(> :only-child) { grid-template-columns: 1fr; }
.sc-read { display: grid; gap: 10px; align-content: start; min-width: 0; }
.sc-head { padding-bottom: 4px; }
.sc-obs { border-top: 1px solid var(--line); padding-top: 9px; display: grid; gap: 7px; }
@media (max-width: 900px) {
  .sc { grid-template-columns: 1fr; }
  .sc-frames { grid-template-columns: repeat(auto-fit, minmax(150px, 240px)); }
}
.fb-row {
  display: grid; grid-template-columns: 232px 1fr; gap: 20px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); padding: 14px;
}
.fb-row.hit { border-left: 3px solid var(--g-yellow); }
.fb-frame { display: grid; gap: 7px; }
.fb-frame img { width: 100%; display: block; border: 1px solid var(--line); border-radius: 2px; }
.fb-noframe { display: grid; place-items: center; height: 118px; border: 1px dashed var(--line-2); border-radius: 2px; }
.fb-time { font-size: 11px; color: var(--ink-3); }
.fb-read { display: grid; gap: 9px; align-content: start; min-width: 0; }
.fb-top { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.fb-statement { margin: 0; font-size: 14.5px; color: var(--ink); max-width: 88ch; }
.fb-findings { display: grid; gap: 6px; }
.fb-finding {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--well);
}
.fb-finding:hover { border-color: var(--g-blue); }
.fb-sev { font-size: 12px; color: var(--ink); }
.fb-why { font-size: 12.5px; color: var(--ink-2); flex: 1 1 320px; min-width: 0; }
.fb-clean { display: flex; align-items: center; gap: 8px; opacity: 0.75; }

@media (max-width: 900px) { .fb-row { grid-template-columns: 1fr; } }

/* ---------- agent mode ----------
   The operator types on the left; whatever the agent opens fills the right.
   Two panes, one screen height, nothing scrolls the page itself. */

/* The screen is one viewport: topbar and console nav are fixed chrome
   (122px together), everything else -- hero, pointers, panes -- divides
   what is left. The panes were on a hardcoded calc(100vh - 132px) written
   when they were the only thing on this page, so once a hero and three
   pointers appeared above them the ask box fell below the fold. */
.agentscreen {
  display: flex; flex-direction: column; gap: 0;
  /* The conversation is the screen. It was sharing a viewport with a hero,
     three pointers and a stats row, and what gave way was the log. */
  height: calc(100vh - 96px); min-height: 620px;
}
.agent {
  /* The conversation column carries three numbered lanes of pre-filled
     questions; at 420px the third pill in a lane fell off the edge, and
     the pane on the right had room to spare. */
  display: grid; grid-template-columns: minmax(360px, 480px) 1fr;
  gap: 14px; flex: 1; min-height: 0; margin: 12px 0 14px;
}
.agent-side, .agent-view {
  display: flex; flex-direction: column; min-height: 0;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); overflow: hidden;
}
.agent-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 11px 14px; border-bottom: 1px solid var(--line); background: var(--ground-2);
}
.agent-head .label { display: inline-flex; align-items: center; gap: 8px; }

.agent-log { flex: 1; min-height: 150px; overflow-y: auto; padding: 14px;
  display: grid; gap: 12px; align-content: start; }
/* Before the first turn there is nothing to scroll, and a log that claims
   the leftover height put a hand's width of blank panel between the
   greeting and the buttons under it. Until a conversation exists, the log
   is only as tall as the sentence in it and the starters sit right below;
   the moment a second message lands it grows and scrolls as before. */
.agent-log:has(> .ag-msg:only-child) { flex: 0 0 auto; overflow: visible; }
.agentscreen:has(.agent-log > .ag-msg:only-child) { height: auto; min-height: 0; }
.agent:has(.agent-log > .ag-msg:only-child) { align-items: start; }
.agent:has(.agent-log > .ag-msg:only-child) .agent-canvas { min-height: 420px; }
.ag-msg { display: grid; grid-template-columns: 18px 1fr; gap: 11px; align-items: start; }
.ag-msg .ic { margin-top: 2px; }
.ag-body { min-width: 0; }
.ag-body p { margin: 0 0 8px; font-size: 13.5px; line-height: 1.5; color: var(--ink-2); }
.ag-body p:last-child { margin-bottom: 0; }
.ag-you .ag-body p { color: var(--ink); }
.ag-agent .ag-body p { color: var(--ink-2); }
.ag-calls { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 7px; }
.ag-call {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.1em;
  text-transform: uppercase; padding: 3px 7px; border-radius: 2px;
  border: 1px solid var(--line-2); color: var(--ink-3);
}
.ag-err { color: var(--g-red) !important; }
.ag-think { display: inline-flex; align-items: center; gap: 8px; color: var(--ink-3); font-size: 13px; }

.agent-suggest { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 14px 12px; }
.sugg {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; padding: 5px 9px; cursor: pointer;
  border: 1px solid var(--line-2); border-radius: 999px;
  background: var(--well); color: var(--ink-2);
}
.sugg:hover { border-color: var(--g-blue); color: var(--ink); }
.sugg .ic { width: 13px; height: 13px; }

.agent-ask { display: flex; gap: 8px; padding: 12px 14px; border-top: 1px solid var(--line); }
.agent-ask .urlin { flex: 1; }
.agent-ask .btn { flex: none; }

.agent-canvas { flex: 1; min-height: 0; background: var(--well); }
.agent-canvas iframe { width: 100%; height: 100%; border: 0; display: block; background: var(--well); }

@media (max-width: 1100px) {
  .agent { grid-template-columns: 1fr; height: auto; }
  .agent-canvas { height: 70vh; }
}

/* the way home, in the topbar on every screen */
.homebtn { display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 11px; border: 1px solid var(--line-2); border-radius: var(--radius);
  background: var(--panel); color: var(--ink-2); text-decoration: none;
  font-family: var(--mono); font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; }
.homebtn:hover { color: var(--ink); border-color: var(--signal); }
.homebtn.on { color: var(--ink); border-color: var(--signal); }
.homebtn .ic { width: 15px; height: 15px; }
@media (max-width: 900px) { .homebtn span { display: none; } }

/* the mode switch, next to the style modes */
.mode-switch { display: inline-flex; border: 1px solid var(--line-2); border-radius: var(--radius); overflow: hidden; }
.mode-switch a {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.14em;
  text-transform: uppercase; padding: 5px 10px; color: var(--ink-3);
}
.mode-switch a + a { border-left: 1px solid var(--line-2); }
.mode-switch a:hover { color: var(--ink); }
.mode-switch a.on { color: var(--ink); background: rgba(66, 133, 244, 0.18); }
.mode-switch .ic { width: 12px; height: 12px; }

/* ---------- phones ----------
   Everything above is built for a desk. On a phone the same screens have to
   work one-handed: nothing horizontally scrolls the page, the tables become
   rows, the two-pane agent becomes one pane with the answer under the ask,
   and every control clears 44px. */

@media (max-width: 720px) {
  .wrap { padding: 0 14px; }
  body { font-size: 14.5px; }

  /* the top bar wraps instead of squeezing */
  .topbar .wrap { height: auto; padding-top: 10px; padding-bottom: 10px;
                  flex-wrap: wrap; gap: 10px; }
  .topbar-right { margin-left: 0; width: 100%; gap: 10px; flex-wrap: wrap; }
  .brand-sub { display: none; }
  .theme-switch .tbtn, .mode-switch a, .view-switch .tbtn { padding: 9px 11px; }

  /* Market rows scroll sideways on their own, the page never does: there
     can be dozens of market codes and stacking those would be a screen of
     nav before any content. */
  .runnav .wrap { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .runnav .wrap::-webkit-scrollbar { display: none; }
  .tab { white-space: nowrap; padding: 13px 11px; }


  .headline { font-size: clamp(24px, 8vw, 34px); }
  .specs { grid-template-columns: 1fr 1fr; }
  .tiles { grid-template-columns: 1fr; }
  .tile-stats { padding-right: 62px; }

  /* findings: a table is a column of rows */
  table.grid, table.grid tbody, table.grid tr, table.grid td { display: block; width: 100%; }
  table.grid thead { display: none; }
  table.grid tr { border-bottom: 1px solid var(--line); padding: 12px 0; }
  table.grid td { border: none; padding: 4px 12px; }
  .as-list .evidence { width: 100%; margin: 8px 0 0; }
  .as-list .rationale, .as-list td > div[style*="margin-top:8px"] { display: block; }

  /* the fix picker: one column, and the panel row is a block like every
     other cell in the stacked table */
  .fixpanel { padding: 12px 12px 10px; box-shadow: none; }
  .fp-cols { grid-template-columns: 1fr; gap: 4px; }
  tr.frow:has(.fixer[open]) + tr.fixrow > td { display: block; padding: 4px 12px 12px; }
  .fix-opt { align-items: flex-start; }
  .fix-cost { margin-left: 0; text-align: left; }
  .btn, .sugg, .market-pick { min-height: 44px; }

  /* The two matrices: on a phone the axis is the icon, not the words.
     A 150px column of dimension names left 340px for eight scenes of
     squares, which is a chart you cannot read to save a label you can
     read from its mark. The names stay in the title attribute. */
  .mg { grid-template-columns: 30px 1fr; column-gap: 6px; }
  .mg-dim span, .mx2-dim span { display: none; }
  .mg-dim, .mx2-dim { justify-content: center; padding-right: 0; }
  .mx2 { grid-template-columns: 30px repeat(var(--cols), minmax(0, 1fr)); }
  .mg-corner, .mx2-corner { font-size: 8px; overflow: hidden; }

  /* timeline lanes stay readable */
  .tl-row { grid-template-columns: 52px 1fr; gap: 8px; }
  .segcard { width: min(84vw, 320px); }

  /* frame board stacks */
  .fb-row { grid-template-columns: 1fr; }
  .fb-frame img { max-width: 320px; }

  /* mission feed: no fixed columns */
  .grp > summary { grid-template-columns: 1fr auto; gap: 8px 12px; }
  .grp > summary .gtext { grid-column: 1 / -1; }
  .grp > summary .gm { white-space: normal; }
  .grp .gt { display: none; }
  .glines { padding-left: 20px; }
  .feed { height: auto; max-height: 70vh; }

  /* agent mode: ask on top, answer underneath */
  .agent { grid-template-columns: 1fr; height: auto; gap: 10px; }
  .agent-log { max-height: 46vh; }
  .agent-canvas { height: 62vh; }
  .agent-ask { position: sticky; bottom: 0; background: var(--panel); }

  /* archive rows */
  .runlist.as-rows .runrow { grid-template-columns: 1fr; gap: 8px; }
  .runrow .flags { justify-content: flex-start; }

  /* cutting room and embeds */
  .pair, .change-body, .embed-grid, .launcher { grid-template-columns: 1fr; }
  .levels .markets { padding: 0 8px 10px; }
  .lv-blurb { display: none; }
}

@media (max-width: 420px) {
  .specs { grid-template-columns: 1fr; }
  .fix-cost b { font-size: 12px; }
}

/* one nav row per jurisdiction level, labelled */
.runnav .wrap.markets-row { min-height: 40px; gap: 2px; }
.runnav .wrap.markets-row + .markets-row { border-top: 1px solid var(--line); }
/* one strip, in ladder order */
.runnav .markets-row.ladder { flex-wrap: wrap; align-items: center;
  row-gap: 0; }
.lvl-sep { width: 1px; align-self: stretch; margin: 8px 8px;
  background: var(--line); flex: none; }
/* a country standing for its broadcasters, and the list it opens */
.mkfold { position: relative; }
.mkfold > summary { list-style: none; cursor: pointer; display: inline-flex;
  align-items: center; gap: 6px; }
.mkfold > summary::-webkit-details-marker { display: none; }
.mkfold[open] > summary { color: var(--ink); }
.mkfold-list { position: absolute; top: 100%; left: 0; z-index: 30;
  display: flex; flex-direction: column; min-width: 148px; padding: 4px;
  border: 1px solid var(--line-2); border-radius: var(--radius);
  background: var(--panel); box-shadow: 0 10px 26px rgba(0, 0, 0, .16); }
.mkfold-list .tab { padding: 7px 9px; }
.lvl-tag {
  flex: none; width: 88px; padding-right: 10px;
  font-size: 9px; letter-spacing: 0.18em; color: var(--ink-3);
}
.runnav .markets-row .tab .ic.country { stroke: var(--g-blue); stroke-width: 2.4; fill: none; }
.runnav .markets-row .tab.on .ic.country { stroke: var(--signal); }
@media (max-width: 720px) {
  .lvl-tag { width: auto; padding-right: 6px; }
}
.agent-shot { display: block; width: 100%; height: auto; }

/* ---------- the screen tabs read as navigation, not as fine print ----------
   The five screens are the main thing an operator moves between, so their
   row is sized like a heading. The market rows underneath stay small: there
   can be thirty of them and they are a filter, not a destination. */

.runnav .wrap.screens { min-height: 62px; gap: 6px; }
.runnav .wrap.screens .tab {
  font-size: 15px;
  letter-spacing: 0.10em;
  padding: 19px 16px 17px;
  gap: 11px;
}
.runnav .wrap.screens .tab .ic { width: 26px; height: 26px; }
.runnav .wrap.screens .tab.on { border-bottom-width: 3px; }

@media (max-width: 720px) {
  .runnav .wrap.screens { min-height: 54px; }
  .runnav .wrap.screens .tab { font-size: 13.5px; padding: 15px 12px 13px; gap: 9px; }
  .runnav .wrap.screens .tab .ic { width: 21px; height: 21px; }
}

/* "Add analysis": the market picker again, on a run that already has
   observations. Same controls as the front door, folded away by default. */
.addmore > summary { display: inline-flex; align-items: center; gap: 8px; }
.addmore .levels { margin-top: 12px; }
.addmore .fix-foot { margin-top: 12px; }

/* The triggering frame is the evidence, so it gets a column of its own
   rather than being tucked under the rationale at thumbnail size: the
   reasoning reads down the left, the picture it is about sits beside it. */
.grid td.ev-cell { width: 34%; min-width: 260px; vertical-align: top; padding-top: 14px; }
.grid td.ev-cell .evidence.big { display: block; margin: 0; }
.grid td.ev-cell .evidence.big img {
  width: 100%; max-width: 460px; height: auto; border-radius: 6px; display: block;
}
.grid td:first-child { width: 34%; }
@media (max-width: 900px) {
  /* stacked on a phone: the frame goes back under its reasoning */
  .grid td.ev-cell, .grid td:first-child { width: auto; min-width: 0; }
  .grid td.ev-cell .evidence.big img { max-width: 100%; }
}

/* ---------- the icon hero ----------
   The console already speaks in icons: a crew, a set of verdicts, and the
   eighteen dimensions every market is judged on. Showing that vocabulary once,
   up front, is the cheapest way to say what the thing actually does. It is a
   legend, not decoration, so it stays quiet: one weight, one size, no colour
   beyond what the icons carry themselves. */
.iconhero { display: flex; flex-wrap: wrap; gap: 24px 44px; padding: 4px 0 2px; }
.ih-group { display: flex; flex-direction: column; gap: 11px; min-width: 0; }
.ih-title {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--ink-3);
}
.ih-row { display: flex; flex-wrap: wrap; gap: 12px 10px; }
.ih-item {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  width: 62px; opacity: 0.82; transition: opacity 0.14s ease;
}
.ih-item:hover { opacity: 1; }
.ih-item .ic { width: 18px; height: 18px; }
.ih-item span {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-3);
  text-align: center; line-height: 1.35;
}
/* the dimensions are the long tail, so they get to be denser */
.ih-group.wide .ih-row { max-width: 760px; }

/* the accordion carries the level's own mark next to the plus */
.level > summary .ic.lvl { align-self: center; }

/* agent mode: the empty canvas is the hero, and it disappears the moment the
   agent opens something real in its place */
.agent-hero { display: flex; flex-direction: column; gap: 13px; align-items: center; margin-top: 26px; }
/* a fixed four-wide grid, so the last item never orphans onto a row of its own
   the way a wrapping strip lets it */
.agent-hero .ih-row {
  display: grid; grid-template-columns: repeat(4, 66px);
  justify-content: center; align-items: start; gap: 16px 12px;
}

/* A frame of the advert beside its id: a list of run_ hashes says nothing
   about which commercial is which. Served from /runs/{id}/poster.jpg, a
   cached ~13KB JPEG rather than the 1.3MB evidence PNGs, because this list
   shows a dozen at once. onerror removes it, so a run whose asset is gone
   degrades to the text row it used to be. */
/* Twice the size it was. The card is about a film and the thumbnail is
   the only part of it that shows one; at 64x36 it was a colour swatch. */
.runthumb {
  width: 128px; height: 72px; object-fit: cover; border-radius: 3px;
  border: 1px solid var(--line-2); background: var(--well); flex: none;
}
.runlist.as-rows .runrow { align-items: center; gap: 12px; }
.runlist:not(.as-rows) .runthumb { width: 100%; margin-bottom: 0; }

/* The line under the progress bar: what an agent said most recently. It
   fades in on change so a new message is noticed without the eye having to
   watch for it, and reserves its height so the bar never jumps. */
.progress-tick {
  min-height: 16px; margin-top: 8px; font-family: var(--mono);
  font-size: 11.5px; color: var(--ink-3); letter-spacing: 0.02em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.progress-tick .a-pipeline, .progress-tick [class^="a-"] {
  text-transform: uppercase; font-size: 9.5px; letter-spacing: 0.14em;
  margin-right: 8px;
}
.progress-tick.flash { animation: tickin 420ms ease-out; }
@keyframes tickin {
  from { opacity: 0; transform: translateY(3px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .progress-tick.flash { animation: none; } }

/* ---------- the main tabs on a phone ----------
   They stacked here for a while: one per row, full width, nothing to
   scroll, because a sideways-scrolling nav hid the last tabs off the edge
   of the screen where nobody found them. Seven console tabs and six run
   tabs made that thirteen rows -- two screens of navigation above the
   thing being navigated to, which is the worse failure.

   So they scroll again, and the two things that made scrolling bad are
   fixed instead: the row fades at both ends so it is visibly cut off, and
   customs.js scrolls the tab you are ON into the middle, so the row can
   never claim you are somewhere you are not. */
@media (max-width: 720px) {
  .runnav .wrap.screens {
    gap: 0; min-height: 0; padding-top: 2px; padding-bottom: 2px;
  }
  .runnav .wrap.screens .tab { padding: 13px 10px; font-size: 12px; }
  .runnav .wrap.screens .tab .ic { width: 18px; height: 18px; }
}

/* The mission feed's two tabs: what is happening, and what came out of it.
   Same control as the theme/view switches so the console has one idiom. */
.mtabs { display: inline-flex; border: 1px solid var(--line-2); border-radius: var(--radius); overflow: hidden; }
.mtabs .tbtn {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.13em;
  text-transform: uppercase; padding: 9px 14px; border: none;
  background: transparent; color: var(--ink-3); cursor: pointer;
}
.mtabs .tbtn + .tbtn { border-left: 1px solid var(--line-2); }
.mtabs .tbtn:hover { color: var(--ink); }
.mtabs .tbtn.on { color: var(--ink); background: rgba(66, 133, 244, 0.14); }
.mtabs .tbtn .ic { width: 15px; height: 15px; }

/* The two frames Veo was handed, and the seconds it made between them. */
.anchors { padding: 14px 0 2px; }
.anchors > .label { display: block; margin-bottom: 8px; }
.anchors .frame img { border-color: var(--signal); }
.genclip { padding: 14px 0 2px; }
.genclip > .label { display: block; margin-bottom: 8px; }
.genclip video { width: 100%; max-width: 640px; border-radius: 6px; display: block; background: #000; }

/* The green rectangle over an evidence frame.

   An OVERLAY, deliberately. The PNG underneath is the file a remediation
   edits and the frame Veo is anchored on, so anything drawn into it would
   become part of the commercial. The viewBox is 0-1000 in both axes with
   preserveAspectRatio:none, so one normalised box fits any frame size. */
.fb-shot { position: relative; display: block; line-height: 0; }
.fb-box {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none;
}
.fb-box rect {
  fill: none; stroke: #34A853; stroke-width: 6;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.65));
}
.fb-boxtoggle {
  display: flex; align-items: center; gap: 7px; margin-top: 7px;
  cursor: pointer; user-select: none;
}
.fb-boxtoggle input { accent-color: #34A853; cursor: pointer; }
.fb-boxtoggle .box-note { color: var(--ink-3); font-size: 9.5px; }

/* Card view: a card is a block, not a stretched column.

   The grid makes every card in a row as tall as the tallest, and a run
   cleared for forty channels carries forty pills. That pushed the id,
   filename and date of its neighbours to the far corners of an empty
   card. The content now sits at the top and the pill list is capped and
   scrolls inside the card instead of setting the row's height. */
.runlist:not(.as-rows) .runrow {
  align-content: start;
  grid-template-rows: auto auto auto auto 1fr;
}
.runlist:not(.as-rows) .runrow .flags {
  max-height: 132px; overflow: hidden; align-content: flex-start;
  -webkit-mask-image: linear-gradient(to bottom, #000 96px, transparent);
  mask-image: linear-gradient(to bottom, #000 96px, transparent);
}
.runlist:not(.as-rows) .runrow .id { order: 2; }
.runlist:not(.as-rows) .runrow .asset { order: 3; font-size: 15px; }
.runlist:not(.as-rows) .runrow .when { order: 4; }
.runlist:not(.as-rows) .runrow .flags { order: 5; margin-top: 2px; }
.runlist:not(.as-rows) .runthumbwrap { order: 1; }

/* The severity profile inside a run card.

   Grafana holds the numbers (Mimir's customs_risk on the run's own mapped
   clock); the app draws them. Not an iframe, because Grafana Cloud sends
   frame-ancestors 'none'; not a rendered PNG, because a card needs a
   chart that is sharp at any width and follows the current style mode. */
.runspark {
  display: block; width: 100%; height: 44px; margin: -2px 0 8px;
  opacity: .95;
}
.runlist.as-rows .runspark { width: 120px; height: 26px; margin: 0; flex: none; }

/* Where this market's risk sits across the film. Same source as the run
   card: Mimir's customs_risk on the run's own mapped clock, drawn here
   rather than embedded, so it is sharp at any tile width and follows the
   style mode. A market with no series simply has no chart. */
.tilespark { display: block; width: 100%; height: 60px; margin: 8px 0 2px; }
.tile:hover .tilespark { opacity: 1; }

/* What a market objected to, as the taxonomy's own icons.

   The tile already carried a COUNT, which tells you a market is unhappy
   without telling you what about. These are the same 18 dimension icons
   the frame board and the library use, so one glyph means one thing
   everywhere in the product. Capped at six with a +n overflow: past that
   they stop being scannable and become a texture. */
.tile-kinds {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  margin: 10px 0 2px; min-height: 22px;
}
.tile-kinds .ic.kind { width: 21px; height: 21px; flex: none; }
.tile-kinds .more {
  font-size: 10px; letter-spacing: .08em; color: var(--ink-3);
  align-self: center;
}

/* Room for the icons and the stat card without anything colliding. The
   tile was sized for a code, a name and three stat lines; it now carries
   two more blocks. */
.tiles { grid-template-columns: repeat(auto-fill, minmax(310px, 1fr)); gap: 14px; }
.tile { min-height: 340px; padding: 18px 18px 16px; }
.tile .tile-name { margin-bottom: 2px; }
.tile .tile-stats { margin-top: 4px; }
.tile .tile-go { margin-top: auto; padding-top: 10px; }
@media (max-width: 720px) {
  .tiles { grid-template-columns: 1fr; }
  .tile { min-height: 0; }
}

/* The agent's answer, in the console's own language.

   A reply naming a market, a rule and a timecode used to arrive as one
   undifferentiated grey line, while three feet away those same three
   things are a country chip, a legal chip and a mono timecode. These
   rules make a sentence and a tile speak the same dialect. */
.ag-body p { margin: 0 0 9px; line-height: 1.55; }
.ag-body p:last-child { margin-bottom: 0; }
.ag-body ul, .ag-body ol { margin: 0 0 10px; padding-left: 20px; line-height: 1.55; }
.ag-body li { margin: 0 0 6px; }
.ag-body li:last-child { margin-bottom: 0; }
.ag-body strong { font-weight: 650; color: var(--ink); }
.ag-body code {
  font-family: var(--mono); font-size: 12px; padding: 1px 5px;
  border-radius: 4px; background: var(--well); border: 1px solid var(--line);
}

/* An inline chip has to sit ON the text baseline, not push the line apart. */
.chip.inl {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 1px 7px 1px 5px; margin: 0 1px; border-radius: 5px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .04em;
  vertical-align: baseline; line-height: 1.5; white-space: nowrap;
  border: 1px solid var(--line-2); background: var(--ground-2);
}
.chip.inl .ic { width: 13px; height: 13px; flex: none; }
.chip.inl.rule { border-color: rgba(234, 67, 53, .32); }
.chip.inl.mkt  { border-color: rgba(66, 133, 244, .32); }
.chip.inl.dim  { border-color: rgba(52, 168, 83, .30); text-transform: lowercase; }

.tc-inl {
  font-size: 11.5px; padding: 1px 5px; border-radius: 4px;
  background: var(--well); color: var(--ink-2); white-space: nowrap;
}
.sev-inl { color: var(--sev); font-weight: 600; }
.sev-inl b { color: var(--sev); }

/* Where the problems are: a lane per category, the film's clock across.

   Inline SVG rather than an <img>, because a dot has to be hoverable and
   an image cannot be. That is also what lets a dot carry its observation
   id, so the frame the analyst actually read can be shown next to it. */
.lanes-wrap { position: relative; overflow-x: auto; padding: 4px 0 2px; }
.lanes { color: var(--ink); display: block; min-width: 720px; }
.lane-dot { cursor: pointer; transition: r .12s ease-out, fill-opacity .12s ease-out; }
.lane-dot:hover { fill-opacity: 1 !important; }
.lane-dot.hit:hover { r: 8; }

.lane-peek {
  position: absolute; z-index: 40; width: 260px; pointer-events: none;
  background: var(--panel); border: 1px solid var(--line-2); border-radius: 8px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, .28); padding: 7px; transform: translate(-50%, -100%);
}
.lane-peek img { display: block; width: 100%; border-radius: 4px; background: var(--well); }
.lane-peek-meta {
  display: flex; gap: 8px; align-items: baseline; margin-top: 6px;
  font-size: 11px; color: var(--ink-3);
}
.lane-peek-meta .when { color: var(--ink-2); }
.lane-peek-meta .what { text-transform: lowercase; overflow-wrap: anywhere; }

/* The archive's front door.

   Every symbol the product owns, drifting behind the headline: the eight
   agents, the eighteen things they watch for, the verdicts, the screens.
   It is the one place in the console that is allowed to be a picture of
   itself, and it earns that by being made of the same glyphs everything
   else is labelled with -- so it reads as this product's front page and
   could not be any other product's.

   The rows drift at different speeds and directions, which is what stops
   47 icons reading as wallpaper. */
.swarm-row {
  display: flex; gap: 30px; align-items: center; white-space: nowrap;
  /* A texture, not a picture. At .5 the drifting glyphs read as content
     and competed with the one sentence on the page they sit behind; a
     reviewer's note for the landing hero, and the same swarm is on the
     archive's. Low enough to be felt rather than read. */
  opacity: .15; will-change: transform;
}
.swarm-row .ic { width: 60px; height: 60px; flex: none; }
.swarm-row:nth-child(1) { animation: drift-l 96s linear infinite; }
.swarm-row:nth-child(2) { animation: drift-r 132s linear infinite; opacity: .1; }
.swarm-row:nth-child(3) { animation: drift-l 116s linear infinite; opacity: .12; }
@keyframes drift-l { from { transform: translateX(0); } to { transform: translateX(-33%); } }
@keyframes drift-r { from { transform: translateX(-33%); } to { transform: translateX(0); } }
@media (prefers-reduced-motion: reduce) { .swarm-row { animation: none; } }

@media (max-width: 720px) { .swarm-row .ic { width: 42px; height: 42px; } }

/* The same lane chart, card sized. Six lanes, no ruler, smaller dots:
   in a card the SHAPE is the point -- one bad moment or trouble all the
   way through -- not the exact second, which the board is for. */
.cardlanes { display: block; width: 100%; height: auto; margin: 6px 0 4px; }
.runlist.as-rows .cardlanes { display: none; }
.card-lanes { margin: 6px 0 4px; overflow: hidden; }
.card-lanes .lanes { min-width: 0; width: 100%; height: auto; }
.runlist.as-rows .card-lanes { display: none; }

/* Mission feed: the agent icons at twice the size.

   The feed is read as a column of WHO did what, and at 15px the eight
   agent marks were indistinguishable -- the eye fell to the text and the
   icon became a bullet point. At 30px each agent is recognisable on its
   own, which is the point of giving them distinct marks at all.

   The summary grid's first column has to grow with them or the icon
   overflows its cell and the agent name shifts. */
/* icon · the sentences · agent · count · clock -- the sentences take the
   1fr track. This rule is what the browser actually applies (it comes
   after the base one), so the column order lives here too. */
.grp > summary { grid-template-columns: 34px 1fr 108px auto auto; }
.grp > summary > .ic { width: 30px; height: 30px; }
.feed .ev .ic, .feed .line .ic { width: 22px; height: 22px; }
@media (max-width: 720px) {
  .grp > summary { grid-template-columns: 28px 1fr auto; }
  .grp > summary .ga { display: none; }
  .grp > summary > .ic { width: 26px; height: 26px; }
}

/* ---------- the market tabs carry the verdict ---------- */
/*
   The tab strip is what you steer a run by, and it was the one part of
   the board that said nothing about the run. Six channel codes look
   identical until you scroll past them to the tiles, which is backwards:
   the answer to "which of these is holding the campaign" should be in
   the thing you are already reading.

   So the underline is the verdict. It stays the verdict on the active
   tab too -- these selectors outrank `.tab.on`'s signal colour on
   purpose -- and "you are here" is carried by the lit text and the
   panel behind it instead, because losing the status of the tab you are
   standing on is exactly the wrong trade. */
.runnav .markets-row .tab { border-bottom-color: var(--pending); }
.runnav .markets-row .tab.mk-cleared { border-bottom-color: var(--cleared); }
.runnav .markets-row .tab.mk-noted   { border-bottom-color: var(--g-blue); }
.runnav .markets-row .tab.mk-at_risk { border-bottom-color: var(--at_risk); }
.runnav .markets-row .tab.mk-blocked { border-bottom-color: var(--blocked); }
.runnav .markets-row .tab.mk-error   { border-bottom-color: var(--error); }
.runnav .markets-row .tab.mk-pending { border-bottom-color: var(--pending); }

/* A blocking market should be findable without reading every code. */
.runnav .markets-row .tab.mk-blocked,
.runnav .markets-row .tab.mk-at_risk { color: var(--ink-2); }
.runnav .markets-row .tab.on {
  background: var(--ground-2);
  border-bottom-width: 3px;
  padding-bottom: 9px;
}

/* ---------- a run card's markets, split by what they are ---------- */
/*
   `ID` and `CAQC-NOOVO` sat side by side in one flat run of codes with
   nothing to say that one is a country and the other a broadcaster
   inside a different one. Two labelled groups, stacked, because they
   answer different questions: where may this run, and who will take it. */
.runlist:not(.as-rows) .runrow .flags {
  display: flex; flex-direction: column; gap: 8px; align-items: stretch;
  /* The cap exists so a run cleared for forty channels does not set the
     height of every card in its row. Two labelled groups need more room
     than one flat run of pills did, and the fade has to start below the
     first group's second line or it eats a label rather than trailing
     off after one. */
  max-height: 196px;
  -webkit-mask-image: linear-gradient(to bottom, #000 158px, transparent);
  mask-image: linear-gradient(to bottom, #000 158px, transparent);
}
.flaggroup { display: grid; grid-template-columns: 46px 1fr; gap: 8px; align-items: start; }
.flaglabel { padding-top: 4px; font-size: 9px; letter-spacing: 0.16em; color: var(--ink-3); }
.flagpills { display: flex; flex-wrap: wrap; gap: 6px; }
.runlist.as-rows .flaggroup { grid-template-columns: 1fr; gap: 4px; }
.runlist.as-rows .flaglabel { display: none; }

/* More of the frame, less of the letterbox. At 92px a 16:9 still was
   cropped to a band you could not read a scene from, and the card had
   the room -- it is the one part of a run card that says what the
   commercial actually IS.

   Twice that height again now, and by ratio rather than by number: the
   card is 361px of frame wide at three columns, so 3:2 is 241px -- double
   the 124 it was -- and it stays double at two columns and at one. A
   fixed pixel height cannot: it crops a different amount of the still at
   every window width. The 3:2 costs about a tenth of the frame's width
   against 16:9, which is the trade for the height. */
.runlist:not(.as-rows) .runthumb { height: auto; aspect-ratio: 3 / 2; }

/* ---------- the front door ---------- */
/*
   Everything behind this page assumes you know what ad clearance is and
   why fifteen markets disagree about a glass of wine. Someone arriving
   from a submission link does not, and the first thing they used to meet
   was an upload form asking for a master they do not have.

   It borrows the archive banner's swarm rather than inventing a second
   hero language -- same drifting glyphs, same four bars -- but taller,
   and with the mark on the right where the swarm is busiest. */
.landhero {
  position: relative; overflow: hidden; border-radius: 16px;
  border: 1px solid var(--line); background: var(--ground-2);
  padding: 40px 48px 36px; margin: 8px 0 0; isolation: isolate;
  display: flex; align-items: center; gap: 48px;
}
.landhero .swarm {
  position: absolute; inset: -18px -60px; display: flex;
  flex-direction: column; justify-content: center; gap: 30px; z-index: 0;
  -webkit-mask-image:
    radial-gradient(64% 82% at 30% 50%, transparent 22%, #000 76%),
    linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
  mask-image:
    radial-gradient(64% 82% at 30% 50%, transparent 22%, #000 76%),
    linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
  -webkit-mask-composite: source-in; mask-composite: intersect;
}
.landhero-front { position: relative; z-index: 1; flex: 1 1 auto; max-width: 76ch; }
.landhero-mark { position: relative; z-index: 1; flex: 0 0 auto; order: 2; }
.landhero-mark img {
  width: 200px; height: auto; display: block;
  filter: drop-shadow(0 10px 30px rgba(0, 0, 0, .22));
}
.landhero .bars { display: flex; gap: 7px; }
.landhero .bars i { width: 46px; height: 6px; border-radius: 3px; display: block; }
.landhero .bars i:nth-child(1) { background: #4285F4; }
.landhero .bars i:nth-child(2) { background: #EA4335; }
.landhero .bars i:nth-child(3) { background: #FBBC05; }
.landhero .bars i:nth-child(4) { background: #34A853; }
.landhero-kicker {
  display: block; margin: 18px 0 6px; font-size: 11px;
  letter-spacing: .3em; text-transform: uppercase; color: var(--ink-3);
}
/* One line, not three. Three stacked lines at 62px made the hero most of
   the first screen; the sentence is short enough to sit on one line at a
   size that still reads as the headline, and the box shrinks with it. */
.landhero-title {
  margin: 0 0 8px; font-size: clamp(32px, 4.4vw, 58px); line-height: 1.04;
  letter-spacing: -0.03em; font-weight: 700; color: var(--ink);
}
/* what it is for, under the name and in the reader's second breath */
.landhero-sub { margin: 0 0 16px; max-width: 44ch;
  font-size: clamp(17px, 1.9vw, 25px); line-height: 1.24;
  letter-spacing: -0.01em; color: var(--ink-2); }
/* Wide enough for the whole sentence: it stays on one line and the column
   widens to hold it rather than the sentence breaking to fit the column. */
@media (min-width: 900px) { .landhero-title { white-space: nowrap; } }
.landhero-lede {
  margin: 0; max-width: 58ch; font-size: 16.5px; line-height: 1.62;
  color: var(--ink-2);
}
.landhero-lede strong { color: var(--ink); font-weight: 600; }

/* The two doors. Neither is authentication and neither is pretending to
   be -- there is no secret and both open -- so they are sized and
   weighted as a choice of route, not as a login. */
.doors { margin-top: 30px; }
.doors-label { display: block; margin-bottom: 10px; }
/* Three doors, one line. Two columns put the third door on a row of its
   own, which read as an afterthought -- and the odd one out was the tour,
   the one a stranger should actually take. */
.doors-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px; max-width: 118ch; }
.door {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: 12px; padding: 15px 16px; border-radius: 12px;
  border: 1px solid var(--line-2); background: var(--panel);
  text-decoration: none; transition: border-color .16s, background .16s, transform .16s;
}
.door:hover { border-color: var(--signal); background: var(--ground-2); transform: translateY(-1px); }
.door .ic { width: 26px; height: 26px; flex: none; }
.door-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
/* one line, whatever the column does: a door whose name wraps reads as
   two doors */
.door-title { font-size: 14px; font-weight: 600; color: var(--ink);
  white-space: nowrap; }
.door-sub { font-size: 12px; line-height: 1.42; color: var(--ink-3); }
.door-go { font-size: 18px; color: var(--ink-3); transition: color .16s, transform .16s; }
.door:hover .door-go { color: var(--signal); transform: translateX(3px); }

/* The archive's pinned showcase card: the landing page's door, promoted to
   the top of the run list and wearing the cleared colour. */
.starthere { margin: 0 0 18px; max-width: 100ch; border-left: 3px solid var(--cleared); }
.starthere .ic { color: var(--cleared); }

.landwhy, .landhow, .landgraf { margin-top: 34px; }
.landwhy-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 26px; margin-top: 14px;
}
.landwhy-grid p { margin: 0; font-size: 14.5px; line-height: 1.66; color: var(--ink-2); }
.landwhy-grid b { color: var(--ink); font-weight: 600; }

/* Six steps, read as a strip rather than a numbered list, because the
   order matters but the reading order does not have to be vertical. */
/* Three across, twice down. auto-fit put all six in one row on a wide
   screen, or five and an orphan, and either way each step was a column of
   two-word lines with the crew's story running sideways. Six things that
   happen in order read as two rows of three. */
.landsteps {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 12px; margin-top: 16px;
}
@media (max-width: 1040px) { .landsteps { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .landsteps { grid-template-columns: 1fr; } }
.landstep {
  position: relative; padding: 18px 18px 16px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--panel);
  display: flex; flex-direction: column; gap: 7px;
}
.landstep .ls-n {
  position: absolute; top: 14px; right: 16px; font-size: 11px;
  letter-spacing: .1em; color: var(--ink-3); opacity: .7;
}
.landstep .ic { width: 26px; height: 26px; }
.landstep b { font-size: 14.5px; color: var(--ink); }
.landstep span:last-child { font-size: 12.8px; line-height: 1.55; color: var(--ink-3); }

.landgraf-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 12px; margin-top: 16px;
}
.landgraf-cell {
  padding: 18px; border-radius: 12px; border: 1px solid var(--line);
  background: var(--panel); display: flex; flex-direction: column; gap: 8px;
  border-left: 3px solid var(--signal);
}
.landgraf-cell .lg-n {
  font-size: 11px; letter-spacing: .18em; color: var(--signal);
}
.landgraf-cell b { font-size: 14.5px; color: var(--ink); }
.landgraf-cell span:last-child { font-size: 12.8px; line-height: 1.55; color: var(--ink-3); }

/* ---------- my edits: the cross-run cutting room ----------
   Two frames a card, the same width, so the eye compares them rather than
   the layout. Wide enough that a hemline or a bottle is legible, which is
   the whole point: a pair of thumbnails nobody can read is a claim, not
   evidence. */
.editkeys { display: flex; flex-wrap: wrap; gap: 7px; margin: 14px 0 0; }
.editkeys .chip b { margin-left: 6px; color: var(--ink); }
.editgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(430px, 1fr));
  gap: 16px; }
.editcard { border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); padding: 14px 15px 12px; display: flex;
  flex-direction: column; gap: 10px; }
.edithead { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; }
.editfilm { font-family: var(--mono); font-size: 12.5px; color: var(--ink);
  text-decoration: none; border-bottom: 1px solid var(--line-2); }
.editfilm:hover { border-color: var(--signal); }
.editmeta { display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  margin-left: auto; }
.editwhen { font-size: 11.5px; color: var(--ink-3); }
/* picture edits or sound edits: two sides of one toggle */
.editswap { display: inline-flex; margin-top: 14px; border: 1px solid var(--line-2);
  border-radius: var(--radius); overflow: hidden; }
.editswap button { appearance: none; border: 0; background: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 7px; padding: 9px 14px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3); }
.editswap button + button { border-left: 1px solid var(--line-2); }
.editswap button.on { background: var(--ground-2); color: var(--ink); }
.editswap button b { color: var(--ink-2); }
.editswap .ic { width: 15px; height: 15px; }
.editgrid[data-kind="video"] .editcard[data-kind="audio"],
.editgrid[data-kind="audio"] .editcard[data-kind="video"] { display: none; }

.editpair { position: relative; display: grid; grid-template-columns: 1fr 1fr;
  gap: 8px; }
.editpair figure { margin: 0; display: grid; gap: 5px; }
.editpair video, .editpair img { width: 100%; display: block; border-radius: 4px;
  border: 1px solid var(--line); aspect-ratio: 16 / 9; object-fit: cover;
  background: var(--well); }
/* a sound edit is played, not watched: the frame is context and the
   controls are the point, so they are never covered by a hover hint */
.editpair-snd video { aspect-ratio: auto; }
.editplay { position: absolute; right: 8px; bottom: 26px; padding: 3px 8px;
  border-radius: 999px; background: rgba(9, 12, 20, .62); color: #fff;
  font-size: 9.5px; letter-spacing: .1em; opacity: 0; transition: opacity .15s; }
.editpair:hover .editplay { opacity: 0; }
.editcard:hover .editplay { opacity: 1; }
.editpair figcaption { display: inline-flex; align-items: center; gap: 6px; }
.editpair figcaption .ic { width: 13px; height: 13px; }
.editgone { display: grid; place-items: center; aspect-ratio: 16 / 9;
  border: 1px dashed var(--line-2); border-radius: 4px; color: var(--ink-3); }
.editwho { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.editwho .label { margin-right: 2px; }
.editdim { width: 19px; height: 19px; }
/* the operator's own words for a generative fix */
.fix-say { margin: 10px 0 0; }
.fix-say > summary { cursor: pointer; }
.fix-say textarea.urlin { width: 100%; margin-top: 8px; font-size: 13px;
  line-height: 1.45; resize: vertical; font-family: inherit; }
.fix-say > .label { margin-top: 6px; display: block; }

.editmethod { display: inline-flex; align-items: center; gap: 6px; }
.editmethod .ic { width: 14px; height: 14px; }
/* ---------- the walkthrough, under the three fixes ----------
   A facade: the thumbnail and a button, with the iframe written in on the
   first click. YouTube's embed pulls about a megabyte of player and its
   cookies before anybody presses anything, on the page most likely to be
   opened and closed again. */
.landvid { margin-top: 34px; }
.vidface { position: relative; display: block; width: 100%; padding: 0;
  margin-top: 12px; border: 1px solid var(--line); border-radius: 12px;
  overflow: hidden; background: var(--well); cursor: pointer;
  aspect-ratio: 16 / 9; }
.vidface img { width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .3s ease, filter .3s ease; }
.vidface:hover img { transform: scale(1.012); filter: brightness(.94); }
.vidplay { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: inline-flex; align-items: center; gap: 10px; padding: 13px 22px;
  border-radius: 999px; background: var(--panel); color: var(--ink);
  box-shadow: 0 10px 30px rgba(9, 12, 20, .28); }
.vidplay .ic { width: 20px; height: 20px; }
.vidplay .mono { font-size: 12.5px; letter-spacing: .1em;
  text-transform: uppercase; }
.vidface:focus-visible { outline: 2px solid var(--g-blue); outline-offset: 3px; }
.landvid iframe { width: 100%; aspect-ratio: 16 / 9; border: 0;
  border-radius: 12px; display: block; margin-top: 12px; }

/* asking for it again: the reason is the instruction, so it gets room */
.reedit { margin-top: 2px; }
.reedit > summary { cursor: pointer; list-style: none; display: inline-flex; }
.reedit > summary::-webkit-details-marker { display: none; }
.reedit[open] > summary { color: var(--signal); border-color: var(--signal); }
.reedit form { display: grid; gap: 8px; margin-top: 10px; padding: 12px;
  border: 1px solid var(--line-2); border-radius: var(--radius);
  background: var(--well); }
.reedit textarea.urlin { width: 100%; font-size: 13px; line-height: 1.45;
  font-family: inherit; resize: vertical; }
.reedit .btn { justify-self: start; }

/* removing an edit: folded away, because it destroys the only copy */
/* A card on its way out: it collapses where it stands, so the eye follows
   the removal instead of the list jumping. customs.js takes it out of the
   layout once this has played, and puts it back if the server refuses. */
.editcard[data-going] {
  opacity: 0.25;
  transform: scale(0.985);
  filter: grayscale(1);
  pointer-events: none;
  transition: opacity 0.28s ease, transform 0.28s ease, filter 0.28s ease;
}
.editrefused { color: var(--blocked); margin-top: 8px; }
@media (prefers-reduced-motion: reduce) {
  .editcard[data-going] { transition: none; }
}

/* The archive's order picker: a select that looks like the rest of the row
   rather than like a form dropped into it. */
.runsort { display: flex; align-items: center; gap: 8px; margin-left: 14px; }
.runsort select { padding: 3px 8px; font-size: 12px; width: auto; }

.editkill { margin-left: 8px; }
.editkill > summary { cursor: pointer; list-style: none; }
.editkill > summary::-webkit-details-marker { display: none; }
.editkill[open] > summary { color: var(--blocked); border-color: var(--blocked); }
.editkill form { display: grid; gap: 8px; margin-top: 10px; padding: 12px;
  border: 1px solid var(--line-2); border-radius: var(--radius);
  background: var(--well); max-width: 320px; }
.editkill .urlin { font-size: 13px; padding: 8px 10px; }
.editkill .btn { justify-self: start; }
.editwho .pill { text-decoration: none; }
.editwhy { margin: 0; font-size: 13px; line-height: 1.5; color: var(--ink-2); }
.editwhy .chip { margin-right: 8px; }
.editrule { margin: 0; font-size: 12px; line-height: 1.5; color: var(--ink-3); }
.editfoot { display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  margin-top: auto; }
.editfoot .runid { margin-left: auto; font-size: 10.5px; color: var(--ink-3); }
@media (max-width: 640px) {
  .editgrid { grid-template-columns: 1fr; }
}

/* The metric table: four scannable columns and one sentence per series,
   on its own line. As a fifth column that sentence made every row as tall
   as its prose and left the three columns worth scanning in a ragged
   ladder beside it. */
.gseries .gser { border-top: 1px solid var(--line); }
.gseries .gser:first-of-type { border-top: 0; }
.gseries .gser > tr > td { border-bottom: 0; }
.gseries .gsample td { padding-top: 0; padding-bottom: 12px; font-size: 12.5px;
  line-height: 1.5; color: var(--ink-3); }
.gseries .gsample .label { display: inline-block; margin-right: 9px;
  color: var(--ink-3); opacity: .75; }

/* ---------- the flow picture on the front page ----------
   Four lanes and eight moves. The lane names, the box labels and the key
   are all SVG text, so they scale with the drawing; the numbered steps are
   real HTML underneath it, which is what a reader on a phone gets and what
   a screen reader reads. */
.flow { margin: 26px 0 44px; }
.flow-scroll { overflow-x: auto; }
.flowsvg { width: 100%; min-width: 900px; height: auto; display: block; }
.fl-lane { font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em;
  fill: var(--ink-3); }
.fl-sep { stroke: var(--line); stroke-width: 1; }
.fl-box { fill: var(--panel); stroke-width: 1.3; }
.fl-box.fl-well { fill: var(--well); }
.fl-box.fl-you { fill: var(--ground-2); }
.fl-edge { stroke: var(--line-2); }
.fl-t { font-size: 13px; font-weight: 600; fill: var(--ink); }
.fl-s { font-family: var(--mono); font-size: 10px; fill: var(--ink-3); }
.fl-n { font-family: var(--mono); font-size: 11px; font-weight: 700; fill: #fff; }
.fl-k { font-family: var(--mono); font-size: 10px; letter-spacing: .06em;
  fill: var(--ink-3); }
.flow-steps { margin-top: 16px; }
.flow-play { display: flex; align-items: center; gap: 12px; margin-top: 14px; }
.flow-play .tbtn { cursor: pointer; }
.flow-now { font-family: var(--mono); font-size: 11px; color: var(--ink-3);
  letter-spacing: .04em; }

/* ---------- playing it ----------
   The picture walks itself through the eight moves: customs.js puts
   data-step on the <svg>, everything not part of that step goes quiet,
   and the detail panel on the left shows what that step is actually
   looking at, drawn from the product's own icons. Nothing here runs
   until somebody presses play or scrolls it into view, and with no
   JavaScript the diagram is simply the whole diagram. */
.fl-panelbg, .fl-detail { opacity: 0; transition: opacity .3s ease; }
.flowsvg[data-step] .fl-panelbg { opacity: 1; }
.fl-panelbg { fill: var(--ground-2); stroke: var(--line); }
.fl-dh { font-size: 11.5px; font-weight: 600; fill: var(--ink); }
/* the real marks, where a real one exists: Agent Builder on the crew,
   Grafana on its lane, Loki and Mimir on the two stores. The models are
   named in text, because a logo nobody shipped here would be a drawing of
   a claim rather than the claim. */
.fl-mark { opacity: .9; }
.fl-logo { opacity: .95; }
.fl-mmark { opacity: .9; }
.fl-model { font-family: var(--mono); font-size: 9px; letter-spacing: .04em;
  fill: var(--ink-3); }
.fl-dc { font-family: var(--mono); font-size: 9.5px; fill: var(--ink-3); }
.flowsvg[data-step] .fl-box, .flowsvg[data-step] .fl-arrow,
.flowsvg[data-step] .fl-badge, .flowsvg[data-step] .fl-t,
.flowsvg[data-step] .fl-s, .flowsvg[data-step] .fl-n {
  opacity: .16; transition: opacity .35s ease;
}
.flowsvg[data-step="1"] [data-lit~="1"], .flowsvg[data-step="2"] [data-lit~="2"],
.flowsvg[data-step="3"] [data-lit~="3"], .flowsvg[data-step="4"] [data-lit~="4"],
.flowsvg[data-step="5"] [data-lit~="5"], .flowsvg[data-step="6"] [data-lit~="6"],
.flowsvg[data-step="7"] [data-lit~="7"], .flowsvg[data-step="8"] [data-lit~="8"] {
  opacity: 1;
}
.flowsvg[data-step="1"] .fl-detail[data-step="1"],
.flowsvg[data-step="2"] .fl-detail[data-step="2"],
.flowsvg[data-step="3"] .fl-detail[data-step="3"],
.flowsvg[data-step="4"] .fl-detail[data-step="4"],
.flowsvg[data-step="5"] .fl-detail[data-step="5"],
.flowsvg[data-step="6"] .fl-detail[data-step="6"],
.flowsvg[data-step="7"] .fl-detail[data-step="7"],
.flowsvg[data-step="8"] .fl-detail[data-step="8"] { opacity: 1; }
/* the arrow carrying the current move gets a heartbeat */
.flowsvg[data-step] .fl-arrow[data-lit] { animation: none; }
.flowsvg[data-step="1"] .fl-arrow[data-lit~="1"],
.flowsvg[data-step="2"] .fl-arrow[data-lit~="2"],
.flowsvg[data-step="3"] .fl-arrow[data-lit~="3"],
.flowsvg[data-step="4"] .fl-arrow[data-lit~="4"],
.flowsvg[data-step="5"] .fl-arrow[data-lit~="5"],
.flowsvg[data-step="6"] .fl-arrow[data-lit~="6"],
.flowsvg[data-step="7"] .fl-arrow[data-lit~="7"],
.flowsvg[data-step="8"] .fl-arrow[data-lit~="8"] {
  animation: fl-beat 1.1s ease-in-out infinite;
}
@keyframes fl-beat { 50% { stroke-width: 3.2; } }
/* and the step being played is the one lit in the list under it */
.flow-steps li { transition: color .3s ease, opacity .3s ease; }
.flow[data-step] .flow-steps li { opacity: .45; }
.flow[data-step] .flow-steps li.on { opacity: 1; color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
  .flowsvg .fl-arrow { animation: none !important; }
}
.flow-steps ol { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 26px;
  margin: 0; padding: 0; list-style: none; max-width: 108ch; }
.flow-steps li { font-size: 12.8px; line-height: 1.5; color: var(--ink-3); }
.flow-steps b { font-family: var(--mono); color: var(--signal); margin-right: 6px; }
@media (max-width: 900px) { .flow-steps ol { grid-template-columns: 1fr; } }

/* agent mode on the front page: the same three cells the Grafana section
   uses, in the agent's own hue, because "click it or ask it" is a claim
   about the product and not a footnote to it */
.landagent-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 12px; margin-top: 16px;
}
.landagent-cell {
  padding: 18px; border-radius: 12px; border: 1px solid var(--line);
  background: var(--panel); display: flex; flex-direction: column; gap: 8px;
  border-left: 3px solid var(--g-blue);
}
.landagent-cell .lg-n { font-size: 11px; letter-spacing: .18em; color: var(--g-blue); }
.landagent-cell b { font-size: 14.5px; color: var(--ink); }
.landagent-cell span:last-child { font-size: 12.8px; line-height: 1.55; color: var(--ink-3); }

.landfoot { margin: 34px 0 8px; }

@media (max-width: 1080px) {
  .landhero-mark img { width: 140px; }
}
@media (max-width: 900px) {
  .landhero { display: block; padding: 34px 24px 30px; }
  .landhero-mark { margin-bottom: 22px; }
  .landhero-mark img { width: 92px; }
  .landhero-front { max-width: none; }
  .doors-row { grid-template-columns: 1fr; max-width: 74ch; }
  .landwhy-grid { grid-template-columns: 1fr; gap: 16px; }
  .swarm-row .ic { width: 24px; height: 24px; }
}

/* ---------- findings: the rule and its citation are two columns ---------- */
/*
   They used to be one stacked cell. A statute title runs twenty words and
   a rationale thirty, and both were sharing a column the browser had
   squeezed to about 200px because six short columns to the right were
   claiming their own width first. The result was a cell twelve lines deep
   with Class, Severity and Window sitting in a column of blank space
   beside it -- the row was as tall as its narrowest content.

   The widths are declared on the HEADER row, not on the body cells:
   table-layout:fixed reads the first row and ignores everything after it,
   so widths hung off td classes are silently dropped and every column
   comes out the same size. Which is what happened the first time. */
.market-room table.grid { table-layout: fixed; }
/* All eight proportional, summing to 100. Mixing percentages with fixed
   pixels meant the declared total outran the table on a narrower panel
   and the last column -- the one carrying the button -- was pushed
   outside it. */
.market-room table.grid th:nth-child(1) { width: 21%; }  /* rule + rationale */
.market-room table.grid th:nth-child(2) { width: 16%; }  /* citation */
.market-room table.grid th:nth-child(3) { width: 13%; }  /* evidence */
.market-room table.grid th:nth-child(4) { width: 8%; }   /* class */
.market-room table.grid th:nth-child(5) { width: 8%; }   /* severity */
.market-room table.grid th:nth-child(6) { width: 11%; }  /* window */
/* Status was 8%, which is 88px on a laptop, and "remediating" set nowrap
   in a fixed-layout table does not shrink -- it overflows, and the next
   column's button (width:100% of its own cell) then painted straight over
   it. Wider, and the cell clips rather than bleeding into its neighbour
   whatever word ends up in it. */
.market-room table.grid th:nth-child(7) { width: 11%; }  /* status */
.market-room table.grid th:nth-child(8) { width: 12%; }  /* the fix */
.market-room table.grid tr.frow > td:nth-child(7) { overflow: hidden; }
.market-room table.grid td { vertical-align: top; }
.market-room table.grid td.tight { white-space: nowrap; }
/* ...except the button, which would rather wrap than leave the panel.
   It also has to be told it may be as narrow as its cell: a button sizes
   to its label by default, so at 12% of a narrow table it simply drew
   past the panel edge and got clipped. */
.market-room table.grid td.tight:last-child { white-space: normal; }
.market-room table.grid tr.frow > td:last-child .btn,
.market-room table.grid tr.frow > td:last-child .fixer > summary {
  width: 100%; box-sizing: border-box; text-align: center;
}
/* ...but not inside the panel row, whose only cell is also a last child:
   "Run the fix" came out a metre wide across the bottom of the panel. */
.market-room table.grid tr.fixrow .btn { width: auto; }
/* and the toggle itself reads on one line rather than breaking after
   "Remediate", now that the panel no longer needs the column to grow */
.market-room table.grid tr.frow > td:last-child .fixer > summary {
  white-space: nowrap; justify-content: center;
}
/* and a backstop, so a narrow window scrolls the table instead of
   spilling it over the page */
.market-room .panel-body { overflow-x: auto; }
.market-room .rationale { max-width: none; line-height: 1.5; }

/* The thumbnail has to live inside its column or it draws over Class.
   168px was set for the old narrow layout and is now a floor, not a fit. */
.market-room .grid td.ev-cell .evidence.big,
.market-room .grid td.ev-cell .evidence.big img { width: 100%; max-width: 100%; }

.cite-cell { vertical-align: top; }
.cite-cell .cite, .cite-cell .cite-plain {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 6;
  overflow: hidden; font-size: 12.5px; line-height: 1.45;
  overflow-wrap: anywhere;
}
.cite-cell .cite-plain { font-family: var(--mono); color: var(--ink-3); }
.cite-cell .cite-state { margin-top: 8px; }

/* List view stays a list: the prose is what makes a row tall, so it goes.
   The provenance chip stays, because "is this finding backed by a real
   source" is exactly the kind of thing you scan a list for. */
.as-list .cite-cell .cite,
.as-list .cite-cell .cite-plain { display: none; }
.as-list .cite-cell .cite-state { margin-top: 0; }
.market-room .as-list table.grid { table-layout: auto; }

@media (max-width: 900px) {
  .market-room table.grid { table-layout: auto; }
  .market-room table.grid th { width: auto !important; }
}

/* ---------- the board shows what it is judging ---------- */
/*
   The launch board discussed a commercial at length without ever showing
   one. Every other screen has the picture somewhere -- the archive card,
   the frame board, the cutting room -- and this is the screen people
   land on, so it was the one place you could read a verdict with no idea
   what it was a verdict about.

   It sits in the header's right, where the specs grid was leaving a
   column of air, and it goes to the cutting room because that is where
   the footage actually plays. */
.board-hero { display: flex; align-items: flex-start; gap: 30px; }
.board-hero-text { flex: 1 1 auto; min-width: 0; }
.board-poster {
  flex: 0 0 auto; width: 288px; display: block; text-decoration: none;
  border-radius: 10px; overflow: hidden; border: 1px solid var(--line-2);
  background: var(--well); transition: border-color .16s, transform .16s;
}
.board-poster:hover { border-color: var(--signal); transform: translateY(-1px); }
.board-poster img { display: block; width: 100%; height: auto; }
.board-poster-cap {
  display: flex; align-items: center; gap: 7px; padding: 8px 11px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-3); background: var(--panel);
}
.board-poster:hover .board-poster-cap { color: var(--signal); }
.board-poster-cap .ic { width: 14px; height: 14px; }

@media (max-width: 1000px) {
  .board-hero { display: block; }
  .board-poster { width: 100%; max-width: 360px; margin-top: 20px; }
}

/* The board still rotates, because one still is a coin flip.

   Commercials open on black, on a fade, or on a logo card, so the frame
   at one second is quite often nothing at all -- and the board was then
   showing a black rectangle as its only reference to the film. Five
   stills from across the middle, crossfading.

   Pure CSS: the images are stacked and each one's animation is delayed
   by its index, so this works with JavaScript off and costs no script.
   --frames is written by the template because the keyframe percentages
   have to divide by however many there are. */
.board-poster-stack { position: relative; display: block; line-height: 0; }
.board-poster-stack img { display: block; width: 100%; height: auto; }
.board-poster.rotating .board-poster-stack img {
  position: absolute; inset: 0; opacity: 0;
  animation: still-cycle calc(var(--frames) * 3.2s) linear infinite;
  animation-delay: calc(var(--i) * 3.2s);
}
/* the first one also holds the box open, since the rest are absolute */
.board-poster.rotating .board-poster-stack img:first-child { position: relative; }

@keyframes still-cycle {
  0%                      { opacity: 0; }
  2%, 20%                 { opacity: 1; }
  24%, 100%               { opacity: 0; }
}

/* A still that changes on its own is motion, and some people have asked
   the system not to do that. */
@media (prefers-reduced-motion: reduce) {
  .board-poster.rotating .board-poster-stack img { animation: none; opacity: 0; }
  .board-poster.rotating .board-poster-stack img:first-child { opacity: 1; }
}

/* A "go deeper" card wears the glyph of the screen it opens -- the same
   one that screen's tab carries in the run nav -- so the card and its
   destination read as the same place rather than two descriptions of it. */
.card-ic {
  width: 30px; height: 30px; display: block; margin-bottom: 12px;
  opacity: .9; transition: opacity .18s ease, transform .18s ease;
}
.card:hover .card-ic { opacity: 1; transform: translateY(-1px); }

/* The clearance gauge, under the thumbnail on a run card. How much is
   still open before this run can ship: empty is cleared and clean, a full
   arc is nothing dealt with yet. */
/* Flex, not `margin: 0 auto` on the child. The card is a grid and the
   span is a grid item, so the auto margins were centring the svg inside
   a box that was not itself centred. */
.cardgauge { display: flex; justify-content: center; margin: 2px 0 -2px; }
.cardgauge .gauge { display: block; color: var(--ink-3); }
.runlist:not(.as-rows) .runrow .cardgauge { order: 2; }
.runlist.as-rows .cardgauge { display: none; }

/* Upload inside the conversation. The point of agent mode is not having to
   go and find the form. */
.agent-drop {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  margin: 0 0 8px; padding: 12px 14px; border-radius: 10px;
  border: 1px dashed var(--line-2); background: var(--panel);
  font-size: 13px; color: var(--ink-3);
  transition: border-color .16s, color .16s, background .16s;
}
.agent-drop:hover, .agent-drop.over {
  border-color: var(--signal); color: var(--ink-2); background: var(--ground-2);
}
.agent-drop .ic { width: 18px; height: 18px; flex: none; }

/* landing: the before/after pair, playing in lockstep */
/* A filmstrip, not a stack. Three full-width before/after pairs were most
   of the front page's scroll for one idea repeated three times; side by
   side they read as a set, which is what they are -- three markets, three
   statutes, three fixes. The pair itself stays before|after, because that
   comparison is the whole point and stacking it would break it. */
.landfix .strip { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 18px; align-items: start; }

/* The rotator. Everything under .fixshow.on only applies once customs.js
   has said it is driving: without it the three lanes stay side by side,
   the tabs stay hidden, and the page is what it was. */
.fixtabs { display: none; }
.fixshow.on .fixtabs {
  display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 12px;
}
.fixtab {
  font: inherit; font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  padding: 6px 11px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line); background: var(--panel); color: var(--ink-3);
}
.fixtab:hover { border-color: var(--line-2); color: var(--ink-2); }
.fixtab.on { border-color: var(--g-blue); color: var(--ink); background: rgba(66, 133, 244, .08); }
.fixshow.on .strip { grid-template-columns: 1fr; max-width: 1080px; }
.fixshow.on .lane.off { display: none; }
.fixshow.on .ba { gap: 10px; }
.fixshow.on .ba-tag { font-size: 11px; }
.fixshow.on .ba-cell figcaption { font-size: 11.5px; }
.fixshow.on .lane-note { font-size: 13.5px; max-width: 92ch; }
.landfix .lane { min-width: 0; }
.landfix .ba { display: grid; grid-template-columns: 1fr 1fr; gap: 5px; }
.ba-cell { margin: 0; min-width: 0; }
.ba-tag { margin: 0 0 6px; font-size: 9.5px; }
.ba-cell video { width: 100%; display: block; border: 1px solid var(--line);
  border-radius: 3px; background: var(--well); }
.ba-cell figcaption { display: flex; align-items: center; gap: 6px; margin-top: 6px;
  font-size: 10px; color: var(--ink-3); }
/* The pill IS the caption's only span, so the rule that used to hide
   "span:last-child" here (to save height when three pairs shared a row)
   was hiding the before/after labels themselves -- on the one comparison
   where which side is which is the whole point. */
.ba-cell figcaption .pill { flex: none; }
.lane-note { margin: 7px 0 0; font-size: 11.5px; color: var(--ink-2); line-height: 1.5; }
@media (max-width: 980px) { .landfix .strip { grid-template-columns: 1fr; } }

/* ---------- the sparkle: work in flight, visible from anywhere ----------
   One treatment for everything that is currently running: an uploading
   master, a run mid-analysis, a fix in the making. A rotating ring of the
   four brand colours behind the element (the element's own background
   keeps the centre clean), plus one twinkle. Reduced motion gets the ring
   without the spin. */
@property --spk { syntax: "<angle>"; initial-value: 0deg; inherits: false; }
@keyframes spk-spin { to { --spk: 360deg; } }
@keyframes spk-twinkle { 0%, 100% { opacity: 0.2; transform: scale(0.8); }
                         50% { opacity: 1; transform: scale(1.15); } }
/* The ring is MASKED to the ring, not tucked behind the element. Putting
   z-index:0 on the sparkle made it its own stacking context, which means a
   ::before at z-index:-1 paints above that element's own background rather
   than beneath it -- so on anything that carries its own background, like a
   run card, the ring became a fill and the content sat on a rainbow. The
   two-layer mask cuts the middle out of the gradient instead, which needs
   no stacking games and works wherever the class lands. */
.sparkle, [data-busy] { position: relative; }
.sparkle::before, [data-busy]::before {
  content: ""; position: absolute; inset: -3px; z-index: 1;
  pointer-events: none;
  padding: 3px;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  border-radius: 6px;
  background: conic-gradient(from var(--spk),
    #4285F4, #EA4335, #FBBC05, #34A853, #4285F4);
  animation: spk-spin 2.6s linear infinite;
  filter: blur(0.5px);
}
.sparkle::after, [data-busy]::after {
  content: "\2726"; position: absolute; top: -9px; right: -6px; z-index: 2;
  font-size: 12px; color: #FBBC05; pointer-events: none;
  animation: spk-twinkle 1.3s ease-in-out infinite;
  text-shadow: 0 0 6px rgba(251, 188, 5, 0.6);
}
tr.sparkle::before, tr.sparkle::after { content: none; }
tr.sparkle td { background: rgba(251, 188, 5, 0.06); }
.lc-current .ic { animation: spk-twinkle 1.8s ease-in-out infinite; }
.beacon {
  display: inline-flex; align-items: center; gap: 7px; padding: 4px 11px;
  border-radius: 999px; background: var(--panel); border: 1px solid var(--line);
  font-size: 11px; color: var(--ink-2); text-decoration: none;
}
.beacon::before { border-radius: 999px; }
.beacon .ic { width: 13px; height: 13px; }
.beacon[hidden] { display: none !important; }
@media (prefers-reduced-motion: reduce) {
  .sparkle::before, [data-busy]::before, .sparkle::after,
  [data-busy]::after, .lc-current .ic { animation: none; }
}

/* ---------- the matrix: occurrence types x scenes, cells that launch ---- */
.mxwrap { overflow-x: auto; }
.mx { border-collapse: separate; border-spacing: 4px; }
.mx-corner { min-width: 150px; }
.mx-scene img, .mx-scene .ss-blank { width: 88px; height: 50px; object-fit: cover;
  display: block; border: 1px solid var(--line); border-radius: 2px; }
.mx-scene a { display: grid; gap: 3px; text-decoration: none; justify-items: start; }
.mx-dim { display: flex; align-items: center; gap: 8px; font-size: 11.5px;
  color: var(--ink-2); font-weight: 400; text-align: left; white-space: nowrap;
  padding-right: 8px; height: 46px; }
.mx-dim .ic { width: 16px; height: 16px; flex: none; }
.mx-cell { padding: 0; }
.mx-box { position: relative; display: grid; place-items: center; width: 88px;
  height: 46px; border-radius: 4px; border: 1px solid var(--line);
  font-family: var(--mono); font-size: 13px; text-decoration: none; }
.mx-empty { }
.mx-clean { background: var(--well); opacity: 0.45; }
.mx-fixed { background: rgba(52, 168, 83, 0.12); border-color: rgba(52, 168, 83, 0.5); }
.mx-fixed .ic { width: 14px; height: 14px; }
.mx-hot { background: rgba(251, 188, 5, 0.14); border-color: var(--g-yellow);
  color: var(--g-yellow); }
.mx-hot.mx-block { background: rgba(234, 67, 53, 0.14);
  border-color: var(--blocked); color: var(--blocked); }
.mx-hot i { display: none; font-style: normal; font-size: 9.5px; line-height: 1.15;
  letter-spacing: 0.06em; text-transform: uppercase; text-align: center; }
.mx-hot:hover b { display: none; }
.mx-hot:hover i { display: block; }
.mx-hot:hover { border-width: 2px; }
.mx-working { background: rgba(66, 133, 244, 0.12); border-color: var(--g-blue);
  color: var(--g-blue); font-size: 12px; }

/* the live Grafana embeds: same box the PNGs had, minus the picture */
.boardlanes.live, .embed iframe { width: 100%; border: 1px solid var(--line);
  border-radius: 2px; background: var(--well); display: block; }
.boardlanes.live { aspect-ratio: 1200 / 420; }
.embed iframe { aspect-ratio: 1200 / 300; }

/* ---------- the grid: occurrence types x scenes, one screen, no scroll ----
   Replaces both the table-shaped matrix and the market Gantt that used to
   sit under it. The label column is fixed and every scene column is
   minmax(0,1fr), so eighteen scenes shrink to fit rather than scrolling
   sideways -- the page has to be readable in one look to be worth having. */
.mx2 { display: grid; gap: 3px; align-items: stretch;
  grid-template-columns: 132px repeat(var(--cols), minmax(0, 1fr)); }
.mx2-corner { align-self: end; padding-bottom: 4px; font-size: 9.5px; }
.mx2-scene { display: grid; gap: 2px; text-decoration: none; min-width: 0; }
.mx2-scene img, .mx2-noframe { width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  display: block; border: 1px solid var(--line); border-radius: 2px;
  background: var(--well); }
.mx2-scene:hover img { border-color: var(--signal); }
.mx2-t { font-size: 8.5px; color: var(--ink-3); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mx2-dim { display: flex; align-items: center; gap: 6px; font-size: 11px;
  color: var(--ink-2); min-height: 30px; padding-right: 6px; }
.mx2-dim .ic { width: 15px; height: 15px; flex: none; }
.mx2-dim span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mx2-cell { display: grid; place-items: center; min-height: 30px; border-radius: 3px;
  border: 1px solid var(--line); font-family: var(--mono); font-size: 11px;
  text-decoration: none; }
.mx2-empty { border-color: transparent; }
.mx2-clean { background: var(--well); opacity: 0.5; }
.mx2-fixed { background: rgba(52, 168, 83, 0.16); border-color: rgba(52, 168, 83, 0.55); }
.mx2-hot { background: rgba(251, 188, 5, 0.18); border-color: var(--g-yellow);
  color: var(--g-yellow); }
.mx2-hot.mx2-block { background: rgba(234, 67, 53, 0.18);
  border-color: var(--blocked); color: var(--blocked); }
a.mx2-hot:hover { border-width: 2px; transform: scale(1.08); }
.mx2-working { background: rgba(66, 133, 244, 0.16); border-color: var(--g-blue); }
.mx2-key { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 12px; }
.mx2-key span { display: inline-flex; align-items: center; gap: 6px; }
.mx2-key .sw { width: 11px; height: 11px; border-radius: 2px; display: inline-block; }

/* ---------- the merged grid: our axes, Grafana's body -------------------
   The console draws what Grafana cannot -- the taxonomy icons down the side
   and each scene's opening frame across the top -- and the live status
   history sits between them as the body. Columns are weighted by scene
   duration, so a column's share of the width is its share of the film,
   which is exactly what the panel's hidden time axis is.

   ponytail: the iframe's plot has a few px of its own padding, so alignment
   is close rather than pixel-exact. Grafana gives no way to zero it from
   outside, and the alternative is not embedding Grafana at all. */
.mg { display: grid; grid-template-columns: 150px 1fr;
      grid-template-rows: auto 1fr; column-gap: 8px; row-gap: 4px; }
.mg-corner { align-self: end; padding-bottom: 6px; font-size: 9.5px; }
.mg-strip { display: grid; grid-template-columns: var(--cols); gap: 2px; }
.mg-scene { display: grid; gap: 2px; text-decoration: none; min-width: 0; }
/* One height for every still, whatever its column is worth. The columns are
   weighted by scene length so they line up with Grafana's squares, and an
   aspect-ratio on top of that gave a ten second scene a tall thumbnail and a
   two second scene a stub -- a ragged strip. Fixed height, cover the width:
   same size everywhere, still over the right square. */
.mg-scene img, .mg-noframe { width: 100%; height: 42px; object-fit: cover;
  display: block; border: 1px solid var(--line); border-radius: 2px;
  background: var(--well); }
.mg-scene:hover img { border-color: var(--signal); }
.mg-t { font-size: 8px; color: var(--ink-3); text-align: center;
  overflow: hidden; white-space: nowrap; }
.mg-labels { display: grid; align-content: stretch; }
.mg-dim { display: flex; align-items: center; gap: 7px; font-size: 11px;
  color: var(--ink-2); height: 44px; }
.mg-dim .ic { width: 15px; height: 15px; flex: none; }
.mg-dim span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Every embedded panel that has to line up with a column of this app's own
   icons is cropped by the height of a Grafana panel header. Nothing else
   removes it: d-solo renders the title, and a title bar above the first
   row is thirty-four pixels of drift between the plot and its axis. */
.mgplot { position: relative; display: block; overflow: hidden; }
.mg-live { position: absolute; left: 0; top: -34px; width: 100%;
  height: calc(100% + 34px); border: 0; display: block;
  background: transparent; }
/* the console's own body, when no viewer is deployed */
.mg-cells { display: grid; grid-template-columns: var(--cols); gap: 2px; }
.mg-cell { display: grid; place-items: center; height: 40px; border-radius: 3px;
  border: 1px solid var(--line); font-family: var(--mono); font-size: 11px;
  text-decoration: none; }
.mg-empty { border-color: transparent; }
.mg-clean { background: var(--well); opacity: 0.5; }
.mg-fixed { background: rgba(52, 168, 83, 0.16); border-color: rgba(52, 168, 83, 0.55); }
.mg-hot { background: rgba(251, 188, 5, 0.18); border-color: var(--g-yellow); color: var(--g-yellow); }
.mg-hot.mg-block { background: rgba(234, 67, 53, 0.18); border-color: var(--blocked); color: var(--blocked); }
.mg-working { background: rgba(66, 133, 244, 0.16); border-color: var(--g-blue); }
/* "nothing observed" has no fill by design, so its legend swatch needs an
   outline to exist at all */
/* the analyst's sentence on either side of an edit */
.change-said { display: grid; grid-template-columns: 1fr 1fr; gap: 18px;
  padding: 12px 0 0; border-top: 1px solid var(--line); margin-top: 10px; }
.change-said p { margin: 4px 0 0; font-size: 13px; line-height: 1.5; }
@media (max-width: 760px) { .change-said { grid-template-columns: 1fr; } }

/* the run's own four numbers, on every screen inside it */
.runstat { display: inline-flex; align-items: baseline; gap: 6px;
  margin-right: 12px; color: var(--ink-3); white-space: nowrap; }
.runstat b { font-family: var(--mono); font-size: 12.5px; color: var(--ink); }
.runstat i { color: var(--line); font-style: normal; }
.runstat b.s-cleared { color: var(--cleared); }
.runstat b.s-blocked { color: var(--blocked); }
.runstat b.s-working { color: var(--g-blue); }
@media (max-width: 900px) { .runstat { display: none; } }

/* one country's broadcasters, folded */
.famfold { border-top: 1px solid var(--line); }
.famfold > summary {
  display: flex; align-items: center; gap: 9px; cursor: pointer;
  padding: 7px 4px; font-family: var(--mono); font-size: 12px;
  letter-spacing: .04em; color: var(--ink-2); list-style: none;
}
.famfold > summary::-webkit-details-marker { display: none; }
.famfold > summary:hover { color: var(--ink); }
.famfold > summary .ic { width: 18px; height: 18px; }
.famfold .fam-name { flex: 1 1 auto; }
.famfold .fam-count { color: var(--ink-3); }
.famfold[open] > summary { color: var(--ink); }

/* the info toggle that folds a screen's own explanation away */
.explain-btn {
  appearance: none; border: 1px solid var(--line); background: var(--well);
  color: var(--ink-2); border-radius: 999px; cursor: pointer;
  margin-left: 10px; padding: 3px; line-height: 0; vertical-align: middle;
}
.explain-btn:hover { border-color: var(--g-blue); color: var(--g-blue); }
.explain-btn .ic { width: 17px; height: 17px; }
.explain-btn[aria-expanded="true"] { background: var(--ground-2); }

.mx2-key .sw.sw-none { border: 1px dashed var(--line); background: none; }
.mx2-key .sw.mg-clean { opacity: 1; }

/* the market room's scene header: its opening and closing frame, then what
   the scene is */
/* The row stays a table cell -- display:flex on a <td> stops it being one,
   which silently kills colspan and squeezes the whole scene into column
   one. The flexing happens on a span inside it. */
.scene-row td { padding: 10px 14px; }
.scene-row-in { display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  width: 100%; }
.sc-pair { display: inline-flex; gap: 4px; }
.sc-pair img { height: 44px; width: auto; display: block; border: 1px solid var(--line);
  border-radius: 2px; }
.sc-pair a:hover img { border-color: var(--signal); }
.sc-meta { display: inline-flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* the archive's one live panel */
/* The earlier runs of a film, as the card's own footer: the card is the
   newest clearance and these are the way back to the ones it replaced.
   Attached rather than floating -- one pixel of negative margin eats the
   card's bottom border so the two read as one object. */
.runolder {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-top: -1px; padding: 8px 18px 9px;
  border: 1px solid var(--line); border-top: none;
  border-radius: 0 0 var(--radius) var(--radius);
  background: var(--ground-2);
}
.runolder .chip { font-size: 10.5px; padding: 2px 7px; }
/* The card view makes every card the height of the tallest one. With a
   footer under it that has to be the card AND the footer, or the footer
   hangs off the bottom of the grid cell. */
.runlist:not(.as-rows) .runcard { display: flex; flex-direction: column; }
.runlist:not(.as-rows) .runcard .runrow { height: auto; flex: 1; }

/* The upload that was not needed: two ways forward, side by side, and
   the expensive one further down the page in its own words. */
.already-do { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 18px; }

.loadmore { display: flex; justify-content: center; margin: 18px 0 4px; }
.loadmore .btn .mono { color: var(--ink-3); margin-left: 6px; font-size: 11.5px; }

/* The two platforms, as marks. One baseline, one divider, and a label
   that does not shout: the logos carry it. */
.builtwith { display: flex; align-items: center; flex-wrap: wrap; gap: 16px;
  margin: 0 0 14px; }
.bw-pair { display: flex; align-items: center; gap: 18px; }
.bw-item { display: flex; align-items: center; gap: 10px; text-decoration: none; }
.bw-item img[src*="logo-adk"] { width: 42px; height: 42px; }
.bw-item:hover { opacity: .82; }
.bw-name { font-size: 13.5px; font-weight: 600; color: var(--ink); line-height: 1.15; }
.bw-name em { display: block; font-style: normal; font-size: 11px; font-weight: 500;
  color: var(--ink-3); letter-spacing: .03em; }
.bw-bar { width: 1px; height: 30px; background: var(--line-2); }
.bw-graf { display: block; }

/* ---------- the agent, working ----------
   A bar that says the machine is busy, and a list that says what it is
   busy WITH. The bar is the four brand colours run through a spectrum and
   swept, with one twinkle riding it: the console already uses a rotating
   ring of those colours for work in flight, and this is the same idea laid
   flat. Reduced motion keeps the colours and drops the movement. */
.ag-phases { display: grid; gap: 9px; }
.ag-bar {
  position: relative; height: 5px; border-radius: 999px; overflow: hidden;
  background: var(--well);
}
.ag-bar i {
  position: absolute; inset: 0;
  background: linear-gradient(90deg,
    #4285F4, #34A853, #FBBC05, #EA4335, #4285F4, #34A853, #FBBC05, #EA4335);
  background-size: 300% 100%;
  animation: ag-sweep 2.4s linear infinite;
}
.ag-bar::after {
  content: ""; position: absolute; top: -50%; bottom: -50%; width: 42px;
  background: linear-gradient(90deg, transparent,
    rgba(255, 255, 255, .95), transparent);
  animation: ag-spark 1.9s ease-in-out infinite;
}
@keyframes ag-sweep { to { background-position: -300% 0; } }
@keyframes ag-spark {
  0% { left: -12%; } 60%, 100% { left: 104%; }
}
.ag-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.ag-steps li {
  font-size: 12.5px; line-height: 1.45; color: var(--ink-3);
  display: flex; align-items: baseline; gap: 7px;
}
.ag-steps li::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--line-2); transform: translateY(-1px);
}
.ag-steps li.done { color: var(--ink-2); }
.ag-steps li.done::before { background: var(--cleared); }
.ag-steps li.on { color: var(--ink); }
.ag-steps li.on::before {
  background: var(--g-blue);
  animation: ag-pulse 1.1s ease-in-out infinite;
}
@keyframes ag-pulse { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) {
  .ag-bar i, .ag-bar::after, .ag-steps li.on::before { animation: none; }
  .ag-bar::after { display: none; }
}

/* ---------- the next-step nudge ----------
   A reference screen -- the library, the Grafana inventory, an empty
   search -- answers a question and then has nowhere to send you. One
   footer, everywhere the same: what to do because of what you just read,
   in the run lifecycle's own words (.lc-cta) rather than a new device. */
.nextstep {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 28px 0 4px; padding: 16px 18px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel);
}
.ns-why { font-size: 13px; color: var(--ink-2); flex: 1 1 320px; min-width: 0; }
.nextstep .chip.on { margin-left: auto; }
@media (max-width: 640px) { .nextstep .chip.on { margin-left: 0; } }

/* ---------- frame search ----------
   A result is a picture with a sentence under it, so the grid is sized by
   the picture: whatever fits at 300px, as many across as the window has
   room for. */
.sform { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0 12px; }
.sform .urlin { flex: 1 1 260px; min-width: 0; }
.sform .sdim { flex: 0 1 200px; }
.sgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 14px; }
.scard { margin: 0; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); overflow: hidden; display: flex; flex-direction: column; }
.scard img { width: 100%; display: block; background: var(--well); }
.scard figcaption { display: grid; gap: 7px; padding: 11px 12px 12px; }
.sstate { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  font-size: 11px; color: var(--ink-3); }
.sstatement { font-size: 13px; line-height: 1.5; color: var(--ink); }
.smeta { display: block; overflow-wrap: anywhere; }
/* why the model kept this frame: its own words, in the colour the
   console uses for a machine's reasoning rather than a fact. */
.swhy { color: var(--g-blue); }

/* ---------- the Grafana resources page ----------
   An inventory reads as tables, so it is built from the console's own
   .grid table with three concessions: a query needs a monospace cell that
   can wrap anywhere, the secondary columns need to be smaller than the
   sentence beside them, and the counts under the title are a line rather
   than the band of numerals the front page used to carry. */
/* Loki, Mimir and Grafana wear their own marks where their name is a
   label. Height only: the three lockups have different aspect ratios and
   a fixed width would squash two of them. */
/* Three times the size they were. At 15px these read as bullets rather
   than as marks, and which store a panel reads is the whole claim this
   page makes -- Mimir and Loki are the reason any of it is upstream of
   the work. Middle-aligned, because at 45px a baseline offset drags the
   row's text off centre. */
.blogo { height: 45px; width: auto; vertical-align: middle; margin-right: 10px; }
.label .blogo { height: 39px; vertical-align: middle; margin-right: 8px; }
/* the rows they sit in have to make room rather than clip */
.gtable td.tight { line-height: 1.4; }
.rule-row .label:has(.blogo) { display: inline-flex; align-items: center; }

.gcount { display: flex; flex-wrap: wrap; gap: 6px 20px; margin: 16px 0 0;
  font-size: 12px; color: var(--ink-3); }
.gcount b { color: var(--ink); font-size: 14px; margin-right: 5px; }
.gtable td { vertical-align: top; }
.gsmall { font-size: 11.5px; line-height: 1.5; color: var(--ink-2); }
.gq { font-family: var(--mono); font-size: 11.5px; overflow-wrap: anywhere;
  background: var(--well); border: 1px solid var(--line); border-radius: 4px;
  padding: 2px 5px; display: inline-block; color: var(--ink-2); }
.gboard summary .libcount .mono { font-size: 11px; color: var(--ink-3); }
.gwhere { padding: 10px 16px 14px; margin: 0; font-size: 12px; }

.archlive { width: 100%; height: 260px; border: 1px solid var(--line);
  border-radius: 3px; background: var(--well); display: block; }

/* the card's live lanes: squares, no axes, small enough for a card */
/* No height here: the rule further down crops this frame by the height of
   a Grafana panel header (the title bar d-solo insists on drawing), and a
   plain height on the same selector is what put the title back on the card
   and pushed every row out of line with its icon. */
.cardlanes.live { width: 100%; border: 0; display: block;
  background: transparent; }
.runlist.as-rows .cardlanes.live { display: none; }

/* the card's still is a video now; it keeps the shape the image had */
video.runthumb { object-fit: cover; background: var(--well); }

/* The card's live lanes, with the row labels Grafana cannot draw.
   Same device as the timeline's merged grid, at card size: the icons are a
   fixed column, the panel is the body, and the icon rows divide the panel's
   own height so the two line up. */
/* Every card is the same height, so the archive reads as a grid rather
   than a ragged column -- which means the panel cannot grow with the run.
   It is a fixed 252px on every card, Grafana divides that by however many
   dimensions the run has, and the icon column divides it the same way. The
   icons then shrink to fit rather than overlapping, which is what a fixed
   13px did at ten rows in 96px. 252 rather than 180: at ten dimensions
   that is 25px a row instead of 18, which is the difference between icons
   that clear each other and icons that merely do not overlap. */
.cardgrid { --panel-h: 252px; display: grid; grid-template-columns: 18px 1fr;
  gap: 6px; align-items: stretch; margin: 6px 0 4px; }
.cardgrid-keys { display: grid;
  grid-auto-rows: calc(var(--panel-h) / var(--rows));
  align-items: center; justify-items: center;
  /* the plot keeps a few pixels of its own inset; this is the closest a
     document can get to it from outside the frame */
  padding: 5px 0; }
.cardgrid-keys .ic { opacity: 0.85;
  width: min(14px, calc(var(--panel-h) / var(--rows) - 4px));
  height: min(14px, calc(var(--panel-h) / var(--rows) - 4px)); }
/* A slot nothing filled. Not padding: a category this system watched for
   and did not see, which is worth a mark rather than a gap. */
.cardgrid-keys .ic.unseen { opacity: 0.22; }
.runlist.as-rows .cardgrid { display: none; }

/* One chart at a time, and the plot cropped to its own rows.
   Grafana draws a header above every panel -- the title, and its own
   attribution badge -- which pushed the squares down by a row's worth and
   left the icons beside them pointing at nothing. The frame is taller than
   its box by exactly that header and pulled up through it, so what remains
   is the plot, top-aligned with the first icon. */
.cardplot { position: relative; overflow: hidden; display: block;
  height: var(--panel-h); border-radius: 4px; }
.cardplot .cardlanes { position: absolute; inset: 0; width: 100%; }
.cardlanes.live { height: calc(100% + 34px); margin-top: -34px; border: 0;
  background: transparent; }
.cardlanes.drawn { height: 100%; object-fit: contain; object-position: left top; }
/* the same chart once it is real markup rather than a picture, so its dots
   can be hovered: same box, and the dot under the pointer grows */
span.cardlanes.drawn { overflow: hidden; }
/* .lanes carries a 720px floor for the full-page chart. Inlined into a
   card that floor made the card 720 wide, which on a phone made the whole
   archive wider than the screen and scrolled the page sideways. */
span.cardlanes.drawn svg { width: 100%; height: 100%; min-width: 0;
  display: block; }
.lane-dot { transition: r .12s ease; }
span.cardlanes.drawn .lane-dot:hover { r: 7px; cursor: pointer; }
/* Which chart is in the box, and what stands in while the live one is
   still coming. One Grafana boots at a time, on approach, so on a long
   archive the panels arrive over several seconds -- and the slot they
   arrive into used to be blank. The card's own drawing holds it instead,
   and hands over the moment the iframe has painted. The drawing carries
   its own icon gutter, so the HTML icon column steps aside while it is
   the one on show, and no card ever wears two. */
.cardviz[data-viz="live"].booted .cardlanes.drawn,
.cardviz[data-viz="drawn"] .cardlanes.live,
.cardviz[data-viz="live"]:not(.booted) .cardlanes.live { display: none; }
.cardviz[data-viz="live"]:not(.booted) .cardgrid-keys { display: none; }
.cardviz[data-viz="live"]:not(.booted) .cardgrid { grid-template-columns: 1fr; }
/* The drawing carries its own labels in its own gutter, so the shared icon
   column beside it was a second copy of the same six marks. Only the live
   panel needs borrowed labels: Grafana cannot draw a taxonomy glyph. */
.cardviz[data-viz="drawn"] .cardgrid-keys { display: none; }
.cardviz[data-viz="drawn"] .cardgrid { grid-template-columns: 1fr; }
/* No viewer deployed: the drawing is the only chart, and it keeps the
   height it had rather than being cropped to a panel that is not there. */
.cardviz:not([data-viz]) .cardplot, .cardplot:not(:has(.cardlanes.live)) {
  height: auto; }
.cardplot:not(:has(.cardlanes.live)) .cardlanes.drawn { position: static; height: auto; }

/* the thumbnail's own wrapper, so a running run can wear the ring */
.runthumbwrap { display: block; position: relative; line-height: 0;
  border-radius: 6px; }
.runlist:not(.as-rows) .runthumbwrap { order: 1; margin-bottom: 10px; }
.runlist:not(.as-rows) .runthumbwrap .runthumb { width: 100%; margin: 0; }
.runthumbwrap.sparkle::before { border-radius: 7px; }
/* the star sits on the picture's top right corner, inside the card */
.runthumbwrap.sparkle::after { top: -6px; right: -2px; }

/* a country standing for its broadcasters */
.pill.folded .foldn { font-family: var(--mono); font-size: 10px;
  padding: 0 4px; border-radius: 999px; background: var(--line);
  color: var(--ink-2); font-weight: 400; }

/* which chart this card is showing */
.vizswap { position: absolute; top: 50px; left: 8px; z-index: 2;
  display: inline-flex; border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel); overflow: hidden; opacity: 0;
  transition: opacity .15s ease; }
.runcard:hover .vizswap, .vizswap:focus-within { opacity: 1; }
.vizswap button { appearance: none; border: 0; background: none; cursor: pointer;
  font-family: var(--mono); font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3); padding: 3px 9px; }
.vizswap button.on { background: var(--ground-2); color: var(--ink); }
.vizswap button:hover { color: var(--ink); }

/* the way in: the one control on a card that names the verb.
   At the top, in the flow, full width. It was at the bottom, under the
   flags and the "also cleared" dates, which is the last place anyone
   looks on a card they have already decided to open -- and before that it
   floated bottom-right, where it covered the channel pills on one card
   and the dates on another. A control out of the flow still needs the
   room it takes. */
.runlist:not(.as-rows) .runcard { display: flex; flex-direction: column; }
.runlist:not(.as-rows) .runcard > .runrow { flex: 1 1 auto; }
.cardgo { position: relative; margin: 0 0 10px; display: flex;
  align-items: center; justify-content: center; text-decoration: none;
  border-radius: 999px; padding: 2px; isolation: isolate; }
.cardgo-ring { position: absolute; inset: 0; border-radius: 999px;
  background: conic-gradient(from var(--spk),
    #4285F4, #EA4335, #FBBC05, #34A853, #4285F4);
  animation: spk-spin 3.4s linear infinite; }
.cardgo-face { position: relative; z-index: 1; display: flex; flex: 1 1 auto;
  align-items: center; justify-content: center; gap: 9px;
  padding: 10px 16px; border-radius: 999px;
  background: var(--panel); color: var(--ink);
  font-family: var(--mono); font-size: 12.5px; letter-spacing: .12em; }
.cardgo .ic { width: 17px; height: 17px; }
.cardgo:hover .cardgo-face { background: var(--ground-2); }
.cardgo:hover .cardgo-ring { animation-duration: 1.1s; }
.cardgo:focus-visible .cardgo-face { outline: 2px solid var(--g-blue);
  outline-offset: 2px; }
/* The list view is a table of rows; a rainbow button per row would be a
   fairground. The whole row stays the link there, as it was. */
.runlist.as-rows .cardgo, .runlist.as-rows .vizswap { display: none; }
@media (prefers-reduced-motion: reduce) {
  .cardgo-ring { animation: none; }
}

/* deleting a run: a quiet control that only shows itself on approach */
.runcard { position: relative; display: block; }
.runkill { position: absolute; top: 50px; right: 8px; z-index: 2; margin: 0;
  opacity: 0; transition: opacity .15s ease; }
.runcard:hover .runkill, .runkill:focus-within { opacity: 1; }
.runkill button { border: 1px solid var(--line-2); background: var(--panel);
  color: var(--ink-3); border-radius: 3px; width: 22px; height: 22px;
  line-height: 1; cursor: pointer; font-size: 14px; padding: 0; }
.runkill button:hover { border-color: var(--blocked); color: var(--blocked); }

/* One height for every card, across every row of the archive -- not just
   within a row, which is all the default stretch gives. grid-auto-rows: 1fr
   makes every row as tall as the tallest card on the page, and the cards
   fill it. What varied was the flag list: a run cleared for two markets and
   one cleared for twelve are otherwise the same card, so that list is
   capped and the rest of the card keeps its shape. */
.runlist:not(.as-rows) { grid-auto-rows: 1fr; }
.runlist:not(.as-rows) .runcard,
.runlist:not(.as-rows) .runrow { height: 100%; }
.runlist:not(.as-rows) .runrow .flags { max-height: 54px; overflow: hidden;
  align-content: flex-start; }

/* ---------- where the run is, right now ----------
   The feed already opens the newest stage and animates its bar, but the
   sparkle is the app's one word for "this is running", and the mission feed
   is the screen people watch while it does. So the live stage wears it too,
   which means the same mark says the same thing on the archive card, the
   market tile, the frame it is fixing and the line narrating it.

   On the summary rather than the whole group: the ring should say where the
   run IS, and a group can be twenty lines tall. The background is explicit
   so the conic gradient reads as a border rather than showing through. */
.grp.live > summary { position: relative; z-index: 0; background: var(--panel); }
.grp.live > summary::before {
  content: ""; position: absolute; inset: -2px; z-index: -1; border-radius: 5px;
  background: conic-gradient(from var(--spk),
    #4285F4, #EA4335, #FBBC05, #34A853, #4285F4);
  animation: spk-spin 2.6s linear infinite;
}
.grp.live > summary::after {
  content: "\2726"; position: absolute; top: -8px; right: -5px; z-index: 2;
  font-size: 12px; color: #FBBC05; pointer-events: none;
  animation: spk-twinkle 1.3s ease-in-out infinite;
  text-shadow: 0 0 6px rgba(251, 188, 5, 0.6);
}
@media (prefers-reduced-motion: reduce) {
  .grp.live > summary::before, .grp.live > summary::after { animation: none; }
}

/* The board's poster plays the film when pointed at. It sits over the
   rotating stack rather than replacing it: the rotation is the idle state,
   and a run whose master has been cleaned up keeps it. */
.board-preview { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity .18s ease;
  pointer-events: none; z-index: 3; }
.board-poster:hover .board-preview { opacity: 1; }
/* the rotation holds still while the film itself is playing over it */
.board-poster:hover.rotating .board-poster-stack img { animation-play-state: paused; }

/* ---------- powered by Grafana ----------
   Credited beside the mark on every screen, not buried in a footer: the
   crew writes to Grafana over MCP before anything reaches the console, and
   an alert in Grafana is what starts a render. The badge is the official
   lockup with our own "POWERED BY" set beside it. */
/* The two platform marks, wherever the console credits them: one row, a
   hairline between, shared baseline. Three sizes for three places. */
.brand-with, .hero-with, .ah-with { display: inline-flex; align-items: center; gap: 10px; }
.brand-with { margin-left: 14px; align-self: center; }
.brand-bar { display: block; width: 1px; height: 18px; background: var(--line-2); }
.brand-adk { display: block; height: 28px; width: 28px; }
.brand-grafana { display: block; height: 22px; width: auto; opacity: 0.9; }
.brand-with:hover .brand-grafana { opacity: 1; }
.hero-with { margin: 0 0 14px; }
.hero-grafana { display: block; height: 26px; width: auto; opacity: 0.92; }
.hero-with .brand-bar { height: 22px; }
@media (max-width: 900px) { .brand-with { display: none; } }

/* ---------- agent mode: a hero, and pointers ----------
   It used to open onto two grey panes and a blinking cursor, which tells a
   first-time visitor nothing. Same hero device as the landing and archive,
   then three numbered pointers, then the two panes. */
.agenthero { position: relative; overflow: hidden; padding: 10px 0 6px;
  border-bottom: 1px solid var(--line); flex: none; }
.agenthero .swarm { position: absolute; inset: 0; display: grid; gap: 26px;
  align-content: center; opacity: 0.05; pointer-events: none;
  mask-image: radial-gradient(120% 100% at 100% 50%, #000 30%, transparent 72%);
  -webkit-mask-image: radial-gradient(120% 100% at 100% 50%, #000 30%, transparent 72%); }
.agenthero-front { position: relative; }
.agenthero-kicker { font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--ink-3); display: block; margin-bottom: 6px; }
.agenthero-title { font-size: clamp(26px, 3.6vw, 40px); line-height: 1.12;
  margin: 6px 0 12px; letter-spacing: -0.015em; }
.agenthero-lede { max-width: 74ch; font-size: 13px; color: var(--ink-2);
  line-height: 1.5; margin: 0 0 8px; }
.agenthero-stats { display: flex; gap: 20px; align-items: center; flex-wrap: wrap;
  margin-bottom: 0; }
.ah-with { margin-left: auto; }
.ah-adk { display: block; height: 36px; width: 36px; }
.ah-grafana { display: block; height: 28px; width: auto; opacity: 0.9; }
.ah-with { display: inline-flex; align-items: center; gap: 12px; }
.ah-with .brand-bar { height: 30px; }
.ah-stat { font-size: 12.5px; color: var(--ink-2); }
.ah-stat b { font-family: var(--mono); font-size: 14px; color: var(--ink);
  margin-right: 4px; }
.agenthero-tools { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.ah-tool { font-family: var(--mono); font-size: 10.5px; padding: 2px 7px;
  border: 1px solid var(--line); border-radius: 3px; color: var(--ink-3);
  background: var(--panel); }

.agentsteps { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px; padding: 4px 0 0; flex: none; }
.ast { display: flex; gap: 12px; align-items: flex-start; }
.ast-n { flex: none; width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; font-size: 12px;
  background: var(--well); border: 1px solid var(--line-2); color: var(--ink-2); }
.ast b { font-size: 13.5px; display: block; margin-bottom: 3px; }
.ast p { font-size: 12px; color: var(--ink-3); line-height: 1.45; margin: 0; }

.sugg-label { display: block; width: 100%; margin-bottom: 2px; }
.ih-point { margin-top: 14px; font-size: 11px; color: var(--ink-3);
  letter-spacing: 0.12em; }

/* ---------- page titles ----------
   In-app screens name themselves; they do not narrate. The editorial
   sentence each of these used to shout at 38px now leads its subline,
   where it reads as explanation rather than as a headline. */
.pagetitle { display: flex; align-items: center; gap: 9px;
  font-size: clamp(17px, 1.9vw, 21px); font-weight: 600; letter-spacing: -0.01em;
  margin: 2px 0 8px; }
.pagetitle .ic { width: 19px; height: 19px; flex: none; }

/* the agent's tool list, folded: checkable, not shouted */
.ah-tools > summary { cursor: pointer; list-style: none; }
.ah-tools > summary::-webkit-details-marker { display: none; }
.ah-tools > summary::after { content: " ▸"; }
.ah-tools[open] > summary::after { content: " ▾"; }
.ah-toolrow { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 7px; }

/* ---------- the mission feed, in words ----------
   The feed is watched live by someone waiting to learn whether their
   commercial can air. It used to show only the crew's shorthand; now each
   stage leads with what is happening and why, and keeps the shorthand
   underneath so nothing is paraphrased away. */
.gtext { display: grid; gap: 3px; min-width: 0; }
.gtitle { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.gprose { font-size: 12.5px; color: var(--ink-2); line-height: 1.5; }
.grp .gm { font-size: 10.5px; color: var(--ink-3); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.grp.live .gtitle::after { content: "…"; color: var(--ink-3); }

/* ---------- the market room, one panel per scene ----------
   Flat, a market with twenty-five findings was one long ladder in which the
   same shot appeared five times. Closed, a scene is how it opens, how it
   closes, and a count; opened, it is every finding written against it. A
   scene with a fix in flight opens itself. */
.mkscene tr:not(.scene-row) { display: none; }
.mkscene[data-open] tr:not(.scene-row) { display: table-row; }
.scene-row { cursor: pointer; }
.scene-row:hover td { background: var(--ground-2); }
.scene-row:focus-visible td { outline: 2px solid var(--signal); outline-offset: -2px; }
.sc-chev { margin-left: auto; font-size: 20px; color: var(--ink-3); line-height: 1;
  transition: transform .15s ease; }
.mkscene[data-open] .sc-chev { transform: rotate(90deg); }

/* the scene strip filters rather than jumps */
.scenescroll .ss.on { outline: 2px solid var(--signal); outline-offset: 1px; }
.ss-all { display: grid; place-items: center; gap: 3px; min-width: 74px;
  border: 1px dashed var(--line-2); border-radius: 3px; background: transparent;
  cursor: pointer; color: var(--ink-2); }
.ss-allmark { font-size: 12px; font-weight: 600; }
tbody.mkscene[hidden] { display: none; }

/* the two doors */
.enter { max-width: 560px; padding: 36px 0; }
.enter-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin: 16px 0 10px; }
.enter-form .label { width: 100%; }
.enter-form .urlin { flex: 1; min-width: 220px; }
.fb > .sc[hidden] { display: none; }


/* ---------- a market judged in another pass of the same file ----------
   Two uploads of ad.mp4, one for France and one for the Gulf, are two runs
   of one commercial. The strip carries both now, and this is the tab that
   leaves this run: dashed underneath, so it reads as the same film seen
   somewhere else rather than as a tab that is merely inactive. */
.runnav .markets-row .tab.mk-elsewhere {
  border-bottom-style: dashed;
  opacity: 0.72;
}
.runnav .markets-row .tab.mk-elsewhere:hover { opacity: 1; }
.runnav .markets-row .tab.mk-elsewhere::after {
  content: "\2197"; margin-left: 4px; font-size: 10px; opacity: 0.8;
}

/* the film's other clearances, named on the board that opens a run */
.otherpass {
  display: flex; align-items: baseline; gap: 8px; margin: 12px 0 0;
  max-width: 92ch; font-size: 13px; color: var(--ink-2); line-height: 1.5;
}
.otherpass .ic { width: 15px; height: 15px; flex: none; align-self: center; }
.otherpass a { color: var(--g-blue); }


/* ---------- the agent's rail: guided, then reactive ----------
   Opening, it is three lanes in the order the work happens -- find out
   where you stand, look at the evidence, act -- because seven chips in one
   flat row read as a word cloud and nothing said which to press first.
   After the first turn customs.js replaces it with what to ask next, and
   that rail is flat on purpose: by then the conversation is the order. */
.agent-suggest.guided { display: grid; gap: 5px; }
/* the starters, folded: three numbered lanes are what you want when you do
   not know what to ask and not what you want to scroll past when you do */
details.agent-suggest > summary { cursor: pointer; display: block;
  padding: 4px 0 2px; }
details.agent-suggest > summary::marker,
details.agent-suggest > summary::-webkit-details-marker { color: var(--ink-3); }
details.agent-suggest[open] > summary { margin-bottom: 4px; }
.sugg-lane {
  display: grid; gap: 4px;
  padding: 5px 8px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--well);
}
.sugg-head { display: flex; align-items: center; gap: 6px; }
/* The pills stay on one line. In a 400px column a wrapping lane broke two
   pre-filled questions across three rows and the numbered lane it belonged
   to stopped reading as a step. It scrolls sideways instead, with a fade
   at the edge where there is more. */
.sugg-row { display: flex; gap: 5px; overflow-x: auto; scrollbar-width: none;
  -ms-overflow-style: none;
  mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 18px), transparent);
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 18px), transparent); }
.sugg-row::-webkit-scrollbar { display: none; }
.sugg-row .sugg { flex: 0 0 auto; white-space: nowrap; }
.sugg-step {
  flex: none; width: 15px; height: 15px; border-radius: 50%;
  display: grid; place-items: center; font-size: 9px; letter-spacing: 0;
  background: var(--g-blue); color: #fff;
}
.sugg-lead { flex: none; margin-right: 2px; font-size: 9.5px; }

/* ---------- the cross-run board: this app's axis, Grafana's bars ----------
   Grafana cannot draw this product's taxonomy marks and this product cannot
   chart Grafana's data live, so each draws the half it is good at: an icon
   column here, a bar chart in an iframe beside it, the panel's own y-axis
   labels hidden and the rows lined up by giving both the same row count.

   Same mechanism as the archive card's .cardgrid, at reading size: the key
   column's rows are the panel's height divided by the number of categories,
   which is the closest a document can get to laying out inside a frame it
   does not own. */
.xgrid {
  --panel-h: 340px;
  display: grid; grid-template-columns: 190px 1fr; gap: 10px;
  align-items: stretch; margin: 10px 0 6px;
}
.xkeys {
  /* Matched to the plot INSIDE the frame, not to the frame. A barchart
     keeps 22px above its first bar and 28px below the last for its value
     axis, so a column of icons spaced across the whole box drifts a row
     out by the bottom of a fifteen-row axis. Measured against the panel,
     the same way the 34px title crop was. */
  display: grid;
  grid-auto-rows: calc((var(--panel-h) - 50px) / var(--rows));
  align-items: center;
  padding: 22px 0 28px;
}
.xkey { display: flex; align-items: center; gap: 8px; min-width: 0; }
.xkey .ic {
  flex: none; opacity: 0.9;
  width: min(17px, calc(var(--panel-h) / var(--rows) - 3px));
  height: min(17px, calc(var(--panel-h) / var(--rows) - 3px));
}
.xkey .ic.country { stroke: var(--g-blue); stroke-width: 2.4; fill: none; }
.xname {
  font-size: 9.5px; color: var(--ink-2); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.xplot {
  position: relative; display: block; overflow: hidden;
  height: var(--panel-h); border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--panel);
}
.xpanel {
  position: absolute; left: 0; top: -34px; width: 100%;
  height: calc(100% + 34px); border: 0; display: block;
}
/* the whole dashboard, one frame. Tall on purpose: sixteen panels that
   scroll inside their own iframe is two scrollbars arguing. */
.xboard {
  width: 100%; height: 1850px; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--panel);
  display: block; margin: 10px 0 8px;
}
@media (max-width: 900px) {
  .xgrid { grid-template-columns: 1fr; }
  .xkeys { display: none; }
  .xboard { height: 1400px; }
}
/* the axis carries its own numbers when there are no bars to label */
.xn { margin-left: auto; font-size: 11px; color: var(--ink); font-weight: 600; }
.xnone {
  display: grid; align-content: center; gap: 6px; padding: 18px;
  height: var(--panel-h); border: 1px dashed var(--line-2);
  border-radius: var(--radius); background: var(--well);
}
.xnone p { margin: 0; max-width: 60ch; font-size: 12.5px; color: var(--ink-3);
  line-height: 1.5; }

/* ---------- the filmstrip: Grafana's blocks over the console's footage ----
   One bar per commercial in the frame above, one poster per commercial in
   the strip below, the same order and the same column widths. The panel
   cannot draw a still and the strip cannot draw a live query, so they are
   laid out to be read as one instrument. */
.filmstrip { --films: 1; --strip-h: 210px; margin: 10px 0 6px; }
.fs-plot {
  position: relative; display: block; overflow: hidden;
  height: var(--strip-h); border: 1px solid var(--line);
  border-bottom: none; border-radius: var(--radius) var(--radius) 0 0;
  background: var(--panel);
}
.fs-panel {
  position: absolute; left: 0; top: -34px; width: 100%;
  height: calc(100% + 34px); border: 0; display: block;
}
.fs-shots {
  display: grid; grid-template-columns: repeat(var(--films), 1fr); gap: 2px;
  border: 1px solid var(--line); border-radius: 0 0 var(--radius) var(--radius);
  border-top: none; padding: 6px; background: var(--well);
}
.fs-shot {
  position: relative; display: block; aspect-ratio: 16 / 10; min-width: 0;
  overflow: hidden; border-radius: 2px; background: var(--ground-2);
  /* the block's own state, carried onto the still: a 3px foot rather than a
     tint, because tinting a frame misrepresents the footage */
  border-bottom: 3px solid var(--line-2);
}
.fs-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fs-shot.s-cleared { border-bottom-color: var(--g-green); }
.fs-shot.s-noted   { border-bottom-color: var(--g-blue); }
.fs-shot.s-blocked { border-bottom-color: var(--g-red); }
.fs-shot:hover { outline: 2px solid var(--g-blue); outline-offset: -2px; }
.fs-n {
  position: absolute; right: 2px; bottom: 5px; padding: 0 3px;
  font-size: 9px; color: #fff; background: rgba(0, 0, 0, 0.55);
  border-radius: 2px;
}
.fs-gone { display: grid; place-items: center; height: 100%; font-size: 8px;
  color: var(--ink-3); }
@media (max-width: 900px) {
  .fs-shots { display: none; }
  .fs-plot { border-bottom: 1px solid var(--line); border-radius: var(--radius); }
}

/* ==========================================================================
   THE TOUR
   Thirteen slides and then a walk across the live console. The rainbow is
   the product's own four hues, rotating; it is loud here and nowhere else,
   because this is the one screen whose job is to be watched rather than
   read past.
   ========================================================================== */

.tour { position: relative; display: flex; flex-direction: column;
  min-height: calc(100vh - 150px); padding: 0 0 18px; }

/* the progress rail: one segment per slide, filled by a rainbow */
.tour-rail { position: relative; display: flex; gap: 3px; height: 4px;
  margin: 2px 0 18px; border-radius: 999px; overflow: hidden; }
.tr-seg { flex: 1 1 0; background: var(--line); }
.tr-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0;
  background: linear-gradient(90deg, #4285F4, #EA4335, #FBBC05, #34A853);
  transition: width .45s cubic-bezier(.2, .8, .2, 1); }

.tour-top { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 10px; }
.tour-count { font-size: 12px; color: var(--ink-3); letter-spacing: .1em; }
.tour-count b { color: var(--ink); }
.tour-tools { display: inline-flex; gap: 8px; align-items: center; }
.tour-tools .tbtn { text-decoration: none; }
#tour-auto[aria-pressed="true"] { color: var(--g-blue); border-color: var(--g-blue); }

/* one slide: words on the left, the product's own parts on the right */
.tour-stage { flex: 1 1 auto; display: flex; align-items: center; }
.ts { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 46px; align-items: center; width: 100%;
  animation: ts-in .5s cubic-bezier(.2, .8, .2, 1) both; }
/* The slide the script is not showing. A class selector beats the browser's
   own [hidden] { display: none }, so without this line all thirteen slides
   paint on top of each other -- which is exactly what the first capture of
   this page showed. */
.ts[hidden] { display: none; }
@keyframes ts-in { from { opacity: 0; transform: translateY(14px); } }
.ts-words { max-width: 62ch; }
.ts-eyebrow { display: block; color: var(--g-blue); margin-bottom: 14px; }
/* Down from 62px: the titles say what happens now instead of being three
   clever words, and a plain sentence at 62px is a wall. */
.ts-title { font-size: clamp(27px, 3.5vw, 44px); line-height: 1.08;
  letter-spacing: -0.025em; margin: 0 0 16px; }
.ts-lede { font-size: clamp(17px, 1.8vw, 24px); line-height: 1.32;
  margin: 0 0 16px; color: var(--ink); }
.ts-body { font-size: 15.5px; line-height: 1.65; color: var(--ink-2);
  margin: 0; max-width: 56ch; }
.ts-stat { display: inline-block; margin-top: 18px; padding: 7px 13px;
  border: 1px solid var(--line); border-radius: 999px; font-size: 11.5px;
  letter-spacing: .06em; color: var(--ink-2); background: var(--well); }
.ts-cta { display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
  margin-top: 26px; }
.ts-cta .tbtn { text-decoration: none; }

/* the button that moves you forward, wearing the ring */
.gobtn { appearance: none; border: 0; background: none; padding: 4px;
  border-radius: 999px; position: relative; cursor: pointer;
  isolation: isolate; text-decoration: none; display: inline-flex; }
.gobtn-ring { position: absolute; inset: 0; border-radius: 999px;
  background: conic-gradient(from var(--spk),
    #4285F4, #EA4335, #FBBC05, #34A853, #4285F4);
  animation: spk-spin 3s linear infinite; }
.gobtn-face { position: relative; z-index: 1; display: inline-flex;
  align-items: center; gap: 10px; padding: 15px 28px; border-radius: 999px;
  background: var(--panel); color: var(--ink); font-family: var(--mono);
  font-size: 14px; letter-spacing: .1em; }
.gobtn:hover .gobtn-ring { animation-duration: 1s; }
.gobtn:hover .gobtn-face { background: var(--ground-2); }
.gobtn .ic { width: 18px; height: 18px; }

/* the art panels */
.ts-art { display: flex; align-items: center; justify-content: center;
  min-height: 440px; }
.ta-logo { display: grid; justify-items: center; gap: 14px; }
.ta-bars { display: flex; gap: 6px; }
.ta-bars i { width: 70px; height: 8px; border-radius: 4px; }
.ta-bars i:nth-child(1) { background: #4285F4; }
.ta-bars i:nth-child(2) { background: #EA4335; }
.ta-bars i:nth-child(3) { background: #FBBC05; }
.ta-bars i:nth-child(4) { background: #34A853; }
.ta-word { font-size: 28px; line-height: 1.15; letter-spacing: .16em;
  text-align: center; }
.ta-with { display: flex; align-items: center; gap: 14px; opacity: .8; }

.ta-grid { display: grid; gap: 12px; width: 100%; }
.ta-world { grid-template-columns: repeat(4, 1fr); max-width: 470px; }
.ta-dims { grid-template-columns: repeat(5, 1fr); max-width: 470px; }
.ta-cell { display: grid; justify-items: center; gap: 7px; padding: 14px 6px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--well); }
.ta-cell .ic { width: 30px; height: 30px; }
.ta-cell b { font-size: 11px; color: var(--ink-2); letter-spacing: .06em; }
.ta-cell .ic.country { stroke: var(--g-blue); stroke-width: 2.4; fill: none; }
/* The taxonomy in the product's own four hues rather than eighteen grey
   glyphs: this slide's whole claim is breadth, and grey does not read as
   eighteen different things. */
.ta-dims .ta-cell:nth-child(4n + 1) .ic { color: #4285F4; }
.ta-dims .ta-cell:nth-child(4n + 2) .ic { color: #EA4335; }
.ta-dims .ta-cell:nth-child(4n + 3) .ic { color: #F9AB00; }
.ta-dims .ta-cell:nth-child(4n) .ic { color: #34A853; }

.ta-ladder { display: grid; gap: 10px; width: 100%; max-width: 520px; }
.ta-rung { display: grid; grid-template-columns: 26px 92px 1fr; gap: 12px;
  align-items: center; padding: 16px 18px; border: 1px solid var(--line);
  border-left: 3px solid var(--line-2);
  border-radius: var(--radius); background: var(--well); }
.ta-rung:nth-child(1) { border-left-color: #4285F4; }
.ta-rung:nth-child(2) { margin-left: 22px; border-left-color: #EA4335; }
.ta-rung:nth-child(3) { margin-left: 44px; border-left-color: #F9AB00; }
.ta-rung-last { margin-left: 66px; border-left-color: #34A853;
  border-color: var(--line-2); box-shadow: 0 6px 18px rgba(9, 12, 20, .06); }
.ta-rung b { font-family: var(--mono); font-size: 13px; }
.ta-rung i { font-style: normal; font-size: 12.5px; color: var(--ink-3); }

.ta-join { display: grid; gap: 10px; justify-items: start; max-width: 480px; }
.ta-jchip { display: inline-flex; align-items: center; gap: 9px; padding: 11px 16px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--well);
  font-size: 13.5px; }
.ta-jop, .ta-jeq { font-family: var(--mono); color: var(--ink-3);
  padding-left: 16px; font-size: 15px; }
.ta-jout { display: inline-flex; align-items: center; gap: 9px; padding: 14px 18px;
  border: 1px solid var(--blocked); border-radius: var(--radius);
  color: var(--blocked); font-size: 14px; background: var(--panel);
  box-shadow: 0 6px 18px rgba(234, 67, 53, .12); }

.ta-guard { display: grid; gap: 11px; max-width: 490px; padding: 24px;
  border: 1px solid var(--line-2); border-left: 3px solid var(--g-blue);
  border-radius: var(--radius); background: var(--panel); }
.ta-guard p { margin: 0; font-size: 14px; color: var(--ink-2); }
.ta-gopts { display: flex; flex-wrap: wrap; gap: 7px; }
.ta-gopts i { font-style: normal; font-size: 11px; padding: 5px 10px;
  border: 1px solid var(--line); border-radius: 999px; color: var(--ink-3); }

.ta-clips { display: grid; gap: 12px; width: 100%; }
.ta-clip { display: grid; gap: 5px; }
.ta-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.ta-pair video { width: 100%; border-radius: 4px; display: block;
  aspect-ratio: 16 / 9; object-fit: cover; background: var(--well); }

.ta-verify { display: grid; gap: 9px; width: 100%; max-width: 520px; }
.ta-vrow { display: grid; grid-template-columns: 62px 1fr; gap: 12px;
  align-items: center; padding: 13px 16px; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--well); font-size: 13.5px; }
.ta-vrow i { font-style: normal; color: var(--ink-2); }
.ta-vrow b { font-size: 10px; letter-spacing: .1em; color: var(--ink-3); }
.ta-vbad, .ta-vgood { grid-template-columns: 20px 1fr; }
.ta-vbad { border-color: rgba(234, 67, 53, .5); }
.ta-vgood { border-color: rgba(52, 168, 83, .5); }

.ta-graf { display: grid; justify-items: center; gap: 24px; }
.ta-graf > img { height: 58px; }
.ta-stores { display: flex; gap: 30px; align-items: center; }
.ta-stores img { height: 42px; }
.ta-gnote { text-align: center; max-width: 32ch; font-size: 12px; }

.ta-loop { display: grid; gap: 6px; justify-items: start; }
.ta-lstep { display: inline-flex; align-items: center; gap: 10px; padding: 12px 18px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--well);
  font-size: 13.5px; }
.ta-larrow { color: var(--ink-3); padding-left: 22px; font-size: 18px; }
.ta-lend { border-color: var(--cleared); color: var(--cleared); }

.ta-take { display: grid; gap: 12px; width: 100%; max-width: 480px; }
.ta-tcard { display: grid; grid-template-columns: 28px 1fr; gap: 14px;
  align-items: baseline; padding: 19px 20px; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--well); }
.ta-tcard b { font-family: var(--mono); font-size: 14px; }
.ta-tcard i { grid-column: 2; font-style: normal; font-size: 12.5px;
  color: var(--ink-3); }

.ta-lcd { display: flex; gap: 8px; align-items: flex-end; height: 280px;
  width: 100%; max-width: 420px; }
.ta-block { flex: 1 1 0; display: grid; align-content: end; justify-items: center;
  border-radius: 3px; padding-bottom: 8px;
  background: repeating-linear-gradient(180deg,
    currentColor 0 4px, transparent 4px 7px); }
.ta-block.s-blocked { color: var(--blocked); height: 100%; }
.ta-block.s-noted { color: var(--g-blue); height: 74%; }
.ta-block b { color: var(--ink); font-size: 12.5px; }

.ta-agent { display: grid; gap: 14px; width: 100%; max-width: 500px; }
.ta-said { padding: 15px 18px; border: 1px solid var(--line-2);
  border-radius: var(--radius) var(--radius) var(--radius) 4px;
  background: var(--panel); font-size: 15px; line-height: 1.4; }
.ta-atools { display: flex; flex-wrap: wrap; gap: 5px; }
.ta-atools .ah-tool { font-size: 10.5px; }
.ta-aout { display: grid; grid-template-columns: 20px 1fr; gap: 10px;
  align-items: start; padding: 14px 16px; border: 1px solid var(--line);
  border-left: 3px solid var(--g-blue); border-radius: var(--radius);
  background: var(--well); }
.ta-aout i { font-style: normal; font-size: 13px; color: var(--ink-2); }

.ta-walkmap { display: grid; gap: 6px 22px; width: 100%;
  grid-template-columns: 1fr 1fr; }
.ta-stop { display: grid; grid-template-columns: 22px 1fr; gap: 10px;
  font-size: 12.5px; color: var(--ink-2); padding: 7px 0;
  border-bottom: 1px solid var(--line); }
.ta-stop b { color: var(--g-blue); }

/* prev, next, and the dots */
.tour-nav { display: flex; align-items: center; justify-content: center;
  gap: 16px; margin-top: 22px; }
.tnav { appearance: none; width: 42px; height: 42px; border-radius: 999px;
  border: 1px solid var(--line-2); background: var(--panel); cursor: pointer;
  color: var(--ink-2); font-size: 17px; }
.tnav:hover { color: var(--ink); border-color: var(--g-blue); }
.tnav[disabled] { opacity: .3; cursor: default; }
.tour-dots { display: flex; gap: 7px; flex-wrap: wrap; justify-content: center; }
.tdot { appearance: none; width: 9px; height: 9px; padding: 0; cursor: pointer;
  border-radius: 999px; border: 1px solid var(--line-2); background: none; }
.tdot.on { background: conic-gradient(from var(--spk),
    #4285F4, #EA4335, #FBBC05, #34A853, #4285F4);
  border-color: transparent; animation: spk-spin 3s linear infinite;
  transform: scale(1.5); }

@media (max-width: 900px) {
  .ts { grid-template-columns: 1fr; gap: 24px; }
  .ts-art { min-height: 0; order: 2; }
  .ta-dims { grid-template-columns: repeat(6, 1fr); }
  .ta-walkmap { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  .ts { animation: none; }
  .tdot.on, .gobtn-ring { animation: none; }
}

/* --------------------------------------------------------------------------
   THE WALK: the same story, spotlit on the live console.
   -------------------------------------------------------------------------- */

.walk-veil { position: fixed; inset: 0; z-index: 90; pointer-events: none;
  background: rgba(9, 12, 20, .62); }
html[data-theme="studio"] .walk-veil { background: rgba(20, 24, 32, .45); }
/* the hole: a ring of shadow around the spotlit element, which needs no
   clip-path and survives the element moving or resizing */
.walk-hole { position: fixed; border-radius: 8px; pointer-events: none;
  box-shadow: 0 0 0 9999px rgba(9, 12, 20, .62), 0 0 0 3px transparent;
  outline: 3px solid transparent; outline-offset: 3px;
  transition: top .35s ease, left .35s ease, width .35s ease, height .35s ease; }
.walk-hole::before { content: ""; position: absolute; inset: -5px;
  border-radius: 11px; padding: 3px;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0); mask-composite: exclude;
  background: conic-gradient(from var(--spk),
    #4285F4, #EA4335, #FBBC05, #34A853, #4285F4);
  animation: spk-spin 2.4s linear infinite; }

.walk-bubble { position: fixed; z-index: 92; width: min(380px, 86vw);
  padding: 18px 20px 16px; border-radius: var(--radius);
  background: var(--panel); border: 1px solid var(--line-2);
  box-shadow: 0 20px 50px rgba(0, 0, 0, .34); }
.walk-bubble .wb-step { font-family: var(--mono); font-size: 10.5px;
  letter-spacing: .12em; color: var(--g-blue); }
.walk-bubble h3 { margin: 7px 0 8px; font-size: 17px; line-height: 1.2; }
.walk-bubble p { margin: 0; font-size: 13.5px; line-height: 1.55;
  color: var(--ink-2); }
.wb-foot { display: flex; align-items: center; gap: 10px; margin-top: 16px; }
.wb-foot .tbtn { text-decoration: none; }
.wb-rail { display: flex; gap: 3px; margin-top: 14px; }
.wb-rail i { flex: 1 1 0; height: 3px; border-radius: 2px; background: var(--line); }
.wb-rail i.on { background: var(--g-blue); }
.walk-arrow { position: absolute; z-index: 91; width: 0; height: 0;
  border: 9px solid transparent; }
@media (prefers-reduced-motion: reduce) {
  .walk-hole::before { animation: none; }
}

/* the third door: the invitation, and the only one with the ring on it */
.door-tour { border-color: var(--line-2); }
.door-tour .ic { color: var(--g-blue); }
/* a grid does not wrap: it drops to two columns and then to one */
@media (max-width: 1240px) { .doors-row { grid-template-columns: 1fr 1fr; } }
