/* ============================================================================
   styles.css — AI Central.

   Built entirely on the custom properties tokens.css defines, so the app inherits
   Geocoding's design system and all 28 themes without duplicating any color.
   Never hard-code a color here; if a token is missing, add it to tokens.css in
   BOTH apps so the two do not drift.
   ============================================================================ */

* { box-sizing: border-box; }

html, body {
  margin: 0; padding: 0; height: 100%;
  background: var(--bg); color: var(--txt);
  font-family: Inter, system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 14px; -webkit-font-smoothing: antialiased;
}

a { color: var(--ink); text-decoration: none; }
kbd, code, .mono { font-family: "JetBrains Mono", ui-monospace, monospace; }

/* ── Top bar ───────────────────────────────────────────────────────────── */

.topbar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  height: 52px; padding: 0 14px;
  background: var(--pane); border-bottom: 1px solid var(--rule);
  position: sticky; top: 0; z-index: 40;
}
.tb-left, .tb-right { display: flex; align-items: center; gap: 14px; }
.brand { display: flex; align-items: center; gap: 10px; }
.brand .mk { height: 26px; display: block; }
/* The mark alone says The Perfect Light, which is the company and not this application -- and there
   are several. The name is set quietly beside it: it identifies the app without competing with the
   screen heading, which is what somebody is actually reading. */
.brand .appname {
  font-size: 10.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--txt2); white-space: nowrap; padding-left: 10px;
  border-left: 1px solid var(--rule);
}
@media (max-width: 980px) { .brand .appname { display: none; } }

.tabs { display: flex; gap: 2px; }
.tab {
  padding: 6px 12px; border-radius: 6px; cursor: pointer; border: 1px solid transparent;
  color: var(--txt2); font-weight: 600; font-size: 13px; background: none;
}
.tab:hover { background: var(--hover); color: var(--txt); }
.tab.on { background: var(--sel); color: var(--a-over); border-color: var(--rule2); }

.clock { text-align: right; line-height: 1.1; font-family: "JetBrains Mono", monospace; }
.clock span { font-size: 13px; color: var(--txt); }
.clock small { display: block; font-size: 10px; color: var(--txt2); }

/* Shared by the agent cards and the running-run chip: the "this is working right now" signal. */

/* Live badge beside the Agents heading: how many agents are working right now. */
.livebadge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 500; letter-spacing: .04em; text-transform: uppercase;
  color: var(--txt2); border: 1px solid var(--line); border-radius: 999px;
  padding: 2px 9px; vertical-align: middle; margin-left: 8px;
}
.livedot { width: 8px; height: 8px; border-radius: 50%; background: var(--txt3); }
.livebadge.live { color: var(--pos); border-color: var(--pos); }
.livebadge.live .livedot { background: var(--pos); animation: pulse 1s ease-in-out infinite; }

@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .35; transform: scale(.8); }
}

/* ── Theme picker (same interaction as Geocoding) ──────────────────────── */

.thpick { position: relative; }
.thbtn {
  display: flex; align-items: center; gap: 7px; cursor: pointer;
  padding: 5px 10px; border: 1px solid var(--rule); border-radius: 6px;
  background: var(--pane2); color: var(--txt2); font-size: 12px; font-weight: 600;
}
.thbtn:hover { background: var(--hover); color: var(--txt); }
.thsw { width: 12px; height: 12px; border-radius: 3px; border: 1px solid var(--rule2); }
.thmenu {
  display: none; position: absolute; right: 0; top: 34px; z-index: 60;
  background: var(--pane); border: 1px solid var(--rule); border-radius: 8px;
  box-shadow: var(--shadow); padding: 6px; max-height: 60vh; overflow-y: auto; min-width: 190px;
}
.thmenu.open { display: block; }
.thmenu button {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 6px 8px; border: 0; border-radius: 5px; cursor: pointer;
  background: none; color: var(--txt2); font-size: 12px; text-align: left;
}
.thmenu button:hover { background: var(--hover); color: var(--txt); }

/* ── Layout ────────────────────────────────────────────────────────────── */

/* Sub-tab bar for a screen that contains other screens (Settings).

   Same pattern as TPL CRM and AutoIQ: a full-bleed band on --head, flat uppercase labels, and a 2px
   accent underline marking the active one. Deliberately NOT the pill treatment the main tab bar uses
   -- the two bars sit directly above one another, and giving them the same shape made it ambiguous
   which level a click would act on. Different shape, same family.

   Hidden with display:none rather than left empty so it takes no vertical space on every other
   screen; an empty band with padding and a border would draw a stray line under the top bar. */

/* ── Dense table ──────────────────────────────────────────────────────────────────────────────────
   The CRM/AutoIQ reading pattern: many rows visible at once, scannable down a column. Used where the
   question is "find the one row I care about" (audit log, users) rather than "read this one thing"
   (an agent card). Tighter padding and a smaller, heavier, uppercase header than the default table,
   because at this density the header has to read as chrome rather than as a first row. */
.dtable { width: 100%; border-collapse: collapse; font-size: 12px; }
/* --txt2, not --txt3. Measured at 3.67:1 against the header band, under the 4.5:1 that small text
   needs -- and these are 9.5px uppercase, the hardest thing on the screen to read. Raised the size a
   little too: a column heading nobody can read is a table nobody can sort. */
.dtable thead th {
  position: sticky; top: 0; z-index: 1; background: var(--head);
  text-align: left; font-size: 10.5px; font-weight: 700; letter-spacing: .6px;
  text-transform: uppercase; color: var(--txt2); white-space: nowrap;
  padding: 8px 10px; border-bottom: 1px solid var(--rule);
}
.dtable tbody td {
  padding: 6px 10px; border-bottom: 1px solid var(--rule);
  color: var(--txt2); vertical-align: middle;
}
.dtable tbody tr:last-child td { border-bottom: 0; }
.dtable tbody tr:hover td { background: var(--hover); }
.dtable tbody tr.clickable { cursor: pointer; }
.dtable td.nowrap, .dtable th.nowrap { white-space: nowrap; }
.dtable td.num { text-align: right; font-family: "JetBrains Mono", monospace; }
/* The header has to follow its column. `.dtable thead th` sets text-align:left and outranks the bare
   `.num` rule on specificity, so every numeric heading sat left while its values sat right -- visible on
   Mappings as MAPPED and UNMAPPED floating away from their figures. Applies to every dtable, not one. */
.dtable thead th.num { text-align: right; }
/* The one column that may wrap. Everything else stays on one line so rows keep a constant height and
   the eye can track down a column. */
.dtable td.wrap { white-space: normal; }
.dtable .t-mono { font-family: "JetBrains Mono", monospace; font-size: 11px; color: var(--txt2); }
.dtable .t-strong { color: var(--txt); font-weight: 600; }

/* Full width, not a centerd column. These screens are wide tables -- thirty columns and a detail
   panel -- and a 1500px column on a 2560px monitor leaves a third of the window empty beside data
   that wanted it. The cap stays only as a guard against a line of text running the whole width on
   the screens that are prose rather than tables. */
#view { padding: 18px 22px; max-width: 2200px; margin: 0 auto; }

.screen-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; margin-bottom: 14px;
}
.screen-head h1 { margin: 0; font-size: 19px; font-weight: 700; color: var(--txt); }
/* --txt2. The subtitle under a screen heading is the sentence explaining what the screen is for --
   the one thing a first-time reader actually reads -- and it measured 3.50 in the light theme. */
.screen-head .sub { color: var(--txt2); font-size: 12px; }
/* The actions on the right of a heading sit on one line. `.row` has no rule of its own, so a select
   here took the global width: 100% and stacked the Agents controls into a column. */
.screen-head > .row:last-child {
  display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 8px;
}
.screen-head > .row:last-child select { width: auto; }

.grid { display: grid; gap: 12px; }
.grid.cards { grid-template-columns: repeat(auto-fill, minmax(310px, 1fr)); }

.panel {
  background: var(--pane); border: 1px solid var(--rule); border-radius: 10px; padding: 14px;
}

/* ── Agent cards ───────────────────────────────────────────────────────── */

.agent {
  background: var(--pane); border: 1px solid var(--rule); border-radius: 10px;
  padding: 13px 14px; cursor: pointer; position: relative; overflow: hidden;
  transition: border-color .15s, transform .1s;
}
.agent:hover { border-color: var(--rule2); transform: translateY(-1px); }

/* The flashing state. A running agent has to be obvious from across the room, so it gets a
   moving sweep as well as a pulsing dot — color alone reads as static on a glance. */
.agent.live { border-color: var(--pos); }
.agent.live::after {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 100%; height: 2px;
  background: linear-gradient(90deg, transparent, var(--pos), transparent);
  animation: sweep 1.4s linear infinite;
}
@keyframes sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}

/* ── Mapping groups ───────────────────────────────────────────────────────────────────────────────
   The screen answers "is anything waiting on me?" before it answers "what does each object map to",
   so the objects live inside collapsible decision groups rather than in one flat list. */
.mapgroup { border: 1px solid var(--rule); border-radius: 10px; overflow: hidden; margin-bottom: 10px; }
.mapgroup-head {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; cursor: pointer;
  padding: 10px 13px; border: 0; background: var(--head); font: inherit; color: var(--txt2);
}
.mapgroup-head:hover { background: var(--hover); }
.mapgroup-caret { color: var(--txt2); font-size: 11px; width: 10px; }
.mapgroup-label {
  font-size: 11px; font-weight: 800; letter-spacing: .6px; text-transform: uppercase; color: var(--txt);
}
.mapgroup.open .mapgroup-head { border-bottom: 1px solid var(--rule); }
.mapgroup .tablewrap { border: 0; border-radius: 0; }
/* A group with work in it for a person: marked like the banner above it, so it cannot be read as settled. */
.mapgroup.needs { border-color: color-mix(in srgb, var(--warn) 45%, var(--rule)); box-shadow: inset 3px 0 0 var(--warn); }
.mapgroup.needs .mapgroup-head { background: color-mix(in srgb, var(--warn) 7%, var(--head)); }
/* The day a row was proposed or landed, heading the rows of that day: newest day first. */
.dtable tr.mapday td {
  background: var(--pane2); color: var(--txt2); font-size: 10.5px; font-weight: 800;
  letter-spacing: .5px; text-transform: uppercase; padding: 6px 12px;
}

/* ── Mapping review ───────────────────────────────────────────────────────────────────────────────
   Source on the left, destination on the right, an arrow between them: the shape of the decision
   being made, so a reviewer reads a row rather than parsing one.

   Rows still needing a decision are marked and sorted to the top. An unmapped field is the only thing
   on this screen that requires action, and a reviewer who has to hunt for them will approve without
   finding them -- which defeats the point of a human gate. */
.maptable .arrow { width: 26px; text-align: center; color: var(--txt3); }
.maptable td { vertical-align: middle; }
.maptable select { font-size: 12px; padding: 3px 6px; }
.maptable tr.needs td { background: color-mix(in srgb, var(--warn) 9%, transparent); }
.maptable tr.needs .arrow { color: var(--warn); font-weight: 700; }
.maptable tr.needs td:first-child { box-shadow: inset 2px 0 0 var(--warn); }

/* ── Agent scope panel ────────────────────────────────────────────────────────────────────────────
   Shown at the top of an agent's drawer, above its history. The limit line carries an accent edge
   because "what this will never do" is the sentence people actually need before trusting an agent to
   run unattended against the CRM, and it reads as boilerplate without one. */
.scope-purpose { margin: 0 0 9px; font-size: 13px; color: var(--txt); line-height: 1.5; }
.scope-limit {
  font-size: 12px; color: var(--txt2); line-height: 1.5;
  border-left: 2px solid var(--amber); padding-left: 9px; margin-bottom: 11px;
}
.scope-limit strong { color: var(--txt); }
.scope-io { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.scope-io h5, .scope-objects h5 {
  margin: 0 0 4px; font-size: 9.5px; font-weight: 800; letter-spacing: .7px;
  text-transform: uppercase; color: var(--txt3);
}
.agent-io { margin: 0; padding-left: 15px; font-size: 11.5px; color: var(--txt2); line-height: 1.55; }
.scope-objects { margin-top: 11px; }

/* ── The chain, drawn as a live wire ──────────────────────────────────────────────────────────────
   The screen used to be eleven cards in a grid, which showed WHAT exists and nothing about the order
   they run in -- the single most important fact about this platform. This draws the order, and lights
   the wires while anything is running so the page reads as a system doing work rather than a list.

   Wraps rather than scrolls: eight stages fit on a wide screen and fold to two rows on a laptop, and a
   horizontally-scrolling diagram hides exactly the end of the chain people forget about. */
.flow {
  background: var(--pane); border: 1px solid var(--rule); border-radius: 10px;
  padding: 13px 14px; margin-bottom: 14px;
}
.flow-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 11px; }
.flow-title {
  font-size: 11px; font-weight: 800; letter-spacing: .7px; text-transform: uppercase; color: var(--txt2);
}
.flow-rail { display: flex; align-items: stretch; flex-wrap: wrap; gap: 0; }

.node {
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  min-width: 108px; padding: 8px 11px; cursor: pointer; text-align: left;
  background: var(--pane2); border: 1px solid var(--rule); border-radius: 6px;
  font: inherit; color: var(--txt2);
}
.node:hover { border-color: var(--rule2); background: var(--hover); }
.node-pos {
  font-family: "JetBrains Mono", monospace; font-size: 9px; font-weight: 700; color: var(--txt2);
}
.node-label { font-size: 12.5px; font-weight: 700; color: var(--txt); }
.node-pos, .node-state { color: color-mix(in srgb, var(--txt2) 78%, var(--txt)); }
.node-state { font-size: 10px; color: var(--txt2); }
.node.passed .node-state { color: color-mix(in srgb, var(--pos) 55%, var(--txt)); }

/* State reads from the border and the label, never from fill alone -- a filled node at eight-across
   turns the rail into a color bar and stops being scannable. */
.node.ok   { border-color: var(--rule2); }
.node.bad  { border-color: var(--neg); }
.node.bad .node-state { color: var(--neg); }
.node.idle { opacity: .72; border-style: dashed; }
.node.live {
  border-color: var(--pos); color: var(--txt);
  box-shadow: 0 0 0 1px var(--pos) inset, 0 0 14px -4px var(--pos);
  animation: nodebeat 1.2s ease-in-out infinite;
}
.node.live .node-state { color: var(--pos); font-weight: 700; }
@keyframes nodebeat {
  0%, 100% { box-shadow: 0 0 0 1px var(--pos) inset, 0 0 14px -4px var(--pos); }
  50%      { box-shadow: 0 0 0 1px var(--pos) inset, 0 0 20px -2px var(--pos); }
}

/* The wire between two stages. Static gray at rest; while anything in the platform is running, a
   pulse travels along it in the direction data actually moves, left to right. */
.wire {
  align-self: center; flex: 0 0 26px; height: 2px; margin: 0 2px;
  background: var(--rule2); border-radius: 2px; position: relative; overflow: hidden;
}
.wire.flowing::after {
  content: ""; position: absolute; inset: 0; width: 40%;
  background: linear-gradient(90deg, transparent, var(--pos), transparent);
  animation: wireflow 1.1s linear infinite;
}
@keyframes wireflow {
  0%   { transform: translateX(-120%); }
  100% { transform: translateX(320%); }
}

/* On a narrow screen the rail wraps, and a wire that lands at the start of a new row points at
   nothing. Hidden there rather than left dangling. */
@media (max-width: 820px) {
  .wire { flex-basis: 14px; }
}

@media (prefers-reduced-motion: reduce) {
  .node.live { animation: none; }
  .wire.flowing::after { animation: none; opacity: .55; }
}

/* ── The brain ────────────────────────────────────────────────────────────────────────────────────
   The orchestrator sits ABOVE the chain, joined to it by a stem. It was previously a card underneath,
   which put the thing that decides the order below the order it decides. It reads as a brain because
   that is exactly what it is: it owns no data and runs no stage itself -- every change is made by a
   stage it called. */
.brain {
  display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  padding: 13px 16px; cursor: pointer;
  border: 1px solid var(--rule2); border-radius: 10px;
  background: linear-gradient(180deg, var(--head), var(--pane) 70%);
  border-top: 3px solid var(--amber);
}
.brain:hover { border-color: var(--amber); }
.brain-core { display: flex; align-items: center; gap: 12px; }
.brain-mark { font-size: 22px; color: color-mix(in srgb, var(--amber) 70%, var(--txt)); line-height: 1; }
.brain-name { font-size: 16px; font-weight: 700; color: var(--txt); }
.brain-sub { font-size: 11.5px; color: var(--txt2); margin-top: 2px; }
.brain-meta { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.brain.live { border-color: var(--pos); box-shadow: 0 0 18px -6px var(--pos); }

/* The stem joining the brain to the chain it drives. Purely connective, and short enough that the two
   read as one unit rather than two stacked panels. */
.brain-stem { display: flex; justify-content: center; height: 14px; }
.brain-stem > span { width: 2px; height: 100%; background: var(--rule2); }
.brain.live + .brain-stem > span { background: var(--pos); }

/* The human gate, drawn in the chain. Amber because it is the one node that is waiting on a PERSON,
   which is a different kind of "not done" from a stage that has simply not run yet. */
.node.gate { border-style: solid; border-color: var(--amber); background: var(--pane); }
.node.gate .node-label { color: color-mix(in srgb, var(--amber) 62%, var(--txt)); }
.node.gate .node-pos { color: color-mix(in srgb, var(--amber) 62%, var(--txt)); }
.node.gate.waiting {
  box-shadow: 0 0 0 1px var(--amber) inset;
  animation: nodebeat 1.6s ease-in-out infinite;
}
.node.gate.waiting .node-state { color: color-mix(in srgb, var(--amber) 62%, var(--txt)); font-weight: 700; }
.node.gate.passed { opacity: .95; }
.node.gate.passed .node-state { color: var(--pos); }

/* Running has to be obvious from across the room, so it is stated three ways: the card sweeps, the
   chip pulses, and the dot beats. Color alone reads as static on a glance, and color plus one
   animation was still easy to miss on a grid of eleven cards. */
.rundot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--pos);
  display: inline-block; animation: pulse 1s ease-in-out infinite;
}
.chip.live { animation: livechip 1.6s ease-in-out infinite; }
@keyframes livechip {
  0%, 100% { border-color: var(--pos); box-shadow: 0 0 0 0 transparent; }
  50%      { border-color: var(--pos); box-shadow: 0 0 0 3px color-mix(in srgb, var(--pos) 22%, transparent); }
}

/* A running agent lifts slightly and keeps the sweep. Reduced-motion users get the color and the
   border only -- the state must still be readable without any animation at all. */
.agent.live { box-shadow: 0 0 0 1px var(--pos) inset; }

@media (prefers-reduced-motion: reduce) {
  .agent.live::after, .rundot, .chip.live { animation: none; }
}

.agent-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.agent-name { font-weight: 700; font-size: 14px; color: var(--txt); }
.agent-id { font-family: "JetBrains Mono", monospace; font-size: 11px; color: var(--txt2); }
.agent-meta { margin-top: 9px; font-size: 12px; color: var(--txt2); line-height: 1.55; }
.agent-last { margin-top: 8px; font-size: 12px; color: var(--txt2); border-top: 1px dashed var(--rule); padding-top: 8px; }

/* --a-over, not --ink. Fourth instance of the same trap: --ink is near-white in the light themes, so
   the stage name on a --sel fill rendered as a pale smudge. */
.stage {
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 4px; background: var(--sel); color: var(--a-over);
}

/* ── Chips / status ──────────────────────────────────────────────────────
   Rectangular, not pill-shaped. Every other edge in this UI is a 4-10px radius -- tables, panels,
   cards, inputs -- and fully-round chips read as a different design language pasted in, which was
   most obvious in the audit log where a column of them sits against square table cells. */

.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 700;
  border: 1px solid var(--rule); color: var(--txt2); background: var(--pane2);
  white-space: nowrap;
}
.chip.ok       { color: var(--pos);  border-color: var(--pos); }
.chip.bad      { color: var(--neg);  border-color: var(--neg); }
/* Tinted like the source chips. A bordered chip with no fill leaves its text on the page color,
   which is where the 3.95 came from. */
.chip.warn     { color: color-mix(in srgb, var(--warn) 55%, var(--txt));
                 border-color: color-mix(in srgb, var(--warn) 70%, transparent);
                 background: color-mix(in srgb, var(--warn) 12%, var(--pane2)); }
.chip.muted    { color: var(--txt2); background: color-mix(in srgb, var(--txt3) 10%, var(--pane2)); }
.chip.live     { color: var(--pos);  border-color: var(--pos); }

/* Source badges. Two sources map to one canonical table -- central.payments arrives from both -- so
   the source has to be readable at a glance rather than looked for. Color is keyed off the source id
   so a source keeps the same badge on every screen; anything unrecognized keeps the neutral chip
   rather than borrowing another source's identity. Tinted background, not just text, because these
   repeat down a column and a full-color chip on every row would shout. */
.chip.src              { font-weight: 700; letter-spacing: .02em; }
/* --cyan and --amber rather than --pos/--warn: every theme defines both, and neither already means
   "good" or "careful" somewhere else on the screen. A source is an identity, not a judgment. */
/* Each hue mixed toward the text color rather than used neat. Measured on the real screens the neat
   accents came out at 3.69 (Jobber), 3.99 (ServiceMinder) and 4.43 (ServiceTitan) against their own
   tint -- all under AA for 11px text, and these repeat down every row of every data screen. Mixing
   keeps the three visibly different while making each of them readable. */
.chip.src-serviceminder{ color: color-mix(in srgb, var(--cyan) 62%, var(--txt));
                         border-color: color-mix(in srgb, var(--cyan) 45%, transparent);
                         background: color-mix(in srgb, var(--cyan) 12%, var(--pane2)); }
.chip.src-servicetitan { color: color-mix(in srgb, var(--amber) 62%, var(--txt));
                         border-color: color-mix(in srgb, var(--amber) 45%, transparent);
                         background: color-mix(in srgb, var(--amber) 12%, var(--pane2)); }
/* Jobber had none and fell through to the neutral chip, which reads as "source unknown" rather than as
   a third source. --a-assign because it is the one accent no other chip on these screens uses. */
.chip.src-jobber       { color: color-mix(in srgb, var(--a-assign) 62%, var(--txt));
                         border-color: color-mix(in srgb, var(--a-assign) 45%, transparent);
                         background: color-mix(in srgb, var(--a-assign) 12%, var(--pane2)); }

/* ── Tables ────────────────────────────────────────────────────────────── */

.tablewrap { overflow-x: auto; border: 1px solid var(--rule); border-radius: 10px; background: var(--pane); }
table { border-collapse: collapse; width: 100%; font-size: 13px; }
thead th {
  position: sticky; top: 0; background: var(--head); z-index: 1;
  text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--txt3);
  padding: 9px 11px; border-bottom: 1px solid var(--rule);
}
tbody td { padding: 9px 11px; border-bottom: 1px solid var(--rule); color: var(--txt2); vertical-align: top; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--hover); }
td.num { text-align: right; font-family: "JetBrains Mono", monospace; }

/* ── Buttons and forms ─────────────────────────────────────────────────── */

.btn {
  padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 600;
  border: 1px solid var(--rule); background: var(--pane2); color: var(--txt);
}
.btn:hover { background: var(--hover); border-color: var(--rule2); }
/* Colored from --a-over, NOT --ink — the same fix already applied to .authbtn.go, which this class
   was missed by. --ink means "text that contrasts with an ACCENT FILL" and is therefore light in the
   light themes; on the --sel fill used here that rendered #f7f8fa on #d6e0ea, a contrast ratio of
   about 1.2:1. The button was there, sized and clickable, and all but invisible — which reads as a
   disabled control rather than the primary action. --a-over is defined to contrast with the pane in
   every one of the 28 themes. */
.btn.primary { background: var(--sel); color: var(--a-over); border-color: var(--a-over); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn.sm { padding: 3px 9px; font-size: 11px; }

input[type=text], input[type=password], input[type=number], input[type=date], select {
  padding: 6px 9px; border-radius: 6px; font-size: 13px;
  border: 1px solid var(--rule); background: var(--bg2); color: var(--txt);
  font-family: inherit; width: 100%;
}
input:focus, select:focus { outline: none; border-color: var(--ink); }
label.fld { display: block; margin-bottom: 11px; }
label.fld .lbl { display: block; font-size: 11px; font-weight: 700; color: var(--txt3); margin-bottom: 4px; text-transform: uppercase; letter-spacing: .04em; }
label.fld .hint { display: block; font-size: 11px; color: var(--txt3); margin-top: 4px; line-height: 1.45; }

/* ── Activity feed ─────────────────────────────────────────────────────── */

.act { border-bottom: 1px solid var(--rule); padding: 10px 12px; }
.act:last-child { border-bottom: 0; }
.act-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.act-when { font-family: "JetBrains Mono", monospace; font-size: 11px; color: var(--txt3); }
.act-sum { margin-top: 5px; color: var(--txt); font-size: 13px; }
.act-actor { font-size: 11px; color: var(--txt3); }
.act-more { margin-top: 7px; }
.act-more summary { cursor: pointer; font-size: 11px; color: var(--txt3); }
.act-more summary:hover { color: var(--ink); }
.diff { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-top: 8px; }
.diff pre {
  margin: 0; padding: 8px; border-radius: 6px; overflow-x: auto;
  background: var(--bg2); border: 1px solid var(--rule);
  font-family: "JetBrains Mono", monospace; font-size: 11px; color: var(--txt2);
}
.diff .before { border-left: 2px solid var(--neg); }
.diff .after  { border-left: 2px solid var(--pos); }
.diff h5 { margin: 0 0 4px; font-size: 10px; color: var(--txt3); text-transform: uppercase; letter-spacing: .05em; }

/* ── Detail drawer ─────────────────────────────────────────────────────── */

.detailwin {
  display: none; position: fixed; top: 52px; right: 0; bottom: 0; width: min(660px, 94vw);
  background: var(--pane); border-left: 1px solid var(--rule); box-shadow: var(--shadow);
  z-index: 50; overflow-y: auto;
}
.detailwin.open { display: block; }
.detailwin.wide { width: min(1180px, 94vw); }
.dw-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 18px; border-bottom: 1px solid var(--rule);
  position: sticky; top: 0; background: var(--pane); z-index: 2;
}
.dw-head h2 {
  margin: 0; font-size: 15.5px; font-weight: 650; color: var(--txt);
  display: flex; align-items: center; gap: 9px; min-width: 0;
}
.dw-body { padding: 16px 18px 26px; }

/* ── Toasts ────────────────────────────────────────────────────────────── */

#toasts { position: fixed; bottom: 18px; right: 18px; z-index: 90; display: flex; flex-direction: column; gap: 8px; }
.toast {
  padding: 9px 13px; border-radius: 7px; font-size: 13px;
  background: var(--pane); border: 1px solid var(--rule); border-left-width: 3px;
  color: var(--txt); box-shadow: var(--shadow); max-width: 430px;
}

/* ── Boot splash ───────────────────────────────────────────────────────── */

#boot {
  position: fixed; inset: 0; z-index: 100; background: var(--bg);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 9px;
  transition: opacity .3s;
}
#boot.gone { opacity: 0; pointer-events: none; }
#boot .bmk { height: 46px; }
#boot .bt { font-size: 17px; font-weight: 800; letter-spacing: .16em; color: var(--txt); }
#boot .bs { font-size: 12px; color: var(--txt3); }
#boot .bootlog { font-family: "JetBrains Mono", monospace; font-size: 11px; color: var(--txt3); margin-top: 7px; }

/* Budget screen. A spend figure and a fill bar, because "are we near the limit" should be readable
   without parsing two numbers and dividing them. */
.bigfig {
  font-family: "JetBrains Mono", monospace;
  font-size: 30px; font-weight: 700; line-height: 1.1;
  color: var(--txt); margin: 6px 0 3px;
  font-variant-numeric: tabular-nums;
}
.meter {
  height: 7px; border-radius: 999px; background: var(--rule);
  overflow: hidden; margin: 10px 0 7px;
}
.meter > span { display: block; height: 100%; background: var(--pos); transition: width .3s ease; }
.meter.warn > span { background: var(--warn); }
.meter.bad  > span { background: var(--neg); }

.empty { padding: 34px; text-align: center; color: var(--txt3); font-size: 13px; }
/* A link inside an empty state was near-white on the light gray pane and could not be seen. */
.empty a { color: var(--a-over); text-decoration: underline; text-underline-offset: 2px; }
/* The way into Digital Agents: a lit tab, so it reads as a different place rather than another table. */
.tab.tab-digital {
  display: inline-flex; align-items: center; gap: 7px; text-decoration: none; margin-left: 6px;
  color: #fff; border-radius: 999px; padding: 0 14px;
  background: linear-gradient(100deg, #7b5cff, #d24bff 55%, #ff5f9e);
  box-shadow: 0 4px 14px rgba(170, 80, 255, .35);
}
.tab.tab-digital:hover { filter: brightness(1.08); color: #fff; }
.tab.tab-digital .lumen-orb { --o: 12px; }
/* --txt2. .muted is applied to counts, captions and timestamps -- text somebody reads -- as well as
   to the em-dash standing in for an empty field. At --txt3 all of it measured 3.31-3.50, under AA. The
   dash keeps its own lower weight where it is used, rather than holding the whole class down. */
.muted { color: var(--txt2); }
.right { text-align: right; }
.brow { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.spacer { flex: 1; }

/* Signed-in user. Reads from /api/me so the role shown is the one actually driving permissions,
   not something the front end guessed. */
.whoami {
  font-size: 12px; color: var(--txt2); padding: 5px 10px;
  border: 1px solid var(--rule); border-radius: 6px; background: var(--pane2);
  max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.whoami b { color: var(--txt); font-weight: 700; }

/* Sign in / out. Matches .whoami's height and border so the header reads as one row rather than a
   button bolted next to a label. Empty and invisible when sign-in is off — see renderAuthControl.

   Colored from --txt and --a-over, NOT --ink. That token means "text that contrasts with an accent
   fill" and is therefore LIGHT in the light themes: in coolgray both --ink and --pane2 are #f7f8fa, so
   an --ink button on a --pane2 background rendered white-on-white with a white border. The button was
   there, sized and clickable, and completely invisible — which looked exactly like the app failing to
   load. --txt and --a-over are defined to contrast with the pane in every theme. */
.authctl:empty { display: none; }
.authbtn {
  font: inherit; font-size: 12px; cursor: pointer;
  color: var(--txt); background: var(--pane2);
  padding: 5px 12px; border: 1px solid var(--rule); border-radius: 6px;
}
.authbtn:hover { background: var(--hover); border-color: var(--rule2); }
/* The way in, when there is no other way in — carries the accent so it reads as the next action. */
.authbtn.go { color: var(--a-over); border-color: var(--a-over); font-weight: 600; }
.authbtn.go:hover { background: var(--sel); }
/* Sign-in configured but unavailable. Warn-colored rather than error-colored: nothing is broken in
   the data, the browser simply cannot reach the sign-in library. */
.authwarn {
  font-size: 12px; color: var(--warn); background: var(--pane2);
  padding: 5px 10px; border: 1px solid var(--warn); border-radius: 6px; white-space: nowrap;
}
input[type=checkbox] { width: 15px; height: 15px; accent-color: var(--ink); cursor: pointer; }
input[type=checkbox]:disabled { cursor: not-allowed; opacity: .45; }

/* ── Users & Roles ────────────────────────────────────────────────────────────────────────────────
   Ported from Geocoding's radar/users.js so the identity screen reads the same in both applications:
   the same segmented control, the same filter row, the same permissions modal. Somebody who
   administers one should not have to learn the other. */

.filters { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin-bottom: 12px; }
.filters input[type=text] { min-width: 240px; }

/* Segmented control: one choice out of a few, always visible. Preferred over a <select> here
   because the options are the screen's main axis and hiding them behind a click would bury them. */
.seg { display: inline-flex; border: 1px solid var(--rule); border-radius: 7px; overflow: hidden; }
.seg button {
  padding: 5px 11px; border: 0; background: var(--pane); cursor: pointer;
  color: var(--txt2); font: inherit; font-size: 12px; font-weight: 600;
  border-right: 1px solid var(--rule);
}
.seg button:last-child { border-right: 0; }
.seg button:hover { background: var(--hover); color: var(--txt); }
/* --txt, not --ink: --ink is near-black and --sel is a dark selection tint, so the selected segment
   measured 1.26:1 -- the label was invisible on the control that says which one is chosen. */
.seg button.on { background: var(--sel); color: var(--txt); }

/* Row-level action. Smaller and quieter than .btn: these repeat on every row, and at .btn weight a
   40-row table reads as a wall of buttons. */
.miniact {
  padding: 3px 9px; border: 1px solid var(--rule); border-radius: 6px; background: var(--pane2);
  color: var(--txt2); font: inherit; font-size: 11px; font-weight: 600; cursor: pointer;
}
.miniact:hover { background: var(--hover); border-color: var(--rule2); color: var(--txt); }
.miniact.go { color: var(--a-over); border-color: var(--a-over); }
.miniact:disabled { opacity: .45; cursor: not-allowed; }

/* How much of the catalog a role holds. A bar rather than a number because the useful reading is
   "nearly everything" vs "barely anything", which a bare "9 of 14" makes you compute. */
.permbar { height: 5px; border-radius: 3px; background: var(--rule); overflow: hidden; }
/* --a-over, not --ink, for the third time on this screen: --ink is near-white in the light themes,
   so the filled portion vanished against its own track and a role holding every permission looked
   identical to one holding none. */
.permbar > span { display: block; height: 100%; background: var(--a-over); }

/* Modal. Roles get one instead of the old permission matrix: the catalog is expected to grow, and a
   grid of every permission against every role stops being readable as soon as either axis does. */
.modal-ov {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .45); padding: 24px;
}
.modal-card {
  background: var(--pane); border: 1px solid var(--rule2); border-radius: 12px;
  width: min(660px, 100%); max-height: 88vh; display: flex; flex-direction: column;
  box-shadow: 0 18px 48px rgba(0, 0, 0, .3);
}
.modal-head { padding: 14px 16px; border-bottom: 1px solid var(--rule); }
.modal-head h2 { margin: 0; font-size: 15px; font-weight: 700; color: var(--txt); }
.modal-head .sub { color: var(--txt2); font-size: 12px; margin-top: 3px; }
.modal-body { padding: 14px 16px; overflow-y: auto; }
.modal-acts {
  padding: 11px 16px; border-top: 1px solid var(--rule);
  display: flex; gap: 9px; justify-content: flex-end; align-items: center;
}
.modal-err { color: var(--neg); font-size: 12px; margin-right: auto; }

/* Permission list inside the modal: group heading, then one row per permission with its plain
   description and its key. The description is the point — "credentials.manage" alone tells an
   approver nothing about what they are handing over. */
.permgroup {
  display: flex; align-items: center; justify-content: space-between; gap: 9px;
  padding: 9px 2px 5px; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--txt3);
}
.permrow {
  display: flex; gap: 10px; align-items: flex-start; padding: 7px 8px; border-radius: 7px;
  cursor: pointer;
}
.permrow:hover { background: var(--hover); }
.permrow input { margin-top: 2px; flex: none; }
.permlabel { display: block; font-size: 13px; color: var(--txt); font-weight: 600; }
.permdesc { display: block; font-size: 11px; color: var(--txt3); margin-top: 2px; }
.permkey {
  display: block; font-family: "JetBrains Mono", monospace; font-size: 10px;
  color: var(--txt3); margin-top: 2px;
}

/* ── Audit log actor badges ───────────────────────────────────────────────────────────────────────
   Who acted, color-coded by kind. An agent running on a schedule and a person clicking a button are
   different in a way that matters when reading an audit trail, and both used to render as plain text.
   Colored from --cyan / --amber / --txt3, which are defined in every theme, rather than from --ink,
   which is light in the light themes and would render white-on-white. */
.actor-badge {
  font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: .02em;
  padding: 2px 8px; border-radius: 4px; border: 1px solid currentColor; white-space: nowrap;
}
.actor-badge.act-agent  { color: var(--cyan); }
.actor-badge.act-user   { color: var(--amber); }
.actor-badge.act-system { color: var(--txt3); }

/* ── Reports ──────────────────────────────────────────────────────────────────────────────────────
   Every chart on this screen is inline SVG this app draws, so its colors live here rather than in
   the markup. That is the point of drawing them ourselves: a chart picks up all 28 themes for free,
   and can never render a hard-coded navy bar on a phosphor-green ground.

   ONE COLOR, VARYING OPACITY. Value ramps — the map, the density matrix — are the accent at an
   opacity the drawing code sets per shape. A multi-hue ramp would have to be invented per theme and
   would fight the palette this app already has in most of them. */

.repcard { display: block; color: inherit; text-decoration: none; }
.repcard .agent-last { color: var(--txt2); }
a.btn { display: inline-block; text-decoration: none; color: var(--txt); }

/* Filter row. Each control is labeled above itself: a bare date box gives no clue whether it bounds
   the invoice date or the valuation date, and a report whose date range is ambiguous is a report
   whose numbers cannot be quoted. */
.fl { display: inline-flex; flex-direction: column; gap: 3px; }
.fl > span {
  font-size: 9.5px; font-weight: 800; letter-spacing: .7px; text-transform: uppercase;
  color: var(--txt3);
}
.fl select, .fl input[type=date] { width: auto; min-width: 130px; }

/* ── Headline tiles ──────────────────────────────────────────────────────── */

.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px; }
.kpi {
  background: var(--pane); border: 1px solid var(--rule); border-radius: 12px; padding: 13px 15px;
  display: flex; flex-direction: column;
}
.kpi .k {
  font-size: 9.5px; font-weight: 800; letter-spacing: .7px; text-transform: uppercase;
  color: var(--txt3);
}
.kpi .v {
  font-family: "JetBrains Mono", monospace; font-size: 25px; font-weight: 700;
  color: var(--txt); margin-top: 4px; font-variant-numeric: tabular-nums; line-height: 1.1;
}
.kpi .v .vsep { color: var(--txt3); margin: 0 4px; font-weight: 400; }
.kpi .n { font-size: 11px; color: var(--txt3); margin-top: 6px; }

/* The figure shown against the distribution it came from. Deliberately unlabeled and not
   interactive: it is context for the number above it, not a chart of its own. */
.kpi .spark { width: 100%; height: 34px; margin-top: 8px; display: block; }
.sparkfill { fill: var(--a-over); fill-opacity: .16; }
.sparkline { fill: none; stroke: var(--a-over); stroke-width: 1.4; vector-effect: non-scaling-stroke; }
.sparkmark { stroke: var(--txt); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.whisker   { stroke: var(--rule2); stroke-width: 2; vector-effect: non-scaling-stroke; }
.zerotick  { stroke: var(--txt3); stroke-width: 1; vector-effect: non-scaling-stroke; }
.box       { fill: var(--a-over); fill-opacity: .55; }
.medtick   { stroke: var(--txt); stroke-width: 2; vector-effect: non-scaling-stroke; }

/* ── Sections ────────────────────────────────────────────────────────────── */

.repsec { margin-top: 20px; }
.repsec h2 {
  margin: 0 0 10px; font-size: 12px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--txt3);
}
.sechead { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.sechead h2 { margin: 0; }

/* Two charts side by side where they answer one question between them, stacking on narrow screens. */
/* Paired panels are equal height. Left to themselves a ranked list and a chart end at whatever height
   their own content wants, and two panels of visibly different height read as one of them having
   failed to load rather than as two answers to different questions. */
.repgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: stretch; }
.repgrid > .repsec { display: flex; flex-direction: column; min-width: 0; }
.repgrid > .repsec > .rank, .repgrid > .repsec > .chart { flex: 1; }
.repgrid > .repsec > .rank { justify-content: space-evenly; }
@media (max-width: 1100px) { .repgrid { grid-template-columns: 1fr; } }

.chart {
  width: 100%; height: auto; background: var(--pane);
  border: 1px solid var(--rule); border-radius: 12px; padding: 4px;
}
.chart .axl { font-size: 9px; fill: var(--txt3); font-family: "JetBrains Mono", monospace; }
.chart .axl.dim { font-size: 8.5px; opacity: .75; }
.chart .gridline { stroke: var(--rule); stroke-width: 1; }
.chart .area { fill: var(--a-over); fill-opacity: .28; stroke: var(--a-over); stroke-width: 1.5; }
.chart .cell { fill: var(--a-over); }
.chart .hit  { fill: transparent; }
.chart .hit:hover { fill: var(--a-over); fill-opacity: .07; }

/* Median and mean as separate rules, not one "average" line. On this data they sit far apart — the
   mean is dragged right by a handful of very expensive homes — and drawing only one of them would let
   a reader take it as the typical customer. Haloed in the panel color via paint-order, because both
   land on top of the tallest part of the curve by definition. */
.chart .mk-med  { stroke: var(--txt);  stroke-width: 1.5; }
.chart .mk-mean { stroke: var(--warn); stroke-width: 1.5; stroke-dasharray: 4 3; }
.chart .mklab {
  font-size: 9.5px; font-weight: 700; fill: var(--txt);
  stroke: var(--pane); stroke-width: 3px; paint-order: stroke;
}

/* ── Dumbbells ───────────────────────────────────────────────────────────── */

.chart .dbstem { stroke: var(--rule2); stroke-width: 2; }
.chart .trend  { fill: none; stroke: var(--a-over); stroke-width: 1.5; stroke-opacity: .35;
                 stroke-dasharray: 3 3; }
.chart .dot { stroke: var(--pane); stroke-width: 2; cursor: default; transition: r .12s; }
.chart .dot.median { fill: var(--a-over); }
.chart .dot.mean   { fill: var(--a-map); }
.chart .dot:hover  { r: 8; }

.legend { display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--txt3);
          margin-bottom: 8px; }
.legend .sw { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }
.legend .sw1 { background: var(--a-over); }
.legend .sw2 { background: var(--a-map); margin-left: 12px; }

/* ── The map ─────────────────────────────────────────────────────────────── */

.mapwrap {
  position: relative; background: var(--pane); border: 1px solid var(--rule);
  border-radius: 12px; padding: 8px; transition: opacity .15s;
}
.mapwrap.busy { opacity: .45; }
.mapsvg { width: 100%; height: auto; display: block; margin: 0 auto; }

.place { stroke: var(--rule2); stroke-width: .8; fill: var(--a-over); transition: fill-opacity .15s; }
.place:hover { stroke: var(--txt); stroke-width: 1.6; }
.place.outline { stroke: var(--rule2); }
.heat { fill: var(--a-map); stroke: none; }

/* Haloed like the chart markers, for the same reason: a place name sits on top of its own fill, and
   that fill ranges from nearly transparent to nearly solid across the map. */
.placelab {
  font-size: 11px; font-weight: 700; fill: var(--txt); text-anchor: middle; pointer-events: none;
  stroke: var(--pane); stroke-width: 3px; paint-order: stroke;
}
.placeval {
  font-size: 9.5px; font-family: "JetBrains Mono", monospace; fill: var(--txt2);
  text-anchor: middle; pointer-events: none;
  stroke: var(--pane); stroke-width: 3px; paint-order: stroke;
}

.maplegend {
  position: absolute; right: 18px; bottom: 16px; width: 190px;
  background: color-mix(in srgb, var(--pane) 86%, transparent);
  border: 1px solid var(--rule); border-radius: 8px; padding: 8px 10px;
  display: flex; flex-direction: column; gap: 5px;
}
.maplegend .lgl {
  font-size: 9px; font-weight: 800; letter-spacing: .6px; text-transform: uppercase; color: var(--txt3);
}
.maplegend .lgbar {
  height: 9px; border-radius: 3px;
  background: linear-gradient(to right,
    color-mix(in srgb, var(--a-over) 10%, var(--pane)),
    color-mix(in srgb, var(--a-over) 95%, var(--pane)));
}
.maplegend .lgticks {
  display: flex; justify-content: space-between; font-size: 9px; color: var(--txt3);
  font-family: "JetBrains Mono", monospace;
}

/* ── Ranked places ───────────────────────────────────────────────────────── */

.rank {
  background: var(--pane); border: 1px solid var(--rule); border-radius: 12px;
  padding: 10px 14px; display: flex; flex-direction: column;
}
.rankrow {
  display: grid; grid-template-columns: 190px 1fr 82px; align-items: center; gap: 14px;
  padding: 5px 0; border-radius: 6px;
}
.rankrow:hover { background: var(--hover); }
.rankname { font-size: 12px; color: var(--txt); font-weight: 600; }
.rankname .dim, .rankval .dim { color: var(--txt3); font-weight: 400; }

/* The track carries three things: the median as a filled bar, the mean as a second dot, and the span
   between them as a joining line — so a place where a few mansions pull the mean away reads as a long
   join, rather than as two numbers to subtract by eye. */
.ranktrack { position: relative; height: 16px; }
.ranktrack::before {
  content: ''; position: absolute; inset: 6px 0; border-radius: 2px; background: var(--rule);
}
.rankfill {
  position: absolute; top: 6px; height: 4px; left: 0; border-radius: 2px;
  background: var(--a-over); opacity: .55;
}
.rankjoin { position: absolute; top: 7.5px; height: 1px; background: var(--a-map); opacity: .8; }
.rankdot {
  position: absolute; top: 4px; width: 8px; height: 8px; border-radius: 50%;
  transform: translateX(-4px); border: 1.5px solid var(--pane);
}
.rankdot.median { background: var(--a-over); }
.rankdot.mean   { background: var(--a-map); }
.rankval {
  font-family: "JetBrains Mono", monospace; font-size: 11.5px; color: var(--txt);
  text-align: right; font-variant-numeric: tabular-nums;
}

/* ── Tooltip ─────────────────────────────────────────────────────────────── */

/* One element serves the whole screen, positioned from the pointer. A per-shape SVG title would have
   been free, but it waits a second before appearing and cannot be styled — on a map you sweep across,
   that delay is the difference between exploring and clicking around. */
.tip {
  position: fixed; z-index: 60; pointer-events: none; opacity: 0;
  transition: opacity .1s; max-width: 260px;
  background: var(--pane2); border: 1px solid var(--rule2); border-radius: 7px;
  padding: 6px 9px; font-size: 11.5px; color: var(--txt);
  box-shadow: 0 6px 20px var(--shadow);
}
.tip.on { opacity: 1; }

@media (max-width: 760px) {
  .rankrow { grid-template-columns: 1fr; gap: 3px; }
  .rankval { text-align: left; }
}

/* ── Reports: log distribution, percentile ladder, basemap ───────────────────────────────────────
   MEDIAN AND MEAN NEVER SHARE A COLOR. They are the two numbers this whole report turns on, they sit
   far apart on this data, and quoting one for the other is the specific mistake it exists to prevent.
   Median is the accent; mean is --warn. Both are defined in all 28 themes and neither is the neutral
   used for everything else, so the pairing survives a theme change. */

.chart .bar { fill: var(--a-over); transition: fill-opacity .1s; }
.chart .bar:hover { fill-opacity: .7; }

.chart .dot.mean { fill: var(--warn); }
.legend .sw2 { background: var(--warn); }
.rankdot.mean { background: var(--warn); }
.rankjoin { background: var(--warn); }

/* The country under the regions. Filled, not just outlined: an unfilled state reads as a hole, and
   the point of the basemap is that the places we do NOT serve are visible as ground. */
.state { fill: var(--pane2); stroke: var(--rule2); stroke-width: .6; pointer-events: none; }

/* ── Percentile ladder ───────────────────────────────────────────────────── */

.ladder {
  background: var(--pane); border: 1px solid var(--rule); border-radius: 12px;
  padding: 12px 16px; display: flex; flex-direction: column; gap: 3px;
}
.ladrow {
  display: grid; grid-template-columns: 44px 1fr 108px; align-items: center; gap: 14px;
  padding: 3px 0; border-radius: 6px;
}
.ladrow:hover { background: var(--hover); }
.ladkey {
  font-size: 11px; color: var(--txt3); text-align: right; font-weight: 600;
  font-family: "JetBrains Mono", monospace;
}
.ladbar { height: 18px; display: flex; align-items: center; }
.ladbar span {
  height: 15px; border-radius: 3px; background: var(--a-over); opacity: .8;
  display: block; transition: width .3s ease;
}
/* The row people actually quote, marked so it is findable without reading the labels. */
.ladbar span.is-median { background: var(--warn); opacity: 1; }
.ladval {
  font-family: "JetBrains Mono", monospace; font-size: 11.5px; color: var(--txt);
  font-variant-numeric: tabular-nums;
}

/* ── Reports: the map grid ───────────────────────────────────────────────────────────────────────
   Four small maps side by side rather than one big map behind a level switcher. Territory, region and
   area are the same question at three depths, and the answer is the comparison between them — a
   switcher makes you hold the previous map in your head to see it. */

.mapgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
@media (max-width: 1300px) { .mapgrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 700px)  { .mapgrid { grid-template-columns: 1fr; } }

.mapcard {
  background: var(--pane); border: 1px solid var(--rule); border-radius: 12px;
  padding: 10px 10px 8px; display: flex; flex-direction: column; gap: 6px; min-width: 0;
}
.mapttl {
  font-size: 10px; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
  color: var(--txt2);
}
.mapcard .mapwrap { border: 0; background: none; padding: 0; flex: 1; }
.mapcard .mapsvg { max-width: 100%; }

/* The legend moves under its map in a small panel — floated over the corner it covered the shapes. */
.mapcard .maplegend {
  position: static; width: auto; background: none; border: 0; padding: 0; margin-top: 4px;
}

.mapnote { margin-top: 8px; font-size: 11px; color: var(--txt3); font-style: italic; }

/* ── Print ───────────────────────────────────────────────────────────────────────────────────────
   Export to PDF is the browser's own print, so this stylesheet IS the export format. Everything that
   belongs to the application rather than to the report comes off, and what remains is laid out for
   paper — which is why the file that comes out reads as a report and not as a screenshot of a tab. */

@media print {
  /* Chrome drops every background fill by default, which would print the charts as outlines with no
     shading at all — the shading IS the value scale on a choropleth. */
  * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }

  .topbar, .sidenav, .filters, #repBack, #repPdf, #boot, #toasts, .detailwin, .tip { display: none !important; }

  body { background: #fff !important; }
  #view { padding: 0; max-width: none; }

  .kpi, .chart, .rank, .ladder, .mapcard, .mapwrap {
    box-shadow: none !important; break-inside: avoid;
  }
  .repsec { break-inside: avoid; margin-top: 16px; }

  /* break-inside: avoid is a REQUEST, and asking it of a block taller than the page is how content
     gets clipped: the browser cannot honor it, so it breaks somewhere arbitrary. It belongs on the
     panels, which are page-sized, and never on the grids that hold them. */
  .repgrid, .mapgrid { break-inside: auto; }

  /* All four maps in one band. Two across made the map section 1,149px tall — nearly twice a
     landscape page — so it could not sit on a page at all. */
  .mapgrid { grid-template-columns: repeat(4, 1fr); gap: 10px; }

  /* The inline max-width sizes each map for the screen's four-column grid. On paper the columns are
     narrower, and leaving the cap in place would float a small map in a wide empty box. */
  .mapcard .mapsvg { max-width: 100% !important; }
  .mapcard { padding: 6px; }
  .mapttl { font-size: 8.5px; }
  .maplegend .lgl, .maplegend .lgticks { font-size: 7px; }

  /* Header on one line, then a stamp line under it. The screen's flex header puts the customer count
     and the export button on the right; with the button gone that left a stray chip mid-air. */
  .screen-head { display: block; border-bottom: 1px solid #ddd; padding-bottom: 6px; }
  .screen-head .chip { border: 0; padding: 0; font-weight: 400; color: #444; }

  /* A page of numbers with no date on it gets forwarded six months later as though it were current.
     attr() reads the element's OWN attribute, so the stamp is written onto this element rather than
     onto body — hung off body it silently renders as an empty string. */
  .screen-head[data-printed]::after {
    content: 'The Perfect Light · exported ' attr(data-printed);
    display: block; font-size: 10px; color: #666; width: 100%; margin-top: 2px;
  }

  @page { margin: 14mm; size: A4 landscape; }
}

/* ── Context surface ───────────────────────────────────────────────────────
   The governed figures, the caller's row scope, and the receipt behind each
   answer. Reuses .panel, .grid, .agent, .chip, .btn and .empty; only what is
   genuinely new is below.

   Every color here is --txt/--txt2/--txt3 or --a-over. NOT --ink: that token
   means "text that contrasts with an accent FILL" and is light in the light
   themes, so using it as a foreground renders near-invisible — the bug already
   fixed on .btn and .authbtn.go. */

.section-head { margin-bottom: 14px; }
.section-head h2 { margin: 0 0 4px; font-size: 16px; font-weight: 700; color: var(--txt); }
.section-head p { margin: 0; font-size: 12px; }

.back {
  display: inline-block; margin-bottom: 8px; font-size: 12px;
  color: var(--a-over); text-decoration: none;
}
.back:hover { text-decoration: underline; }

.panel-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  margin-bottom: 8px;
}
.panel-head h3 { margin: 0; font-size: 13px; font-weight: 700; color: var(--txt); }

.small { font-size: 11.5px; }
.accent { color: var(--a-over); }
.strong { font-weight: 700; color: var(--txt); }
.num { text-align: right; font-variant-numeric: tabular-nums; }

.agent-head { margin-bottom: 6px; }
.agent-desc { margin: 0 0 8px; font-size: 12px; color: var(--txt2); line-height: 1.5; }

/* Row scope, shown before any figure. "Why is my number different from hers" is
   almost always answered here, so it leads rather than sits in a footnote. */
.scope-panel { margin-bottom: 16px; }
.scope-panel p { margin: 0; font-size: 12.5px; color: var(--txt2); line-height: 1.55; }
.scope-panel strong { color: var(--txt); }

/* Deliberately warn-colored rather than neutral: seeing no records is a state
   somebody needs to notice and query, not a quiet default. */
.scope-none { color: var(--warn) !important; }
.scope-none strong { color: var(--warn); }

.filters { display: flex; flex-wrap: wrap; align-items: end; gap: 10px; margin-bottom: 8px; }
.filters label { display: flex; flex-direction: column; gap: 4px; font-size: 11.5px; color: var(--txt3); }

table.data { width: 100%; border-collapse: collapse; font-size: 12.5px; }
table.data th {
  text-align: left; padding: 6px 10px 8px; font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .05em; color: var(--txt3);
  border-bottom: 1px solid var(--rule);
}
table.data th.num { text-align: right; }
table.data td { padding: 7px 10px; color: var(--txt2); border-bottom: 1px solid var(--rule); }
table.data tr:last-child td { border-bottom: none; }

/* The receipt is part of the answer, not an appendix -- a figure whose version,
   sources and scope are unknown is one nobody can defend later. */
.receipt { margin-top: 12px; border-color: var(--rule2); }
.receipt-grid {
  display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px;
  margin: 0 0 10px; font-size: 12px;
}
.receipt-grid dt { color: var(--txt3); }
.receipt-grid dd { margin: 0; color: var(--txt); word-break: break-word; }

/* A refusal is NOT an empty result. Rendered as prose so it cannot be read as
   "there is nothing there" -- the two mean opposite things. */
.refusal { border-color: var(--warn); margin-top: 12px; }
.refusal h3 { margin: 0 0 6px; font-size: 13px; color: var(--warn); }
.refusal p { margin: 0 0 6px; font-size: 12.5px; color: var(--txt2); line-height: 1.55; }

/* The chain-health strip on the Agents screen.

   Bordered and tinted from --neg rather than filled with it: this sits above the whole screen, and a
   solid red band reads as an error the page itself has hit rather than a report about last night.
   Colored from --txt and --neg, never --ink — the same trap this file has documented four times.

   Only rendered when something is wrong, so it can afford to be loud. */
.alertstrip {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  margin: 0 0 16px; padding: 12px 14px;
  border: 1px solid var(--neg); border-radius: 8px;
  background: color-mix(in srgb, var(--neg) 8%, var(--pane));
  box-shadow: inset 3px 0 0 var(--neg);
}
.alertstrip strong { color: var(--neg); font-size: 14px; }
.alertstrip .chip { margin-left: 8px; vertical-align: 1px; }
.alertstrip .sub { margin-top: 3px; }
.alertstrip .sub.dim { opacity: .72; }
.alertstrip .btn { white-space: nowrap; }

/* Narrow enough that the button under the text beats the two of them squeezed side by side. */
@media (max-width: 640px) {
  .alertstrip { flex-direction: column; align-items: flex-start; }
}

/* A stage that repeats for every connected system.
   Marked rather than duplicated: the alternative was one node per stage per source, which reads fine
   with two systems and becomes a wall with three. */
.node.per-source { border-style: dashed; }
.node.per-source .node-pos { letter-spacing: .02em; }

/* ── Records browser ───────────────────────────────────────────────────────
   The data screens: one table per canonical object, a detail panel beside it.

   Built on .dtable rather than a table of its own, so a row here looks like a
   row on every other screen. What is added is only what those screens had no
   need of: a horizontal scroller, because a canonical table has more columns
   than fit; sortable headers; and a pager, because these lists are the first
   in the platform long enough to need one.
   ------------------------------------------------------------------------ */

/* A toolbar, not a stack. The first version used .filters, whose rules let a <select> take the full
   width -- so the search box sat alone on one line and each dropdown claimed a row of its own across
   the whole window. Search takes the slack; everything else is sized to what it holds. */
.rec-bar {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin-bottom: 12px;
}
/* The screen says what it is. Every other screen in the app has a heading and these had none, so the
   only clue to which of eight objects you were looking at was the tab you clicked to get here. */
.rec-head {
  display: flex; align-items: baseline; gap: 12px;
  margin: 0 0 14px;
}
.rec-head h1 {
  margin: 0; font-size: 19px; font-weight: 650; letter-spacing: -.01em; color: var(--txt);
}
.rec-count { font-size: 12.5px; white-space: nowrap; color: var(--txt2); }

/* Search, with the magnifier drawn into the field rather than sat beside it as another element. */
.rec-q {
  flex: 1 1 280px; min-width: 210px; max-width: 460px; height: 34px;
  padding: 0 12px 0 32px;
  border: 1px solid var(--rule); border-radius: 8px;
  background: var(--pane) no-repeat 10px 50%;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%237b8a99' stroke-width='1.6'><circle cx='7' cy='7' r='4.4'/><path d='M10.4 10.4 14 14' stroke-linecap='round'/></svg>");
  background-size: 14px 14px;
  color: var(--txt); font: inherit; font-size: 13px;
}
.rec-q::placeholder { color: var(--txt3); }
.rec-q:focus {
  outline: none; border-color: var(--rule2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rule2) 30%, transparent);
}
.rec-q::-webkit-search-cancel-button { filter: grayscale(1) opacity(.6); cursor: pointer; }

/* Sized to their content, with the browser's own arrow replaced so the three controls match. */
.rec-sel {
  flex: 0 0 auto; width: auto; min-width: 158px; height: 34px;
  padding: 0 30px 0 11px;
  border: 1px solid var(--rule); border-radius: 8px;
  background: var(--pane) no-repeat right 10px center;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%237b8a99' stroke-width='1.6'><path d='M2.5 4.5 6 8l3.5-3.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-size: 11px 11px;
  color: var(--txt); font: inherit; font-size: 13px;
  appearance: none; -webkit-appearance: none; cursor: pointer;
}
.rec-sel:focus {
  outline: none; border-color: var(--rule2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rule2) 30%, transparent);
}

.rec-bar .btn.sm { height: 34px; }

/* ── List filters ───────────────────────────────────────────────────────────
   Date range, status, owner, city and state on every Data list. Every class is
   prefixed rflt- because .row and friends are app-wide utilities and a generic
   name here would restyle every toolbar in the platform.

   One control height (34px) and one radius (8px) across the bar, so search,
   selects, the date group and the pickers read as one toolbar. Popovers carry
   the panel chrome -- lit top edge, two stacked shadows -- so they read as a
   surface lifted off the page rather than a browser dropdown. Selected states
   use --sel with --a-over text, never --ink (light in the light themes). */

/* On a wide screen the collapsible box is not a box at all: its children flow in the bar. */
.rflt-more { display: contents; }
.rflt-toggle { display: none; }

.rflt-date {
  display: inline-flex; align-items: center; gap: 0; height: 34px;
  border: 1px solid var(--rule); border-radius: 8px; background: var(--pane); overflow: hidden;
}
.rflt-date:focus-within { border-color: var(--rule2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rule2) 30%, transparent); }
.rflt-df {
  height: 100%; border: 0; border-right: 1px solid var(--rule); border-radius: 0;
  padding: 0 26px 0 11px; width: auto; min-width: 0;
  background: var(--pane2) no-repeat right 9px center;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%237b8a99' stroke-width='1.6'><path d='M2.5 4.5 6 8l3.5-3.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-size: 10px 10px;
  color: var(--txt); font: inherit; font-size: 12px; font-weight: 600;
  appearance: none; -webkit-appearance: none; cursor: pointer;
}
.rflt-dt {
  height: 100%; border: 0; background: transparent; color: var(--txt);
  font: inherit; font-size: 12.5px; padding: 0 8px; width: auto; min-width: 0;
  color-scheme: light dark;
}
.rflt-df:focus, .rflt-dt:focus { outline: none; border-color: var(--rule); }
.rflt-sep { font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--txt3); }

/* A picker: a trigger shaped like .rec-sel, and a popover under it. */
.rflt-ms { position: relative; }
.rflt-trig {
  display: inline-flex; align-items: center; gap: 7px; height: 34px;
  padding: 0 30px 0 11px; border: 1px solid var(--rule); border-radius: 8px;
  background: var(--pane) no-repeat right 10px center;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%237b8a99' stroke-width='1.6'><path d='M2.5 4.5 6 8l3.5-3.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-size: 11px 11px;
  color: var(--txt); font: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;
  transition: border-color .15s, transform .15s;
}
.rflt-trig:hover { border-color: var(--rule2); }
.rflt-trig.on { border-color: var(--a-over); background-color: var(--sel); color: var(--a-over); font-weight: 600; }
.rflt-ms.open .rflt-trig { border-color: var(--rule2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rule2) 30%, transparent); }

/* The count of chosen values, on a trigger and on the phone-width Filters button. */
.rflt-n {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
  font-size: 10.5px; font-weight: 800; line-height: 1;
  background: var(--a-over); color: var(--pane);
}

.rflt-pop {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;
  width: 290px; max-width: calc(100vw - 32px);
  background: var(--pane); border: 1px solid var(--rule); border-radius: 12px;
  padding: 10px 10px 9px;
  box-shadow:
    0 1px 2px color-mix(in srgb, var(--txt) 6%, transparent),
    0 14px 34px -12px color-mix(in srgb, var(--txt) 26%, transparent);
}
.rflt-pop[hidden] { display: none; }
.rflt-pop::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 1px; border-radius: 12px 12px 0 0;
  background: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--txt) 12%, transparent) 18%,
    color-mix(in srgb, var(--txt) 12%, transparent) 82%, transparent);
  pointer-events: none;
}
.rflt-ph { font-size: 10.5px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--txt2); margin: 0 2px 8px; }
.rflt-find {
  width: 100%; height: 30px; margin-bottom: 6px; padding: 0 10px;
  border: 1px solid var(--rule); border-radius: 7px; background: var(--bg2, var(--pane2));
  color: var(--txt); font: inherit; font-size: 12.5px;
}
.rflt-find:focus { outline: none; border-color: var(--rule2); }
.rflt-opts { max-height: 260px; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.rflt-opt {
  display: flex; align-items: center; gap: 8px; padding: 6px 6px; border-radius: 6px;
  font-size: 13px; color: var(--txt); cursor: pointer;
}
.rflt-opt[hidden] { display: none; }
.rflt-opt:hover { background: var(--hover); }
.rflt-opt input { margin: 0; flex: 0 0 auto; width: auto; accent-color: var(--a-over); }
.rflt-ol { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rflt-opt .chip { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.rflt-oc { margin-left: auto; font-size: 11.5px; color: var(--txt3); }
.rflt-empty, .rflt-note { font-size: 12px; color: var(--txt2); padding: 6px 4px; line-height: 1.45; }
.rflt-note { border-top: 1px solid var(--rule); margin-top: 6px; padding-top: 8px; }
.rflt-pf { display: flex; justify-content: flex-end; gap: 6px; margin-top: 8px;
  padding-top: 8px; border-top: 1px solid var(--rule); }

/* Active filters, each one removable. Rectangular like every chip in this UI, and clickable ones lift. */
.rflt-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: -4px 0 12px; }
.rflt-chips:empty { display: none; }
.rflt-chip {
  display: inline-flex; align-items: center; gap: 7px; max-width: 100%;
  padding: 4px 6px 4px 9px; border-radius: 6px; cursor: pointer;
  border: 1px solid var(--rule); background: var(--pane2); color: var(--txt);
  font: inherit; font-size: 12px; font-weight: 600;
  transition: transform .15s, border-color .15s;
}
.rflt-chip > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rflt-chip:hover { transform: translateY(-1px); border-color: var(--a-over); }
.rflt-x { font-size: 14px; line-height: 1; color: var(--txt3); }
.rflt-chip:hover .rflt-x { color: var(--txt); }
.rflt-all { background: transparent; color: var(--txt2); padding-right: 9px; }

@media (prefers-reduced-motion: reduce) {
  .rflt-chip, .rflt-trig { transition: none; }
  .rflt-chip:hover { transform: none; }
}

/* Phone width: search and a Filters button on one line; everything else folds behind the button and
   opens as a stack of full-width controls. Nothing is wider than the screen. */
@media (max-width: 720px) {
  .rec-bar { gap: 8px; }
  .rec-bar .rec-q { flex: 1 1 0; min-width: 0; max-width: none; }
  .rflt-toggle { display: inline-flex; align-items: center; gap: 6px; height: 34px; flex: 0 0 auto; }
  .rflt-toggle.on { background: var(--sel); color: var(--a-over); border-color: var(--a-over); }
  .rflt-more { display: none; }
  .rflt-more.open {
    display: flex; flex-direction: column; align-items: stretch; gap: 8px; flex: 1 1 100%; min-width: 0;
  }
  .rflt-more .rec-sel, .rflt-more .rflt-trig { width: 100%; min-width: 0; }
  .rflt-date { display: grid; grid-template-columns: auto 1fr; height: auto; }
  .rflt-df { grid-column: 1 / -1; height: 34px; border-right: 0; border-bottom: 1px solid var(--rule); }
  .rflt-dt { height: 34px; width: 100%; }
  .rflt-sep { padding: 0 0 0 10px; }
  /* "from" input, then a "to" row: label and field side by side. */
  .rflt-date #recFrom { grid-column: 1 / -1; border-bottom: 1px solid var(--rule); }
  .rflt-pop { position: static; width: auto; max-width: none; margin-top: 6px; box-shadow: none; }
  .rflt-clear { align-self: flex-start; }
}

.tablewrap .dtable { width: 100%; }

.dtable th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.dtable th.sortable:hover { background: var(--hover); }
.dtable th.sortable.on { color: var(--txt); }

/* The whole row is the target, not a link inside it: the row IS the record, and a 4px link in one
   column is a smaller target than the thing it opens. */
.dtable tr.rowlink { cursor: pointer; }
.dtable tr.rowlink:hover { background: var(--hover); }

/* --txt2 rather than the shared .muted: "1-200 of 2,335" is a figure somebody reads, and .muted is
   tuned for em-dash placeholders standing in for empty fields. */
.pager { display: flex; align-items: center; gap: 10px; margin-top: 13px; flex-wrap: wrap; }
.pager .muted, .pager label { color: var(--txt2); font-size: 12.5px; }
.pager .grow, .filters .grow { flex: 1; }
.pad { padding: 16px 4px; }

/* Figures line up under each other, which is the only way a column of money can be read at a glance. */
.dtable td .num { font-variant-numeric: tabular-nums; }

code.json {
  font-family: var(--mono); font-size: 11px; background: var(--bg);
  border: 1px solid var(--rule); border-radius: 4px; padding: 1px 5px;
  display: inline-block; max-width: 460px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; vertical-align: bottom;
}

/* ── Detail panel ────────────────────────────────────────────────────────── */

/* --txt3, NOT --ink. --ink is the foreground for text sitting ON a bright accent -- near-black, by
   design -- and using it as a label color on a dark panel rendered every field name at about 1.1:1
   against its own background. The names were there the whole time and could not be read. */
.dw-sect {
  margin: 20px 0 8px; padding-bottom: 6px;
  font-size: 10px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--txt2); border-bottom: 1px solid var(--rule);
}
.dw-sect:first-child { margin-top: 0; }

/* Two columns, label left. A forty-field record read as a paragraph is unreadable; read as a table it
   is scannable, and the eye can drop down the labels without reading the values. */
.dw-fields {
  display: grid; grid-template-columns: minmax(132px, 36%) 1fr;
  margin: 0; align-items: baseline;
}
/* A hairline under each pair. Forty fields in two unbroken columns makes the eye lose its line between
   a label and the value three inches away; the rule is what carries it across. */
.dw-fields dt, .dw-fields dd {
  padding: 6px 0; border-bottom: 1px solid color-mix(in srgb, var(--rule) 55%, transparent);
}
.dw-fields dt { color: var(--txt2); font-size: 11.5px; padding-right: 14px; }
.dw-fields dd { margin: 0; color: var(--txt); font-size: 13px; word-break: break-word; }
/* An empty field still occupies its row, so the panel does not silently shorten -- but it recedes. */
.dw-fields dd .muted { opacity: .55; }

/* Ids, hashes and a source's own opaque keys. Monospaced so a long hex string can be compared
   character by character, and dimmed so it never competes with the fields somebody came here to read.
   The full value is on the title, because the truncated one is useless in a support ticket. */
code.opaque {
  font-family: var(--mono); font-size: 11.5px; color: var(--txt3);
  background: color-mix(in srgb, var(--rule) 30%, transparent);
  border-radius: 4px; padding: 1px 5px; cursor: help;
}
.dw-fields dd code.opaque { font-size: 12px; }
.dw-fields dd a { color: var(--a-map); }

@media (max-width: 720px) {
  .dw-fields { grid-template-columns: 1fr; gap: 1px 0; }
  .dw-fields dd { margin-bottom: 8px; }
}

/* ── Section menus on the bar ──────────────────────────────────────────────
   The top bar carries the sections; each one opens the screens under it.

   Modeled on radar's More menu, which solved the same problem the other way
   round: a bar that cannot grow past its width, and screens that still need to
   be reachable. Here every section is that menu, so the bar stays one row at
   five sections or fifteen, and the second row of tabs this briefly had — a
   strip of buttons under a strip of buttons — is gone.

   The alternative considered and rejected was a left sidebar. It reads the whole
   structure at once, which is genuinely better for twenty screens, and it costs
   236px of width on every screen for the sake of a menu opened a few times an
   hour. These screens are wide tables; the width is worth more.
   ------------------------------------------------------------------------ */

.navsect { position: relative; }
.navsect .tab { position: relative; }

/* Drawn, not typed. The glyph version rendered as noise beside the label in the bar's font -- and a
   character that depends on the font resolving it is a character that will fail on somebody's machine. */
.tab[data-menu] { padding-right: 22px; }
.tab[data-menu]::after {
  content: ''; position: absolute; right: 9px; top: 50%;
  width: 5px; height: 5px; margin-top: -3px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg); opacity: .5;
}
.tab[data-menu]:hover::after, .tab[data-menu][aria-expanded="true"]::after { opacity: .9; }
.tab[data-menu][aria-expanded="true"]::after { transform: rotate(-135deg); margin-top: -1px; }

.navmenu {
  position: absolute; top: calc(100% + 7px); left: 0; z-index: 1200; display: none;
  min-width: 216px; padding: 5px;
  background: var(--pane2); border: 1px solid var(--rule2);
  border-radius: 9px; box-shadow: 0 14px 34px -10px var(--shadow);
}
.navmenu.on { display: block; }

.nm-grp {
  padding: 7px 9px 5px; color: var(--txt2);
  font: 700 9px/1 var(--mono); letter-spacing: .1em; text-transform: uppercase;
}

.nm-item {
  display: block; width: 100%; text-align: left;
  padding: 7px 10px; border: 0; background: none; cursor: pointer;
  color: var(--txt); font: inherit; font-size: 12.5px; border-radius: 6px;
}
.nm-item:hover { background: var(--hover); }

/* The open screen is marked inside the menu as well as on the bar. The bar says which section you are
   in; without this the menu cannot say which of its screens you are looking at. */
.nm-item.on { background: var(--sel); color: var(--txt); font-weight: 600; }

@media (max-width: 760px) {
  .navmenu { left: auto; right: 0; }
}


/* ══ Standardized status badges ═══════════════════════════════════════════════════════════════════
   Six tones, shared by every domain, so green means settled on an invoice and on a job and on a
   proposal. The tone is chosen per value on the Lookups screen and stored in the database, which is
   why these are classes keyed on a name rather than colors written next to each status.

   Each hue is mixed toward --txt rather than used neat, for the reason the source chips were: measured
   on the real screens the neat accents came out between 3.69 and 4.43 against their own tint, under AA
   for 11px text, and a status badge repeats down every row of every data screen. */
.chip.tone-good    { color: color-mix(in srgb, var(--pos) 60%, var(--txt));
                     border-color: color-mix(in srgb, var(--pos) 45%, transparent);
                     background: color-mix(in srgb, var(--pos) 12%, var(--pane2)); }
.chip.tone-warn    { color: color-mix(in srgb, var(--warn) 55%, var(--txt));
                     border-color: color-mix(in srgb, var(--warn) 45%, transparent);
                     background: color-mix(in srgb, var(--warn) 12%, var(--pane2)); }
.chip.tone-bad     { color: color-mix(in srgb, var(--neg) 60%, var(--txt));
                     border-color: color-mix(in srgb, var(--neg) 45%, transparent);
                     background: color-mix(in srgb, var(--neg) 12%, var(--pane2)); }
.chip.tone-info    { color: color-mix(in srgb, var(--cyan) 62%, var(--txt));
                     border-color: color-mix(in srgb, var(--cyan) 45%, transparent);
                     background: color-mix(in srgb, var(--cyan) 12%, var(--pane2)); }
.chip.tone-neutral { color: var(--txt2); border-color: var(--rule); background: var(--pane2); }
/* Terminal states nobody needs to act on -- void, cancelled, archived. Quiet on purpose: they are the
   majority on some screens and should not compete with the rows that need attention. */
.chip.tone-muted   { color: var(--txt2); border-color: var(--rule2);
                     background: color-mix(in srgb, var(--txt3) 8%, var(--pane2)); }

/* A value no canonical value maps. Deliberately conspicuous -- it is a thing to notice, not to hide,
   and it means a cross-source report is currently counting this row on its own. */
.chip.tone-unknown { color: color-mix(in srgb, var(--warn) 55%, var(--txt));
                     border: 1px dashed color-mix(in srgb, var(--warn) 60%, transparent);
                     background: transparent; }

/* The due date under an unpaid invoice. "Past due" is not a status here -- the badge says Not Paid and
   this says how late, which answers both questions with one state instead of two overlapping ones. */
.sub-note   { font-size: 10.5px; margin-top: 3px; line-height: 1.3; }
.due-late   { color: color-mix(in srgb, var(--neg) 55%, var(--txt)); font-weight: 600; }

/* ══ Lookups screen ══════════════════════════════════════════════════════════════════════════════ */
.lk-note {
  margin: 12px 0 4px; padding: 10px 12px; border-radius: 6px;
  background: var(--pane2); border: 1px solid var(--rule); color: var(--txt2); font-size: 12.5px;
}
.lk-note code { font-size: 11.5px; }
.lk-h        { font-size: 14px; margin: 22px 0 2px; color: var(--txt); }
.lk-sub      { margin-bottom: 8px; }
/* Two columns: the vocabulary is a short reference list and the mappings are the working surface, so
   they sit side by side rather than stacked with the working half below the fold. Collapses to one
   column where there is not room for both. */
.lk-cols {
  display: grid; grid-template-columns: minmax(300px, 380px) minmax(0, 1fr);
  gap: 22px; align-items: start; margin-top: 4px;
}
@media (max-width: 1100px) { .lk-cols { grid-template-columns: 1fr; } }

.lk-vocab .lk-h, .lk-maps .lk-h { margin-top: 14px; }
.lk-code  { margin-top: 3px; }
.lk-means { margin-top: 3px; font-size: 11px; color: var(--txt2); line-height: 1.4; }

/* FIXED widths, and the reason this screen was rebuilt: it drew one table per source, each sizing its
   own columns to its own contents, so "We store" sat in a different place for every system. On a screen
   whose entire purpose is comparing what three systems call the same thing, that is the one thing the
   layout must not do. One table, fixed layout, every column in the same place all the way down. */
.lk-t-maps  { table-layout: fixed; width: 100%; }
.lk-t-maps th:nth-child(1), .lk-t-maps td:nth-child(1) { width: 118px; }
.lk-t-maps th:nth-child(2), .lk-t-maps td:nth-child(2) { width: 190px; }
.lk-t-maps th:nth-child(3), .lk-t-maps td:nth-child(3) { width: 78px; }
.lk-t-maps th:nth-child(4), .lk-t-maps td:nth-child(4) { width: 172px; }
.lk-t-maps th:nth-child(5), .lk-t-maps td:nth-child(5) { width: 86px; }
/* The source value can be long -- ServiceTitan sends a serialized object -- so it wraps inside its
   fixed column instead of widening it and pushing the controls off screen. */
.lk-t-maps td:nth-child(2) code { white-space: normal; word-break: break-all; font-size: 10.5px; }

.lk-t-vocab { table-layout: fixed; width: 100%; }
.lk-t-vocab th:nth-child(2), .lk-t-vocab td:nth-child(2) { width: 124px; }
.lk-t-vocab th:nth-child(3), .lk-t-vocab td:nth-child(3) { width: 66px; }

/* .rec-sel carries min-width:158px, which is right on the toolbar it was written for and wrong inside a
   fixed table column: the select kept its 158px, overflowed its cell and printed the row count on top of
   itself. Inside these tables the column decides the width, not the control. */
.lk-t-vocab .rec-sel, .lk-t-maps .rec-sel {
  width: 100%; min-width: 0; height: 30px; font-size: 11.5px; padding: 0 26px 0 9px;
}

/* The source badge, drawn once per group. The rule marks where one system ends and the next begins,
   which is what the separate tables used to do. */
.lk-src { vertical-align: top; padding-top: 10px; }
.lk-newsrc > td { border-top: 2px solid var(--rule2); }
/* The reasoning column. Wide enough to read a sentence, capped so it cannot push the controls off. */
.lk-desc {
  color: var(--txt2); font-size: 11.5px;
  white-space: normal; line-height: 1.45;
}
/* A row with nothing mapped, marked down the edge rather than by coloring the text -- the text still
   has to be readable, it is the thing being decided about. */
.lk-unmapped td:first-child { box-shadow: inset 3px 0 0 var(--warn); }

/* ══ Banners and count pips ══════════════════════════════════════════════════════════════════════ */
.banner {
  margin: 12px 0; padding: 10px 12px; border-radius: 6px; font-size: 12.5px; line-height: 1.5;
  background: var(--pane2); border: 1px solid var(--rule); border-left: 3px solid var(--rule2);
  color: var(--txt2);
}
.banner b { color: var(--txt); }
.banner-warn { border-left-color: var(--warn);
               background: color-mix(in srgb, var(--warn) 7%, var(--pane2)); }

/* A count carried on a tab, for "this one needs attention" without a second line of text. */
.pip {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; margin-left: 6px; padding: 0 4px;
  border-radius: 8px; font-size: 10px; font-weight: 800; line-height: 1;
}
.pip-warn { color: var(--txt); background: color-mix(in srgb, var(--warn) 55%, var(--pane2));
            border: 1px solid color-mix(in srgb, var(--warn) 70%, transparent); }

/* ── ETL Runs ─────────────────────────────────────────────────────────────────────────────────────
   One card per source, stacked. The chain diagram on the Agents screen answers "what runs, in what
   order"; these answer "what is each source doing, and how did it last go" — which with three sources
   folded into every stage, and a call-center pipeline the chain never touches, the chain cannot.

   Deliberately its own vocabulary rather than reusing .flow/.node. Those describe ONE rail of many
   stages; these are many rails of three, sized so five of them stack on a laptop without scrolling
   past the last one — which is Contact Center, and the one nobody was watching. */
.etl {
  background: var(--pane); border: 1px solid var(--rule); border-radius: 10px;
  padding: 12px 14px 11px; margin-bottom: 11px;
}
.etl.live { border-color: var(--pos); box-shadow: 0 0 20px -8px var(--pos); }
.etl.bad { border-color: var(--neg); }

.etl-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  flex-wrap: wrap; margin-bottom: 10px;
}
.etl-id { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.etl-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--txt3); flex: none; }
.etl.live .etl-dot { background: var(--pos); animation: pulse 1s ease-in-out infinite; }
.etl.bad .etl-dot { background: var(--neg); }
.etl-name { font-size: 15px; font-weight: 700; color: var(--txt); }
.etl-kind {
  font-size: 9.5px; font-weight: 800; letter-spacing: .7px; text-transform: uppercase;
  color: var(--txt2); border: 1px solid var(--rule2); border-radius: 3px; padding: 1px 5px;
}
.etl-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.etl-scopes { font-size: 11px; color: var(--txt2); }

/* Three tiles and the wires between them. Equal width, because a stage that takes two hours and one
   that takes four seconds are equally important to see, and sizing by duration would hide the fast
   one exactly when it is the one that failed. */
.etl-rail { display: flex; align-items: stretch; gap: 0; flex-wrap: wrap; }
.etl-tile {
  flex: 1 1 140px; min-width: 128px; display: flex; flex-direction: column; gap: 2px;
  padding: 8px 11px; border: 1px solid var(--rule); border-radius: 6px; background: var(--pane2);
}
.etl-tile-label { font-size: 12.5px; font-weight: 700; color: var(--txt); }
.etl-tile-state {
  font-size: 10.5px; color: var(--txt2);
  /* One line, ellipsized: a running stage's summary can be a sentence, and letting it wrap would
     change the card's height three times a second while the run moves through it. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* State from the border and the label, never fill alone — fifteen filled tiles down a page is a
   color chart, not a status board. */
.etl-tile.ok { border-color: var(--rule2); }
.etl-tile.ok .etl-tile-state { color: color-mix(in srgb, var(--pos) 55%, var(--txt)); }
.etl-tile.bad { border-color: var(--neg); }
.etl-tile.bad .etl-tile-state { color: var(--neg); }
.etl-tile.idle { opacity: .66; border-style: dashed; }
/* A stage this pipeline does not have. Distinct from idle, which is one that has not run YET —
   drawing them the same would claim the call center is perpetually about to transform something. */
.etl-tile.skip { opacity: .5; border-style: dotted; }
.etl-tile.live {
  border-color: var(--pos); background: var(--pane);
  box-shadow: 0 0 0 1px var(--pos) inset, 0 0 14px -4px var(--pos);
  animation: nodebeat 1.2s ease-in-out infinite;
}
.etl-tile.live .etl-tile-state { color: var(--pos); font-weight: 700; }

.etl-wire {
  align-self: center; flex: 0 0 22px; height: 2px; margin: 0 3px;
  background: var(--rule2); border-radius: 2px; position: relative; overflow: hidden;
}
.etl-wire.flowing::after {
  content: ""; position: absolute; inset: 0; width: 40%;
  background: linear-gradient(90deg, transparent, var(--pos), transparent);
  animation: wireflow 1.1s linear infinite;
}

.etl-foot {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  margin-top: 9px; font-size: 11.5px; color: var(--txt2);
}
.etl-counts { font-family: "JetBrains Mono", monospace; font-size: 10.5px; }
.etl-more {
  margin-left: auto; font: inherit; font-size: 11px; cursor: pointer;
  background: none; border: 1px solid var(--rule2); border-radius: 4px;
  padding: 2px 8px; color: var(--txt2);
}
.etl-more:hover { border-color: var(--rule2); background: var(--hover); color: var(--txt); }
.etl-more.on { border-color: var(--pos); color: var(--txt); }
/* .chip sets display:inline-block, which wins over the hidden attribute's UA rule — so every card
   showed "0 objects failed". Scoped rather than fixing .chip globally, which other screens rely on. */
.etl-failed[hidden] { display: none; }
.etl-feeds { margin-top: 10px; }
.etl-feeds .err { color: var(--neg); font-size: 10.5px; }

@media (max-width: 720px) {
  /* The wires point at nothing once the tiles stack, so they go rather than dangle. */
  .etl-wire { display: none; }
  .etl-tile { flex-basis: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .etl-tile.live { animation: none; }
  .etl-wire.flowing::after { animation: none; opacity: .55; }
  .etl.live .etl-dot { animation: none; }
}

/* The connection band. Deliberately not a tile: it sits above the rail and reads as a property of the
   SOURCE rather than a stage of the pipeline, because reachability and run outcome fail independently
   — a key revoked this morning leaves last night's run looking perfect. */
.etl-conn {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin-bottom: 9px; padding: 5px 9px;
  border: 1px solid var(--rule); border-left-width: 3px; border-radius: 5px;
  background: var(--pane2); font-size: 11.5px; color: var(--txt2);
}
.etl-conn-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--txt3); flex: none; }
.etl-conn-label {
  font-size: 9.5px; font-weight: 800; letter-spacing: .7px; text-transform: uppercase;
  color: var(--txt2);
}
.etl-conn-state { font-weight: 700; color: var(--txt); }
.etl-conn-when { color: var(--txt2); }
.etl-conn-error {
  color: var(--neg); font-size: 10.5px;
  /* One line: a vendor error can be a paragraph, and letting it wrap would push the tiles down the
     page every time a source went down — exactly when the tiles matter most. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.etl-conn.ok    { border-left-color: var(--pos); }
.etl-conn.ok    .etl-conn-dot { background: var(--pos); }
.etl-conn.bad   { border-left-color: var(--neg); }
.etl-conn.bad   .etl-conn-dot { background: var(--neg); }
.etl-conn.warn  { border-left-color: var(--warn); }
.etl-conn.warn  .etl-conn-dot { background: var(--warn); }
.etl-conn.muted { border-left-color: var(--rule2); }

/* A card reconstructed from ingest history rather than read from stage rows. Marked only by a dashed
   left edge and a line of text: it is ordinary history, just less complete, and shouting about it
   would be worse than the gap it describes. Disappears by itself after the next run. */
.etl.historical { border-left: 3px dashed var(--rule2); }

/* A stage that succeeded while some of its objects did not. Amber, because the chain does not fail
   ingest for one bad feed -- the other seven landed and their data is good -- so red overstates it and
   green hid two throttled Jobber feeds behind a summary sentence nobody reads. */
.etl-tile.partial { border-color: var(--warn); }
.etl-tile.partial .etl-tile-state { color: var(--warn); font-weight: 700; }

/* ── Call Center ──────────────────────────────────────────────────────────────────────────────────
   Built in the same language as the MCC analytics dashboard, on this app's own tokens so all 28
   themes keep working: one panel chrome for every widget, a 1px lit top edge that makes a flat
   rectangle read as a surface, tabular numerals on every figure, and a lift on anything that opens
   something. Color is never the only signal -- every band carries a label in the legend, every
   delta carries an arrow, and every bar carries its number.

   One color scale across the whole screen: how often calls reach a person. The reader learns it
   once, in a legend that is stated rather than implied. */

.tnum { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* ── Panel ──────────────────────────────────────────────────────────────────────────────────── */
.pnl {
  position: relative; background: var(--pane); border: 1px solid var(--rule);
  border-radius: 14px; padding: 16px 18px 17px; margin-bottom: 14px;
  /* Two stacked shadows: a tight edge to seat the panel on the page, and a wide soft one for depth.
     One large blur reads as a drop shadow; this reads as a surface. */
  box-shadow:
    0 1px 2px color-mix(in srgb, var(--txt) 5%, transparent),
    0 8px 24px -14px color-mix(in srgb, var(--txt) 16%, transparent);
}
/* The lit top edge. It is what turns a flat rectangle into a surface, at a fraction of the visual
   noise a bevel used to cost. */
.pnl::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 1px; border-radius: 14px 14px 0 0;
  background: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--txt) 12%, transparent) 18%,
    color-mix(in srgb, var(--txt) 12%, transparent) 82%, transparent);
  pointer-events: none;
}
.pnl-h {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  flex-wrap: wrap; margin-bottom: 14px;
}
.pnl-t h2 {
  font-size: 11px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--txt2); margin: 0;
}
.pnl-hint { font-size: 12px; color: var(--txt2); margin: 4px 0 0; line-height: 1.45; max-width: 62ch; }
.pnl-r { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* A panel whose data covers only part of the business says so in its own border, not only in prose. */
.pnl-scoped { border-style: dashed; }

.cc-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: start; }

/* ── Legend ─────────────────────────────────────────────────────────────────────────────────── */
.lgd { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.lgd-i { display: inline-flex; align-items: center; gap: 5px; font-size: 10.5px; color: var(--txt2); }
.lgd-i i { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
.lgd-i.good i { background: var(--pos); }
.lgd-i.mid  i { background: var(--warn); }
.lgd-i.poor i { background: var(--neg); }
.lgd-n {
  font-size: 9.5px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase;
  color: var(--txt3); padding-left: 2px; border-left: 1px solid var(--rule2); padding-left: 10px;
}

/* ── Hero tiles ─────────────────────────────────────────────────────────────────────────────── */
.cc-hero {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 14px; margin-bottom: 14px;
}
.tile {
  position: relative; display: flex; flex-direction: column; gap: 2px; text-align: left;
  font: inherit; cursor: pointer; padding: 16px 17px 14px;
  background: var(--pane); border: 1px solid var(--rule); border-radius: 14px; color: var(--txt2);
  box-shadow:
    0 1px 2px color-mix(in srgb, var(--txt) 5%, transparent),
    0 8px 24px -14px color-mix(in srgb, var(--txt) 16%, transparent);
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
/* Anything that opens a breakdown lifts, so "this is clickable" is felt before it is read. */
.tile:hover {
  transform: translateY(-3px); border-color: color-mix(in srgb, var(--a-feed) 40%, var(--rule));
  box-shadow:
    0 2px 4px color-mix(in srgb, var(--txt) 6%, transparent),
    0 16px 32px -16px color-mix(in srgb, var(--txt) 22%, transparent);
}
.tile:focus-visible { outline: 2px solid var(--a-feed); outline-offset: 2px; }
.tile-l {
  font-size: 10.5px; font-weight: 800; letter-spacing: .7px; text-transform: uppercase;
  color: var(--txt2);
}
.tile-n {
  font-size: 32px; font-weight: 800; letter-spacing: -.6px; line-height: 1.1; color: var(--txt);
  margin-top: 3px;
}
.tile-n small { font-size: 16px; font-weight: 700; color: var(--txt2); margin-left: 1px; }
.tile-s { font-size: 11.5px; color: var(--txt2); margin-top: 3px; }
.tile-go {
  font-size: 10.5px; font-weight: 700; color: var(--a-feed); margin-top: 9px;
  opacity: 0; transition: opacity .16s ease;
}
.tile:hover .tile-go, .tile:focus-visible .tile-go { opacity: 1; }
/* The scale, on the top edge rather than on the number: a 32px figure in red reads as an alarm even
   when it is merely below target. */
.tile.good { border-top: 3px solid var(--pos); }
.tile.mid  { border-top: 3px solid var(--warn); }
.tile.poor { border-top: 3px solid var(--neg); }

/* ── The journey ────────────────────────────────────────────────────────────────────────────── */
.rib-svg { width: 100%; height: auto; display: block; overflow: visible; }
.rib { fill: url(#ccFlow); }
.rib-lost { fill: var(--neg); opacity: .5; }
.rib-lost-n { font-size: 11px; font-weight: 800; fill: var(--neg); }

.stgs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-top: 4px; }
.stg {
  display: flex; flex-direction: column; gap: 1px; text-align: left; font: inherit; cursor: pointer;
  padding: 10px 12px; background: var(--pane2); border: 1px solid var(--rule);
  border-radius: 9px; color: var(--txt2);
  transition: transform .16s ease, border-color .16s ease;
}
.stg:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--a-feed) 40%, var(--rule)); }
.stg:focus-visible { outline: 2px solid var(--a-feed); outline-offset: 2px; }
.stg-n { font-size: 19px; font-weight: 800; color: var(--txt); line-height: 1.15; }
.stg-p { font-size: 11px; font-weight: 700; color: var(--txt2); }
.stg-l { font-size: 12.5px; font-weight: 700; color: var(--txt); margin-top: 4px; }
.stg-d { font-size: 10.5px; color: var(--txt2); line-height: 1.4; }

/* ── Where calls are lost: a band, not a column ─────────────────────────────────────────────── */
.pnl-band .lost {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.5fr);
  gap: 14px; align-items: stretch;
}
.lost-i {
  display: grid; grid-template-columns: auto auto; grid-template-rows: auto auto auto;
  gap: 1px 10px; align-content: start; text-align: left; font: inherit; cursor: pointer;
  padding: 12px 14px; background: var(--pane2); border: 1px solid var(--rule); border-radius: 9px;
  color: var(--txt2); transition: transform .16s ease, border-color .16s ease;
}
.lost-i:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--neg) 45%, var(--rule)); }
.lost-i:focus-visible { outline: 2px solid var(--a-feed); outline-offset: 2px; }
.lost-n { grid-row: 1; font-size: 25px; font-weight: 800; color: var(--txt); line-height: 1.1; }
.lost-share {
  grid-row: 1; align-self: center; justify-self: start; font-size: 12px; font-weight: 700; color: var(--neg);
  background: color-mix(in srgb, var(--neg) 12%, transparent); border-radius: 20px; padding: 1px 8px;
}
.lost-t { grid-row: 2; grid-column: 1 / -1; font-size: 12.5px; font-weight: 700; color: var(--txt); }
.lost-w { grid-row: 3; grid-column: 1 / -1; font-size: 11px; color: var(--txt2); }
.lost-s { grid-row: 4; grid-column: 1 / -1; font-size: 11px; color: var(--txt2); margin-top: 6px; }
.pnl-band .note { margin: 0; align-self: center; }

/* ── Rows: one horizontal-bar shape, used by lines, hours and wait buckets ──────────────────── */
.brows { display: flex; flex-direction: column; gap: 4px; }
.brows.tight { gap: 2px; --lw: 62px; }
.brow {
  display: grid; grid-template-columns: var(--lw, 150px) 1fr 50px 58px; align-items: center; gap: 10px;
  width: 100%; padding: 5px 8px; text-align: left; font: inherit; color: var(--txt2);
  background: none; border: 1px solid transparent; border-radius: 7px;
}
button.brow { cursor: pointer; }
button.brow:hover { background: var(--hover); border-color: var(--rule); }
button.brow:focus-visible { outline: 2px solid var(--a-feed); outline-offset: 1px; }
.brow.on { border-color: var(--a-feed); background: var(--hover); }
.brow-l {
  font-size: 12px; font-weight: 600; color: var(--txt);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.brow-track {
  position: relative; height: 10px; border-radius: 5px; background: var(--pane2);
  border: 1px solid var(--rule); overflow: hidden;
}
/* The benchmark, drawn on the track it is judging rather than floating above the list. */
.brow-track[style*="--mark"]::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: var(--mark); width: 0;
  border-left: 1px dashed color-mix(in srgb, var(--pos) 75%, transparent);
}
.brow-fill { display: block; height: 100%; width: var(--w); border-radius: 5px; }
.brow.good .brow-fill { background: var(--pos); }
.brow.mid  .brow-fill { background: var(--warn); }
.brow.poor .brow-fill { background: var(--neg); }
.brow.neutral .brow-fill { background: var(--a-feed); }
.brow-p { font-size: 12.5px; font-weight: 800; text-align: right; color: var(--txt); }
.brow-p small { font-size: 9.5px; font-weight: 600; color: var(--txt2); }
.brow-v { font-size: 11px; text-align: right; color: var(--txt2); }

/* ── Notes ──────────────────────────────────────────────────────────────────────────────────── */
.note {
  font-size: 11.5px; line-height: 1.6; color: var(--txt2); margin: 12px 0 0;
  padding: 9px 12px; border-radius: 8px; background: var(--pane2);
  border-left: 3px solid var(--rule2);
}
.note b { color: var(--txt); font-weight: 700; }
.note.warn { border-left-color: var(--warn); }

.delta { font-size: 12px; font-weight: 800; }
.delta.up { color: color-mix(in srgb, var(--pos) 65%, var(--txt)); }
.delta.down { color: var(--neg); }
.scope-chip {
  font-size: 10.5px; font-weight: 700; color: var(--txt2); padding: 2px 9px;
  border: 1px dashed var(--rule2); border-radius: 20px;
}

/* ── Trend ──────────────────────────────────────────────────────────────────────────────────── */
.trend-svg { width: 100%; height: auto; display: block; }
.trend-svg .grid { stroke: var(--rule); stroke-width: 1; shape-rendering: crispEdges; }
.trend-svg .axis { fill: var(--txt2); font-size: 10px; font-weight: 500; }
.trend-svg .axis-r { fill: var(--txt3); font-size: 9.5px; font-weight: 500; }
.trend-svg .area { fill: color-mix(in srgb, var(--a-feed) 18%, transparent); }
.trend-svg .line { fill: none; stroke: var(--pos); stroke-width: 2.2; stroke-linejoin: round; }
.trend-svg .dot { fill: var(--pos); stroke: var(--pane); stroke-width: 1.5; }

/* ── Queue stats ────────────────────────────────────────────────────────────────────────────── */
.qstats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 14px; }
.qstat {
  display: flex; flex-direction: column; gap: 2px; padding: 9px 11px;
  background: var(--pane2); border: 1px solid var(--rule); border-radius: 9px;
}
.qstat-n { font-size: 18px; font-weight: 800; color: var(--txt); line-height: 1.1; }
.qstat-l {
  font-size: 9.5px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase;
  color: var(--txt2);
}

/* ── Drilldown drawer ───────────────────────────────────────────────────────────────────────── */
.drw {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: flex-end;
  justify-content: center; background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: blur(2px);
}
.drw-in {
  width: min(1180px, 96vw); max-height: 82vh; overflow: auto;
  background: var(--pane); border: 1px solid var(--rule2); border-radius: 16px 16px 0 0;
  padding: 18px 20px 22px; box-shadow: 0 -18px 50px -20px color-mix(in srgb, var(--txt) 45%, transparent);
}
@media (prefers-reduced-motion: no-preference) {
  .drw-in { animation: drwup .22s cubic-bezier(.16, 1, .3, 1) both; }
}
@keyframes drwup { from { transform: translateY(18px); opacity: .5; } to { transform: none; opacity: 1; } }
.drw-h {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 14px;
  margin-bottom: 14px;
}
.drw-h h3 { font-size: 15px; font-weight: 700; color: var(--txt); margin: 0; }
.drw-x {
  font: inherit; font-size: 14px; cursor: pointer; color: var(--txt2); line-height: 1;
  background: none; border: 1px solid var(--rule2); border-radius: 7px; padding: 5px 10px;
}
.drw-x:hover { color: var(--txt); border-color: var(--neg); }
.tblwrap { overflow-x: auto; }

.cc-filter {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin-bottom: 12px;
  padding: 8px 13px; border: 1px solid var(--a-feed); border-radius: 9px; background: var(--pane2);
  font-size: 12px; color: var(--txt2);
}
.cc-filter-label {
  font-size: 9.5px; font-weight: 800; letter-spacing: .6px; text-transform: uppercase;
}
.cc-filter b { color: var(--txt); font-size: 13px; }
.cc-filter-clear {
  margin-left: auto; font: inherit; font-size: 11px; cursor: pointer; color: var(--txt2);
  background: none; border: 1px solid var(--rule2); border-radius: 5px; padding: 2px 10px;
}
.cc-filter-clear:hover { color: var(--txt); border-color: var(--a-feed); }

@media (max-width: 1000px) {
  .cc-2col { grid-template-columns: 1fr; }
  .stgs { grid-template-columns: repeat(2, 1fr); }
  .pnl-band .lost { grid-template-columns: 1fr; }
  .qstats { grid-template-columns: repeat(2, 1fr); }
  .brow { --lw: 100px; grid-template-columns: var(--lw) 1fr 44px 52px; }
}

/* ============================================================================
   Lumen — the company assistant. A button bottom-right on every screen, a panel
   above it on a desktop, the whole screen on a phone, and Talk mode: the whole
   window, given over to a living orb and live captions.

   Colored from --a-over (contrasts with a pane in all 28 themes), never --ink,
   which is a foreground for accent FILLS and is near-white in the light themes.
   The aurora blends --a-over, --a-assign and --a-map, so every theme gets its
   own Lumen rather than one hard-coded gradient.
   ============================================================================ */
.lumen-fab {
  position: fixed; z-index: 900;
  right: max(18px, env(safe-area-inset-right)); bottom: max(18px, env(safe-area-inset-bottom));
  display: inline-flex; align-items: center; gap: 10px;
  height: 50px; padding: 0 20px 0 12px; border-radius: 25px;
  background: var(--pane); color: var(--txt);
  border: 1px solid color-mix(in srgb, var(--a-over) 55%, transparent);
  box-shadow: 0 2px 6px var(--shadow), 0 10px 30px color-mix(in srgb, var(--a-over) 22%, transparent);
  font: 600 14px/1 Inter, system-ui, sans-serif; cursor: pointer;
  transition: transform .2s ease, box-shadow .2s ease;
}
.lumen-fab:hover { transform: translateY(-3px); box-shadow: 0 2px 6px var(--shadow), 0 14px 36px color-mix(in srgb, var(--a-over) 32%, transparent); }
.lumen-fab:focus-visible { outline: 2px solid var(--a-over); outline-offset: 3px; }
.lumen-fab.on { background: var(--sel); }

/* The orb: Lumen's face at every size. A turning conic gradient of three theme accents. */
@property --spin { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
.lumen-orb {
  --o: 14px;
  position: relative; width: var(--o); height: var(--o); border-radius: 50%; flex: none;
  /* An agent other than Lumen sets --orb-a/b/c (LUMEN.agentColors) so its orb is its own. */
  background: conic-gradient(from var(--spin, 0deg), var(--orb-a, var(--a-over)), var(--orb-b, var(--a-assign)), var(--orb-c, var(--a-map)), var(--orb-a, var(--a-over)));
  box-shadow: 0 0 calc(var(--o) * .6) color-mix(in srgb, var(--orb-a, var(--a-over)) 55%, transparent);
  animation: lumen-spin 6s linear infinite;
}
.lumen-orb::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: radial-gradient(circle at 32% 28%, rgba(255,255,255,.65), rgba(255,255,255,0) 55%);
}
.lumen-orb-fab { --o: 28px; }
.lumen-orb-hero { --o: 64px; margin-bottom: 18px; animation: lumen-spin 8s linear infinite, lumen-breathe 4s ease-in-out infinite; }
.lumen-orb[data-state="thinking"], .lumen-fab[data-state="thinking"] .lumen-orb { animation: lumen-spin 1.2s linear infinite, lumen-breathe 1.2s ease-in-out infinite; }
.lumen-orb[data-state="listening"] { animation: lumen-spin 3s linear infinite, lumen-breathe .8s ease-in-out infinite; }
.lumen-orb[data-state="speaking"] { animation: lumen-spin 2.4s linear infinite, lumen-breathe .5s ease-in-out infinite; }
@keyframes lumen-spin { to { --spin: 360deg; } }
@keyframes lumen-breathe { 0%,100% { transform: scale(1); } 50% { transform: scale(1.12); } }

.lumen-panel {
  position: fixed; z-index: 901;
  right: max(18px, env(safe-area-inset-right)); bottom: calc(max(18px, env(safe-area-inset-bottom)) + 64px);
  width: min(440px, calc(100vw - 36px)); height: min(680px, calc(100vh - 110px));
  display: flex; flex-direction: column;
  background: var(--pane); color: var(--txt);
  border: 1px solid var(--rule2); border-radius: 18px;
  box-shadow: 0 2px 6px var(--shadow), 0 24px 64px var(--shadow);
  overflow: hidden;
  animation: lumen-pop .28s cubic-bezier(.2,.9,.3,1.2);
  transform-origin: bottom right;
}
/* The lit top edge every MCC surface carries. */
.lumen-panel::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; z-index: 2;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--a-over) 80%, transparent), color-mix(in srgb, var(--a-assign) 80%, transparent), transparent);
}
.lumen-panel[hidden] { display: none; }
@keyframes lumen-pop { from { opacity: 0; transform: translateY(12px) scale(.96); } to { opacity: 1; transform: none; } }

.lumen-head {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 14px 12px 14px 16px; border-bottom: 1px solid var(--rule);
  background:
    radial-gradient(120% 140% at 0% 0%, color-mix(in srgb, var(--a-over) 16%, transparent), transparent 60%),
    radial-gradient(90% 120% at 100% 0%, color-mix(in srgb, var(--a-assign) 12%, transparent), transparent 60%),
    var(--pane2);
}
.lumen-id { display: flex; align-items: center; gap: 11px; min-width: 0; }
.lumen-id .lumen-orb { --o: 22px; }
.lumen-name { font-weight: 800; font-size: 15px; letter-spacing: .3px; }
.lumen-sub { font-size: 12px; color: var(--txt3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lumen-tools { display: flex; gap: 4px; }
.lumen-ic {
  width: 34px; height: 34px; display: grid; place-items: center;
  background: transparent; color: var(--txt2); border: 1px solid transparent; border-radius: 9px; cursor: pointer;
}
.lumen-ic:hover { background: var(--hover); color: var(--txt); }
.lumen-ic.on { color: var(--a-over); border-color: var(--a-over); }

.lumen-body { flex: 1; overflow-y: auto; padding: 18px 16px; display: flex; flex-direction: column; gap: 14px; }
/* Cards clip their accent edge (overflow: hidden), which lets a flex column squash them; they never shrink. */
.lumen-body > * { flex: none; }

/* ---- Hello ---- */
.lumen-hello { margin: auto 0; display: flex; flex-direction: column; align-items: flex-start; }
.lumen-hi { font-size: 21px; font-weight: 800; letter-spacing: -.2px; margin: 0 0 8px; line-height: 1.25; }
.lumen-lede { font-size: 13px; line-height: 1.55; color: var(--txt2); margin: 0 0 16px; }
.lumen-hero-talk {
  display: inline-flex; align-items: center; gap: 10px; margin-bottom: 20px;
  height: 46px; padding: 0 22px 0 16px; border-radius: 23px; border: 0; cursor: pointer;
  font: 700 14px/1 Inter, system-ui, sans-serif; color: #fff;
  background: linear-gradient(120deg, var(--a-over), var(--a-assign));
  box-shadow: 0 8px 24px color-mix(in srgb, var(--a-over) 40%, transparent);
  transition: transform .2s ease, box-shadow .2s ease;
}
.lumen-hero-talk:hover { transform: translateY(-3px); box-shadow: 0 12px 30px color-mix(in srgb, var(--a-over) 50%, transparent); }
.lumen-hero-talk:focus-visible { outline: 2px solid var(--a-over); outline-offset: 3px; }
.lumen-try { text-transform: uppercase; letter-spacing: .8px; font-size: 10.5px; font-weight: 700; color: var(--txt3); margin-bottom: 8px; }
.lumen-chips { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; width: 100%; }
.lumen-chip {
  padding: 10px 12px; border-radius: 12px; font: 500 12.5px/1.35 Inter, system-ui, sans-serif;
  background: var(--pane2); color: var(--txt2); border: 1px solid var(--rule); cursor: pointer; text-align: left;
  transition: transform .18s ease, border-color .18s ease, color .18s ease;
  animation: lumen-rise .45s ease both; animation-delay: calc(var(--i, 0) * 50ms + 80ms);
}
.lumen-chip:hover { color: var(--txt); border-color: var(--a-over); transform: translateY(-3px); }

/* ---- Conversation: questions as headings, answers as cards ---- */
.lumen-q { display: flex; align-items: flex-start; gap: 8px; }
.lumen-q-mark { color: var(--a-over); margin-top: 3px; flex: none; }
.lumen-q p { margin: 0; font-size: 14px; font-weight: 600; line-height: 1.45; color: var(--txt); overflow-wrap: anywhere; }

/* Editing the last question: a pencil beside it, and the question itself turns into the editor. */
.lumen-q-edit {
  margin-left: auto; flex: none; display: grid; place-items: center; width: 26px; height: 26px; margin-top: -3px;
  border-radius: 8px; border: 1px solid transparent; background: transparent; color: var(--txt3); cursor: pointer;
  opacity: .6; transition: opacity .15s ease, color .15s ease, border-color .15s ease;
}
.lumen-q:hover .lumen-q-edit, .lumen-q-edit:focus-visible { opacity: 1; }
.lumen-q-edit:hover { color: var(--a-over); border-color: var(--rule2); }
.lumen-q-edit:focus-visible { outline: 2px solid var(--a-over); outline-offset: 1px; }
@media (hover: none) { .lumen-q-edit { opacity: 1; } }
.lumen-q-editor { flex: 1; min-width: 0; display: grid; gap: 8px; }
.lumen-q-editor textarea {
  width: 100%; box-sizing: border-box; resize: none; padding: 8px 10px; border-radius: 10px;
  border: 1px solid var(--a-over); background: var(--pane2); color: var(--txt);
  font: 600 14px/1.45 Inter, system-ui, sans-serif;
}
.lumen-q-editor textarea:focus { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--a-over) 18%, transparent); }
.lumen-q-edit-row { display: flex; align-items: center; justify-content: flex-end; gap: 6px; flex-wrap: wrap; }
.lumen-q-edit-note { flex: 1 1 180px; font-size: 12px; line-height: 1.35; color: var(--txt3); }
.lumen-q-edit-keep, .lumen-q-edit-ask {
  height: 30px; padding: 0 14px; border-radius: 15px; cursor: pointer; font: 600 12.5px/1 Inter, system-ui, sans-serif;
  background: transparent; color: var(--txt2); border: 1px solid var(--rule2);
}
.lumen-q-edit-ask { background: var(--sel); color: var(--a-over); border-color: var(--a-over); }
.lumen-q-edit-keep:hover { color: var(--txt); }
.lumen-stale { opacity: .38; filter: saturate(.4); pointer-events: none; transition: opacity .2s ease; }

.lumen-card {
  position: relative; padding: 14px 16px 12px; border-radius: 14px;
  background: var(--pane2); border: 1px solid var(--rule);
  box-shadow: 0 1px 2px var(--shadow), 0 8px 24px color-mix(in srgb, var(--shadow) 40%, transparent);
  overflow: hidden;
}
.lumen-card::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: linear-gradient(90deg, var(--a-over), var(--a-assign), var(--a-map));
  opacity: .85;
}
.lumen-card.declined { border-style: dashed; }
.lumen-card.declined::before { background: var(--rule2); }
.lumen-figure {
  font-size: 34px; font-weight: 800; letter-spacing: -.8px; line-height: 1.05; margin: 2px 0 8px;
  background: linear-gradient(110deg, var(--a-over), var(--a-assign));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.lumen-answer { font-size: 14px; line-height: 1.6; overflow-wrap: anywhere; }
.lumen-card.declined .lumen-answer { color: var(--txt2); }
.lumen-answer p { margin: 0 0 7px; }
.lumen-answer p:last-child { margin: 0; }
.lumen-answer ul { margin: 4px 0 8px; padding-left: 0; list-style: none; }
.lumen-answer li { position: relative; margin: 4px 0; padding-left: 16px; }
.lumen-answer li::before { content: ""; position: absolute; left: 3px; top: .62em; width: 6px; height: 6px; border-radius: 50%; background: var(--a-over); }
.lumen-num { color: var(--a-over); font-weight: 700; }

.lumen-card-foot { display: flex; align-items: flex-end; gap: 8px; margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--rule); }
.lumen-card-foot .lumen-sources { flex: 1; margin: 0; }
.lumen-sources { display: flex; flex-wrap: wrap; gap: 4px 6px; align-items: center; font-size: 11.5px; color: var(--txt3); }
.lumen-from { text-transform: uppercase; letter-spacing: .6px; font-weight: 700; font-size: 10.5px; }
.lumen-src { padding: 2px 8px; border: 1px solid var(--rule2); border-radius: 10px; color: var(--txt2); background: var(--pane); }
.lumen-asof { width: 100%; }

/* Thinking: a skeleton of the card that is coming, shimmering in the accent. */
.lumen-thinking { padding: 14px 16px; border-radius: 14px; background: var(--pane2); border: 1px solid var(--rule); }
.lumen-thinking-row { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--txt2); margin-bottom: 12px; }
.lumen-thinking-row .lumen-orb { --o: 16px; }
.lumen-skel {
  display: block; height: 10px; border-radius: 5px; margin-top: 8px;
  background: linear-gradient(90deg, var(--rule) 0%, color-mix(in srgb, var(--a-over) 35%, var(--rule)) 50%, var(--rule) 100%);
  background-size: 200% 100%; animation: lumen-shimmer 1.3s linear infinite;
}
.lumen-skel:first-of-type { width: 45%; height: 22px; }
.lumen-skel.short { width: 62%; }
@keyframes lumen-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }

.lumen-enter { animation: lumen-rise .42s cubic-bezier(.2,.8,.2,1) both; }
@keyframes lumen-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ---- Compose ---- */
.lumen-compose {
  display: flex; align-items: flex-end; gap: 8px; padding: 12px;
  padding-bottom: max(12px, env(safe-area-inset-bottom));
  border-top: 1px solid var(--rule); background: var(--pane2);
}
.lumen-field {
  flex: 1; display: flex; align-items: flex-end; gap: 4px; min-width: 0;
  background: var(--pane); border: 1px solid var(--rule2); border-radius: 14px; padding: 3px 3px 3px 0;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.lumen-field:focus-within { border-color: var(--a-over); box-shadow: 0 0 0 3px color-mix(in srgb, var(--a-over) 18%, transparent); }
.lumen-field textarea {
  flex: 1; resize: none; min-height: 38px; max-height: 140px; padding: 9px 12px; min-width: 0;
  background: transparent; color: var(--txt); border: 0; outline: none;
  font: 400 14px/1.4 Inter, system-ui, sans-serif; overflow-y: hidden;
}
.lumen-send {
  width: 36px; height: 36px; flex: none; display: grid; place-items: center; border-radius: 11px; cursor: pointer;
  background: transparent; color: var(--a-over); border: 0;
}
.lumen-send:hover { background: var(--sel); }
.lumen-send:disabled { opacity: .45; cursor: default; }

/* Talk: the call to action, not an icon among icons. */
.lumen-talk {
  position: relative; height: 46px; flex: none; display: inline-flex; align-items: center; gap: 7px;
  padding: 0 16px 0 13px; border-radius: 23px; border: 0; cursor: pointer;
  font: 700 13.5px/1 Inter, system-ui, sans-serif; color: #fff;
  background: linear-gradient(120deg, var(--a-over), var(--a-assign));
  box-shadow: 0 6px 18px color-mix(in srgb, var(--a-over) 38%, transparent);
  transition: transform .2s ease, box-shadow .2s ease;
}
.lumen-talk[hidden] { display: none; }
.lumen-talk:hover { transform: translateY(-2px); box-shadow: 0 10px 26px color-mix(in srgb, var(--a-over) 48%, transparent); }
.lumen-talk:focus-visible { outline: 2px solid var(--a-over); outline-offset: 3px; }
.lumen-talk-ic { display: grid; place-items: center; }

/* Arming: the button swells and throws a ring before the window opens up. */
.lumen-arming { animation: lumen-arm .62s cubic-bezier(.3,1.4,.5,1) both; }
.lumen-arming::after {
  content: ""; position: absolute; inset: -2px; border-radius: inherit; pointer-events: none;
  border: 2px solid var(--a-over); animation: lumen-ring .62s ease-out both;
}
@keyframes lumen-arm { 0% { transform: scale(1); } 35% { transform: scale(1.12); filter: brightness(1.2); } 100% { transform: scale(1); } }
@keyframes lumen-ring { from { opacity: .9; transform: scale(1); } to { opacity: 0; transform: scale(1.7); } }

/* ============================================================================
   Talk mode — the whole window. It opens out of the panel: the clip starts as
   the panel's own rectangle (--from, measured in JS) and grows to the screen.
   ============================================================================ */
.lumen-stage {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; flex-direction: column;
  background: var(--bg); color: var(--txt);
  clip-path: var(--from, inset(40% 40% 40% 40% round 16px));
  opacity: 0;
  transition: clip-path .5s cubic-bezier(.7,0,.2,1), opacity .25s ease;
  overflow: hidden;
}
.lumen-stage.in { clip-path: inset(0 0 0 0 round 0); opacity: 1; }
.lumen-stage[hidden] { display: none; }
body.lumen-talking { overflow: hidden; }

.lumen-aurora { position: absolute; inset: -20%; pointer-events: none; filter: blur(70px); opacity: .5; transition: opacity .6s ease; }
html.theme-light .lumen-aurora { opacity: .32; }
.lumen-aurora i { position: absolute; width: 55vmax; height: 55vmax; border-radius: 50%; }
.lumen-aurora i:nth-child(1) { left: 5%; top: 10%; background: radial-gradient(circle, var(--a-over), transparent 65%); animation: lumen-drift1 18s ease-in-out infinite alternate; }
.lumen-aurora i:nth-child(2) { right: 0; top: 30%; background: radial-gradient(circle, var(--a-assign), transparent 65%); animation: lumen-drift2 22s ease-in-out infinite alternate; }
.lumen-aurora i:nth-child(3) { left: 30%; bottom: -5%; background: radial-gradient(circle, var(--a-map), transparent 65%); animation: lumen-drift3 26s ease-in-out infinite alternate; }
@keyframes lumen-drift1 { to { transform: translate(18vw, 12vh) scale(1.15); } }
@keyframes lumen-drift2 { to { transform: translate(-20vw, -8vh) scale(.9); } }
@keyframes lumen-drift3 { to { transform: translate(10vw, -16vh) scale(1.1); } }
.lumen-stage[data-state="speaking"] .lumen-aurora { opacity: .62; }
html.theme-light .lumen-stage[data-state="speaking"] .lumen-aurora { opacity: .4; }

.lumen-stage-top, .lumen-stage-center, .lumen-stage-bottom { position: relative; z-index: 1; }
.lumen-stage-top {
  display: flex; align-items: center; justify-content: space-between;
  padding: max(18px, env(safe-area-inset-top)) max(22px, env(safe-area-inset-right)) 0 max(22px, env(safe-area-inset-left));
}
.lumen-stage-brand { display: flex; align-items: center; gap: 10px; font-weight: 800; font-size: 16px; letter-spacing: .3px; }
.lumen-stage-brand .lumen-orb { --o: 22px; }
.lumen-stage-live {
  font-size: 10px; font-weight: 800; letter-spacing: 1px; text-transform: uppercase;
  padding: 3px 7px; border-radius: 6px; color: var(--neg); border: 1px solid var(--neg);
  display: inline-flex; align-items: center; gap: 5px;
}
.lumen-stage-live::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--neg); animation: lumen-blink 1.4s infinite; }
@keyframes lumen-blink { 0%,100% { opacity: 1; } 50% { opacity: .25; } }
.lumen-stage-x {
  display: inline-flex; align-items: center; gap: 8px; height: 40px; padding: 0 10px 0 12px; border-radius: 20px;
  background: color-mix(in srgb, var(--pane) 70%, transparent); color: var(--txt2);
  border: 1px solid var(--rule2); cursor: pointer; backdrop-filter: blur(8px);
}
.lumen-stage-x:hover { color: var(--txt); border-color: var(--a-over); }
.lumen-stage-x:focus-visible { outline: 2px solid var(--a-over); outline-offset: 2px; }
.lumen-key { font: 600 11px/1 "JetBrains Mono", monospace; padding: 3px 5px; border-radius: 4px; border: 1px solid var(--rule2); }

.lumen-stage-center {
  flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 8px 22px; text-align: center; gap: 6px;
}
.lumen-stage-state {
  text-transform: uppercase; letter-spacing: 3px; font-size: 12px; font-weight: 800; color: var(--a-over);
  min-height: 16px;
}
.lumen-stage-state[data-state="listening"] { color: var(--a-map); }
.lumen-stage-state[data-state="speaking"] { color: var(--a-assign); }
.lumen-canvas-wrap { width: min(380px, 62vw, 44vh); aspect-ratio: 1; flex: none; transition: width .55s cubic-bezier(.2,.8,.2,1); }
/* Once there is an answer to read, the orb steps back to make room for it. */
.lumen-stage.answered .lumen-canvas-wrap { width: min(230px, 46vw, 26vh); }
.lumen-canvas { width: 100%; height: 100%; display: block; }

.lumen-heard {
  margin: 0; max-width: 760px; min-height: 1.4em;
  font-size: clamp(16px, 2.2vw, 20px); line-height: 1.4; color: var(--txt2); font-style: italic;
}
.lumen-heard:not(:empty)::before { content: "\201C"; }
.lumen-heard:not(:empty)::after { content: "\201D"; }
/* Talk mode: the question Lumen heard can be tapped to change it, and then edited where it stands. */
.lumen-heard.editable { cursor: text; border-radius: 10px; padding: 2px 8px; transition: background .15s ease; }
.lumen-heard.editable:hover, .lumen-heard.editable:focus-visible {
  background: color-mix(in srgb, var(--a-over) 10%, transparent); outline: none;
}
.lumen-heard[contenteditable="true"] {
  font-style: normal; color: var(--txt); background: color-mix(in srgb, var(--a-over) 12%, transparent);
  outline: 2px solid var(--a-over); outline-offset: 3px; -webkit-line-clamp: unset; overflow: visible;
}
.lumen-stage.editing .lumen-stage-answer, .lumen-stage.editing .lumen-stage-follow, .lumen-stage.editing .lumen-stage-viz {
  opacity: .3; pointer-events: none;
}
@media (prefers-reduced-motion: reduce) { .lumen-stale, .lumen-heard.editable { transition: none; } }

.lumen-stage-answer {
  max-width: 820px; width: 100%; opacity: 0; transform: translateY(14px);
  transition: opacity .4s ease, transform .5s cubic-bezier(.2,.8,.2,1);
  max-height: 46vh; overflow-y: auto;
  /* It scrolls itself as it is read (see followWords), so no scrollbar: a fade at the edges says there is
     more instead. position: relative makes it the offsetParent the scrolling measures from. */
  position: relative; scrollbar-width: none; overscroll-behavior: contain;
}
.lumen-stage-answer::-webkit-scrollbar { display: none; }
/* Fade whichever edge has more beyond it: the bottom before it has scrolled, both while it scrolls, the
   top once it reaches the end. Never the top at the start, where the headline figure sits. */
.lumen-stage-answer.overflowing:not(.at-end) {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 48px), transparent 100%);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 48px), transparent 100%);
}
.lumen-stage-answer.overflowing:not(.at-start):not(.at-end) {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 28px, #000 calc(100% - 48px), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 28px, #000 calc(100% - 48px), transparent 100%);
}
.lumen-stage-answer.overflowing.at-end:not(.at-start) {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 28px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 28px);
}
.lumen-stage-answer.shown { opacity: 1; transform: none; }
.lumen-stage-figure {
  font-size: clamp(40px, 7vw, 76px); font-weight: 800; letter-spacing: -2px; line-height: 1.05; margin: 6px 0;
  background: linear-gradient(110deg, var(--a-over), var(--a-assign) 55%, var(--a-map));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.lumen-stage-figure:empty { display: none; }
.lumen-said { margin: 0; font-size: clamp(18px, 2.5vw, 24px); line-height: 1.5; font-weight: 500; color: var(--txt); }
.lumen-said span { opacity: .18; transition: opacity .25s ease; }
.lumen-said span.on { opacity: 1; }
.lumen-stage-answer.declined .lumen-said { color: var(--txt2); }
.lumen-stage-src { margin-top: 12px; display: flex; justify-content: center; }
.lumen-stage-src .lumen-sources { justify-content: center; }
.lumen-stage-src .lumen-asof { width: auto; }

.lumen-stage-mic {
  position: relative; width: 74px; height: 74px; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(135deg, var(--a-over), var(--a-assign));
  box-shadow: 0 10px 30px color-mix(in srgb, var(--a-over) 45%, transparent);
  transition: transform .2s ease;
}
.lumen-stage-mic:hover { transform: scale(1.06); }
.lumen-stage-mic:focus-visible { outline: 3px solid var(--a-over); outline-offset: 4px; }
.lumen-stage-mic.on { background: linear-gradient(135deg, var(--neg), var(--a-assign)); }
.lumen-stage-ring { position: absolute; inset: -8px; border-radius: 50%; border: 2px solid var(--a-over); opacity: 0; }
.lumen-stage-mic.on .lumen-stage-ring { animation: lumen-ring 1.4s ease-out infinite; border-color: var(--neg); }

/* Phones: the panel is the screen, and the button steps aside while it is open. */
@media (max-width: 640px) {
  .lumen-fab { height: 58px; width: 58px; padding: 0; justify-content: center; border-radius: 50%; }
  .lumen-fab-label { display: none; }
  .lumen-panel {
    /* inset, not 100vw: the viewport width includes a scrollbar, so 100vw scrolls sideways. */
    inset: 0; width: auto; height: auto; border-radius: 0; border: 0;
    padding-top: env(safe-area-inset-top);
  }
  body.lumen-open .lumen-fab { display: none; }
  .lumen-chips { grid-template-columns: 1fr; }
  .lumen-figure { font-size: 30px; }
  .lumen-field textarea { font-size: 16px; } /* below 16px, iOS zooms the page on focus */
  .lumen-talk-label { display: none; }
  .lumen-talk { width: 46px; padding: 0; justify-content: center; }
  .lumen-stage-x .lumen-key { display: none; }
  .lumen-stage-answer { max-height: 38vh; }
}

@media (prefers-reduced-motion: reduce) {
  .lumen-orb, .lumen-orb-hero, .lumen-chip, .lumen-enter, .lumen-skel, .lumen-panel,
  .lumen-arming, .lumen-arming::after, .lumen-aurora i, .lumen-stage-live::before,
  .lumen-stage-mic.on .lumen-stage-ring { animation: none !important; }
  .lumen-stage { transition: opacity .15s ease; clip-path: none; }
  .lumen-stage-answer, .lumen-said span { transition: none; }
  .lumen-fab:hover, .lumen-chip:hover, .lumen-talk:hover, .lumen-hero-talk:hover { transform: none; }
}

/* ============================================================================
   Lumen v2 — follow-ups, charts, feedback, history, languages, and the chart
   column in talk mode. Same language as the rest of Lumen: theme accents only,
   tabular figures, uppercase micro-labels, lit top edges, and a change always
   carries an arrow and a sign, never color alone.
   ============================================================================ */

/* The panel's body and compose sit in one column, so the history drawer can cover exactly them. */
.lumen-main { flex: 1; min-height: 0; display: flex; flex-direction: column; position: relative; }
/* A long placeholder (Spanish, French, German) stays on its one line rather than wrapping out of sight. */
.lumen-field textarea::placeholder { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---- Language picker: a short code on show, the platform's native menu underneath ---- */
.lumen-lang {
  position: relative; display: inline-flex; align-items: center; gap: 5px; height: 34px; padding: 0 8px;
  border-radius: 9px; color: var(--txt2); border: 1px solid transparent; cursor: pointer;
  font: 700 11.5px/1 Inter, system-ui, sans-serif; letter-spacing: .5px;
}
.lumen-lang:hover { background: var(--hover); color: var(--txt); }
.lumen-lang:focus-within { outline: 2px solid var(--a-over); outline-offset: 1px; }
.lumen-lang select {
  position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer;
  font-size: 16px; /* below 16px, iOS zooms on focus */
}
.lumen-lang select option { color: initial; }
.lumen-stage-tools { display: flex; align-items: center; gap: 8px; }
.lumen-stage-tools .lumen-lang {
  height: 40px; padding: 0 12px; border-radius: 20px; border-color: var(--rule2);
  background: color-mix(in srgb, var(--pane) 70%, transparent); backdrop-filter: blur(8px);
}

/* ---- Card footer: time taken, feedback, listen ---- */
.lumen-card-foot { flex-wrap: wrap; }
.lumen-card-acts { display: flex; align-items: center; gap: 4px; margin-left: auto; }
.lumen-took {
  display: inline-flex; align-items: center; gap: 3px; margin-right: 4px;
  font-size: 11px; color: var(--txt3); white-space: nowrap;
}
/* Excel: a labeled pill rather than a bare icon, because "download every record" is not a thing an icon says. */
.lumen-xl {
  display: inline-flex; align-items: center; gap: 5px; height: 28px; padding: 0 10px; margin-right: 4px;
  border-radius: 14px; cursor: pointer; font: 600 12px/1 Inter, system-ui, sans-serif; white-space: nowrap;
  color: var(--pos); background: color-mix(in srgb, var(--pos) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--pos) 40%, transparent);
  transition: background .15s ease, transform .15s ease;
}
.lumen-xl:hover { background: color-mix(in srgb, var(--pos) 18%, transparent); transform: translateY(-1px); }
.lumen-xl:focus-visible { outline: 2px solid var(--a-over); outline-offset: 1px; }
.lumen-xl:disabled { opacity: .65; cursor: progress; transform: none; }
.lumen-fb { display: inline-flex; gap: 2px; }
.lumen-fb-b {
  width: 30px; height: 30px; display: grid; place-items: center; border-radius: 50%; cursor: pointer;
  background: transparent; border: 1px solid transparent; color: var(--txt3);
  transition: color .15s ease, background .15s ease, transform .15s ease;
}
.lumen-fb-b:hover { color: var(--txt); background: var(--hover); }
.lumen-fb-b:focus-visible { outline: 2px solid var(--a-over); outline-offset: 1px; }
.lumen-fb-b.on.up { color: var(--pos); background: color-mix(in srgb, var(--pos) 14%, transparent); border-color: color-mix(in srgb, var(--pos) 45%, transparent); }
.lumen-fb-b.on.down { color: var(--neg); background: color-mix(in srgb, var(--neg) 12%, transparent); border-color: color-mix(in srgb, var(--neg) 45%, transparent); }
.lumen-fb-b.on { animation: lumen-pop-in .3s cubic-bezier(.3,1.6,.5,1); }
@keyframes lumen-pop-in { 0% { transform: scale(.8); } 60% { transform: scale(1.15); } 100% { transform: scale(1); } }
.lumen-fb-box { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; animation: lumen-rise .3s ease both; }
.lumen-fb-box textarea {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 54px; padding: 8px 10px; border-radius: 10px;
  background: var(--pane); color: var(--txt); border: 1px solid var(--rule2); font: 400 13px/1.45 Inter, system-ui, sans-serif;
}
.lumen-fb-box textarea:focus { outline: none; border-color: var(--a-over); box-shadow: 0 0 0 3px color-mix(in srgb, var(--a-over) 18%, transparent); }
.lumen-fb-row { display: flex; justify-content: flex-end; gap: 6px; }
.lumen-fb-skip, .lumen-fb-send {
  height: 30px; padding: 0 14px; border-radius: 15px; cursor: pointer; font: 600 12.5px/1 Inter, system-ui, sans-serif;
  background: transparent; color: var(--txt2); border: 1px solid var(--rule2);
}
.lumen-fb-send { background: var(--sel); color: var(--a-over); border-color: var(--a-over); }
.lumen-fb-skip:hover { color: var(--txt); }

/* ---- Follow-ups: what to ask next, one tap away ---- */
.lumen-follow { display: flex; flex-direction: column; gap: 7px; margin-top: -4px; }
.lumen-follow-t {
  display: flex; align-items: center; gap: 5px; color: var(--a-over);
  text-transform: uppercase; letter-spacing: .8px; font-size: 10.5px; font-weight: 800;
}
.lumen-follow-t span { color: var(--txt3); }
.lumen-follow-list { display: flex; flex-direction: column; gap: 6px; }
.lumen-fu {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; text-align: left; cursor: pointer;
  padding: 9px 12px; border-radius: 12px; font: 500 13px/1.35 Inter, system-ui, sans-serif;
  background: var(--pane2); color: var(--txt); border: 1px solid var(--rule);
  transition: transform .18s ease, border-color .18s ease, background .18s ease;
  animation: lumen-rise .4s ease both; animation-delay: calc(var(--i, 0) * 70ms + 120ms);
}
.lumen-fu svg { flex: none; color: var(--txt3); transition: transform .18s ease, color .18s ease; }
.lumen-fu:hover { border-color: var(--a-over); transform: translateY(-2px); background: color-mix(in srgb, var(--a-over) 7%, var(--pane2)); }
.lumen-fu:hover svg { color: var(--a-over); transform: translateX(3px); }
.lumen-fu:focus-visible { outline: 2px solid var(--a-over); outline-offset: 2px; }

/* In talk mode: pills under the caption, appearing once the answer has been read out. */
.lumen-stage-follow { width: 100%; max-width: 820px; opacity: 0; transform: translateY(10px); pointer-events: none;
  transition: opacity .4s ease, transform .5s cubic-bezier(.2,.8,.2,1); }
.lumen-stage-follow.shown { opacity: 1; transform: none; pointer-events: auto; }
.lumen-stage-follow:empty { display: none; }
.lumen-follow.on-stage { align-items: center; margin: 10px 0 0; }
.lumen-follow.on-stage .lumen-follow-list { flex-direction: row; flex-wrap: wrap; justify-content: center; }
.lumen-follow.on-stage .lumen-fu {
  border-radius: 20px; padding: 9px 14px; font-size: 14px; max-width: 360px;
  background: color-mix(in srgb, var(--pane) 72%, transparent); backdrop-filter: blur(8px); border-color: var(--rule2);
}

/* ---- Talk mode with a chart: caption on the left, the chart beside it ---- */
.lumen-stage-center { flex-direction: row; align-items: center; justify-content: center; gap: clamp(20px, 4vw, 56px); }
.lumen-stage-col {
  flex: 0 1 820px; min-width: 0; max-height: 100%; display: flex; flex-direction: column; align-items: center; gap: 6px;
}
.lumen-stage-viz { display: none; }
.lumen-stage.has-viz .lumen-stage-viz {
  display: block; flex: 0 1 460px; min-width: 300px; max-height: 100%; overflow-y: auto; scrollbar-width: none;
  opacity: 0; transform: translateX(28px) scale(.98); text-align: left;
  transition: opacity .5s ease, transform .6s cubic-bezier(.2,.8,.2,1);
}
.lumen-stage-viz::-webkit-scrollbar { display: none; }
/* Several charts can run past the screen: they fade out at the foot rather than being cut off. */
@media (min-width: 961px) {
  .lumen-stage.has-viz .lumen-stage-viz {
    padding: 8px 4px 40px;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 8px, #000 calc(100% - 40px), transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 8px, #000 calc(100% - 40px), transparent 100%);
  }
}
.lumen-stage.has-viz .lumen-stage-viz.shown { opacity: 1; transform: none; }
.lumen-stage.has-viz .lumen-stage-col { flex-basis: 640px; }
/* The chart's own draw-in waits for its entrance, so it animates where it can be seen. */
.lumen-stage-viz:not(.shown) .lviz, .lumen-stage-viz:not(.shown) .lviz * { animation-play-state: paused !important; }

@media (max-width: 960px) {
  /* One column, with the chart straight after the answer it illustrates and the follow-ups last. */
  .lumen-stage-center {
    flex-direction: column; align-items: center; justify-content: flex-start; gap: 6px;
    overflow-y: auto; scrollbar-width: none; padding-top: 4px; padding-bottom: 8px;
  }
  .lumen-stage-center::-webkit-scrollbar { display: none; }
  .lumen-stage-col { display: contents; }
  .lumen-stage-state { order: 1; }
  .lumen-canvas-wrap { order: 2; }
  .lumen-heard { order: 3; }
  .lumen-stage-answer { order: 4; }
  .lumen-stage-viz { order: 5; margin-top: 8px; }
  .lumen-stage-follow { order: 6; }
  /* Inside a scrolling column nothing may shrink: the caption would be squeezed to two lines. */
  .lumen-stage-state, .lumen-canvas-wrap, .lumen-heard, .lumen-stage-answer, .lumen-stage-viz, .lumen-stage-follow { flex-shrink: 0; }
  .lumen-stage.has-viz .lumen-stage-viz {
    flex: none; width: 100%; max-width: 560px; min-width: 0; max-height: none; overflow: visible;
    transform: translateY(16px);
  }
  .lumen-stage.has-viz.answered .lumen-canvas-wrap { width: min(150px, 34vw, 18vh); }
  .lumen-stage.has-viz .lumen-stage-answer { max-height: 30vh; }
}

/* ---- History drawer ---- */
.lumen-hist {
  position: absolute; inset: 0; z-index: 3; display: flex; flex-direction: column;
  background: var(--pane); animation: lumen-hist-in .22s cubic-bezier(.2,.8,.2,1);
}
.lumen-hist[hidden] { display: none; }
@keyframes lumen-hist-in { from { opacity: 0; transform: translateX(-16px); } to { opacity: 1; transform: none; } }
.lumen-hist-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 14px 14px 10px 16px; }
.lumen-hist-top h3 { margin: 0 auto 0 0; font-size: 15px; font-weight: 800; letter-spacing: .2px; }
.lumen-hist-top .lumen-hist-back { margin-left: -6px; }
.lumen-hist-new {
  display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px 0 10px; border-radius: 16px; cursor: pointer;
  background: var(--sel); color: var(--a-over); border: 1px solid var(--a-over); font: 700 12.5px/1 Inter, system-ui, sans-serif;
}
.lumen-hist-new:hover { transform: translateY(-1px); }
.lumen-hist-search {
  display: flex; align-items: center; gap: 8px; margin: 0 14px 8px; padding: 0 10px; height: 38px;
  border: 1px solid var(--rule2); border-radius: 12px; background: var(--pane2); color: var(--txt3);
}
.lumen-hist-search:focus-within { border-color: var(--a-over); box-shadow: 0 0 0 3px color-mix(in srgb, var(--a-over) 18%, transparent); }
.lumen-hist-search input { flex: 1; min-width: 0; background: transparent; border: 0; outline: none; color: var(--txt); font: 400 13.5px/1 Inter, system-ui, sans-serif; }
.lumen-hist-list { flex: 1; overflow-y: auto; padding: 2px 8px 12px; }
.lumen-hist-g {
  padding: 12px 8px 5px; text-transform: uppercase; letter-spacing: .8px; font-size: 10px; font-weight: 800; color: var(--txt3);
}
.lumen-hist-item {
  position: relative; display: flex; align-items: center; gap: 2px; border-radius: 10px;
  transition: background .15s ease;
}
.lumen-hist-item:hover, .lumen-hist-item:focus-within { background: var(--hover); }
.lumen-hist-item.current { background: var(--sel); }
.lumen-hist-item.current::before {
  content: ""; position: absolute; left: 0; top: 8px; bottom: 8px; width: 3px; border-radius: 2px; background: var(--a-over);
}
.lumen-hist-open {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; text-align: left; cursor: pointer;
  padding: 9px 10px 9px 12px; background: transparent; border: 0; color: var(--txt); border-radius: 10px;
}
.lumen-hist-open:focus-visible { outline: 2px solid var(--a-over); outline-offset: -2px; }
.lumen-hist-title { font-size: 13.5px; font-weight: 600; line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lumen-hist-meta { font-size: 11.5px; color: var(--txt3); }
.lumen-hist-del {
  flex: none; width: 30px; height: 30px; margin-right: 6px; display: grid; place-items: center; border-radius: 8px; cursor: pointer;
  background: transparent; border: 0; color: var(--txt3); opacity: 0; transition: opacity .15s ease, color .15s ease;
}
.lumen-hist-item:hover .lumen-hist-del, .lumen-hist-item:focus-within .lumen-hist-del, .lumen-hist-del:focus-visible { opacity: 1; }
.lumen-hist-del:hover { color: var(--neg); background: color-mix(in srgb, var(--neg) 10%, transparent); }
@media (hover: none) { .lumen-hist-del { opacity: .7; } }
.lumen-hist-item.confirm {
  gap: 6px; padding: 8px 8px 8px 12px; background: color-mix(in srgb, var(--neg) 9%, var(--pane2));
  border: 1px solid color-mix(in srgb, var(--neg) 35%, transparent);
}
.lumen-hist-cq { flex: 1; font-size: 13px; font-weight: 600; color: var(--txt); }
.lumen-hist-yes, .lumen-hist-no {
  height: 28px; padding: 0 11px; border-radius: 14px; cursor: pointer; font: 700 12px/1 Inter, system-ui, sans-serif;
  background: transparent; color: var(--txt2); border: 1px solid var(--rule2);
}
.lumen-hist-yes { background: var(--neg); color: #fff; border-color: var(--neg); }
.lumen-hist-empty { padding: 28px 16px; text-align: center; font-size: 13px; color: var(--txt3); line-height: 1.5; }
.lumen-hist-log {
  display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--rule);
  font-size: 12.5px; font-weight: 600; color: var(--a-over); text-decoration: none; background: var(--pane2);
}
.lumen-hist-log:hover { background: var(--hover); }

/* ============================================================================
   Charts (lumenviz.js)
   ============================================================================ */
.lviz-set { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.lviz {
  position: relative; margin: 0; padding: 12px 13px 11px; border-radius: 12px;
  background: var(--pane); border: 1px solid var(--rule);
  animation: lumen-rise .5s cubic-bezier(.2,.8,.2,1) both; animation-delay: var(--d, 0ms);
}
.lviz::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 1px; border-radius: 12px 12px 0 0; pointer-events: none;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--txt) 14%, transparent) 18%,
    color-mix(in srgb, var(--txt) 14%, transparent) 82%, transparent);
}
.lviz-h { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 2px 10px; margin-bottom: 9px; }
.lviz-t { font-size: 10.5px; font-weight: 800; letter-spacing: .7px; text-transform: uppercase; color: var(--txt2); }
.lviz-s { font-size: 11px; color: var(--txt3); }
.lviz-note { margin-top: 8px; font-size: 11px; color: var(--txt3); line-height: 1.4; }
.lviz-more { margin-top: 6px; font-size: 11px; color: var(--txt3); }

/* Change: an arrow and a sign, as well as a color. */
.lviz-pill {
  display: inline-flex; align-items: center; gap: 3px; padding: 2px 8px; border-radius: 20px; white-space: nowrap;
  font: 700 11.5px/1.3 Inter, system-ui, sans-serif; font-variant-numeric: tabular-nums;
  color: var(--txt2); background: color-mix(in srgb, var(--txt3) 14%, transparent);
}
.lviz-pill i { font-style: normal; font-size: 8.5px; }
.lviz-pill.up { color: var(--pos); background: color-mix(in srgb, var(--pos) 14%, transparent); }
.lviz-pill.down { color: var(--neg); background: color-mix(in srgb, var(--neg) 12%, transparent); }
.lviz-was { font-size: 11.5px; color: var(--txt3); }
.lviz-was b { color: var(--txt); }

/* Stat */
.lviz-stat { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.lviz-big {
  font-size: 32px; font-weight: 800; letter-spacing: -.8px; line-height: 1.05;
  background: linear-gradient(110deg, var(--a-over), var(--a-assign)); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.lviz-stat-r { display: inline-flex; align-items: center; gap: 8px; }
.lviz-stat-l { width: 100%; font-size: 12px; color: var(--txt2); }

/* Ranked bars: horizontal, every bar carrying its number. */
.lviz-bars { display: grid; gap: 6px; }
.lviz-bar { display: grid; grid-template-columns: minmax(0, 36%) minmax(0, 1fr) auto; align-items: center; gap: 9px; }
.lviz-bl { font-size: 12px; color: var(--txt); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lviz-bt { display: block; height: 10px; border-radius: 5px; overflow: hidden; background: color-mix(in srgb, var(--txt) 7%, transparent); }
.lviz-bf {
  display: block; height: 100%; width: var(--w); border-radius: 5px; transform-origin: left center;
  background: linear-gradient(90deg, var(--a-over), var(--a-assign));
  animation: lviz-grow .75s cubic-bezier(.2,.8,.2,1) both; animation-delay: calc(var(--i, 0) * 45ms + var(--d, 0ms) + 120ms);
}
.lviz-bf.neg { background: var(--neg); }
.lviz-bf.was { background: color-mix(in srgb, var(--txt3) 45%, transparent); }
.lviz-bv { font-size: 12px; font-weight: 700; color: var(--txt); text-align: right; white-space: nowrap; }
.lviz-share { margin-left: 4px; font-size: 10.5px; font-weight: 500; color: var(--txt3); }
.lviz-tot {
  display: flex; justify-content: space-between; margin-top: 9px; padding-top: 7px; border-top: 1px solid var(--rule);
  font-size: 11px; text-transform: uppercase; letter-spacing: .6px; font-weight: 700; color: var(--txt3);
}
.lviz-tot b { font-size: 12.5px; letter-spacing: 0; color: var(--txt); text-transform: none; }
@keyframes lviz-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* Trend line */
.lviz-lgd { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-bottom: 6px; font-size: 11px; color: var(--txt2); }
.lviz-lgd span { display: inline-flex; align-items: center; gap: 5px; }
.lviz-lgd i { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
.lviz-lgd i.was { background: color-mix(in srgb, var(--txt3) 45%, transparent); }
.lviz-plot { position: relative; }
.lviz-svg { width: 100%; height: auto; display: block; overflow: visible; }
.lviz-grid { stroke: var(--rule); stroke-width: 1; stroke-dasharray: 2 3; }
.lviz-ax { fill: var(--txt3); font: 500 9px Inter, system-ui, sans-serif; font-variant-numeric: tabular-nums; }
.lviz-line {
  fill: none; stroke: var(--c); stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 1; stroke-dashoffset: 0; animation: lviz-draw 1.1s cubic-bezier(.4,0,.2,1) both; animation-delay: calc(var(--d, 0ms) + 150ms);
}
.lviz-area { animation: lviz-fade .9s ease both; animation-delay: calc(var(--d, 0ms) + 500ms); }
.lviz-dot { fill: var(--c); stroke: var(--pane); stroke-width: 2; animation: lviz-fade .4s ease both; animation-delay: calc(var(--d, 0ms) + 1.1s); }
.lviz-last { fill: var(--txt); font: 800 10px Inter, system-ui, sans-serif; font-variant-numeric: tabular-nums;
  paint-order: stroke; stroke: var(--pane); stroke-width: 3px; animation: lviz-fade .4s ease both; animation-delay: calc(var(--d, 0ms) + 1.1s); }
.lviz-guide { stroke: var(--txt3); stroke-width: 1; stroke-dasharray: 3 3; }
.lviz-tip {
  position: absolute; top: 2px; z-index: 2; pointer-events: none; white-space: nowrap;
  padding: 6px 9px; border-radius: 8px; background: var(--pane2); border: 1px solid var(--rule2);
  box-shadow: 0 6px 18px color-mix(in srgb, var(--shadow) 60%, transparent); font-size: 11.5px;
}
.lviz-tip[hidden] { display: none; }
.lviz-tip-t { font-weight: 700; color: var(--txt2); margin-bottom: 3px; }
.lviz-tip-r { display: flex; align-items: center; gap: 6px; color: var(--txt2); }
.lviz-tip-r i { width: 8px; height: 8px; border-radius: 2px; }
.lviz-tip-r b { margin-left: auto; padding-left: 8px; color: var(--txt); }
@keyframes lviz-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes lviz-fade { from { opacity: 0; } to { opacity: 1; } }

/* Donut */
.lviz-donut { display: grid; grid-template-columns: 108px minmax(0, 1fr); align-items: center; gap: 16px; }
.lviz-ring { position: relative; width: 108px; height: 108px; }
.lviz-ring svg { width: 100%; height: 100%; display: block; }
.lviz-track { fill: none; stroke: color-mix(in srgb, var(--txt) 7%, transparent); stroke-width: 5.5; }
.lviz-seg {
  fill: none; stroke: var(--c); stroke-width: 5.5; stroke-dasharray: var(--len) 100; stroke-dashoffset: var(--off);
  animation: lviz-seg .9s cubic-bezier(.2,.8,.2,1) both; animation-delay: calc(var(--i, 0) * 80ms + var(--d, 0ms) + 120ms);
}
@keyframes lviz-seg { from { stroke-dasharray: 0 100; } }
.lviz-ring-c { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; }
.lviz-ring-c b { font-size: 15px; font-weight: 800; color: var(--txt); }
.lviz-ring-c span { font-size: 9px; font-weight: 800; letter-spacing: .7px; text-transform: uppercase; color: var(--txt3); }
.lviz-legend { list-style: none; margin: 0; padding: 0; display: grid; gap: 5px; }
.lviz-legend li { display: grid; grid-template-columns: 9px minmax(0, 1fr) auto 34px; align-items: center; gap: 7px; font-size: 12px; }
.lviz-legend i { width: 9px; height: 9px; border-radius: 2px; }
.lviz-ln { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--txt); }
.lviz-legend b { font-weight: 700; }
.lviz-lp { text-align: right; font-size: 11px; color: var(--txt3); }

/* Comparison: earlier and later, side by side per row. */
.lviz-cmp-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.lviz-cmp { display: grid; gap: 10px; }
.lviz-cl { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; font-weight: 600; color: var(--txt); }
.lviz-cl > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lviz-cb { display: grid; grid-template-columns: minmax(0, 1fr) 64px; align-items: center; gap: 8px; margin-top: 3px; }
.lviz-cb .lviz-bt { height: 7px; }
.lviz-cv { font-size: 11.5px; color: var(--txt2); text-align: right; white-space: nowrap; }
.lviz-cv b { color: var(--txt); }
.lviz-cr { animation: lumen-rise .4s ease both; animation-delay: calc(var(--i, 0) * 40ms + var(--d, 0ms)); }

/* Table */
.lviz-tw { overflow-x: auto; }
.lviz-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.lviz-table th {
  text-align: left; padding: 4px 6px 6px; border-bottom: 1px solid var(--rule2); white-space: nowrap;
  font-size: 9.5px; font-weight: 800; letter-spacing: .6px; text-transform: uppercase; color: var(--txt3);
}
.lviz-table td { padding: 5px 6px; border-bottom: 1px solid var(--rule); color: var(--txt); }
.lviz-table tr:last-child td { border-bottom: 0; }
.lviz-table td:first-child { max-width: 170px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--txt2); }
.lviz-table .num { text-align: right; }

/* Bigger, glassier, in talk mode. */
.lviz-set.big { margin-top: 0; gap: 14px; }
.lviz-set.big .lviz {
  padding: 16px 18px 14px; border-radius: 16px; border-color: var(--rule2);
  background: color-mix(in srgb, var(--pane) 78%, transparent); backdrop-filter: blur(12px);
  box-shadow: 0 2px 6px color-mix(in srgb, var(--shadow) 40%, transparent), 0 18px 48px -18px color-mix(in srgb, var(--shadow) 80%, transparent);
}
.lviz-set.big .lviz-t { font-size: 11.5px; }
.lviz-set.big .lviz-big { font-size: 46px; letter-spacing: -1.5px; }
.lviz-set.big .lviz-bl, .lviz-set.big .lviz-bv, .lviz-set.big .lviz-legend li, .lviz-set.big .lviz-cl { font-size: 13.5px; }
.lviz-set.big .lviz-bt { height: 12px; }
.lviz-set.big .lviz-bar { gap: 11px; }
.lviz-set.big .lviz-ax { font-size: 9.5px; }

@media (prefers-reduced-motion: reduce) {
  .lviz, .lviz *, .lumen-fu, .lumen-fb-box, .lumen-hist, .lumen-fb-b.on { animation: none !important; }
  .lumen-stage-follow, .lumen-stage.has-viz .lumen-stage-viz { transition: opacity .15s ease; transform: none; }
  .lumen-fu:hover, .lumen-hist-new:hover { transform: none; }
}

@media (max-width: 640px) {
  .lumen-tools { gap: 0; }
  .lumen-ic { width: 32px; }
  .lumen-lang { padding: 0 6px; }
  .lumen-took { display: none; }
  .lviz-bar { grid-template-columns: minmax(0, 40%) minmax(0, 1fr) auto; }
  .lviz-donut { grid-template-columns: 92px minmax(0, 1fr); gap: 12px; }
  .lviz-ring { width: 92px; height: 92px; }
  .lumen-stage-tools .lumen-lang { height: 40px; padding: 0 10px; }
  .lumen-follow.on-stage .lumen-fu { font-size: 13px; }
  .lviz-set.big .lviz-big { font-size: 36px; }
}

/* ============================================================================
   Lumen v3 — Listen, Auto-read, and the talk-mode layout
   A speaker icon read as "press to hear", so it is gone: hearing one answer is
   a Listen pill on that answer, and reading every answer aloud is a labeled
   switch in the header. Talk mode is a grid whose regions cannot overlap: the
   orb and what was heard on the left, the answer filling the middle, the chart
   on the right, and the status line in the footer beside the microphone.
   ============================================================================ */

/* ---- Listen: play triangle and the word; Stop with a moving level while it plays ---- */
.lumen-listen {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  height: 30px; padding: 0 12px 0 10px; border-radius: 15px; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 700; letter-spacing: .2px; white-space: nowrap;
  color: var(--a-over); background: color-mix(in srgb, var(--a-over) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--a-over) 45%, var(--rule2));
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.lumen-listen:hover { background: color-mix(in srgb, var(--a-over) 16%, transparent); border-color: var(--a-over); }
.lumen-listen:focus-visible { outline: 2px solid var(--a-over); outline-offset: 2px; }
.lumen-listen svg { flex: none; }
.lumen-listen.playing {
  color: #fff; border-color: transparent;
  background: linear-gradient(135deg, var(--a-over), var(--a-assign));
}
.lumen-listen[hidden] { display: none; }
.lumen-card-acts .lumen-listen { margin-left: 4px; }

.lumen-eq { display: inline-flex; align-items: flex-end; gap: 2px; height: 12px; }
.lumen-eq i { width: 2.5px; height: 100%; border-radius: 1px; background: currentColor; transform-origin: bottom; animation: lumen-eq 0.9s ease-in-out infinite; }
.lumen-eq i:nth-child(2) { animation-delay: -.3s; }
.lumen-eq i:nth-child(3) { animation-delay: -.6s; }
.lumen-eq i:nth-child(4) { animation-delay: -.15s; }
@keyframes lumen-eq { 0%, 100% { transform: scaleY(.3); } 50% { transform: scaleY(1); } }

/* On the stage: larger, glass, beside the microphone. */
.lumen-listen.on-stage {
  height: 44px; padding: 0 18px 0 15px; border-radius: 22px; font-size: 14px;
  background: color-mix(in srgb, var(--pane) 72%, transparent); backdrop-filter: blur(8px);
  border-color: var(--rule2); color: var(--txt);
}
.lumen-listen.on-stage svg { color: var(--a-over); }
.lumen-listen.on-stage:hover { border-color: var(--a-over); }

/* ---- Auto-read: a switch with its name, never an icon ---- */
.lumen-auto {
  display: inline-flex; align-items: center; gap: 7px; height: 34px; padding: 0 10px 0 7px; flex: none;
  border-radius: 17px; border: 1px solid var(--rule2); background: transparent; color: var(--txt2);
  font: inherit; font-size: 12px; font-weight: 600; white-space: nowrap; cursor: pointer;
}
.lumen-auto:hover { color: var(--txt); background: var(--hover); }
.lumen-auto:focus-visible { outline: 2px solid var(--a-over); outline-offset: 2px; }
.lumen-auto-knob {
  position: relative; width: 26px; height: 16px; border-radius: 8px; flex: none;
  background: var(--rule2); transition: background .18s ease;
}
.lumen-auto-knob::after {
  content: ""; position: absolute; left: 2px; top: 2px; width: 12px; height: 12px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.3); transition: transform .18s ease;
}
.lumen-auto.on { color: var(--txt); border-color: color-mix(in srgb, var(--a-over) 55%, var(--rule2)); }
.lumen-auto.on .lumen-auto-knob { background: var(--a-over); }
.lumen-auto.on .lumen-auto-knob::after { transform: translateX(10px); }

/* ---- Talk mode footer: status | microphone | hear it again, each in its own cell ---- */
.lumen-stage-bottom {
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center;
  gap: 12px clamp(14px, 3vw, 36px);
  /* The microphone's pulse ring grows to 1.7x its 90px box, about 40px past the button on every side, so
     the footer keeps that much clear below it (above the home indicator) and above it. */
  padding: 34px max(22px, env(safe-area-inset-right)) calc(44px + env(safe-area-inset-bottom, 0px)) max(22px, env(safe-area-inset-left));
}
.lumen-stage-hint { justify-self: end; text-align: right; font-size: 14px; line-height: 1.4; color: var(--txt2); max-width: 420px; }
.lumen-stage-mic { justify-self: center; }
.lumen-stage-side { justify-self: start; min-width: 0; }

/* Everything above the footer stays above it: the middle never spills into the status line. */
.lumen-stage-center { overflow: hidden; }

/* ---- Wide screens, once there is an answer: a grid that uses the whole screen ---- */
@media (min-width: 961px) {
  .lumen-stage.answered .lumen-stage-center {
    display: grid; width: 100%; max-width: 1760px; margin: 0 auto; box-sizing: border-box;
    grid-template-columns: minmax(200px, 300px) minmax(0, 1fr);
    grid-template-rows: auto auto minmax(0, 1fr) auto;
    grid-template-areas: "state answer" "orb answer" "heard answer" "heard follow";
    align-items: start; text-align: left;
    gap: 10px clamp(28px, 3.5vw, 64px);
    padding: 18px clamp(24px, 4vw, 72px) 6px;
  }
  .lumen-stage.answered.has-viz .lumen-stage-center {
    grid-template-columns: minmax(190px, 260px) minmax(0, 1fr) minmax(360px, 36%);
    grid-template-areas: "state answer viz" "orb answer viz" "heard answer viz" "heard follow viz";
  }
  .lumen-stage.answered .lumen-stage-col { display: contents; }
  .lumen-stage.answered .lumen-stage-state { grid-area: state; justify-self: center; }
  .lumen-stage.answered .lumen-canvas-wrap { grid-area: orb; justify-self: center; width: min(220px, 100%, 24vh); }
  .lumen-stage.answered .lumen-heard {
    grid-area: heard; text-align: center; font-size: 17px; max-width: none; align-self: start;
    display: -webkit-box; -webkit-line-clamp: 8; -webkit-box-orient: vertical; overflow: hidden;
  }
  .lumen-stage.answered .lumen-stage-answer {
    grid-area: answer; align-self: stretch; max-width: none; max-height: none; min-height: 0; height: 100%;
    padding-right: 8px;
  }
  .lumen-stage.answered .lumen-stage-figure { margin-top: 0; }
  .lumen-stage.answered .lumen-said { font-size: clamp(20px, 1.65vw, 30px); line-height: 1.55; max-width: 68ch; }
  .lumen-stage.answered .lumen-stage-src { justify-content: flex-start; }
  .lumen-stage.answered .lumen-stage-src .lumen-sources { justify-content: flex-start; }
  .lumen-stage.answered .lumen-stage-follow { grid-area: follow; max-width: none; }
  .lumen-stage.answered .lumen-follow.on-stage { align-items: flex-start; margin-top: 4px; }
  .lumen-stage.answered .lumen-follow.on-stage .lumen-follow-list { justify-content: flex-start; }
  .lumen-stage.answered.has-viz .lumen-stage-viz {
    grid-area: viz; align-self: stretch; max-height: none; height: 100%; min-height: 0; min-width: 0;
  }
}

/* ---- Tablets and phones: one scrolling column, the footer stacked ---- */
@media (max-width: 960px) {
  .lumen-stage-center { overflow-y: auto; }
  .lumen-stage-answer { text-align: left; }
  .lumen-stage-figure { text-align: center; }
}
@media (max-width: 640px) {
  .lumen-stage { height: var(--lumen-vh, 100dvh); bottom: auto; }
  .lumen-stage-top { padding-left: max(16px, env(safe-area-inset-left)); padding-right: max(16px, env(safe-area-inset-right)); }
  .lumen-stage-center { padding-left: 16px; padding-right: 16px; }
  .lumen-stage-bottom {
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    grid-template-areas: "hint hint hint" ". mic side";
    /* The 64px microphone's ring reaches about 36px past it. */
    gap: 6px 12px; padding: 8px 16px calc(40px + env(safe-area-inset-bottom, 0px));
  }
  .lumen-stage-hint { grid-area: hint; justify-self: center; text-align: center; font-size: 13px; max-width: none; }
  .lumen-stage-mic { grid-area: mic; width: 64px; height: 64px; }
  .lumen-stage-side { grid-area: side; }
  .lumen-listen.on-stage { padding: 0 14px 0 12px; font-size: 13px; }
  .lumen-said { font-size: 18px; }
  .lumen-panel { height: var(--lumen-vh, 100dvh); bottom: auto; }
  .lumen-auto { height: 32px; padding: 0 8px 0 6px; font-size: 11px; gap: 5px; }
  .lumen-sub { display: none; }
}
@media (max-width: 380px) {
  .lumen-tools .lumen-ic { width: 30px; }
  .lumen-lang .lumen-lang-code { display: none; }
}
/* Fingers, not cursors: every control at least 44px tall. */
@media (pointer: coarse) {
  .lumen-listen { min-height: 40px; padding-left: 12px; padding-right: 14px; }
  .lumen-listen.on-stage { min-height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  .lumen-eq i { animation: none; transform: scaleY(.7); }
  .lumen-auto-knob, .lumen-auto-knob::after { transition: none; }
}

/* ============================================================================
   Lumen Log (lumenlog.js)
   ============================================================================ */
.ll-filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 14px; margin-bottom: 14px; }
.ll-filters .seg button { padding: 6px 11px; }
.ll-q { flex: 1; min-width: 220px; }
.ll-q input { width: 100%; }
.ll-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 14px; }
.ll-kpis .kpi .v { font-size: 22px; }
.ll-pnl { padding: 0; overflow: hidden; }
.ll-pnl .tablewrap { border: 0; border-radius: 0; }
.ll-pnl .pager { margin: 0; padding: 10px 14px; border-top: 1px solid var(--rule); }
.ll-table tbody tr.ll-r { cursor: pointer; }
.ll-table tbody tr.ll-r:focus-visible { outline: 2px solid var(--a-over); outline-offset: -2px; }
.ll-table tbody tr.ll-r.open td { background: var(--sel); border-bottom-color: transparent; }
.ll-table td { vertical-align: top; }
.ll-who b { display: block; font-size: 12.5px; color: var(--txt); white-space: nowrap; }
.ll-who span { display: block; font-size: 11px; color: var(--txt3); }
.ll-ch { display: inline-grid; place-items: center; color: var(--txt2); margin-top: 1px; }
.ll-qcell { min-width: 240px; }
.ll-qtext {
  max-width: 560px; color: var(--txt); line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ll-fu { display: inline-block; margin-top: 2px; font-size: 10.5px; font-weight: 600; color: var(--a-over); }
.ll-speed { font-weight: 700; }
.ll-speed.fast { color: var(--pos); }
.ll-speed.mid { color: color-mix(in srgb, var(--warn) 70%, var(--txt)); }
.ll-speed.slow { color: var(--neg); }
.ll-rate { position: relative; font-size: 14px; }
.ll-dot { position: absolute; right: -6px; top: -1px; width: 6px; height: 6px; border-radius: 50%; background: var(--a-over); }
.ll-table tr.ll-detail td { padding: 0; background: var(--pane2); }
.ll-table tr.ll-detail:hover td { background: var(--pane2); }
.ll-d { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: 22px; padding: 16px 18px 18px; cursor: auto; }
.ll-lbl {
  margin: 14px 0 5px; font-size: 9.5px; font-weight: 800; letter-spacing: .7px; text-transform: uppercase; color: var(--txt3);
}
.ll-d-main > .ll-lbl:first-child { margin-top: 0; }
.ll-qfull { font-size: 14px; font-weight: 600; color: var(--txt); line-height: 1.45; }
.ll-answer { font-size: 13.5px; line-height: 1.6; color: var(--txt); max-width: 80ch; }
.ll-answer p { margin: 0 0 6px; }
.ll-answer ul { margin: 4px 0 8px; padding-left: 18px; }
.ll-comment { margin: 0; padding: 7px 12px; border-left: 3px solid var(--neg); background: color-mix(in srgb, var(--neg) 7%, transparent);
  border-radius: 0 8px 8px 0; font-style: italic; color: var(--txt); }
.ll-thread { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.ll-turn { padding: 10px 12px; border: 1px solid var(--rule); border-radius: 8px; background: var(--pane); }
.ll-turn.self { border-color: var(--a-over); box-shadow: inset 3px 0 0 var(--a-over); }
.ll-turn-h { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: 11.5px; margin-bottom: 4px; }
.ll-turn-h .grow { flex: 1; }
.ll-turn-n { display: inline-grid; place-items: center; width: 18px; height: 18px; border-radius: 50%;
  background: var(--pane2); font-size: 10.5px; font-weight: 700; color: var(--txt2); }
.ll-turn-q { font-size: 13.5px; font-weight: 600; color: var(--txt); margin-bottom: 4px; }
.ll-turn .ll-comment { margin-top: 6px; }
.ll-srcs { display: flex; flex-wrap: wrap; gap: 5px; font-size: 11.5px; }
.ll-tools { display: grid; gap: 8px; }
.ll-tool { padding: 8px 10px; border: 1px solid var(--rule); border-radius: 8px; background: var(--pane); }
.ll-tool.bad { border-color: color-mix(in srgb, var(--neg) 45%, var(--rule)); }
.ll-tool-h { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.ll-tool-h code { font: 600 12px "JetBrains Mono", monospace; color: var(--txt); }
.ll-err { color: var(--neg); font-size: 12px; }
.ll-tool pre {
  margin: 7px 0 0; padding: 8px 10px; max-height: 240px; overflow: auto; white-space: pre-wrap; word-break: break-word;
  font: 11.5px/1.45 "JetBrains Mono", monospace; color: var(--txt2); background: var(--bg2); border-radius: 6px;
}
.ll-meta { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 7px 12px; margin: 0; align-content: start; font-size: 12px; }
.ll-meta dt { font-size: 9.5px; font-weight: 800; letter-spacing: .6px; text-transform: uppercase; color: var(--txt3); padding-top: 2px; }
.ll-meta dd { margin: 0; color: var(--txt); overflow-wrap: anywhere; }
@media (max-width: 760px) {
  .ll-kpis { grid-template-columns: 1fr 1fr; gap: 8px; }
  .ll-kpis .kpi { padding: 10px 12px; }
  .ll-kpis .kpi .v { font-size: 18px; }
  .ll-d { grid-template-columns: 1fr; }
  .ll-who span { display: none; }
  .ll-qcell { min-width: 180px; }
}

/* ---- The records behind a chart (Parik, 15 Oct 2026) ---- */
.lviz-drill {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 10px; padding: 5px 11px; border-radius: 14px; cursor: pointer;
  background: transparent; color: var(--a-over); border: 1px solid color-mix(in srgb, var(--a-over) 45%, transparent);
  font: 600 12px/1.2 Inter, system-ui, sans-serif;
}
.lviz-drill:hover { background: color-mix(in srgb, var(--a-over) 12%, transparent); }
.lviz-drill span { font-size: 14px; }
.lviz-pick { cursor: pointer; border-radius: 6px; }
.lviz-pick:hover, .lviz-pick:focus-visible { background: color-mix(in srgb, var(--a-over) 10%, transparent); outline: none; }
.lviz-rec-back {
  position: fixed; inset: 0; z-index: 1200; display: grid; place-items: center; padding: 16px;
  background: rgba(4, 6, 18, .55); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
}
.lviz-rec {
  width: min(1100px, 100%); max-height: min(80vh, 760px); display: flex; flex-direction: column;
  background: var(--bg); color: var(--txt); border: 1px solid var(--rule2); border-radius: 16px;
  box-shadow: 0 30px 90px rgba(0, 0, 0, .45); overflow: hidden;
}
.lviz-rec-h { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 14px 16px 10px 18px; border-bottom: 1px solid var(--rule); }
.lviz-rec-t { font-size: 15px; font-weight: 800; }
.lviz-rec-s { font-size: 12px; color: var(--txt3); margin-top: 2px; }
.lviz-rec-x { background: none; border: 0; color: var(--txt2); font-size: 16px; cursor: pointer; padding: 4px 6px; border-radius: 8px; }
.lviz-rec-x:hover { background: var(--hover); }
.lviz-rec-b { overflow: auto; }
.lviz-rec-msg { padding: 28px 18px; color: var(--txt2); font-size: 13px; }
.lviz-rec-msg.bad { color: var(--neg); }
.lviz-rec-tbl { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.lviz-rec-tbl th {
  position: sticky; top: 0; background: var(--pane); text-align: left; padding: 8px 12px; white-space: nowrap;
  font-size: 10.5px; font-weight: 800; letter-spacing: .4px; text-transform: uppercase; color: var(--txt3); border-bottom: 1px solid var(--rule);
}
.lviz-rec-tbl td { padding: 7px 12px; border-bottom: 1px solid var(--rule); vertical-align: top; max-width: 320px; overflow-wrap: anywhere; }
.lviz-rec-tbl tr:hover td { background: var(--hover); }
.lviz-rec-src { font-size: 11px; color: var(--txt3); margin-top: 2px; }

/* ---- Stop speaking: shown only while Lumen is talking, above the box where the next question goes ---- */
.lumen-compose { position: relative; }
.lumen-stop {
  position: absolute; left: 50%; bottom: calc(100% + 10px); transform: translateX(-50%); z-index: 2;
  display: inline-flex; align-items: center; gap: 7px; height: 34px; padding: 0 16px 0 13px; border-radius: 17px;
  font: inherit; font-size: 13px; font-weight: 700; white-space: nowrap; cursor: pointer;
  color: #fff; border: 0; background: linear-gradient(135deg, var(--a-over), var(--a-assign));
  box-shadow: 0 6px 18px color-mix(in srgb, var(--a-over) 35%, transparent);
}
.lumen-stop:focus-visible { outline: 2px solid var(--a-over); outline-offset: 2px; }
.lumen-stop[hidden], .lumen-panel.hist-open .lumen-stop { display: none; }
