/* ── ClawTerminal Studio ───────────────────────────────────────────────
   An app, not a page: fixed chrome, one scrolling canvas, dense hairline
   panels. Builds on the tokens in /ascii.css so Studio and the landing
   read as one product. Extra tokens below are app-only. */
:root{
  --rail:264px; --top:52px;
  /* --ok, --warn, --ease and the eight series slots now come from
     /ascii.css, which is the design system's token layer. They used to be
     redeclared here, and because this file loads AFTER ascii.css that
     redeclaration won — so correcting the palette upstream would have
     changed nothing in Studio, which is the only place the palette is
     actually used. The old set failed colour-vision validation: the olive
     #9aa66b read as grey (chroma 0.082) and collided with the pink #e07a9c
     under deuteranopia at ΔE 6.0. The replacements are validated as an
     ordered set — see the note in ascii.css. ct-render.js reads them by
     name from getComputedStyle, so the names are unchanged. */
}
/* A class rule with `display` beats the UA stylesheet's [hidden]{display:none},
   so `.scrim{display:flex}` left the palette scrim and the chart lightbox
   permanently on top of the page — the whole app rendered behind a dimmer
   with an empty modal over it. Restore the attribute's meaning globally. */
[hidden]{display:none !important}

/* ── OBJECT KINDS ─────────────────────────────────────────────────────
   Studio has four things you can open — a thread, a report, a dashboard,
   or a question to run — and until now the rail and the palette named them
   in two different vocabularies, both of them plain uppercase text at the
   same weight. Worse, the palette put a suggestion's TOPIC (macro, flow,
   health) in the same column as an object's TYPE, so "HEALTH" and "THREAD"
   read as the same class of label when one is a subject and the other is a
   thing that already exists.

   One glyph and one hue per kind, shared by both surfaces, so a row is
   identifiable before it is read.

   These are IDENTITY, not status — an amber dashboard is not a warning.
   They are aliased rather than used inline for exactly that reason, and
   these four are the only non-neutral tokens that clear AA as TEXT on both
   grounds: coral 5.80:1, info 7.53:1, green 11.30:1, amber 9.21:1. The
   --color-series-* set is validated at 3:1 for chart marks, which is the
   non-text bar, so it is not usable here. */
:root{
  --kind-ask:var(--accent);
  --kind-thread:var(--color-info);
  --kind-report:var(--color-accent-2);
  --kind-dash:var(--color-warn);
  /* News reuses the report hue rather than adding a fifth: both are "a body
     of text to read", and the note above is explicit that only four
     non-neutral tokens clear AA as text on both grounds here. */
  --kind-news:var(--color-accent-2);
  --kind-new:var(--dim);
}
/* Glyphs are mono text, not SVG: they sit on the same grid as everything
   else, inherit colour, scale with font-size, need no sprite, and speak the
   same language as the ├─ └─ connectors the system already draws.

   Silhouette, not texture: ▤ and ▦ were the first pick, and at 11px both
   collapse into the same small filled square — the internal lines and grid
   are not resolved at that size, so colour carried the whole distinction and
   the glyph earned nothing. An outline diamond, three lines and a crossed
   square differ in OUTLINE, which survives. Checked in the browser at the
   real size rather than judged from the source. */
.k-glyph{flex:none;display:inline-block;width:1.1em;text-align:center;font-size:11px;line-height:1}
.k-ask{color:var(--kind-ask)}
.k-thread{color:var(--kind-thread)}
.k-report{color:var(--kind-report)}
.k-dash{color:var(--kind-dash)}
.k-news{color:var(--kind-news)}
.k-new{color:var(--kind-new)}

html,body{height:100%}
body{overflow:hidden;background:var(--bg);background-image:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
input,textarea{font:inherit;color:inherit;background:none;border:0;outline:none}

/* ONE focus indicator per field.
   ascii.css gives everything a 2px coral :focus-visible ring at +2px offset,
   which is the right default and stays. But these fields already announce
   focus — the composer, the watch-name box and the compose bar light their
   own coral border via :focus-within, and the gate and demo inputs swap
   their border to accent — so the ring lands just inside that border and
   reads as a second, redundant frame around the text.
   Same convention the rest of the codebase already follows: ascii.css's
   .input, .filters and .keybar, and ideas.html's composer, all suppress the
   default ring where they draw their own.
   .rail-search is deliberately NOT here: its focus style is a shift to
   --line-2, too quiet to be the only indicator, so it keeps the ring. */
.composer-in textarea:focus-visible,
.wnew input:focus-visible,
.compose-bar input:focus-visible,
.gate input:focus-visible,
.demo-cta-f input:focus-visible{outline:none}
kbd{border:1px solid var(--line-2);border-radius:3px;padding:1px 5px;color:var(--dim);font-size:10px}
::-webkit-scrollbar{width:9px;height:9px}
::-webkit-scrollbar-thumb{background:#1d1719;border-radius:6px}
::-webkit-scrollbar-thumb:hover{background:#2a2124}
::-webkit-scrollbar-track{background:transparent}

/* ── chrome ─────────────────────────────────────────────────────── */
.app{display:grid;grid-template-columns:var(--rail) 1fr;grid-template-rows:var(--top) 1fr;height:100vh;height:100dvh}
.app.norail{grid-template-columns:0 1fr}
.app.norail .rail{display:none}
.topbar{
  grid-column:1/-1;display:flex;align-items:center;gap:16px;padding:0 16px;
  border-bottom:1px solid var(--line);background:var(--panel);z-index:20;
}
.rail-toggle{color:var(--dim);font-size:14px;padding:5px 8px;border-radius:4px;display:none}
.rail-toggle:hover{color:var(--ink);background:var(--panel-2)}
.tb-brand{display:flex;align-items:center;gap:9px;font-size:12px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;flex:none}
.tb-brand img{width:24px;height:24px;border-radius:4px;object-fit:cover;object-position:50% 28%;transform:scale(1.3);image-rendering:pixelated;border:1px solid rgba(238,87,65,.4)}
.tb-brand b{color:var(--accent)}
.tb-tabs{display:flex;gap:2px;flex:none}
.tb-tabs button{
  font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--dim);
  padding:7px 13px;border-radius:4px;transition:color .15s,background .15s
}
.tb-tabs button:hover{color:var(--ink)}
.tb-tabs button.on{color:var(--accent);background:rgba(238,87,65,.09)}
.palette-hint{
  flex:1;max-width:420px;margin:0 auto;display:flex;align-items:center;gap:10px;
  border:1px solid var(--line);border-radius:6px;background:var(--bg);
  padding:7px 12px;font-size:12px;color:var(--faint);transition:border-color .15s,color .15s
}
.palette-hint:hover{border-color:var(--line-2);color:var(--dim)}
.palette-hint kbd{margin-left:auto}
.tb-right{display:flex;align-items:center;gap:16px;font-size:11px;color:var(--dim);letter-spacing:.1em;flex:none}
.tb-dot{width:6px;height:6px;border-radius:50%;background:var(--ok);display:inline-block;margin-right:7px;vertical-align:middle}
.tb-dot.off{background:var(--accent-dim)}
.tb-right a:hover{color:var(--ink)}

/* ── rail ─────────────────────────────────────────────────────────
   A workspace, not a file list — see the block comment above renderRail in
   terminal.js for what each section is for. Two row densities: a three-line
   `rich` row for the handful of things you are actively watching, one line
   for the index below them. */
.rail{border-right:1px solid var(--line);background:var(--panel);overflow-y:auto;display:flex;flex-direction:column;position:relative}
.rail-search{padding:12px 14px 8px;position:sticky;top:0;background:var(--panel);z-index:2}
.rail-search input{
  width:100%;border:1px solid var(--line);border-radius:5px;padding:7px 10px;font-size:12px;background:var(--bg)
}
.rail-search input:focus{border-color:var(--line-2)}
#railBody{padding-bottom:26px}
.rail-h{display:flex;align-items:center;gap:8px;padding:14px 16px 7px;font-size:10px;letter-spacing:.22em;text-transform:uppercase;color:var(--faint)}
/* No opacity multiplier. Stacking .6 on a quiet text token re-creates the
   illegibility the token was just fixed to remove: --faint is 4.63:1, and
   at .6 over the surface it lands back at 2.30:1. */
.rail-h .ct{color:var(--faint)}
.rail-h button{margin-left:auto;color:var(--dim);font-size:15px;line-height:1;padding:2px 6px;border-radius:3px}
.rail-h button:hover{color:var(--accent);background:rgba(238,87,65,.1)}

/* Subject groups. A disclosure triangle and a neutral label — deliberately
   NOT a colour per subject: the OBJECT KINDS note above is explicit that
   only four non-neutral tokens clear AA as text on both grounds here, and
   they are spoken for. Ten more hues would either fail contrast or start
   competing with the kind glyph, which is the distinction that matters. */
.rail-g{
  display:flex;align-items:center;width:100%;text-align:left;
  font-size:11px;color:var(--dim);
  border-left:2px solid transparent;transition:color .12s,background .12s;user-select:none
}
.rail-g .gtoggle{
  flex:1;min-width:0;display:flex;align-items:center;gap:8px;cursor:pointer;
  padding:6px 8px 6px 13px;color:inherit;text-align:left
}
.rail-g:hover{color:var(--ink);background:var(--panel-2)}
/* The triangle was 9px in --faint and vanished: a disclosure control you
   cannot see is a group you do not know is collapsible. It is the only
   affordance on the row, so it gets the row's own colour. */
.rail-g .tw{flex:none;width:11px;font-size:10px;color:var(--dim);text-align:center;line-height:1}
.rail-g:hover .tw{color:var(--accent)}
.rail-g .gt{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.04em}
.rail-g .ct{flex:none;color:var(--faint);font-size:10px;margin-left:auto;padding-left:8px}

/* ── folders ────────────────────────────────────────────────────────
   A folder outranks the automatic bucket below it, so it reads a step
   louder: its name is the surface's own ink rather than the quiet --dim the
   keyword groups use, and it carries a colour dot they never get. */
.rail-g.fold{color:var(--soft)}
.rail-g.fold .gt{color:var(--ink);letter-spacing:.02em}
/* Slot 0 is "no colour" — a hollow ring, so a folder without one still has
   the same silhouette and the column of dots never breaks alignment. */
.rail-g .fdot{flex:none;width:7px;height:7px;border-radius:50%;box-shadow:inset 0 0 0 1.5px var(--line-2)}
.rail-g .fdot.c1{background:var(--color-series-1);box-shadow:none}
.rail-g .fdot.c2{background:var(--color-series-2);box-shadow:none}
.rail-g .fdot.c3{background:var(--color-series-3);box-shadow:none}
.rail-g .fdot.c4{background:var(--color-series-4);box-shadow:none}
.rail-g .fdot.c5{background:var(--color-series-5);box-shadow:none}
.rail-g .fdot.c6{background:var(--color-series-6);box-shadow:none}
.rail-g .fdot.c7{background:var(--color-series-7);box-shadow:none}
.rail-g .fdot.c8{background:var(--color-series-8);box-shadow:none}
.rail-g .rail-acts{flex:none;padding-right:6px}
.rail-g .act{color:var(--faint);font-size:12px;padding:4px 5px;line-height:1;border-radius:3px;transition:color .12s,background .12s}
.rail-g:hover .act{color:var(--dim)}
.rail-g .act:hover{color:var(--ink);background:var(--panel-2)}
.rail-g .act.danger:hover{color:var(--accent);background:rgba(238,87,65,.1)}
/* An automatic bucket sitting inside Unfiled — one step in, so it reads as
   living there rather than as a sibling of your own folders. */
.rail-g.sub .gtoggle{padding-left:27px}
.rail-row.sub .go{padding-left:28px}
.rail-g.unfiled .gt{color:var(--faint);letter-spacing:.16em;text-transform:uppercase;font-size:10px}

.fswatch{display:flex;gap:5px;padding:6px 14px 9px 34px;flex-wrap:wrap}
.fswatch .sw{
  width:14px;height:14px;border-radius:50%;flex:none;
  box-shadow:inset 0 0 0 1.5px var(--line-2);transition:transform .1s var(--ease)
}
.fswatch .sw:hover{transform:scale(1.22)}
/* The current choice keeps a ring at a distance, which survives on a dark
   swatch and on the hollow one alike. */
.fswatch .sw.on{box-shadow:0 0 0 2px var(--panel),0 0 0 3.5px var(--ink)}
.fswatch .sw.c1{background:var(--color-series-1)}
.fswatch .sw.c2{background:var(--color-series-2)}
.fswatch .sw.c3{background:var(--color-series-3)}
.fswatch .sw.c4{background:var(--color-series-4)}
.fswatch .sw.c5{background:var(--color-series-5)}
.fswatch .sw.c6{background:var(--color-series-6)}
.fswatch .sw.c7{background:var(--color-series-7)}
.fswatch .sw.c8{background:var(--color-series-8)}

.rail-add{
  display:block;width:calc(100% - 22px);margin:6px 11px 2px;padding:6px 8px;text-align:left;
  font-size:11px;color:var(--faint);border:1px dashed var(--line);border-radius:5px;
  transition:color .12s,border-color .12s,background .12s
}
.rail-add:hover{color:var(--accent);border-color:var(--accent-dim);background:rgba(238,87,65,.06)}

/* ── dragging ───────────────────────────────────────────────────────
   The row being dragged stays visible but recedes; the target it would land
   in is the only thing that lights up. Two signals, one question. */
.rail-row.dragging{opacity:.4}
.rail-g.drop{background:rgba(238,87,65,.12);border-left-color:var(--accent)}
.rail-g.drop .gt{color:var(--ink)}
/* Children must not swallow the dragover/dragleave pair — a drop target that
   flickers as the pointer crosses its own label is a target that looks
   broken. */
.rail-g.drop *{pointer-events:none}

.rail-row{display:flex;align-items:center;border-left:2px solid transparent;transition:background .12s}
.rail-row:hover{background:var(--panel-2)}
.rail-row.on{border-left-color:var(--accent);background:var(--panel-2)}
.rail-row .go{display:flex;align-items:center;gap:9px;
  flex:1;text-align:left;padding:8px 6px 8px 14px;font-size:12px;color:var(--soft);
  min-width:0
}
.rail-row .go-c{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.rail-row .go-t{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rail-row:hover .go,.rail-row.on .go{color:var(--ink)}
/* The live line. It is the last thing the thread SAID, so it is quieter than
   the title and never wraps — two lines of prose per row and the rail is a
   feed, not an index. */
.rail-row .go-d{
  min-width:0;font-size:11px;line-height:1.45;color:var(--dim);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis
}
.rail-row .go-d::before{content:'↳ ';color:var(--faint)}
.rail-row .go-m{font-size:10px;letter-spacing:.04em;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Three lines, so the row's controls sit at the top rather than floating
   next to the timestamp. */
.rail-row.rich .go{align-items:flex-start;padding-top:9px;padding-bottom:9px}
.rail-row.rich .k-glyph{margin-top:2px}
.rail-row.rich .rail-acts,.rail-row.rich .rail-st{align-self:flex-start;margin-top:8px}

/* Status, not identity — these two are the only places in the rail allowed
   to use the semantic tokens, and they are 6px dots, not text. */
.rail-st{flex:none;width:6px;height:6px;border-radius:50%;margin-right:2px}
.rail-st.run{background:var(--ok);animation:railPulse 1.6s var(--ease) infinite}
.rail-st.err{background:var(--accent)}
@keyframes railPulse{0%,100%{opacity:1}50%{opacity:.25}}
@media (prefers-reduced-motion:reduce){.rail-st.run{animation:none}}

.rail-acts{display:flex;align-items:center;flex:none}
/* Present at low emphasis rather than hidden until hover: an opacity:0
   control is still focusable and still tappable, so it was reachable by
   keyboard while invisible and unreachable by touch while unhoverable. */
.rail-row .act{color:var(--faint);font-size:13px;padding:4px 6px;line-height:1;transition:color .12s,background .12s;border-radius:3px}
.rail-row:hover .act,.rail-row.on .act{color:var(--dim)}
.rail-row .act:hover{color:var(--ink);background:var(--panel-2)}
.rail-row .act.danger:hover{color:var(--accent);background:rgba(238,87,65,.1)}
/* A set pin stays lit whether or not the row is hovered — it is the state
   the section above is built from, so it has to be visible at rest. */
.rail-row .act.on,.rail-row:hover .act.on{color:var(--kind-dash)}
.rail-empty{padding:4px 16px 12px;font-size:11px;color:var(--faint);line-height:1.7}
/* In-place rename. Sits exactly where the label was so the row does not
   jump when it turns into a field. */
.rail-row .go-edit{
  flex:1;min-width:0;margin:0 6px 0 12px;padding:5px 6px;font:inherit;font-size:12px;
  color:var(--ink);background:var(--bg);border:1px solid var(--accent);border-radius:4px;outline:none
}

/* The resize handle. Sits ON the border so the border is the affordance —
   a visible bar would be a permanent 6px stripe next to every row for a
   control used once. */
.rail-grip{
  position:absolute;top:0;right:-3px;bottom:0;width:7px;cursor:col-resize;z-index:3;
  background:transparent;transition:background .12s
}
.rail-grip:hover,.rail-grip:focus-visible,body.rail-drag .rail-grip{background:var(--accent);opacity:.55}
/* Whatever is under the pointer mid-drag must not fight for the cursor or
   start selecting text. */
body.rail-drag{cursor:col-resize;user-select:none}
body.rail-drag *{pointer-events:none}
body.rail-drag .rail-grip{pointer-events:auto}

/* ── the hover preview ──────────────────────────────────────────────
   The row you are pointing at, in full: the title that did not fit, the
   last thing it said, what it contains and how it is filed. Fixed, so it is
   never clipped by the rail's own scroll box. */
.rail-peek{
  position:fixed;z-index:60;width:320px;max-width:38vw;pointer-events:none;
  border:1px solid var(--line-2);border-radius:7px;background:var(--panel);
  box-shadow:0 18px 48px rgba(0,0,0,.55);padding:12px 13px
}
.rail-peek .pk-h{display:flex;align-items:center;gap:7px;margin-bottom:7px}
.rail-peek .pk-k{font-size:9.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--faint)}
.rail-peek .pk-pub{
  font-size:9px;letter-spacing:.14em;text-transform:uppercase;color:var(--dim);
  border:1px solid var(--line-2);border-radius:3px;padding:1px 5px
}
.rail-peek .pk-t{font-size:12.5px;line-height:1.45;color:var(--ink)}
.rail-peek .pk-d{margin-top:8px;font-size:11.5px;line-height:1.6;color:var(--soft);
  border-left:2px solid var(--line-2);padding-left:9px}
.rail-peek .pk-m{margin-top:9px;font-size:10px;letter-spacing:.06em;color:var(--faint)}
.rail-peek .pk-tags{display:flex;flex-wrap:wrap;gap:5px;margin-top:9px}
.rail-peek .chip{
  font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);
  border:1px solid var(--line);border-radius:3px;padding:2px 6px
}

/* ── canvas ─────────────────────────────────────────────────────── */
/* Flex column so a SHORT answer still leaves the composer docked at the
   bottom (#view takes the slack) while a long one lets it stick there. */
.canvas{position:relative;overflow-y:auto;overflow-x:hidden;scroll-behavior:smooth;
  display:flex;flex-direction:column}
#view{flex:1 0 auto}
/* The composer is sticky and therefore occupies its own flow space, so
   the stream no longer has to reserve room for it by hand. */
.stream{max-width:920px;margin:0 auto;padding:32px 30px 36px}

/* empty state */
.hero{max-width:900px;margin:0 auto;padding:7vh 30px 40px;text-align:center;transition:opacity .3s ease,transform .35s var(--ease)}
.hero.out,#below.out{opacity:0;transform:translateY(-10px);pointer-events:none}
.app.landing #view{flex:none}
/* The empty stream still carries its 150px bottom padding (reserved for
   the docked composer), which on the landing state is 180px of dead air
   between the headline and the composer. */
.app.landing .stream{padding:0}
.app.landing .hero{padding:0 30px 18px}
.app.landing .hero .mark{margin-bottom:14px}
.app.landing .hero h1{margin-bottom:12px}
.app.landing .hero p{font-size:12.5px;max-width:54ch}
#below{transition:opacity .3s ease,transform .35s var(--ease);max-width:900px;margin:0 auto;width:100%;padding:0 30px}
#below .starts,#below .surface{opacity:0;transform:translateY(8px);animation:ct-riseup .55s var(--ease) .3s forwards}
#below .surface{margin-top:16px}
@keyframes ct-riseup{to{opacity:1;transform:none}}
.hero .mark{font-size:10px;letter-spacing:.32em;text-transform:uppercase;color:var(--faint);margin-bottom:20px}
.hero h1{font-size:clamp(23px,3.5vw,36px);font-weight:500;line-height:1.28;letter-spacing:.005em;margin-bottom:14px}
.hero h1 em{color:var(--accent)}
.hero p{color:var(--dim);font-size:13px;max-width:58ch;margin:0 auto;line-height:1.75}
.starts{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--line);border:1px solid var(--line);border-radius:7px;overflow:hidden;text-align:left}
.start{background:var(--bg);padding:13px 15px;transition:background .15s}
.start:hover{background:var(--panel)}
.start .k{display:block;font-size:9.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--accent);margin-bottom:6px}
.start .t{font-size:12.5px;color:var(--soft);line-height:1.55}
.start:hover .t{color:var(--ink)}
.surface{display:flex;flex-wrap:wrap;gap:7px;justify-content:center;margin-top:26px}
.surface span{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);border:1px solid var(--line);border-radius:20px;padding:4px 11px}

/* blocks */
.blk{margin-bottom:26px;animation:rise .3s var(--ease) both}
@keyframes rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.q{display:flex;gap:12px;align-items:flex-start;padding-bottom:4px;flex-wrap:wrap}
.q .caret{color:var(--accent);font-weight:700;flex:none}
.q .txt{font-size:15px;color:var(--ink);line-height:1.55}

/* Copy + edit on the question. `Retry` under the answer re-runs a question
   verbatim; most re-runs are not verbatim, and the fix is usually one word.

   Revealed on hover, and never on hover alone — a control that only exists
   for a mouse does not exist. Focus brings it back for the keyboard, and
   `visibility` rather than `display` keeps it in the layout so the row does
   not reflow under the cursor. */
.qacts{
  margin-left:auto;display:flex;gap:2px;align-self:center;flex:none;
  visibility:hidden;opacity:0;transition:opacity .14s var(--ease);
}
.blk:hover .qacts,.q:focus-within .qacts{visibility:visible;opacity:1}
.qact{
  display:flex;align-items:center;justify-content:center;
  width:24px;height:24px;padding:0;border:0;border-radius:4px;
  background:none;color:var(--faint);cursor:pointer;
  transition:color .12s,background .12s;
}
.qact:hover{color:var(--accent);background:var(--panel-2)}
.qact:focus-visible{outline:1px solid var(--accent);outline-offset:1px;color:var(--accent)}
.qact svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:1.2;
  stroke-linecap:round;stroke-linejoin:round}

/* The editor takes the whole row under the question, so the original stays
   readable while you amend a copy of it. */
.q.editing .qacts{display:none}
.qedit{flex:1 0 100%;margin-top:8px;display:flex;flex-direction:column;gap:7px}
.qedit textarea{
  width:100%;resize:none;font:inherit;font-size:15px;line-height:1.55;color:var(--ink);
  background:var(--panel-2);border:1px solid var(--accent-dim);border-radius:5px;
  padding:9px 11px;outline:none;
}
.qedit textarea:focus{border-color:var(--accent)}
.qerow{display:flex;gap:6px}
.qrun,.qcancel{
  font:inherit;font-size:11px;letter-spacing:.06em;padding:5px 12px;border-radius:4px;
  border:1px solid var(--line-2);background:none;color:var(--dim);cursor:pointer;
  transition:color .12s,border-color .12s;
}
.qrun{color:var(--accent);border-color:var(--accent-dim)}
.qrun:hover{border-color:var(--accent)}
.qcancel:hover{color:var(--soft);border-color:var(--line-2)}

/* Touch has no hover, so the controls would be permanently invisible. */
@media (hover:none){
  .qacts{visibility:visible;opacity:1}
}
@media (prefers-reduced-motion:reduce){
  .qacts{transition:none}
}

/* ── tool trace: the convergence motif, running live ──────────────────
   Every source runs a wire into a shared bus, and the bus feeds the
   answer — the same picture the landing page draws in ASCII, except
   here the wires are real calls and a packet only moves while one is
   actually open. The 15-30s before the first token is the most
   interesting thing this machine does; a spinner throws all of it away.
   The panel opens itself to show the work and folds back to one line
   the moment prose starts, unless the reader has touched it. */
.trace{border:1px solid var(--line);border-radius:6px;background:var(--panel);margin:10px 0 14px;overflow:hidden}
.trace-h{display:flex;align-items:center;gap:9px;padding:9px 13px;font-size:11px;color:var(--dim);letter-spacing:.08em;cursor:pointer;width:100%;text-align:left}
.trace-h:hover{color:var(--soft)}
.trace-h .n{color:var(--accent)}
/* Live, the header is the loud thing on screen. Finished, it is a
   receipt sitting above the answer — it should not out-shout it. */
.trace[data-state=done] .trace-h .n{color:var(--soft)}
.trace-h .chev{margin-left:auto;transition:transform .18s var(--ease)}
.trace.open .trace-h .chev{transform:rotate(90deg)}

/* State glyph. Same vocabulary as the design system's progress row, so
   a run reads the same here as it does anywhere else in the product. */
/* Scoped to `.trace-h`. Without it these are `.trace[data-state] .st`, which
   is a descendant selector matching every per-call `.st` in the body too —
   same specificity as the row's own rules and declared later, so the header
   won. A finished trace stamped a tick on every row it contained, including
   the ones the stream abandoned, which is the exact thing the row rules go
   out of their way not to do. */
.trace-h .st{flex:none;font-size:10px;line-height:1;width:8px;color:var(--dim)}
.trace-h .st::before{content:"\00b7"}
.trace[data-state=planning] .trace-h .st::before,
.trace[data-state=reading] .trace-h .st::before,
.trace[data-state=writing] .trace-h .st::before{content:"\25b8"}
.trace[data-state=done] .trace-h .st::before{content:"\2713"}
.trace[data-state=failed] .trace-h .st::before{content:"\00d7"}
.trace[data-state=planning] .trace-h .st,
.trace[data-state=reading] .trace-h .st,
.trace[data-state=writing] .trace-h .st{color:var(--accent);animation:ct-blink 1.5s ease-in-out infinite}
.trace[data-state=done] .trace-h .st{color:var(--green)}
.trace[data-state=failed] .trace-h .st{color:var(--accent)}
@keyframes ct-blink{0%,100%{opacity:1}50%{opacity:.35}}

/* Collapse animates on grid-template-rows so it works without knowing
   the height, which changes on every appended call. */
.trace-body{display:grid;grid-template-rows:0fr;transition:grid-template-rows .34s var(--ease)}
.trace.open .trace-body{grid-template-rows:1fr}
/* The single grid child everything else hangs off. A second child of
   .trace-body would take an implicit auto row and never collapse. */
.trace-inner{overflow:hidden;min-height:0}
.tflow{display:flex;align-items:center;border-top:1px solid var(--line)}

/* the bus: one vertical line every wire terminates on */
.tflow-wires{
  flex:1;min-width:0;padding:6px 0;position:relative;
  border-right:1px solid var(--line-2);
  max-height:min(44vh,360px);overflow-y:auto;scrollbar-width:thin;
  transition:border-color .3s var(--ease);
}
.trace[data-state=reading] .tflow-wires{border-right-color:var(--accent-dim)}

/* Before the first call there is nothing to draw, so the empty bus
   sweeps. Deliberately abstract — inventing placeholder rows here would
   read as data that does not exist yet. */
.trace.planning .tflow-wires{min-height:68px}
.trace.planning .tflow-wires::before{
  content:"selecting sources";position:absolute;left:13px;top:50%;transform:translateY(-50%);
  font-size:11px;color:var(--faint);letter-spacing:.04em;
}
.trace.planning .tflow-wires::after{
  content:"";position:absolute;left:13px;right:0;top:0;height:1px;
  background:linear-gradient(90deg,transparent,var(--accent),transparent);
  opacity:.5;animation:ct-scan 2.2s var(--ease) infinite;
}
@keyframes ct-scan{0%{top:6px;opacity:0}18%{opacity:.5}82%{opacity:.5}100%{top:62px;opacity:0}}

/* Once the log outgrows its box the cut edge is masked, so a row is
   never sliced in half at the fold. Only when it actually scrolls —
   otherwise the first and last rows would fade for no reason. */
.tflow-wires.fade{
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 14px,#000 calc(100% - 14px),transparent 100%);
  mask-image:linear-gradient(to bottom,transparent 0,#000 14px,#000 calc(100% - 14px),transparent 100%);
}

.tcall{display:flex;gap:10px;align-items:center;padding:5px 0 5px 13px;font-size:11px;line-height:1.6}
.tcall .nm{color:var(--soft);flex:none}
.tcall .ms{color:var(--faint);flex:none;font-size:10px;width:112px;text-align:right;font-variant-numeric:tabular-nums}
.tcall.run .nm{color:var(--accent)}

/* Per-call state, in the landing page's vocabulary: a ring that spins while
   the call is open, a filled disc with a tick once it lands. The ASCII
   glyphs it replaces (· ▸ ✓) were legible but flat — the same run reads as
   a demo on the landing and as a debug log in here, and it is the same run.

   The disc stays GREEN rather than taking the landing's coral. Coral is
   what "open" means three pixels to the left; painting "done" the same
   colour would cost the one thing this column is for, which is knowing at a
   glance how much of the run is still outstanding.

   `done` is set explicitly rather than inferred from "no longer running",
   so a call the stream abandoned keeps the neutral dot instead of
   inheriting a tick it never earned. */
.tcall .st{
  flex:none;width:13px;height:13px;border-radius:999px;
  display:grid;place-items:center;line-height:1;
  font-size:9px;color:var(--faint);
}
.tcall .st::before{content:"\00b7"}
.tcall.run .st{
  border:1.5px solid var(--accent-dim);border-top-color:var(--accent);
  animation:ct-spin .75s linear infinite;
}
.tcall.run .st::before{content:""}
.tcall.done .st{
  background:var(--green);color:var(--color-bg);
  font-size:8.5px;font-weight:700;animation:none;
}
.tcall.done .st::before{content:"\2713"}
@keyframes ct-spin{to{transform:rotate(360deg)}}

/* Arguments as chips + key/value pairs instead of raw JSON. The value is
   the part worth reading, so the key is the dimmer of the two. */
.tcall .ar{position:relative;flex:1;min-width:0;display:flex;align-items:center;gap:8px;overflow:hidden;white-space:nowrap}
.tcall .chip{
  flex:none;font-size:10px;letter-spacing:.06em;color:var(--soft);
  border:1px solid var(--line-2);border-radius:3px;padding:1px 6px;
  background:var(--panel-2);
}
.tcall.run .chip{color:var(--accent);border-color:var(--accent-dim)}
.tcall .kv{flex:none;display:inline-flex;gap:5px;align-items:baseline;min-width:0}
.tcall .kv i{font-style:normal;color:var(--faint);font-size:10px}
.tcall .kv b{font-weight:400;color:var(--dim);min-width:0;overflow:hidden;text-overflow:ellipsis}
/* The tail is what gets cut on a narrow panel, so fade it out rather
   than ending on a hard clip mid-word. */
.tcall .ar::after{
  content:"";position:absolute;right:0;top:0;bottom:0;width:24px;pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--panel));
}

/* the wire, and the packet that travels it */
.tcall .wire{
  flex:none;width:64px;height:1px;background:var(--line-2);position:relative;
  transition:background .5s var(--ease);
}
.tcall .wire i{
  position:absolute;left:0;top:50%;width:4px;height:4px;border-radius:50%;
  background:var(--accent);transform:translate(0,-50%);opacity:0;
}
.tcall.run .wire i{animation:ct-packet 1.15s linear infinite}
@keyframes ct-packet{
  0%{transform:translate(0,-50%);opacity:0}
  14%{opacity:1}
  86%{opacity:1}
  100%{transform:translate(62px,-50%);opacity:0}
}
/* delivered: the packet parks on the bus */
.tcall.done .wire::after{
  content:"";position:absolute;right:0;top:50%;width:3px;height:3px;border-radius:50%;
  background:var(--dim);transform:translate(50%,-50%);
}
.tcall.land .wire{background:var(--accent);transition:none}

/* the sink */
.tflow-sink{flex:none;display:flex;align-items:center;padding:0 14px 0 0}
.tflow-sink .lead{position:relative;width:34px;height:1px;background:var(--line-2);flex:none}
.tflow-sink .lead::after{
  content:"\25b8";position:absolute;right:-7px;top:50%;transform:translateY(-50%);
  font-size:9px;line-height:1;color:var(--line-2);
}
.tflow-sink .lead i{
  position:absolute;left:0;top:50%;width:4px;height:4px;border-radius:50%;
  background:var(--accent);transform:translate(0,-50%);opacity:0;
}
.trace[data-state=reading] .lead::after,
.trace[data-state=writing] .lead::after{color:var(--accent)}
.trace[data-state=reading] .lead i,
.trace[data-state=writing] .lead i{animation:ct-packet-lead 1.15s linear infinite}
@keyframes ct-packet-lead{
  0%{transform:translate(0,-50%);opacity:0}
  20%{opacity:1}
  80%{opacity:1}
  100%{transform:translate(32px,-50%);opacity:0}
}
.sink-box{
  margin-left:12px;padding:11px 14px;min-width:140px;
  border:1px solid var(--line-2);border-radius:5px;background:var(--panel-2);
  display:flex;flex-direction:column;gap:4px;
  transition:border-color .3s var(--ease),box-shadow .3s var(--ease);
}
.sink-t{font-size:9.5px;letter-spacing:.18em;color:var(--dim)}
.sink-s{font-size:12.5px;color:var(--soft)}
.sink-n{font-size:10px;color:var(--faint);font-variant-numeric:tabular-nums}
.trace[data-state=reading] .sink-box,
.trace[data-state=writing] .sink-box{border-color:var(--accent-dim);box-shadow:0 0 30px -14px var(--accent)}
.trace[data-state=reading] .sink-s,
.trace[data-state=writing] .sink-s{color:var(--accent)}
.trace[data-state=done] .sink-s{color:var(--green)}
.trace[data-state=failed] .sink-s{color:var(--accent)}

/* ── the think strip ──────────────────────────────────────────────────
   Full panel width, under the flow, because the model's monologue is
   prose and prose needs a measure. Two lines, height fixed in advance:
   a box that grew with the stream would shove the call log — the thing
   you are actually reading — down the page every few hundred ms.

   The text is pinned to the BOTTOM of that window and overflows upward,
   so the newest sentence always sits on the same line and the older one
   slides up under the fade. That is the one arrangement where the
   ragged, half-visible edge is the part you have already read. */
/* Its own block, not a footnote. At 11px in `--faint` over two lines it was
   technically present and practically invisible — the complaint was "apenas
   se ve", and the fix is not one more notch of contrast: it is treating the
   think phase as a thing being shown rather than a status line. Panel
   ground, a rule up the left in the accent, three lines at reading size. */
.tthink{
  display:flex;gap:12px;align-items:flex-start;
  padding:11px 14px 12px 13px;border-top:1px solid var(--line);
  background:var(--panel-2);
}
.tt-l{
  flex:none;font-size:9.5px;letter-spacing:.18em;line-height:1.7;
  color:var(--accent);opacity:.8;padding-top:1px;
  animation:ct-blink 1.8s ease-in-out infinite;
}
.tt-x{
  flex:1;min-width:0;font-size:12.5px;line-height:1.7;
  /* Grows with the thought and then stops. A fixed window opened as an
     empty box and never looked like it was filling; unbounded growth would
     push the answer down the page for a minute. So: one line to eight, and
     from there it behaves as a window again — oldest line slides up under
     the fade, newest stays put. */
  min-height:1.7em;max-height:13.6em;
  display:flex;align-items:flex-end;overflow:hidden;
  border-left:1px solid var(--accent-dim);padding-left:12px;
  /* The fade keeps a floor rather than reaching transparent: the lines above
     are the sentences you just read, and they should stay legible enough to
     carry the newest one into context. Shallower than it was — at three
     lines the top one is still content, not decoration. */
  -webkit-mask-image:linear-gradient(to bottom,rgba(0,0,0,.42) 0,#000 42%);
  mask-image:linear-gradient(to bottom,rgba(0,0,0,.42) 0,#000 42%);
}
.tt-s{color:var(--soft);overflow-wrap:anywhere}

/* Narrow: the diagram is the first thing to go — the log still reads.
   The think strip stays: on a phone it is the only sign the run is alive
   between the last tool result and the first written word. */
@media (max-width:860px){
  .tflow-sink{display:none}
  .tcall .wire{display:none}
  .tflow-wires{border-right:0}
  .tcall{padding-right:13px}
}
@media (prefers-reduced-motion:reduce){
  .tcall .wire i,.tflow-sink .lead i,.trace.planning .tflow-wires::after,
  .trace-h .st,.tcall.run .st,.tt-l{animation:none}
  .tcall.run .wire i{opacity:1;transform:translate(30px,-50%)}
}

/* thinking skeleton */
.skel{display:flex;flex-direction:column;gap:9px;padding:4px 0 6px}
.skel i{display:block;height:9px;border-radius:3px;background:linear-gradient(90deg,var(--panel) 25%,var(--panel-2) 50%,var(--panel) 75%);background-size:280% 100%;animation:sh 1.3s linear infinite}
.skel i:nth-child(1){width:88%}.skel i:nth-child(2){width:96%}.skel i:nth-child(3){width:62%}
@keyframes sh{from{background-position:180% 0}to{background-position:-80% 0}}

/* prose */
.prose{font-size:14px;line-height:1.78;color:var(--soft)}
.prose h1,.prose h2,.prose h3{color:var(--ink);font-weight:500;letter-spacing:.01em;margin:22px 0 9px;line-height:1.35}
.prose h1{font-size:18px}.prose h2{font-size:16px}.prose h3{font-size:14px}
.prose p{margin:0 0 12px}
.prose strong{color:var(--ink);font-weight:700}
.prose em{color:var(--accent);font-style:normal}
.prose code{background:var(--panel-2);border:1px solid var(--line);border-radius:3px;padding:1px 5px;font-size:12.5px;color:var(--ink)}
.prose pre{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:13px 15px;overflow-x:auto;margin:0 0 14px}
.prose pre code{background:none;border:0;padding:0;font-size:12.5px;color:var(--soft)}
.prose ul,.prose ol{margin:0 0 13px;padding-left:20px}
.prose li{margin:4px 0}
.prose li::marker{color:var(--color-accent-500)}
.prose a{color:var(--accent);border-bottom:1px solid rgba(238,87,65,.3)}
.prose blockquote{border-left:2px solid var(--accent-dim);padding-left:14px;color:var(--dim);margin:0 0 13px}
.prose hr{border:0;border-top:1px solid var(--line);margin:20px 0}
/* Scoped to .tblwrap, NOT .prose. A ```table block resolved server-side is
   built by tableCard() into a .card, which is not inside .prose — so these
   rules used to miss it entirely and it rendered with the browser's default
   table styling: no cell padding (hence `99.86$586.2M` reading as one
   value), no sticky header, and no sort arrow even though wireTables had
   made the header sortable. Dashboard tiles and the public /r/ viewer lift
   the same .tblwrap out of the card, so all three surfaces were affected.
   .tblwrap is the one ancestor every table path shares. */
.tblwrap{overflow-x:auto;margin:0 0 16px;border:1px solid var(--line);border-radius:6px}
.tblwrap table{width:100%;border-collapse:collapse;font-size:12.5px}
.tblwrap th{
  text-align:left;color:var(--dim);font-weight:500;font-size:10px;letter-spacing:.13em;text-transform:uppercase;
  padding:9px 14px;border-bottom:1px solid var(--line-2);white-space:nowrap;cursor:pointer;user-select:none;
  position:sticky;top:0;background:var(--panel)
}
.tblwrap th:hover{color:var(--ink)}
/* The affordance, not just the behaviour: a header you can sort should look
   like one before you click it. */
.tblwrap th::after{content:"↕";opacity:.3;margin-left:6px;letter-spacing:0}
.tblwrap th:hover::after{opacity:.6}
.tblwrap th.asc::after{content:"↑";color:var(--accent);opacity:1}
.tblwrap th.desc::after{content:"↓";color:var(--accent);opacity:1}
.tblwrap td{padding:8px 14px;border-bottom:1px solid var(--line);color:var(--soft);white-space:nowrap}
/* Numbers read as a column only when the header sits over the digits. */
.tblwrap th.num,.tblwrap td.num{text-align:right;font-variant-numeric:tabular-nums}
.tblwrap tbody tr:last-child td{border-bottom:0}
.tblwrap tbody tr:hover td{background:var(--panel)}
/* Long free-text cells (fund names, issuer names) get a ceiling instead of
   pushing every numeric column off the right edge. */
.tblwrap td.wide{max-width:min(38ch,42vw);overflow:hidden;text-overflow:ellipsis}
.tbl-more{
  display:block;width:100%;background:none;border:0;border-top:1px solid var(--line);
  color:var(--dim);font:inherit;font-size:11px;letter-spacing:.06em;padding:9px 14px;
  cursor:pointer;text-align:center
}
.tbl-more:hover{color:var(--accent);background:var(--panel)}
.tik{color:var(--accent);cursor:pointer;border-bottom:1px dotted rgba(238,87,65,.45)}
.tik:hover{background:rgba(238,87,65,.1)}
.tik:focus-visible{outline:1px solid var(--accent);outline-offset:2px;background:rgba(238,87,65,.1)}

/* ── TICKER HOVER CARD ────────────────────────────────────────────────
   A ticker in an answer is a name you may not know, and the terminal
   knows it. Everything on the card is a daily-baked column, so it opens
   in one round trip and never blocks on R2.

   Reads as an instrument panel rather than a tooltip: mono throughout,
   one accent, hairline rules between the four registers (identity,
   price, fundamentals, rating). Fixed-position on the body so a card
   opened at the bottom of the canvas is never clipped by a panel. */
.tcard{
  position:fixed;z-index:80;width:302px;
  background:var(--panel);border:1px solid var(--line-2);border-radius:6px;
  box-shadow:0 24px 48px -20px rgba(0,0,0,.75),0 0 0 1px rgba(0,0,0,.25);
  font-size:11px;line-height:1.5;color:var(--soft);
  opacity:0;transform:translateY(-3px);transition:opacity .14s var(--ease),transform .14s var(--ease);
  pointer-events:auto;overflow:hidden;
}
.tcard.in{opacity:1;transform:none}

.cd-h{display:flex;align-items:center;gap:8px;padding:10px 12px 0}
.cd-t{font-size:13px;letter-spacing:.08em;color:var(--accent)}
.cd-x{
  font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);
  border:1px solid var(--line-2);border-radius:3px;padding:1px 5px;white-space:nowrap;
}
.cd-ev{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 40%,transparent)}
.cd-n{padding:3px 12px 9px;color:var(--dim);font-size:11px}
/* The industry sits under the name rather than beside it: a two-line
   identity block is fine, a name truncated to fit a sector is not. */
.cd-i{display:block;color:var(--faint);font-size:10px;margin-top:2px}

.cd-px{display:flex;align-items:baseline;gap:9px;padding:0 12px 8px}
.cd-p{font-size:19px;color:var(--ink);font-variant-numeric:tabular-nums}
.cd-c{font-size:11.5px;font-variant-numeric:tabular-nums}
.cd-c.up{color:var(--green)}
.cd-c.down{color:var(--accent)}
.cd-d{margin-left:auto;font-size:9.5px;color:var(--faint);font-variant-numeric:tabular-nums}

.cd-sp{height:46px;padding:0 12px}
.cd-spark{width:100%;height:100%;display:block}
.cd-fill{opacity:.13}
.cd-fill.pos{fill:var(--green)}
.cd-fill.neg{fill:var(--accent)}
.cd-line{fill:none;stroke-width:1.2;vector-effect:non-scaling-stroke}
.cd-line.pos{stroke:var(--green)}
.cd-line.neg{stroke:var(--accent)}
/* Without the 52-week extremes the sparkline has no scale and reads as a
   mood rather than a range. */
.cd-r{
  display:flex;align-items:center;gap:6px;padding:1px 12px 9px;
  font-size:9px;color:var(--faint);font-variant-numeric:tabular-nums;
}
.cd-rl{flex:1;text-align:center;letter-spacing:.14em;text-transform:uppercase;color:var(--line-2)}

.cd-grid{
  display:grid;grid-template-columns:1fr 1fr 1fr;gap:1px;
  background:var(--line);border-top:1px solid var(--line);border-bottom:1px solid var(--line);
}
.cd-s{background:var(--panel);padding:7px 10px;display:flex;flex-direction:column;gap:2px;min-width:0}
.cd-s i{font-style:normal;font-size:8.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.cd-s b{font-weight:400;font-size:11px;color:var(--soft);font-variant-numeric:tabular-nums}

.cd-rt{padding:9px 12px 10px;display:flex;flex-direction:column;gap:6px}
.cd-meter{display:flex;align-items:center;gap:8px}
.cd-ml{
  flex:none;width:48px;font-size:8.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);
}
.cd-track{flex:1;position:relative;height:4px;background:var(--panel-2);border:1px solid var(--line);border-radius:2px}
/* Neutral for the unsigned axes. Everywhere else on this card coral means
   "down" — the price change, the sparkline — so a coral bar under QUALITY
   would make one colour mean two opposite things, and a 93 would read as
   bad news. Only FLOW has a real zero, so only FLOW takes a sign colour. */
.cd-track i{position:absolute;top:0;bottom:0;background:var(--soft);opacity:.55;border-radius:1px}
.cd-track i.up{background:var(--green);opacity:1}
.cd-track i.down{background:var(--accent);opacity:1}
/* The zero tick on a signed track. Flow runs -100..+100 and fills out from
   the middle, so the middle has to be visible or the fill has no origin. */
.cd-track[data-mid]::before{
  content:"";position:absolute;left:50%;top:-2px;bottom:-2px;width:1px;background:var(--line-2);
}
.cd-mv{flex:none;width:66px;text-align:right;font-size:9.5px;color:var(--dim);font-variant-numeric:tabular-nums}

.cd-news{
  border-top:1px solid var(--line);padding:8px 12px;display:flex;flex-direction:column;gap:3px;
}
.cd-nh{
  font-size:10.5px;color:var(--dim);line-height:1.45;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.cd-na{font-size:9px;color:var(--faint)}

.cd-f{
  border-top:1px solid var(--line);padding:6px 12px 7px;background:var(--panel-2);
  font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);
}
.cd-empty{padding:2px 12px 10px;font-size:10.5px;color:var(--faint);line-height:1.5}

/* Loading. Three bars rather than a spinner — it is the same skeleton
   vocabulary the answer body uses while it waits. */
.cd-load{padding:10px 12px 14px;display:flex;flex-direction:column;gap:7px}
.cd-load i{height:8px;border-radius:2px;background:var(--panel-2);animation:ct-blink 1.4s ease-in-out infinite}
.cd-load i:nth-child(1){width:70%}
.cd-load i:nth-child(2){width:100%;animation-delay:.15s}
.cd-load i:nth-child(3){width:45%;animation-delay:.3s}

/* Coarse pointers have no hover, and a card that opens on tap steals the
   tap that was meant to run the workup. */
@media (hover:none),(max-width:620px){
  .tcard{display:none}
}
@media (prefers-reduced-motion:reduce){
  .tcard{transition:none}
  .cd-load i{animation:none}
}

/* answer actions + followups */
/* Sources behind an answer. Closed by default — this is the thing you open to
   check a number you just read, not part of reading it. A <details> so the
   toggle is the browser's and it survives with no JS. */
.srcs{margin-top:8px;border:1px solid var(--line);border-radius:5px;background:var(--panel-2)}
.srcs>summary{
  display:flex;align-items:center;gap:8px;cursor:pointer;list-style:none;
  padding:8px 12px;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--dim)
}
.srcs>summary::-webkit-details-marker{display:none}
/* The disclosure caret, drawn rather than relying on the platform marker,
   which differs per browser and sits on the wrong side in some. */
.srcs>summary::before{content:"+";color:var(--faint);font-size:11px;width:9px}
.srcs[open]>summary::before{content:"−"}
.srcs>summary:hover{color:var(--ink)}
.srcs-ms{margin-left:auto;color:var(--faint);font-variant-numeric:tabular-nums;letter-spacing:0}
.srcs-body{border-top:1px solid var(--line);padding:4px 0}
.src{
  display:flex;align-items:baseline;gap:8px;padding:5px 12px;
  font-size:11px;color:var(--soft)
}
.src:hover{background:var(--panel)}
.src-i{color:var(--faint);font-variant-numeric:tabular-nums;min-width:14px;text-align:right}
.src-nm{color:var(--accent)}
.src-ar{display:flex;gap:5px;flex-wrap:wrap;min-width:0;flex:1}
.src-got{margin-left:auto;color:var(--faint);font-size:10px;font-variant-numeric:tabular-nums;white-space:nowrap}
.src .chip{font-size:10px;color:var(--ink);border:1px solid var(--line-2);border-radius:3px;padding:0 4px}
.src .kv{display:inline-flex;gap:3px;font-size:10px}
.src .kv i{color:var(--faint);font-style:normal}
.src .kv b{color:var(--soft);font-weight:400}

.acts{display:flex;gap:4px;flex-wrap:wrap;margin-top:6px}
.acts button{
  font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--dim);
  border:1px solid var(--line);padding:6px 11px;border-radius:5px;transition:color .14s,border-color .14s
}
.acts button:hover{color:var(--accent);border-color:var(--accent)}
.fups{margin-top:14px;display:flex;flex-direction:column;gap:6px}
.fups .lbl{font-size:9.5px;letter-spacing:.22em;text-transform:uppercase;color:var(--faint);margin-bottom:2px}
.fup{
  text-align:left;border:1px solid var(--line);border-radius:6px;padding:10px 13px;font-size:12.5px;color:var(--soft);
  transition:border-color .15s,color .15s,background .15s;display:flex;gap:10px;align-items:baseline
}
.fup:hover{border-color:var(--accent);color:var(--ink);background:rgba(238,87,65,.05)}
.fup::before{content:"→";color:var(--accent);flex:none}

/* ── asides: a question about one passage ─────────────────────────────
   Reads as a margin note and not as a second answer: indented behind a
   rule, one step smaller, quieter ink. The whole point is that it does
   not become the conversation — so it must not look like it either. */
.selpop{
  position:fixed;z-index:60;display:flex;gap:2px;padding:3px;
  border:1px solid var(--line-2);border-radius:6px;background:var(--panel);
  box-shadow:0 10px 30px -12px #000;animation:rise .14s var(--ease) both;
}
.selpop button{
  font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--dim);
  padding:5px 10px;border-radius:4px;transition:color .13s,background .13s;
}
.selpop button:hover{color:var(--accent);background:var(--panel-2)}

.asides{margin:2px 0 16px;display:flex;flex-direction:column;gap:10px}
.aside{
  border-left:2px solid var(--accent-dim);background:var(--panel);
  border-radius:0 6px 6px 0;padding:11px 14px 10px;
  animation:rise .22s var(--ease) both;
}
.aside.busy{border-left-color:var(--accent)}

.ah{display:flex;gap:8px;align-items:flex-start;padding-bottom:8px}
.ah .mk{color:var(--color-accent-500);font-size:15px;line-height:1;flex:none}
/* The passage, not the answer to it. Two lines is plenty to recognise
   what was highlighted; more turns the note into a second copy of it. */
.ah .qt{
  flex:1;min-width:0;font-size:11.5px;line-height:1.5;color:var(--dim);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.ah .ax{flex:none;color:var(--faint);font-size:13px;line-height:1;padding:0 2px;transition:color .13s}
.ah .ax:hover{color:var(--accent)}

.aturns{display:flex;flex-direction:column;gap:12px}
.aturn{display:flex;flex-direction:column;gap:5px}
.aq{font-size:12px;color:var(--ink);display:flex;gap:8px;align-items:baseline}
.aq::before{content:">";color:var(--accent);flex:none}
.aa{font-size:13px;line-height:1.68}
.aa p:last-child{margin-bottom:0}
/* A chart inside a note is a footnote's illustration — smaller margins,
   no shouting. */
.aside .card{margin:8px 0 2px}
.aside .skel.sm i{height:8px}
.aside .skel.sm i:nth-child(1){width:70%}.aside .skel.sm i:nth-child(2){width:44%}

.aform{display:flex;gap:8px;align-items:baseline;margin-top:10px;border-top:1px solid var(--line);padding-top:9px}
.aform .ac{color:var(--accent);flex:none;font-size:12px}
.aform input{flex:1;min-width:0;font-size:12.5px;color:var(--ink)}
.aform input::placeholder{color:var(--faint)}

/* chart card */
.card{border:1px solid var(--line);border-radius:7px;background:var(--panel);margin:16px 0 18px;overflow:hidden}
.card-h{display:flex;align-items:center;gap:12px;padding:11px 14px;border-bottom:1px solid var(--line)}
.card-h .ttl{font-size:12px;color:var(--ink);letter-spacing:.02em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.card-h .meta{font-size:10px;color:var(--faint);letter-spacing:.1em;flex:none}
.card-acts{margin-left:auto;display:flex;gap:2px;flex:none}
.card-acts button{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--dim);padding:5px 9px;border-radius:4px;transition:color .14s,background .14s}
.card-acts button:hover{color:var(--accent);background:rgba(238,87,65,.1)}
.card-body{padding:8px 6px 4px;position:relative}
.legend{display:flex;flex-wrap:wrap;gap:14px;padding:2px 14px 12px;font-size:10.5px;color:var(--dim)}
.legend i{display:inline-block;width:9px;height:2px;margin-right:6px;vertical-align:middle;border-radius:1px}

/* ── chart motion ──────────────────────────────────────────────────
   The signature moment: you ask, the terminal visibly works, and then the
   chart draws itself. Each mark type animates the way that mark actually
   comes into being — a line strokes along its own path, a bar grows from
   the zero baseline it is measured against, a heatmap wipes along its
   diagonal. Decorative motion would be noise; this is the data arriving. */
@keyframes ct-draw{to{stroke-dashoffset:0}}
@keyframes ct-grow{from{transform:scaleY(0)}to{transform:scaleY(1)}}
@keyframes ct-grow-x{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes ct-fade{from{opacity:0}}
@keyframes ct-pop{from{opacity:0;transform:scale(.2)}}
.ctchart .draw-grow{animation:ct-grow .5s var(--ease) both;animation-delay:calc(var(--i,0) * .035s)}
.ctchart .draw-grow-x{animation:ct-grow-x .5s var(--ease) both;animation-delay:calc(var(--i,0) * .035s)}
.ctchart .draw-fade{animation:ct-fade .5s ease both;animation-delay:calc(var(--i,0) * .03s)}
.ctchart .draw-pop{animation:ct-pop .32s var(--ease) both;animation-delay:calc(var(--i,0) * .012s);transform-box:fill-box;transform-origin:50% 50%}
/* Axes and labels settle first so the marks land on a drawn stage. */
.ctchart .grid,.ctchart .axis,.ctchart .tick{animation:ct-fade .45s ease both}

/* svg chart internals */
.ctchart{display:block;width:100%;height:auto;overflow:visible}
.ctchart .grid{stroke:rgba(233,228,221,.055);stroke-width:1}
.ctchart .axis{stroke:var(--line-2);stroke-width:1}
.ctchart .tick{fill:var(--faint);font-size:9.5px;font-family:var(--mono)}
.ctchart .ln{fill:none;stroke-width:1.6;stroke-linejoin:round;stroke-linecap:round}
.ctchart .dot{stroke:var(--panel);stroke-width:1}
.ctchart .cross{stroke:var(--dim);stroke-width:1;stroke-dasharray:3 3;pointer-events:none}
.ctchart .up{fill:var(--s2);stroke:var(--s2)}
.ctchart .dn{fill:var(--accent);stroke:var(--accent)}
.tip{
  position:absolute;pointer-events:none;z-index:5;background:#0e0a0c;border:1px solid var(--line-2);
  border-radius:5px;padding:8px 11px;font-size:11px;color:var(--soft);white-space:nowrap;
  box-shadow:0 8px 26px rgba(0,0,0,.6);opacity:0;transition:opacity .1s
}
.tip.on{opacity:1}
.tip .th{color:var(--ink);margin-bottom:5px;font-size:10px;letter-spacing:.1em}
.tip .tr{display:flex;gap:14px;justify-content:space-between}
.tip .tr b{color:var(--ink);font-weight:500}

/* composer */
/* The composer IS the product on an empty canvas, so it starts centred
   and large; asking docks it to the bottom in one move. The transition is
   the app opening up — the same gesture as the answer arriving. */
/* STICKY, not absolute.
   Absolute inside a scrolling container resolves `bottom:0` against the
   initial box and then scrolls away with the content — so scrolling up
   dragged the input over the middle of the answer. Reserving padding
   underneath (the first attempt) hid the symptom at the very bottom of
   the page and did nothing anywhere else. Sticky pins it to the
   scrollport, which is what it always looked like it was doing. */
.composer{
  position:sticky;bottom:0;padding:14px 30px 18px;
  background:linear-gradient(to top,var(--bg) 64%,rgba(6,4,5,0));
  transition:bottom .55s var(--ease),padding .55s var(--ease);
}
/* On the empty canvas the whole thing is ONE centred column in normal
   flow — headline, then the composer, then the suggestions. Floating the
   composer at 50% instead made it land on top of the suggestions, because
   nothing was reserving the space it needed. */
/* `safe center` centres while there is room and falls back to flex-start
   when the column is taller than the viewport — plain `center` overflows
   BOTH ways and silently clips the headline off the top. */
.app.landing .canvas{display:flex;flex-direction:column;justify-content:safe center;padding:3vh 0 4vh}
.app.landing .composer{position:static;background:none;padding-top:0;padding-bottom:10px}
.app.landing #below{flex:none}
.app.landing .composer-in{
  max-width:660px;padding:16px 18px;border-color:rgba(238,87,65,.28);
  box-shadow:0 24px 70px rgba(0,0,0,.45);
}
/* The landing variant sets border-color and box-shadow with three classes,
   which outranks the two-class `.composer-in:focus-within` below — so on the
   empty canvas the composer never showed focus at all. That went unnoticed
   while the textarea drew its own ring; suppressing the ring made it the
   only indicator, and it was not applying. Restated here at four classes,
   keeping the drop shadow the state depends on. */
.app.landing .composer-in:focus-within{
  border-color:rgba(238,87,65,.55);
  box-shadow:0 24px 70px rgba(0,0,0,.45),0 0 0 3px rgba(238,87,65,.07);
}
.app.landing .composer-in textarea{font-size:15px}
.app.landing .hintbar{max-width:660px;justify-content:center;opacity:.75}
.app.landing #quota{margin-left:0}
.composer-in{
  max-width:920px;margin:0 auto;display:flex;align-items:flex-end;gap:11px;
  border:1px solid var(--line-2);border-radius:9px;background:var(--panel);padding:11px 13px;
  transition:border-color .18s,box-shadow .18s
}
.composer-in:focus-within{border-color:rgba(238,87,65,.55);box-shadow:0 0 0 3px rgba(238,87,65,.07)}
.composer-in .caret{color:var(--accent);font-weight:700;padding-bottom:2px;flex:none}
.composer-in textarea{flex:1;resize:none;font-size:14px;line-height:1.6;max-height:180px;color:var(--ink)}
.composer-in textarea::placeholder{color:var(--faint)}
.send{
  flex:none;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--bg);
  background:var(--accent);padding:8px 15px;border-radius:5px;font-weight:500;transition:filter .15s,opacity .15s
}
.send:hover{filter:brightness(1.09)}
.send:disabled{opacity:.32;cursor:not-allowed}
.send.stop{background:transparent;color:var(--accent);border:1px solid var(--accent)}
.hintbar{max-width:920px;margin:8px auto 0;display:flex;gap:16px;font-size:10px;color:var(--faint);letter-spacing:.08em;align-items:center}
#quota{margin-left:auto}

/* dashboards */
.dash{max-width:1240px;margin:0 auto;padding:26px 30px 60px}
.dash-h{display:flex;align-items:center;gap:14px;margin-bottom:8px}
.dash-h h2{font-size:17px;font-weight:500;color:var(--ink)}
.dash-h .sp{margin-left:auto;display:flex;gap:6px}
.dash-h button{font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--dim);border:1px solid var(--line-2);padding:7px 12px;border-radius:5px}
.dash-h button:hover{color:var(--accent);border-color:var(--accent)}
.dash-sub{font-size:11px;color:var(--faint);margin-bottom:20px}
.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;align-items:start}
.grid .card{margin:0}
/* A widget spanning both columns — the "wide" toggle. */
.wcard.wide{grid-column:1/-1}
.wcard{cursor:grab;transition:border-color .15s,opacity .15s,transform .15s var(--ease)}
.wcard:active{cursor:grabbing}
.wcard.dragging{opacity:.4}
.wcard.dropto{border-color:var(--accent);transform:translateY(-2px)}
.wcard .tblwrap{border:0;border-radius:0;margin:0;max-height:340px}
#dashTitle{outline:none;border-bottom:1px solid transparent;padding-bottom:2px}
#dashTitle:hover{border-bottom-color:var(--line-2)}
#dashTitle:focus{border-bottom-color:var(--accent)}
@media(max-width:980px){.grid{grid-template-columns:1fr}}
.wempty{border:1px dashed var(--line-2);border-radius:7px;padding:44px 20px;text-align:center;color:var(--faint);font-size:12px;line-height:1.95}
.wempty b{color:var(--soft)}

/* command palette */
.scrim{position:fixed;inset:0;z-index:70;background:rgba(4,3,3,.66);backdrop-filter:blur(3px);display:flex;justify-content:center;align-items:flex-start;padding:12vh 20px 20px;animation:fade .14s var(--ease)}
.scrim.dark{background:rgba(4,3,3,.86);align-items:center;padding:40px}
@keyframes fade{from{opacity:0}to{opacity:1}}
.palette{width:min(640px,100%);border:1px solid var(--line-2);border-radius:10px;background:var(--panel);overflow:hidden;box-shadow:0 30px 80px rgba(0,0,0,.6);animation:pop .16s var(--ease)}
@keyframes pop{from{opacity:0;transform:translateY(-8px) scale(.99)}to{opacity:1;transform:none}}
.pal-in{display:flex;align-items:center;gap:11px;padding:14px 16px;border-bottom:1px solid var(--line)}
.pal-in .caret{color:var(--accent);font-weight:700}
.pal-in input{flex:1;font-size:14px}
.pal-list{max-height:52vh;overflow-y:auto;padding:6px 0}
.pal-item{display:flex;align-items:center;gap:10px;width:100%;text-align:left;padding:9px 16px;font-size:13px;color:var(--soft)}
.pal-item .kind{font-size:9px;letter-spacing:.18em;text-transform:uppercase;color:var(--faint);flex:none;width:66px}
/* An existing object carries its own hue; a suggestion's topic does not,
   which is what stops the two axes reading as one column. */
.pal-item .kind.obj{font-weight:500}
/* The selected row used to recolour every kind coral, erasing the type it
   had just been given. Only the topic labels take the highlight. */
.pal-item.sel .kind:not(.obj){color:var(--accent)}
.pal-item .lbl{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pal-item.sel{background:var(--panel-2);color:var(--ink)}

.pal-empty{padding:18px 16px;font-size:12px;color:var(--faint)}

/* lightbox */
.lb-frame{width:min(1240px,100%);max-height:88vh;border:1px solid var(--line-2);border-radius:9px;background:var(--panel);display:flex;flex-direction:column;overflow:hidden}
.lb-head{display:flex;align-items:center;padding:13px 16px;border-bottom:1px solid var(--line);font-size:12px;color:var(--ink)}
.lb-head button{margin-left:auto;font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--dim)}
.lb-head button:hover{color:var(--accent)}
.lb-body{padding:16px;overflow:auto}

/* gate + toast */
.gate{max-width:540px;margin:12vh auto;text-align:center;padding:0 30px}
.gate h2{font-size:20px;font-weight:500;color:var(--ink);margin-bottom:12px}
.gate p{color:var(--dim);font-size:13px;line-height:1.8;margin-bottom:26px}
.gate form{display:flex;gap:8px;justify-content:center}
.gate input{border:1px solid var(--line-2);border-radius:6px;padding:11px 14px;font-size:13px;flex:1;max-width:290px;background:var(--panel)}
.gate input:focus{border-color:var(--accent)}
.gate .msg{margin-top:16px;font-size:12px;color:var(--dim);min-height:18px}
/* alerts */
.bell{display:flex;align-items:center;gap:7px;font-size:11px;letter-spacing:.1em;color:var(--dim);padding:5px 9px;border-radius:5px;transition:color .15s,background .15s}
.bell:hover{color:var(--ink);background:var(--panel-2)}
.bell i{font-style:normal;background:var(--accent);color:var(--bg);border-radius:9px;padding:1px 6px;font-size:10px;font-weight:700}
/* ── the Alerts section ───────────────────────────────────────────────
   Written in the design system's own token names (--color-*, --space-*)
   rather than Studio's legacy aliases, because this surface was rebuilt
   against `design-system/templates/alerts/` and the two should stay
   diffable. The aliases in ascii.css resolve to the same values either
   way; using the system names means a token change upstream lands here.

   The panel this replaces showed only what a watch FIRED. What it matches
   was invisible — which is how a watch labelled "New 13D activist filings"
   with every filter silently dropped read as healthy for a month. And
   state was carried by opacity alone, which is how fifteen deliberately
   paused watches read as fifteen broken ones. */
.alerts-view{max-width:1120px;margin:0 auto;padding:var(--space-8) var(--space-6) 90px}
.av-h h2{font-size:20px;font-weight:var(--font-heading-weight);color:var(--color-text);margin:0}
.av-h p{font-size:13px;color:var(--color-neutral-400);line-height:1.7;margin:var(--space-2) 0 0;max-width:64ch}
.av-h em{color:var(--color-neutral-200);font-style:normal}

/* ── the health row ─────────────────────────────────────────────────────
   Four figures that answer, unprompted, the question a user arrives at this
   page holding: is this thing even running? "Never fired" cannot tell a
   quiet watch from a dead scanner. These can. */
.health{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--color-divider);
  border:1px solid var(--color-divider);border-radius:var(--radius-md);overflow:hidden;
  margin:var(--space-6) 0}
.hstat{background:var(--color-bg);padding:var(--space-4) var(--space-6);display:grid;gap:2px}
.hstat .lb{font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--color-neutral-500)}
.hstat .v{font-size:26px;line-height:1.15;font-weight:var(--font-heading-weight);
  font-variant-numeric:tabular-nums;color:var(--color-text)}
.hstat .v.is-live{color:var(--color-accent-2)}
.hstat .v.is-warn{color:var(--color-accent)}
.hstat .nt{font-size:11px;color:var(--color-neutral-500)}

.av-sec{display:flex;align-items:center;gap:var(--space-3);margin:var(--space-8) 0 var(--space-3);
  font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--color-neutral-500)}
.av-sec i{flex:1;height:1px;background:var(--color-divider);display:block}
.av-sec b{font-weight:400;color:var(--color-neutral-400);font-variant-numeric:tabular-nums;letter-spacing:.1em}

/* ── compose ────────────────────────────────────────────────────────── */
.wnew{display:flex;gap:var(--space-3);align-items:center;border:1px solid var(--color-divider);
  border-radius:var(--radius-md);padding:var(--space-3) var(--space-4);background:var(--color-surface);
  transition:border-color var(--motion-fast)}
.wnew:focus-within{border-color:var(--color-accent)}
.wnew .ac{color:var(--color-accent);font-weight:700;flex:none}
.wnew input{flex:1;min-width:0;font-size:13px;color:var(--color-text)}
.wnew input::placeholder{color:var(--faint)}
.wnew button{flex:none;font-size:10px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--color-neutral-500);border:1px solid var(--color-divider);border-radius:var(--radius-sm);
  padding:var(--space-2) var(--space-3);transition:color var(--motion-fast),border-color var(--motion-fast)}
.wnew button:hover{color:var(--color-accent);border-color:var(--color-accent)}

/* The draft. Shown, read, and only then saved — reviewing it is the whole
   feature. */
.wdraft{margin-top:var(--space-3);border:1px solid var(--color-accent-700);border-radius:var(--radius-md);
  background:var(--color-surface);padding:var(--space-4) var(--space-6);animation:rise .2s var(--ease) both}
.wd-h{display:flex;align-items:center;gap:var(--space-3)}
.wd-t{font-size:14px;color:var(--color-text)}
.wd-note{font-size:12px;color:var(--color-neutral-300);line-height:1.65;margin-top:var(--space-2)}
.wd-json{background:var(--color-bg);border:1px solid var(--color-divider);border-radius:var(--radius-sm);
  padding:var(--space-3);font-size:11px;color:var(--color-neutral-400);overflow-x:auto;
  margin:var(--space-3) 0 0;line-height:1.7}
.wd-a{display:flex;gap:var(--space-2);margin-top:var(--space-3)}
.wd-a button{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--color-neutral-500);
  border:1px solid var(--color-divider);border-radius:var(--radius-sm);padding:var(--space-2) var(--space-4);
  transition:color var(--motion-fast),border-color var(--motion-fast)}
.wd-a button:hover{color:var(--color-text);border-color:var(--color-divider-strong)}
.wd-a .go{color:var(--color-accent);border-color:var(--color-accent-700)}
.wd-a .go:hover{background:var(--color-accent-900);border-color:var(--color-accent)}
.wd-a button:disabled{opacity:.45;cursor:default}

/* ── watch grid ─────────────────────────────────────────────────────────
   One shared hairline between cells rather than a border per card: the grid
   is the container, the cards are its cells. */
.wgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(390px,1fr));gap:1px;
  background:var(--color-divider);border:1px solid var(--color-divider);
  border-radius:var(--radius-md);overflow:hidden}
.wcard{background:var(--color-bg);padding:var(--space-4) var(--space-6);display:flex;flex-direction:column;
  gap:var(--space-3);transition:background var(--motion-fast)}
.wcard:hover{background:var(--color-surface)}

/* State is said TWICE — a mark and a word — never by colour alone and never
   by opacity. Fifteen paused watches dimmed to 50% is precisely what made a
   user conclude none of their alerts worked. */
.wstate{display:inline-flex;align-items:center;gap:var(--space-2);font-size:9.5px;letter-spacing:.14em;
  text-transform:uppercase;flex:none}
.wstate::before{font-size:9px;line-height:1}
.wstate.is-live{color:var(--color-accent-2)}
.wstate.is-live::before{content:"\25CF";animation:ct-pulse 2s ease-in-out infinite}
.wstate.is-paused{color:var(--color-neutral-500)}
.wstate.is-paused::before{content:"\25CB"}
.wstate.is-broken{color:var(--color-accent)}
.wstate.is-broken::before{content:"\00D7";font-size:12px}
@keyframes ct-pulse{50%{opacity:.35}}

.wc-h{display:flex;align-items:center;gap:var(--space-3)}
.wc-t{font-size:13px;color:var(--color-text);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.wc-ty{flex:none;font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--color-neutral-200);background:var(--color-neutral-900);border-radius:var(--radius-sm);
  padding:1px var(--space-2)}
/* Same chip vocabulary as the tool trace: a watch's filters and a tool
   call's arguments are the same kind of thing and should read alike. */
.wc-p{display:flex;flex-wrap:wrap;gap:var(--space-2);align-items:baseline;min-height:18px}
.wc-p .chip{font-size:10px;letter-spacing:.06em;color:var(--color-accent-200);
  background:var(--color-accent-900);border-radius:var(--radius-sm);padding:1px var(--space-2)}
.wc-p .kv{display:inline-flex;gap:var(--space-1);align-items:baseline;font-size:10px}
.wc-p .kv i{font-style:normal;color:var(--color-neutral-500)}
.wc-p .kv b{font-weight:400;color:var(--color-neutral-300);font-variant-numeric:tabular-nums}
/* Said out loud, because this is the shape of the bug. */
.wc-any{font-size:11px;color:var(--color-accent)}
/* A watch that cannot work carries the reason in its own card. */
.wc-err{font-size:11px;line-height:1.55;color:var(--color-accent-200);background:var(--color-accent-900);
  border-left:2px solid var(--color-accent);border-radius:var(--radius-sm);
  padding:var(--space-2) var(--space-3)}
.wc-err b{font-weight:400;color:var(--color-accent-100)}

.wc-f{display:flex;align-items:center;gap:var(--space-3);margin-top:auto;padding-top:var(--space-1)}
.wc-n{font-size:10px;color:var(--color-neutral-400);font-variant-numeric:tabular-nums;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.wc-n em{font-style:normal;color:var(--color-neutral-500)}
.wc-a{margin-left:auto;display:flex;gap:var(--space-1);flex:none}
.wc-a button{font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--color-neutral-500);
  padding:var(--space-1) var(--space-2);border-radius:var(--radius-sm);
  transition:color var(--motion-fast),background var(--motion-fast)}
.wc-a button:hover{color:var(--color-accent);background:var(--color-accent-900)}
.wc-a button.on:hover{color:var(--color-accent-2);background:var(--color-accent-2-900)}
.wspark{flex:none;overflow:visible}
.wspark rect.on{fill:var(--color-accent)}
.wspark rect.off{fill:var(--color-neutral-800)}

/* ── fired feed ─────────────────────────────────────────────────────────
   A list, not a card grid: these are scanned down a column, and one shared
   rule per day beats fifty boxes. The left edge carries the scanner's own
   salience so a 54%-of-class stake does not look like a routine 5.1% one. */
.day{display:flex;align-items:center;gap:var(--space-3);margin:var(--space-6) 0 var(--space-2);
  font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--color-neutral-500)}
.day i{flex:1;height:1px;background:var(--color-divider);display:block}
.day b{font-weight:400;color:var(--color-neutral-400);font-variant-numeric:tabular-nums}
.afeed{border:1px solid var(--color-divider);border-radius:var(--radius-md);overflow:hidden}
.arow{display:grid;grid-template-columns:74px 1fr auto auto;gap:var(--space-4);align-items:baseline;
  padding:var(--space-3) var(--space-6) var(--space-3) var(--space-4);
  border-bottom:1px solid var(--color-divider);
  border-left:2px solid color-mix(in srgb,var(--color-accent) calc(var(--sal,.3)*100%),var(--color-neutral-800));
  background:var(--color-surface);transition:background var(--motion-fast)}
.arow:last-child{border-bottom:0}
.arow:hover{background:var(--color-bg)}
.arow .tk{font-size:12px;color:var(--color-accent);overflow:hidden;text-overflow:ellipsis}
.arow .why{font-size:12px;line-height:1.5;color:var(--color-neutral-300)}
.arow .why b{font-weight:400;color:var(--color-text)}
.arow .lb{font-size:10px;letter-spacing:.08em;color:var(--color-neutral-400);white-space:nowrap}
.arow .ask{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--color-neutral-500);
  padding:var(--space-1) var(--space-2);border-radius:var(--radius-sm);white-space:nowrap;
  transition:color var(--motion-fast)}
.arow .ask::before{content:"\2192\00A0";color:var(--color-accent)}
.arow:hover .ask{color:var(--color-accent)}

/* ── watch detail sheet ─────────────────────────────────────────────────
   Ported from `design-system/templates/alerts-detail/`. Docked right rather
   than a centred modal: the grid it came from stays in place behind it, so
   opening a watch reads as going deeper into the page, not leaving it. */
.wsheet{position:fixed;inset:0;z-index:70;display:flex;justify-content:flex-end;
  background:color-mix(in srgb,var(--color-bg) 72%,transparent);animation:fade .16s var(--ease) both}
.wsheet .sheet{width:min(680px,100%);background:var(--color-bg);
  border-left:1px solid var(--color-divider);box-shadow:var(--shadow-lg);
  display:flex;flex-direction:column;overflow:hidden;animation:slidein .22s var(--ease) both}
@keyframes fade{from{opacity:0}to{opacity:1}}
@keyframes slidein{from{transform:translateX(24px);opacity:.4}to{transform:none;opacity:1}}
.sh-h{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-6);
  border-bottom:1px solid var(--color-divider);background:var(--color-surface);flex:none}
.sh-t{font-size:14px;color:var(--color-text);flex:1;min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.sh-x{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--color-neutral-500);
  padding:var(--space-1) var(--space-2);border-radius:var(--radius-sm);
  transition:color var(--motion-fast),background var(--motion-fast)}
.sh-x:hover{color:var(--color-accent);background:var(--color-accent-900)}
.sh-b{flex:1;overflow-y:auto;padding:var(--space-6)}
.sh-sec{display:flex;align-items:center;gap:var(--space-3);margin:var(--space-6) 0 var(--space-3);
  font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--color-neutral-500)}
.sh-sec:first-child{margin-top:0}
.sh-sec i{flex:1;height:1px;background:var(--color-divider);display:block}
.sh-sec u{text-decoration:none;font-size:10px;letter-spacing:.1em;color:var(--color-neutral-400);
  font-variant-numeric:tabular-nums}

/* the number that decides whether the watch is worth keeping */
.perf{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--color-divider);
  border:1px solid var(--color-divider);border-radius:var(--radius-md);overflow:hidden}
.pcell{background:var(--color-bg);padding:var(--space-4) var(--space-6);display:grid;gap:2px}
.pcell .lb{font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--color-neutral-500)}
.pcell .v{font-size:22px;line-height:1.2;font-weight:var(--font-heading-weight);
  font-variant-numeric:tabular-nums;color:var(--color-text)}
.pcell .v.pos{color:var(--color-pos)}
.pcell .v.neg{color:var(--color-neg)}
.pcell .nt{font-size:11px;color:var(--color-neutral-500);font-variant-numeric:tabular-nums}
/* Below the sample floor a median is noise, so it is not dressed as a
   result: no direction ink, and the cell says what it is short of. */
.pcell.thin .v{color:var(--color-neutral-400)}
.pnote{font-size:11px;line-height:1.65;color:var(--color-neutral-400);margin:var(--space-3) 0 0}
.pnote b{font-weight:400;color:var(--color-neutral-200)}

.ev{border:1px solid var(--color-divider);border-radius:var(--radius-md);overflow:hidden}
.ev-h,.erow{display:grid;grid-template-columns:58px 1fr 54px 72px;gap:var(--space-3)}
.ev-h{padding:var(--space-2) var(--space-4);font-size:10px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--color-neutral-500);border-bottom:1px solid var(--color-divider-strong)}
.ev-h .r{text-align:right}
.erow{align-items:baseline;padding:var(--space-3) var(--space-4);
  border-bottom:1px solid var(--color-divider);
  border-left:2px solid color-mix(in srgb,var(--color-accent) calc(var(--sal,.3)*100%),var(--color-neutral-800));
  background:var(--color-surface);transition:background var(--motion-fast)}
.erow:last-child{border-bottom:0}
.erow:hover{background:var(--color-bg)}
.erow .tk{font-size:12px;color:var(--color-accent);overflow:hidden;text-overflow:ellipsis}
.erow .why{font-size:11px;line-height:1.5;color:var(--color-neutral-300)}
.erow .why b{font-weight:400;color:var(--color-text)}
.erow .dt{font-size:10px;color:var(--color-neutral-500);font-variant-numeric:tabular-nums;text-align:right}
.erow .al{font-size:11px;font-variant-numeric:tabular-nums;text-align:right}
.erow .al.pos{color:var(--color-pos)}
.erow .al.neg{color:var(--color-neg)}
.erow .al.none{color:var(--color-neutral-500)}

/* A card is a door now, so it has to look like one. */
.wcard{cursor:pointer}
.wcard:focus-visible{outline:2px solid var(--color-accent);outline-offset:-2px}
.wc-a button{cursor:pointer}

/* ── an alert row, opened ───────────────────────────────────────────────
   The row already carries the whole payload — the accession, the holder,
   the stake. Expanding in place beats a second panel: you keep your place
   in the column you were reading. */
.arow{cursor:pointer}
.arow:focus-visible{outline:2px solid var(--color-accent);outline-offset:-2px}
.amore{grid-column:1/-1;display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));
  gap:var(--space-2) var(--space-6);padding:var(--space-3) 0 var(--space-1);
  border-top:1px solid var(--color-divider);margin-top:var(--space-2);
  animation:rise .18s var(--ease) both}
.amore dl{display:flex;gap:var(--space-2);align-items:baseline;margin:0;font-size:11px}
.amore dt{color:var(--color-neutral-500);flex:none}
.amore dd{margin:0;color:var(--color-neutral-200);font-variant-numeric:tabular-nums;
  overflow:hidden;text-overflow:ellipsis}
.amore a{color:var(--color-accent);border-bottom:1px dotted var(--color-accent-700)}
.amore a:hover{border-bottom-style:solid}
@media(max-width:720px){
  .ev-h,.erow{grid-template-columns:52px 1fr 56px}
  .ev-h .h5,.erow .al{display:none}
}

.wempty{font-size:12px;color:var(--color-neutral-500);padding:var(--space-6);text-align:center;
  border:1px solid var(--color-divider);border-radius:var(--radius-md)}

@media(max-width:900px){
  .health{grid-template-columns:repeat(2,1fr)}
}
@media(max-width:720px){
  .alerts-view{padding:var(--space-4) var(--space-4) 90px}
  .wgrid{grid-template-columns:1fr}
  .arow{grid-template-columns:1fr auto;gap:var(--space-2) var(--space-3)}
  .arow .why{grid-column:1/-1}
  .arow .lb{display:none}
}
.toast{
  position:fixed;bottom:24px;left:50%;transform:translateX(-50%) translateY(14px);z-index:80;
  background:#0e0a0c;border:1px solid var(--line-2);border-radius:6px;padding:10px 17px;font-size:12px;
  color:var(--soft);opacity:0;transition:opacity .2s,transform .2s var(--ease);pointer-events:none
}
.toast.on{opacity:1;transform:translateX(-50%)}
/* A toast that has somewhere to go has to be able to accept the click.
   The base toast is pointer-events:none so a passive notification never
   eats a click meant for the page underneath — but that also swallowed
   every click on the clickable variant, so a `go` toast announced a
   finished run, said "click to open", and then ignored being clicked.
   The JS had set .go and an onclick all along; CSS was discarding it. */
.toast.go{
  pointer-events:auto;cursor:pointer;
  border-color:var(--accent-dim);color:var(--ink)
}
.toast.go::after{content:" ↗";color:var(--accent)}
.toast.go:hover{border-color:var(--accent);background:#150e10}
.warn{border:1px solid rgba(232,176,75,.4);background:rgba(232,176,75,.06);border-radius:6px;padding:10px 13px;font-size:11.5px;color:var(--soft);margin:8px 0}
.err{border:1px solid var(--accent-dim);background:rgba(238,87,65,.07);border-radius:6px;padding:11px 14px;font-size:12px;color:var(--soft);margin:10px 0;display:flex;align-items:center;gap:12px}
.err button{margin-left:auto;font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--accent);flex:none}

@media(max-width:980px){
  .palette-hint{display:none}
}
@media(max-width:860px){
  /* ONE column, not `0 1fr`. Taking the rail out of flow (position:fixed)
     removes it as a grid item, so auto-placement shifts everything left and
     the canvas lands in the zero-width first column — the whole app renders
     as a 60px-wide sliver of unreadable text. */
  .app{grid-template-columns:1fr}
  .rail{position:fixed;top:var(--top);bottom:0;left:0;width:280px;z-index:30;transform:translateX(-101%);transition:transform .2s var(--ease)}
  .app.railopen .rail{transform:none;box-shadow:0 0 60px rgba(0,0,0,.6)}
  .rail-toggle{display:block}
  /* The drawer's width is the drawer's business, and there is no pointer to
     drag the handle with. The hover preview is suppressed in JS for the same
     reason — both are enhancements, and everything they show is reachable by
     opening the row. */
  .rail-grip{display:none}
  .stream,.composer{padding-left:16px;padding-right:16px}
  .starts{grid-template-columns:1fr}
  .hero{padding-top:4vh}
}

/* ── phone ──────────────────────────────────────────────────────────
   Every topbar child is flex:none, so below ~600px the bar could not
   shrink: it stayed at its intrinsic ~860px, and because it is a grid
   item spanning the app it dragged the whole document out to that width.
   The page then scrolled sideways and everything — heading, composer,
   example cards — was cut off at the right edge. Spanish hits this first
   (PREGUNTAR/PANELES/ALERTAS are wider than ASK/DASHBOARDS/ALERTS) but
   English overflows too, just a little later.

   What survives on a phone: the drawer toggle, the mascot, the three
   view tabs, the unread count, settings. What goes: the wordmark (the
   mascot still links home), the connection label (the dot keeps the
   signal), the bell's word, and the exit link. */
@media(max-width:600px){
  .topbar{gap:8px;padding:0 10px}
  .tb-brand span{display:none}
  .tb-tabs{flex:1 1 auto;min-width:0;overflow-x:auto;scrollbar-width:none}
  .tb-tabs::-webkit-scrollbar{display:none}
  .tb-tabs button{padding:7px 9px;letter-spacing:.07em;white-space:nowrap}
  .tb-right{gap:9px;min-width:0}
  .bell{padding:4px 6px}
  .bell span{display:none}
  .bell:has(i[hidden]){display:none}   /* nothing unread → no empty chip */
  /* Clip to the dot: the label is the user's email once signed in. */
  #status{width:6px;overflow:hidden;white-space:nowrap;flex:none}
  .tb-right > a{display:none}
}

/* The sign-in / gate form put input and button on one row with no wrap,
   so at phone widths the button was pushed off the edge. */
@media(max-width:560px){
  .gate{padding:0 18px;margin:8vh auto}
  .gate form{flex-wrap:wrap}
  .gate input{max-width:none;flex:1 1 100%}
  .gate form .send{flex:1 1 100%}
}
@media(prefers-reduced-motion:reduce){
  *{animation-duration:.01ms !important;transition-duration:.01ms !important}
}

/* ── the job tray ────────────────────────────────────────────────────
   Runs continue on the server whether or not anyone is watching. The
   tray is what makes that legible: without it a backgrounded job and a
   lost one look identical. Bottom-LEFT so it never sits under the
   composer or the toast. */
.tray{
  position:fixed;left:16px;bottom:16px;z-index:60;width:min(330px,calc(100vw - 32px));
  background:var(--panel);border:1px solid var(--line-2);border-radius:8px;
  box-shadow:0 18px 46px rgba(0,0,0,.5);overflow:hidden;
  animation:slidein .18s var(--ease);
}
.tray-h{display:flex;align-items:center;justify-content:space-between;
  padding:9px 12px;border-bottom:1px solid var(--line);}
.tray-h .ttl{font-size:11px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
.tray-h .cap{font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
.tray-j{display:flex;align-items:center;gap:9px;padding:9px 12px;font-size:12px;
  border-top:1px solid var(--line);color:var(--dim);cursor:default}
.tray-j:first-of-type{border-top:none}
.tray-j.open{cursor:pointer}
.tray-j:hover{background:rgba(255,255,255,.02)}
.tray-j .br{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tray-j .st{font-size:11px;color:var(--faint);flex:none}
/* The clock. Tabular figures so a ticking second does not shuffle the row
   sideways, and its own column so "running" stays where the eye left it. */
.tray-j .el{
  font-size:11px;color:var(--dim);flex:none;font-variant-numeric:tabular-nums;
  min-width:44px;text-align:right
}
.tray-j.run .el{color:var(--accent)}
.tray-j .dot{width:6px;height:6px;border-radius:50%;background:var(--faint);flex:none}
.tray-j.run .dot{background:var(--accent);animation:pulse 1.1s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.25}}

/* ── compose a dashboard by describing it ───────────────────────────── */
.compose-bar{display:flex;align-items:center;gap:10px;margin:0 0 18px;
  padding:11px 14px;border:1px solid var(--line-2);border-radius:8px;
  background:var(--panel);transition:border-color .15s var(--ease)}
.compose-bar:focus-within{border-color:rgba(238,87,65,.5)}
.compose-bar .caret{color:var(--accent);font-weight:700;flex:none}
.compose-bar input{flex:1;font-size:13px;color:var(--ink);background:none;border:none;outline:none}
.compose-bar input::placeholder{color:var(--faint)}
.compose-bar button{flex:none;font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  padding:6px 12px;border:1px solid var(--line-2);border-radius:5px;color:var(--dim);
  background:none;cursor:pointer}
.compose-bar button:hover{border-color:var(--accent);color:var(--accent)}

.building{padding:22px 18px;border:1px dashed var(--line-2);border-radius:8px}
.building .lbl{font-size:12px;color:var(--dim);margin-bottom:14px}

/* The thinking indicator. A static panel during the 15-20s before the
   first tool call reads as a hang — the dots are the difference between
   "working" and "broken". */
.bthink{display:flex;align-items:center;gap:6px;margin-bottom:12px}
.bthink i{width:5px;height:5px;border-radius:50%;background:var(--accent);
  animation:bpulse 1.05s infinite}
.bthink i:nth-child(2){animation-delay:.16s}
.bthink i:nth-child(3){animation-delay:.32s}
.bthink .bstat{margin-left:6px;font-size:12px;color:var(--dim)}
@keyframes bpulse{0%,60%,100%{opacity:.22;transform:scale(.82)}30%{opacity:1;transform:scale(1)}}

/* Calls COUNTED, not listed. A board that reads fourteen ratings printed
   `get_rating` fourteen times down the page. */
.btools{display:flex;flex-wrap:wrap;gap:5px}
.bchip{display:inline-flex;align-items:baseline;gap:4px;padding:2px 7px;
  border:1px solid var(--line);border-radius:4px;font-size:11px;color:var(--faint);
  animation:fade .2s var(--ease)}
.bchip .k{color:var(--accent);font-variant-numeric:tabular-nums}

.bwidgets{margin-top:12px;display:flex;flex-direction:column;gap:4px}
.bw{display:flex;align-items:baseline;gap:8px;font-size:12px;color:var(--dim);
  animation:fade .22s var(--ease)}
.bw .ok{color:var(--accent);font-weight:700}
.tray-h .x{background:none;border:none;color:var(--faint);font-size:15px;line-height:1;
  cursor:pointer;padding:0 0 0 10px}
.tray-h .x:hover{color:var(--ink)}

/* A toast that can be opened says so, and reads as a control rather than
   a status line. */
#toast.go{cursor:pointer;border-color:rgba(238,87,65,.45)}
#toast.go:hover{border-color:var(--accent);color:var(--ink)}

/* Treemap / waterfall / bubble labels. Small and quiet: the shape is the
   message, the text is only there to say which shape you are looking at. */
.tm-l{fill:var(--ink);font-size:11px;font-weight:600}
.tm-p{fill:var(--dim);font-size:10px}
.wf-v{fill:var(--dim);font-size:10px;font-variant-numeric:tabular-nums}
.wf-link{stroke:var(--line-2);stroke-width:1;stroke-dasharray:2 3}
.bub-l{fill:var(--dim);font-size:10px}
.bw.fail{color:var(--faint)}
.bw.fail .ok{color:var(--faint)}
.tray-j .retry{background:none;border:1px solid var(--line-2);border-radius:4px;
  color:var(--faint);font-size:10px;letter-spacing:.06em;text-transform:uppercase;
  padding:2px 7px;cursor:pointer;flex:none}
.tray-j .retry:hover{border-color:var(--accent);color:var(--accent)}
.tray-j .retry:disabled{opacity:.4;cursor:default}

/* ── signed-out demo ─────────────────────────────────────────────────────
   The ask for an email, shown under a finished answer instead of in front
   of one. Styled as part of the answer rather than as an interruption. */
.demo-cta{
  margin:22px 0 6px;padding:18px 20px;border:1px solid var(--line-2);border-radius:8px;
  background:linear-gradient(180deg,rgba(238,87,65,.05),transparent)
}
.demo-cta-h{font-size:14px;color:var(--ink);font-weight:500;margin-bottom:6px}
.demo-cta-p{font-size:13px;color:var(--dim);line-height:1.6;margin-bottom:14px;max-width:56ch}
.demo-cta-f{display:flex;gap:8px;flex-wrap:wrap}
.demo-cta-f input{
  flex:1 1 240px;min-width:0;border:1px solid var(--line-2);border-radius:6px;
  padding:10px 13px;font-size:13px;background:var(--bg);color:var(--ink)
}
.demo-cta-f input:focus{border-color:var(--accent)}
.demo-cta-msg{margin-top:10px;font-size:12px;color:var(--dim);min-height:16px}
/* Why the demo stopped. This used to render in the same quiet grey as the
   form's status line, at the bottom of a card whose headline claimed a
   question had been answered — so the one sentence explaining an empty
   screen was the least readable thing on it. */
.demo-cta-why{
  margin-top:12px;padding-left:10px;border-left:2px solid var(--accent);
  font-size:12.5px;color:var(--soft);line-height:1.5
}
@media(max-width:560px){ .demo-cta-f .send{flex:1 1 100%} }

/* ── NEWS ─────────────────────────────────────────────────────────────
   The tape. Two decisions carry this view.

   One: the category badges are NOT colour-coded. There are nineteen of
   them, and this file's own kind-token note is the reason — only four
   non-neutral tokens clear AA as text here, so nineteen hues would mean
   fifteen near-identical greys pretending to be a taxonomy. The badge
   earns its keep as a small uppercase label; the only coloured thing on a
   card is the ticker, which is the one field a reader scans for.

   Two: every card ends in an action, not just a link out. Reading a
   headline is the cheap half — the point of the tape living inside the
   terminal rather than in an RSS reader is that the question is one click
   away with the story already loaded. */
.news-view{max-width:1120px;margin:0 auto;padding:var(--space-8) var(--space-6) 90px}
.nv-h h2{font-size:20px;font-weight:var(--font-heading-weight);color:var(--color-text);margin:0}
.nv-h p{font-size:13px;color:var(--color-neutral-400);line-height:1.7;
  margin:var(--space-2) 0 0;max-width:64ch}

/* ── filter bar ─────────────────────────────────────────────────────── */
.nbar{display:flex;gap:var(--space-3);align-items:center;flex-wrap:wrap;margin:var(--space-6) 0 var(--space-4)}
.nsearch{display:flex;gap:var(--space-3);align-items:center;flex:1 1 320px;min-width:0;
  border:1px solid var(--color-divider);border-radius:var(--radius-md);
  padding:var(--space-3) var(--space-4);background:var(--color-surface);
  transition:border-color var(--motion-fast)}
.nsearch:focus-within{border-color:var(--color-accent)}
.nsearch .ac{color:var(--color-accent);font-weight:700;flex:none}
.nsearch input{flex:1;min-width:0;font-size:13px;color:var(--color-text)}
.nsearch input::placeholder{color:var(--faint)}
.nbar select{flex:none;font-size:12px;color:var(--color-neutral-300);background:var(--color-surface);
  border:1px solid var(--color-divider);border-radius:var(--radius-sm);
  padding:var(--space-2) var(--space-3);cursor:pointer}
.nsynth{flex:none;display:flex;align-items:center;gap:var(--space-2);font-size:11px;
  color:var(--color-neutral-500);cursor:pointer;user-select:none}
.nsynth input{accent-color:var(--color-accent);cursor:pointer}

.nchips{display:flex;gap:var(--space-2);flex-wrap:wrap;margin-bottom:var(--space-4)}
/* ── category families ───────────────────────────────────────────────
   Nineteen categories, five colours. Colouring each one separately was the
   obvious move and the wrong one: only four non-neutral tokens are validated
   as TEXT on this surface (see the kind-token note above), so nineteen hues
   would be fifteen near-identical greys posing as a taxonomy.

   Grouping answers what a reader actually wants at a glance — what KIND of
   event is this — and every family reuses a hue the system already blesses.
   `--h` is set once per family and both the chip and the badge read it, so
   the control you click and the label you then see are the same object.

   Text sits ON a 16% tint of its own hue. Measured against #060405 rather
   than assumed: coral 5.11:1, neutral 5.00:1, info 6.38:1, amber 7.51:1,
   green 8.93:1 — all clear of the 4.5:1 AA text bar, with the two tightest
   checked because a tint moves the background toward the text colour. */
.f-ret{--h:var(--color-accent-2)}
.f-own{--h:var(--color-warn)}
.f-cat{--h:var(--color-info)}
.f-risk{--h:var(--color-accent)}
.f-ctx{--h:var(--color-neutral-400)}
.f-all{--h:var(--color-neutral-300)}

.nchip{font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:color-mix(in srgb,var(--h) 72%,var(--color-neutral-500));
  border:1px solid transparent;border-radius:999px;padding:3px var(--space-3);
  transition:color var(--motion-fast),background var(--motion-fast)}
.nchip:hover{color:var(--h);background:color-mix(in srgb,var(--h) 10%,transparent)}
/* Active state is the filled one. An outline-only "on" was indistinguishable
   from hover, so you could not tell which filter was actually applied. */
.nchip.on{color:var(--h);background:color-mix(in srgb,var(--h) 16%,transparent);
  border-color:color-mix(in srgb,var(--h) 45%,transparent)}
/* The count is context, not the label — same size would make the chip read
   as two words and the row stops scanning as a set of categories. */
.nchip i{font-style:normal;font-size:10px;color:var(--faint);font-variant-numeric:tabular-nums}
.nchip.on i{color:var(--h);opacity:.75}

.npill{display:inline-flex;align-items:center;gap:var(--space-2);font-size:11px;
  color:var(--color-accent);border:1px solid var(--color-accent);border-radius:999px;
  padding:2px var(--space-3);margin-bottom:var(--space-4)}
.npill button{color:inherit;font-size:13px;line-height:1;opacity:.7}
.npill button:hover{opacity:1}

/* ── the tape ───────────────────────────────────────────────────────── */
.nday{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--color-neutral-500);
  margin:var(--space-6) 0 var(--space-3);padding-bottom:var(--space-2);
  border-bottom:1px solid var(--color-divider)}
.nday:first-child{margin-top:0}

/* A row, not a box. The tape is read by scanning down it, so the cards share
   one hairline rule rather than each carrying its own border and shadow —
   sixty bordered boxes read as sixty things to decide about.

   The left rule appears on hover only, in the row's own family colour: it
   confirms which row the pointer owns and repeats the category without
   spending permanent ink on it. */
.ncard{position:relative;padding:var(--space-4) 0 var(--space-4) var(--space-4);
  border-bottom:1px solid var(--color-divider);
  transition:background var(--motion-fast)}
.ncard::before{content:'';position:absolute;left:0;top:0;bottom:0;width:2px;
  background:var(--h);opacity:0;transition:opacity var(--motion-fast)}
.ncard:hover{background:color-mix(in srgb,var(--h) 4%,transparent)}
.ncard:hover::before{opacity:.85}
.nmeta{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap;margin-bottom:var(--space-2)}
.ncat{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--h);
  background:color-mix(in srgb,var(--h) 16%,transparent);
  border-radius:var(--radius-sm);padding:2px var(--space-2);flex:none}
.ntick{font-size:11px;font-weight:500;color:var(--color-accent);letter-spacing:.04em}
.ntick:hover{text-decoration:underline}
.nsrc,.nwhen{font-size:11px;color:var(--faint)}
.nwhen{margin-left:auto;font-variant-numeric:tabular-nums}
.nhead{font-size:14.5px;line-height:1.45;font-weight:500;color:var(--color-text);margin:0;
  letter-spacing:-.005em;max-width:88ch}
.nsum{font-size:12.5px;line-height:1.65;color:var(--color-neutral-400);margin:var(--space-2) 0 0;
  /* Two lines: enough to tell a dividend from a special dividend, not so
     much that the tape stops being scannable. */
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.nact{display:flex;align-items:center;gap:var(--space-4);margin-top:var(--space-3)}
/* Quiet until the row is under the pointer. Sixty always-lit buttons turned
   a reading surface into a wall of calls to action; the affordance still has
   to be there when you look for it, so it is dimmed, not hidden — hiding it
   would also put it out of reach of keyboard and touch. */
.nask{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);
  border:1px solid var(--color-divider);border-radius:var(--radius-sm);
  padding:var(--space-2) var(--space-3);
  transition:color var(--motion-fast),border-color var(--motion-fast)}
.ncard:hover .nask{color:var(--color-neutral-300)}
.nask:hover,.nask:focus-visible{color:var(--color-accent);border-color:var(--color-accent)}
.nsrc-link{font-size:11px;color:var(--faint);text-decoration:none}
.nsrc-link:hover{color:var(--color-text);text-decoration:underline}

.nempty{padding:var(--space-8) 0;font-size:13px;color:var(--color-neutral-500);text-align:center}

@media(max-width:560px){
  .nwhen{margin-left:0}
  .nbar select,.nsynth{flex:1 1 auto}
}

/* ── reaction + sort ──────────────────────────────────────────────────
   The move across the news, and the control that ranks by it.

   Up/down uses the app's existing --color-pos / --color-neg rather than a
   new pair, so a green number means the same thing here as on every chart.
   The arrow is not decoration: it carries the sign for anyone who cannot
   separate the two hues, which is the whole reason the value is not left to
   colour alone. */
.nmove{font-size:11px;font-variant-numeric:tabular-nums;letter-spacing:.02em;
  padding:1px var(--space-2);border-radius:var(--radius-sm);white-space:nowrap}
.nmove.up{color:var(--color-pos);background:color-mix(in srgb,var(--color-pos) 12%,transparent)}
.nmove.down{color:var(--color-neg);background:color-mix(in srgb,var(--color-neg) 12%,transparent)}

/* A segmented control, not two buttons: they are one choice with two states,
   and spacing them apart made "Latest" read as an action you could also
   click while "Notable" was on. */
.nsort{display:inline-flex;flex:none;border:1px solid var(--color-divider);
  border-radius:var(--radius-sm);overflow:hidden}
.nsort button{font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--color-neutral-500);padding:var(--space-2) var(--space-3);
  transition:color var(--motion-fast),background var(--motion-fast)}
.nsort button:hover{color:var(--color-text)}
.nsort button.on{color:var(--color-accent);
  background:color-mix(in srgb,var(--color-accent) 14%,transparent)}

/* ── the first-run tour ─────────────────────────────────────────────
   A ring over one real element and a card beside it. The dimming is the
   RING's box-shadow, not a panel with a hole punched in it: one element,
   one rectangle to position, and the cutout follows the border-radius for
   free. The mask underneath is transparent and exists only to catch the
   clicks that would otherwise land on a live Run button while the tour is
   explaining what it does. */
.tb-help{
  flex:none;width:18px;height:18px;border-radius:50%;
  border:1px solid var(--line-2);color:var(--dim);
  font-size:11px;line-height:1;display:flex;align-items:center;justify-content:center;
  transition:color .15s,border-color .15s
}
.tb-help:hover{color:var(--accent);border-color:var(--accent)}

.tour{position:fixed;inset:0;z-index:200}
.tour-mask{position:absolute;inset:0}
/* No ring on the opening and closing steps — nothing to point at, so the
   dim moves onto the mask and the card centres. */
.tour-mask.dim{background:rgba(4,3,3,.80)}
.tour-ring{
  position:fixed;z-index:1;pointer-events:none;
  border:1px solid var(--accent);border-radius:6px;
  box-shadow:0 0 0 100vmax rgba(4,3,3,.80),
             0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
  transition:left .2s var(--ease),top .2s var(--ease),
             width .2s var(--ease),height .2s var(--ease)
}
.tour-card{
  position:fixed;z-index:2;width:min(345px,calc(100vw - 24px));
  border:1px solid var(--line-2);border-radius:10px;background:var(--panel);
  padding:13px 15px 11px;box-shadow:0 24px 60px rgba(0,0,0,.6);
  animation:pop .16s var(--ease)
}
.tour-card.mid{
  left:50%;top:50%;transform:translate(-50%,-50%);
  width:min(430px,calc(100vw - 24px));padding:20px 22px 16px
}
.tour-top{display:flex;align-items:center;gap:10px;margin-bottom:9px}
.tour-n{font-size:10px;letter-spacing:.16em;color:var(--faint)}
.tour-skip{margin-left:auto;font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint);transition:color .15s}
.tour-skip:hover{color:var(--ink)}
.tour-card h3{font-size:14px;font-weight:700;color:var(--ink);margin-bottom:7px;letter-spacing:.01em}
.tour-card.mid h3{font-size:17px}
.tour-card p{font-size:12.5px;line-height:1.55;color:var(--soft)}
.tour-foot{display:flex;align-items:center;gap:8px;margin-top:14px}
/* Squares, not dots: this whole system draws with box-drawing characters
   and hairlines, and a row of circles is the one shape it never uses. */
.tour-dots{display:flex;gap:4px;flex:1;align-items:center}
.tour-dot{width:5px;height:5px;background:var(--line-2)}
.tour-dot.on{background:var(--accent)}
.tour-back,.tour-next{
  font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  padding:6px 12px;border-radius:5px;border:1px solid var(--line-2);
  color:var(--dim);transition:color .15s,border-color .15s,background .15s
}
.tour-back:hover{color:var(--ink);border-color:var(--line-2)}
.tour-next{color:var(--accent);border-color:var(--accent)}
.tour-next:hover{background:rgba(238,87,65,.12)}
@media(max-width:560px){
  /* Narrower and shorter on a phone so the card and the element it is
     describing can both be on screen — the placement itself stays in JS,
     which is the only thing that knows where the ring ended up. */
  .tour-card{width:calc(100vw - 24px);padding:12px 13px 10px}
  .tour-card p{font-size:12px;line-height:1.5}
}

/* ── SIGNALS ──────────────────────────────────────────────────────────
   The daily funnel, brought in from the landing page. Same snapshot, but
   the landing's version was a shop window: nine sections with marketing
   headlines, two of them demoing a feature rather than reporting anything.
   In here it is a reading of the tape, so it is built out of the same
   section rule + row the watchlist view uses, and every ticker is a real
   chip — hover for the card, click to run the workup. That is the whole
   reason it belongs inside: on the landing those tickers were text. */
.sig-view{max-width:1120px;margin:0 auto;padding:var(--space-8) var(--space-6) 90px}
.sv-h h2{font-size:20px;font-weight:var(--font-heading-weight);color:var(--color-text);margin:0}
.sv-h p{font-size:13px;color:var(--color-neutral-400);line-height:1.7;
  margin:var(--space-2) 0 0;max-width:64ch}
.sg-asof{margin-top:var(--space-4);font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--faint);font-variant-numeric:tabular-nums}
.sg-note{font-size:11px;color:var(--faint);margin:calc(var(--space-2) * -1) 0 var(--space-3)}
.sg-box{border:1px solid var(--line);border-radius:var(--radius-md);overflow:hidden}
.sg-row{
  display:flex;align-items:center;gap:var(--space-4);
  padding:9px var(--space-4);border-bottom:1px solid var(--line);min-width:0;
}
.sg-row:last-child{border-bottom:0}
.sg-row:hover{background:var(--panel-2)}
.sg-l{flex:0 0 232px;display:flex;align-items:baseline;gap:8px;min-width:0}
.sg-nm{font-size:12px;color:var(--soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sg-not{color:var(--faint);font-size:11px}
/* The middle column is the sentence that explains the number on the right,
   and it is the first thing to give up its width. */
.sg-m{flex:1;min-width:0;font-size:11.5px;color:var(--dim);line-height:1.5;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sg-r{flex:none;display:flex;align-items:center;gap:var(--space-3)}
.sg-sp{display:block;width:76px;height:22px}
.sg-sp svg{width:100%;height:100%;display:block}
.sg-v{font-size:12.5px;font-variant-numeric:tabular-nums;color:var(--soft);
  min-width:64px;text-align:right}
.sg-v.up{color:var(--green)}
.sg-v.down{color:var(--accent)}
.sg-j{font-size:10px;color:var(--faint);font-variant-numeric:tabular-nums;min-width:66px;text-align:right}

@media (max-width:760px){
  /* The name and the number are the row; the explanation and the chart are
     what a phone cannot afford. */
  .sg-m,.sg-sp{display:none}
  .sg-l{flex:1}
}
/* IV already at the top of its own year — the one row in this section that
   is a finding rather than a reading. */
.sg-m.hot{color:var(--accent)}
