/* ════════════════════════════════════════════════════════════════════
   thesis.trade — design tokens (shared, design-side)
   ────────────────────────────────────────────────────────────────────
   Foundation sweep 2026-06-09. Supersedes the per-file :root blocks
   duplicated across the canonical mockups. Values are the LOCKED system
   (sans / navy / amber) — identical to repo redesign.css where they
   overlap. New in this sweep (additive, no relitigated values):
     · display ramp        (--d1/--d2/--d3 + affix rule)
     · chip system         (.chip — 2 sizes × state/content variants)
     · per-surface widths  (--content-narrow / -decision / full-bleed)
     · table ink hierarchy (.t-lead / .t-data / .t-meta)
     · mono/prose rule     (mono = data tokens, sans = sentences)
   Iteration-2 streaming tokens (flash / connection / stale) carried
   over unchanged from iteration_2_dev_reading.md.
   ════════════════════════════════════════════════════════════════════ */

:root {
  /* ── Surfaces / ink ─────────────────────────────── */
  --bg-page:    #ebedf0;
  --bg:         #f5f6f8;
  --surface:    #ffffff;
  --surface-2:  #fafbfc;
  --border:     #e4e7ec;
  --border-2:   #d0d5dd;
  --ink-1:      #0f1729;
  --ink-2:      #475467;
  --ink-3:      #98a2b3;
  --ink-4:      #cbd2dc;

  /* ── Brand / state ──────────────────────────────── */
  --blue-50:    #eef2ff;
  --blue-100:   #e0e7ff;
  --blue-300:   #8ea2f2;   /* on-navy accent — light tint of blue-500, kept bluer than a pastel so it reads as brand blue (not lavender) on --blue-700. AA ≥5.7:1 on navy. */
  --blue-500:   #5468e3;
  --blue-600:   #4456d1;
  --blue-700:   #1a2761;   /* deep navy ink — candidate surface (Direction B) */

  /* Semantic accent aliases — role, not shade. Use these on their surface. */
  --accent-on-navy:  var(--blue-300);  /* headline accents + links on navy/plate/band surfaces */
  --accent-on-light: var(--blue-600);  /* links on white/card surfaces */

  --red-500:    #e54a4a;
  --red-600:    #c93939;
  --red-50:     #fef2f2;
  --neg-500:    #c93939;   /* directional-negative (puts) — semantically ≠ red-500 */

  --pos-500:    #2c8a52;
  --pos-600:    #246d42;
  --pos-50:     #effaf0;

  --warn-500:   #c97e22;
  --warn-50:    #fef7eb;
  --warn-100:   #f3d8a0;
  --warn-ink:   #6e4214;

  /* ── Type ───────────────────────────────────────── */
  --font-sans: 'Pangram Sans', 'DM Sans', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-mono: 'Berkeley Mono', 'JetBrains Mono', 'DM Mono', ui-monospace, 'SF Mono', monospace;
  /* --font-serif intentionally absent. */
  /* Number face (2026-07): figures dropped mono — the slab terminals read
     as too aggressive. .num now uses the SANS with tabular-nums. Mono is
     retained for tickers, chip STATE labels, table headers, code tokens.
     Reverse by pointing --font-num back at --font-mono. */
  --font-num:  var(--font-sans);

  /* Data-surface side gutter (operator 2026-07-16: full-width pages
     "too wide... both left and right side need to come in a little
     bit"). One number moves Research + Discovery + Portfolio. */
  /* DESKTOP VALUE — unchanged. See the phone override below the :root
     block; this declaration is the only one that applies at ≥600px. */
  --page-gutter: 66px;   /* 30 -> 48 -> 66: two operator +18 passes (2026-07-16) */

  /* ── Display ramp (NEW) ─────────────────────────── */
  /* Every hero number snaps to a stop. Mono, weight 500, tabular.    */
  --d1-size: 72px;  --d1-track: -0.028em;   /* one per screen, max    */
  --d2-size: 44px;  --d2-track: -0.025em;   /* section hero           */
  --d3-size: 28px;  --d3-track: -0.02em;    /* card hero / stat       */
  /* Affix rule: $ % ≈ d render at 55% size, --ink-3, weight 500,
     baseline-aligned. Never full-size, never full-ink.               */

  /* ── Per-surface content widths (NEW) ───────────── */
  /* Replaces the single 680px cap. Each screen declares ONE.         */
  --content-narrow:   680px;    /* focus surfaces: Thesis, Settings, TC mobile-flow */
  --content-decision: 1140px;   /* decision surfaces: Picks comparison */
  /* full-bleed (Portfolio blotter, Market hub): no cap — use width as
     capability. No token; the screen simply doesn't set a max-width. */

  /* Brand mark (Design handoff 2026-07-14): the "thesis." period — ONE
     value everywhere the wordmark renders. Deliberately NOT --pos-500
     (the LIVE-signal green, deeper): the brand dot and a live-state
     signal must never read as the same thing. If --pos-500 ever
     changes, this must NOT follow it. */
  --brand-dot: #35c46e;

  /* ── Chrome ─────────────────────────────────────── */
  /* 280px (operator 2026-07-14; handoff + old chrome were both 240px —
     the rail read too narrow on the dark ground). One token, every
     converted screen follows. */
  --sidebar-width:      280px;
  --sidebar-width-rail: 64px;
  --topbar-h:           56px;
  --topbar-h-mobile:    48px;

  /* ── App shell: rail (dark chrome) ──────────────── */
  /* The dark navigation rail. One palette, consumed by app-shell.css. */
  --rail-bg:      #121a2f;
  --rail-line:    #232c44;
  --rail-ink:     #c4cbda;   /* hover / resting label */
  --rail-ink-2:   #7b8598;   /* dim label */
  --rail-active:  #1d2740;   /* active item fill */
  --rail-accent:  var(--blue-300);  /* wordmark period, active hints */

  /* ── App shell: focus plane + field ─────────────── */
  /* The clean white working plane (Trade/Research/Settings focus surfaces)
     and the floating composer field that lifts off it. */
  --plane:            #ffffff;
  --field-border:     #dfe3e9;
  --field-shadow:     0 1px 2px rgba(16,24,40,.04), 0 4px 16px rgba(16,24,40,.05);
  --field-focus-ring: 0 0 0 3px rgba(50,71,189,.12);

  /* ── Radii / shadows ────────────────────────────── */
  --r-card:  14px;
  --r-inner: 10px;
  --r-pill:  999px;
  --shadow-card:     0 1px 2px rgba(15,23,41,.04);
  --shadow-elevated: 0 12px 30px rgba(15,23,41,.10);

  /* ── Streaming (iteration-2, unchanged) ─────────── */
  --flash-up:   rgba(36, 109, 66, 0.18);
  --flash-down: rgba(229, 74, 74, 0.18);

  /* ── Heatmap tint scale (Market, unchanged) ─────── */
  --p-soft-bg:#e6f4ec; --p-soft-fg:#246d42;
  --p-mid-bg:#c4e0cf;  --p-mid-fg:#1c5532;
  --p-deep-bg:#246d42; --p-deep-fg:#effaf0;
  --n-soft-bg:#fae9e9; --n-soft-fg:#b32f2f;
  --n-mid-bg:#efc4c4;  --n-mid-fg:#802222;
  --n-deep-bg:#b32f2f; --n-deep-fg:#fef2f2;
  --flat-bg:#f1f3f7;   --flat-fg:#667085;
}

/* ════════════════════════════════════════════════════════════════════
   PHONE GUTTER (2026-08-25)
   --------------------------------------------------------------------
   --page-gutter was declared ONCE, on bare :root, with no media override
   anywhere in the repo — so the 66px arrived at by two desktop +18 passes
   also applied at 375px, where it spends 132px of the screen, MORE THAN A
   THIRD of the viewport, on margin. Every consumer inherited it:
   portfolio_kit.css `.pf-scroll`, research_discovery.css `.dsc-scroll`,
   research_home.css `.rh-ask`, research_workspace.css `.rw-inner`, and
   redesign/discovery.html `.dsc-wrap`. One number moves all five, which
   is exactly why it is a token — and exactly why the missing media
   override was invisible.

   DESKTOP IS UNTOUCHED. The 66px declaration above is the only one that
   applies at ≥600px; this block adds a second declaration below it, it
   does not modify the first.

   18px matches the app shell's own mobile `.wrap` gutter (app-shell.css,
   <860px block), so a full-bleed data surface and a narrow focus surface
   line up on a phone instead of stepping in by different amounts.

   600px is the codebase's existing phone threshold. The LIVE precedent is
   app-shell.css's phone ergonomics block, which uses the same number.
   (redesign.css `.phone` uses it too and is where the number came from,
   but no template loads that file — provenance only, never evidence that
   anything in it runs.) It is a literal in both live files because a
   media query cannot read a custom property.
   ════════════════════════════════════════════════════════════════════ */
@media (max-width: 599px){
  :root{ --page-gutter: 18px; }
}

/* ════════════════════════════════════════════════════════════════════
   NUMBERS — the "relaxed" treatment + display ramp
   Sans + tabular-nums, weight 500 (never 600/700 at display sizes),
   gently negative tracking. .num is the base; .d1/.d2/.d3 are the ramp.
   (2026-07: face is --font-num, formerly --font-mono — see Type section.)
   ════════════════════════════════════════════════════════════════════ */
.num {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--ink-1);
}
.d1 { font-size: var(--d1-size); letter-spacing: var(--d1-track); line-height: 1; }
.d2 { font-size: var(--d2-size); letter-spacing: var(--d2-track); line-height: 1.05; }
.d3 { font-size: var(--d3-size); letter-spacing: var(--d3-track); line-height: 1.1; }
.num .affix { font-size: 55%; color: var(--ink-3); font-weight: 500; }
.num.pos { color: var(--pos-600); }
.num.neg { color: var(--red-600); }

/* ════════════════════════════════════════════════════════════════════
   CHIP SYSTEM — one vocabulary, two sizes, two grammars
   Grammar 1 · STATE  — uppercase mono 10px/0.08em. System + data
                        tokens: LIVE, PAPER, HALT, PUT, CALL, HIGH,
                        MACRO, severity, regime.
   Grammar 2 · LABEL  — sentence-case sans 12px/500. Qualitative
                        content: High conviction, Alternative, Filled.
   Sizes: .chip (20px meta — inline in tables/cards)
          .chip.md (26px control — topbar, filters)
   Tint pairs are fixed: never invent a new bg/fg combination.
   ════════════════════════════════════════════════════════════════════ */
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  height: 20px; padding: 0 8px;
  border-radius: var(--r-pill);
  font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  background: var(--bg-page); color: var(--ink-2);
  border: 1px solid var(--border-2);
  white-space: nowrap;
}
.chip.md { height: 26px; padding: 0 10px; font-size: 11px; }
.chip.label {
  font-family: var(--font-sans); font-size: 12px; font-weight: 500;
  letter-spacing: 0; text-transform: none;
}
.chip.md.label { font-size: 13px; }
.chip .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-3); flex: 0 0 auto; }

/* State tints — fixed pairs */
.chip.navy   { background: var(--blue-50);  color: var(--blue-700); border-color: var(--blue-100); }
.chip.pos    { background: var(--pos-50);   color: var(--pos-600);  border-color: #cfe8d8; }
.chip.neg    { background: var(--red-50);   color: var(--red-600);  border-color: #f3d4d4; }
.chip.warn   { background: var(--warn-50);  color: var(--warn-ink); border-color: var(--warn-100); }
.chip.solid  { background: var(--blue-700); color: #fff;            border-color: var(--blue-700); }
.chip.live .dot         { background: var(--pos-500); }
.chip.reconnecting .dot { background: var(--warn-500); }
.chip.offline .dot      { background: var(--red-500); }
.chip.halt {
  background: var(--surface); color: var(--red-600); border-color: #f3d4d4;
}
/* PAPER — simulated money. A FULL amber chip, deliberately unmistakable from
   LIVE (which is a neutral chip + green dot). Never a subtle shade apart:
   PAPER reading as LIVE is the R3 defect. Fixed tint pair (warn family). */
.chip.paper {
  background: var(--warn-50); color: var(--warn-ink); border-color: var(--warn-100);
}
.chip.paper .dot { background: var(--warn-500); }

/* ════════════════════════════════════════════════════════════════════
   TABLE INK HIERARCHY — one column owns the ink
   In any table/grid, exactly one column (the scan column) renders
   .t-lead. Everything else steps down. No table where every cell
   carries equal weight.
   ════════════════════════════════════════════════════════════════════ */
.t-lead { color: var(--ink-1); font-weight: 600; }   /* the scan column: P&L, est. reward, % move */
.t-data { color: var(--ink-2); font-weight: 500; }   /* supporting numerics */
.t-meta { color: var(--ink-3); font-weight: 400; }   /* timestamps, qualifiers */

/* ════════════════════════════════════════════════════════════════════
   FRESHNESS (iteration-2, unchanged)
   ════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: no-preference) {
  @keyframes flash-fade { 0% { background-color: var(--flash-color); } 100% { background-color: transparent; } }
  .flash-up, .flash-down { animation: flash-fade 600ms ease-out; }
  @keyframes connection-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
  .chip.reconnecting .dot { animation: connection-pulse 1.2s ease-in-out infinite; }
}
.flash-up   { --flash-color: var(--flash-up); }
.flash-down { --flash-color: var(--flash-down); }
/* Staleness is a data-honesty surface: a stale price must never render as a
   fresh green/red number. It wins by SPECIFICITY + source order, not
   !important — so nothing ends up in an !important war where the honest
   state is the one that loses. .num.pos.stale (0-3-0) beats .num.pos (0-2-0);
   the bare .num.stale (0-2-0), declared after .num.pos, wins by order. */
.stale { color: var(--ink-3); }
.num.stale, .num.pos.stale, .num.neg.stale { color: var(--ink-3); }
.stale::after { content: '↻'; display: inline-block; margin-left: 4px; font-size: 0.85em; color: var(--ink-3); transform: translateY(-0.5px); }
