/* ─────────────────────────────────────────────────────────────────────────────
   GENERATED FILE — DO NOT EDIT.

   Source:    frontend/src/index.css  (the :root block)
   Regenerate: python3 scripts/build-tokens.py

   Edit the source, run the command, commit both. A value changed here is a
   value that disappears the next time anyone runs the script.
───────────────────────────────────────────────────────────────────────────── */

:root {
  /* ══ 1 · BRAND ═══════════════════════════════════════════════════════════
     Two brand colours, full stop: near-black and orange. Everything else in
     this file is either a neutral derived from the black, or a functional
     signal colour. The accent is locked to ONE triplet (255,140,0) so the
     system can never drift into a dozen near-identical oranges again.

     Orange is a scarce resource. It is allowed on exactly four things:
       · the primary call-to-action,
       · the active / selected state,
       · the single most important number on screen (P&L, primary metric),
       · the focus ring.
     It is NOT a default border, not a card background, not a divider,
     not a hover tint, not a decorative glow. */
  --accent:            #FF8C00;  /* CTA fill, active state, key figure       */
  --accent-hover:      #FFA033;  /* hover/lit state of anything accent-filled */
  --accent-wash:       rgba(255, 140, 0, 0.045); /* a full-bleed tinted row   */
  --accent-subtle-bg:  rgba(255, 140, 0, 0.10);  /* selected chip / row fill  */
  --accent-bg-hover:   rgba(255, 140, 0, 0.18);  /* the same object, hovered  */
  --accent-border:     rgba(255, 140, 0, 0.32);  /* edge of a selected object */
  --accent-focus-ring: rgba(255, 140, 0, 0.70);  /* ≥3.9:1 on every surface   */

  /* ══ 2 · SURFACES ════════════════════════════════════════════════════════
     Neutral, marginally cool ramp — no orange tint anywhere. Elevation is
     read from luminance alone: L* 2.8 → 4.8 → 7.4 → 11.0 → 14.9, an even
     perceptual staircase. Pick by role, never by "how dark do I want it". */
  --bg:            #0A0A0B;  /* page canvas (desaturated black, not #000)    */
  --surface-1:     #101013;  /* raised: outer card, sticky bar, table header */
  --surface-2:     #16161A;  /* default card / panel                         */
  --surface-3:     #1D1D22;  /* nested section, input + select at rest       */
  --surface-4:     #25252B;  /* floats above the page: modal, popover, menu  */
  --surface-hover: rgba(255, 255, 255, 0.04);  /* neutral hover wash         */

  /* ══ 3 · BORDERS ═════════════════════════════════════════════════════════
     Default borders are neutral and almost invisible — separation comes from
     the surface step, the border only sharpens it. Orange borders belong to
     --accent-border (selected) and --accent-focus-ring (focused) only. */
  --border:        rgba(255, 255, 255, 0.07);  /* cards, dividers, table rows */
  --border-strong: rgba(255, 255, 255, 0.12);  /* inputs at rest, hover edge  */

  /* ══ 4 · TEXT ════════════════════════════════════════════════════════════
     Neutral ladder replacing the tan one: hierarchy is luminance, not hue.
     Verified on all five surfaces (worst case = --surface-4):
       primary 13.0:1 · secondary 5.95:1 · muted 4.52:1 — all WCAG 2.1 AA.
     Contrast is unchanged-or-better vs the previous --t1/--t2/--t3 work. */
  --text-primary:   #EDEDF0;  /* headings, values, anything that must be read */
  --text-secondary: #A1A1AA;  /* body copy, labels, legends                   */
  --text-muted:     #8B8B96;  /* captions, units, placeholders, inactive tabs */
  --text-disabled:  #4A4A55;  /* disabled controls + decoration — NOT for text*/
  /* The ink for ANY solid signal fill, not just the orange one: the four
     fills are bright, so near-black clears AA on every one of them (amber
     9.21:1 · positive 8.69:1 · accent 8.48:1 · negative 6.47:1) while
     near-white clears NONE of them (1.84 … 2.62:1). A filled button takes
     this ink whatever its fill; --text-primary on a fill is always a bug. */
  --text-on-accent: #0A0A0B;

  /* ══ 5 · FUNCTIONAL STATE ════════════════════════════════════════════════
     Meaning-bearing colours, unchanged in intent. --negative was lifted from
     #EF4444 (4.05:1 on --surface-4, below AA) to #FF5A5A (4.98:1) — same hue,
     just legible on the lighter surfaces.

     FIVE steps per signal, and a component picks one by the AREA it paints,
     never by taste. The solid is ink — text, icon, dot, chart stroke — and is
     the ONE step that may never become a background: an element whose fill is
     its own text colour renders at 1:1 and disappears, which is exactly how
     this ladder came to be written down.
         (solid)   ink
         -wash     4.5%  a full-bleed row — the colour is spread over 1900px
         -bg        10%  a bounded chip, tile, pill or banner
         -bg-hover  18%  that same bounded object under the pointer
         -border    28%  its hairline edge
     Every alpha lives here, so no component invents its own, and no component
     has to concatenate a hex suffix onto a colour to get one. */
  --positive:          #22C55E;
  --positive-wash:     rgba(34, 197, 94, 0.045);
  --positive-bg:       rgba(34, 197, 94, 0.10);
  --positive-bg-hover: rgba(34, 197, 94, 0.18);
  --positive-border:   rgba(34, 197, 94, 0.28);
  --negative:          #FF5A5A;
  --negative-wash:     rgba(255, 90, 90, 0.045);
  --negative-bg:       rgba(255, 90, 90, 0.10);
  --negative-bg-hover: rgba(255, 90, 90, 0.18);
  --negative-border:   rgba(255, 90, 90, 0.28);
  --warning:           #F59E0B;
  --warning-wash:      rgba(245, 158, 11, 0.045);
  --warning-bg:        rgba(245, 158, 11, 0.10);
  --warning-bg-hover:  rgba(245, 158, 11, 0.18);
  --warning-border:    rgba(245, 158, 11, 0.28);
  /* Info — the fourth signal, and the one that was missing. A note, a tip or
     a "read this first" callout needs the same five steps the others have;
     without them every surface that wanted one invented its own blue, which
     is exactly how the wiki ended up carrying a tint the product does not
     own. It borrows the series blue rather than adding a fifth hue, and
     --info-text is the AA-safe variant for a tinted label, the same way
     --series-2-text is for a chart legend. */
  --info:              var(--series-2);
  --info-text:         var(--series-2-text);
  --info-wash:         rgba(59, 130, 246, 0.045);
  --info-bg:           rgba(59, 130, 246, 0.10);
  --info-bg-hover:     rgba(59, 130, 246, 0.18);
  --info-border:       rgba(59, 130, 246, 0.28);
  /* The fifth signal is the absence of one: "nothing to report" needs the same
     five steps so a neutral row is built the same way as a red one. Its ink is
     --text-muted (4.52:1) and its fills are plain white alpha. */
  --neutral-wash:      rgba(255, 255, 255, 0.02);
  --neutral-bg-hover:  rgba(255, 255, 255, 0.08);

  /* ══ 6 · DATA VISUALISATION ══════════════════════════════════════════════
     Series colours, not UI colours. --series-2 exists only to separate two
     lines in one chart (strategy vs benchmark, A vs B); never use it for a
     button, a border or a state. Use --series-2-text when the LABEL is tinted
     — the base blue drops below AA on --surface-4.

     ── The categorical scale — five slots, a fixed order, never cycled ──
     Slots 1 and 2 are the brand's A/B pair and do not move. Slots 3–5 were
     chosen by running every candidate ordering through the colour-vision
     gates rather than by eye, on --surface-2:

       worst ALL-PAIRS separation  ΔE 12.1 under deuteranopia/protanopia
                                   (target ≥ 8)
       worst all-pairs normal      ΔE 18.2 (floor ≥ 15)
       contrast vs the surface     4.9 : 1 (blue) … 13.7 : 1 (yellow)
       vs the status colours       every slot clears ΔE 8 / 15 against
                                   positive, negative and warning, so a
                                   series can never impersonate a state

     FIVE is the cap, and it is a real one: a sixth hue drops the normal-vision
     worst pair to 14.0 and no ordering recovers it. A chart with more than
     five series repeats the scale from slot 2 with a DASH pattern — identity
     then lives in (hue × dash), which is a second channel and therefore also
     the answer for a colour-blind reader. Slot 1 is reserved for the primary
     or aggregate series (the portfolio, the live curve), so the thing being
     judged is always the brand colour.

     The slots are deliberately brighter than a filled-mark palette would be:
     these draw 1.5–2px LINES on a near-black canvas, where a mid-lightness
     hue disappears. Contrast, not lightness band, is the gate that matters. */
  --series-1:      var(--accent);          /* primary / aggregate / A        */
  --series-2:      #3B82F6;                /* comparison / benchmark / B     */
  --series-2-text: #60A5FA;                /* the blue series' LABEL only    */
  --series-3:      #5EEAD4;                /* mint                           */
  --series-4:      #FDE047;                /* yellow                         */
  --series-5:      #A5B4FC;                /* periwinkle                     */

  --chart-grid:    rgba(255, 255, 255, 0.05);
  --chart-axis:    var(--text-muted);
  --chart-cursor:  var(--accent-border);
  --chart-tooltip-bg:     var(--surface-4);
  --chart-tooltip-border: var(--border-strong);

  /* ── The diverging ramp — heatmaps and correlation matrices ──────────
     Polarity, not magnitude: two poles and a NEUTRAL middle, four equal steps
     per arm. The poles are the semantic pair (a loss is red here as it is
     everywhere else in the product), and the middle is a surface wash rather
     than a third hue — a hue at the midpoint reads as a category of its own.
     Built in JS by heatFill() in components/tokens.js so the steps can be
     computed from a value; these two are the anchors it interpolates between.
     Every surface that uses it MUST also expose the signed number (a cell
     tooltip or a printed value): red↔green is the one pair a colour-blind
     reader cannot separate, so colour is never the only channel. */
  --heat-zero: rgba(255, 255, 255, 0.04);

  /* ══ 7 · ELEVATION ═══════════════════════════════════════════════════════
     Shadows are black, never orange. Depth is depth; orange is meaning. */
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.40);
  --shadow-2: 0 4px 16px rgba(0, 0, 0, 0.50);
  --shadow-3: 0 16px 48px rgba(0, 0, 0, 0.60);

  /* The workspace texture. It is a TEXTURE, not a decoration: it belongs on
     the dense trading surfaces where it reads as graph paper behind data,
     and nowhere near long-form prose, where the same lines read as noise.
     Two variables, one job each — --grid-image is the drawing and never
     changes; --grid-bg is the SWITCH, set to none by .omnis-editorial and
     on phones, where a 52px lattice across a 375px screen is only paint.
     Alpha dropped 0.022 → 0.013 (~1.3:1 against --bg): still legible as
     structure at a glance, no longer competing with a chart gridline. */
  --grid-image: linear-gradient(rgba(255, 255, 255, 0.013) 1px, transparent 1px),
                linear-gradient(90deg, rgba(255, 255, 255, 0.013) 1px, transparent 1px);
  --grid-bg:    var(--grid-image);
  --grid-size:  52px;

  /* ══ 7b · BRAND MOMENTS — the glow ═══════════════════════════════════════
     The radial orange wash is the only atmospheric effect in the system, and
     it is a BRAND MOMENT, not a surface treatment. It used to appear 20 times
     across 15 files, each with its own hand-mixed orange (255,60,0 · 255,80,0
     · 255,100,0 · 255,120,0 · 255,140,0 · 255,180,0 · 200,60,0) at seven
     different alphas — which is how a deliberate effect turns into a default
     and the product starts reading as a template.

     ONE drawing, ONE alpha (0.07 — atmosphere, not heat), ONE brand orange.
     It is allowed in exactly three places, and nowhere else:

       1. the HERO of a marketing / trust page — one per page, behind the
          headline, never behind data;
       2. a full-screen INTERRUPTION — auth, the onboarding and KYC gates,
          the TOTP gate, the error boundary: there is no data on screen to
          compete with, and the wash says the app is paused;
       3. the RUNNING state of a strategy or backtest — the `bt-pulse`
          keyframe, which is an active state (a sanctioned use of orange) and
          carries information because it moves.

     It is NOT allowed on a card, a metric tile, a dashboard, a table, a
     builder canvas, a ticker or a chart — those are data surfaces, and an
     orange fog behind a number is the thing that makes a figure look like
     decoration instead of a fact. */
  --glow-hero:   radial-gradient(ellipse 70% 50% at 50% 0%,   rgba(255, 140, 0, 0.07) 0%, transparent 65%);
  --glow-center: radial-gradient(ellipse 60% 50% at 50% 50%,  rgba(255, 140, 0, 0.07) 0%, transparent 70%);

  /* ══ 8 · RADIUS ══════════════════════════════════════════════════════════
     A closed scale of four. It replaces 15 ad-hoc values (1 → 20px) that had
     no relationship to each other. Radius tracks the SIZE and ROLE of the
     box, never its importance — an important card is not a rounder card:
       sm    short controls and chips: small button, segmented segment,
             swatch, tag, colour box
       md    leaf boxes and controls: input, select, textarea, button,
             stat/metric tile, nested section, tooltip, menu
       lg    containers that hold other components: card, panel, modal, drawer
       full  fully rounded: status pill, badge, progress bar + its track,
             status dot, avatar. On a square it is a circle, on a 6px bar a
             capsule — one token instead of hand-tuned 1/2/3px values.
     Nesting works because lg > md > sm: an inner corner always sits inside
     its parent's. */
  --radius-sm:   6px;
  --radius-md:   10px;
  --radius-lg:   16px;
  --radius-full: 999px;

  /* ══ 9 · TYPOGRAPHY ══════════════════════════════════════════════════════
     Three families, one job each — the font declares what the content IS:
       --font-h    display  · Chakra Petch  · H1/H2/H3, wordmark, hero metrics
       --font-sans prose    · IBM Plex Sans · everything a human reads as words
       --font-m    data     · JetBrains Mono· figures, tickers, IDs, timestamps
     Mono is no longer the default. A paragraph in mono reads as a terminal
     dump; a price in prose loses its column alignment. Neither is negotiable.

     IBM Plex Sans is the prose face: an engineered grotesque (squared bowls,
     flat terminals) that keeps the technical register of Chakra Petch without
     Inter's template neutrality, and it is the only candidate whose family
     also covers Arabic — so 13 of the 14 locales share one design language.

     Script coverage: Plex Sans ships latin / latin-ext / cyrillic /
     cyrillic-ext / greek / vietnamese, Plex Sans Arabic covers ar. CJK
     (ja/ko/zh) has no self-hosted cut — it resolves to the platform face via
     --font-intl, which every family below falls through to. Subsets are
     unicode-range gated, so a Latin reader downloads only the Latin files. */
  --font-intl: 'IBM Plex Sans Arabic', 'Hiragino Sans', 'Yu Gothic UI',
               'Apple SD Gothic Neo', 'Malgun Gothic', 'PingFang SC',
               'Microsoft YaHei', 'Noto Sans';
  --font-h:    'Chakra Petch', var(--font-intl), system-ui, sans-serif;
  --font-sans: 'IBM Plex Sans', var(--font-intl), system-ui, sans-serif;
  --font-m:    'JetBrains Mono', var(--font-intl), ui-monospace, monospace;

  /* ── Type scale — 8 levels, nothing between them ──────────────────────
     Replaces 30 ad-hoc font sizes (6.5px → 44px) and 25 letter-spacings.
     Consume via the .t-* classes below, the Tailwind text-* utilities, or
     these vars directly in an inline style. Weight and family are fixed per
     level and live in the class, not here — a level is a whole decision. */
  --ts-display:    clamp(40px, 5.2vw, 76px);  --lh-display:    1.05;  --tr-display:    -0.02em;
  --ts-title:      clamp(26px, 3.2vw, 44px);  --lh-title:      1.12;  --tr-title:      -0.01em;
  --ts-heading:    20px;                      --lh-heading:    1.3;   --tr-heading:    -0.01em;
  --ts-subheading: 16px;                      --lh-subheading: 1.4;   --tr-subheading: 0;
  --ts-body:       15px;                      --lh-body:       1.65;  --tr-body:       0;
  --ts-body-sm:    13px;                      --lh-body-sm:    1.55;  --tr-body-sm:    0;
  --ts-label:      12px;                      --lh-label:      1.4;   --tr-label:      0.01em;
  --ts-micro:      10.5px;                    --lh-micro:      1.2;   --tr-micro:      0.1em;

  /* The 9th level, added because the components demanded it: a KPI figure
     sits between --ts-heading (20px) and --ts-title (fluid 26→44px, which
     would balloon inside a fixed-width tile). Display face, tabular figures.
     Used by Stat / PnlStat and the backtest result headline only. */
  --ts-metric:     26px;                      --lh-metric:     1;     --tr-metric:     -0.01em;

  /* Long-form prose sits at --ts-body with a bounded measure. 60–75ch is the
     readable range; below 45ch the eye ping-pongs, above 85ch it loses the
     line. Applied by .t-prose and by --container-prose (§11), not by every
     page guessing a max-width — which is why the legal and trust pages used
     to run copy the full 1080px of their container. Measured: 72ch is 675px
     at body size and 720px at the document's, i.e. inside the readable band
     from both directions. */
  --measure: 72ch;

  /* ══ 10 · BACKTEST SUBSYSTEM (--bt-*) ═════════════════════════════════════
     Namespaced so the backtest UI owns its vocabulary without forking the
     scale. Every value below is an alias of a token above — changing a
     colour here is impossible without changing the system. */

  /* Execution states — colour + fill + edge, one row per lifecycle status.
     'idle' is deliberately neutral: an idle run is not an active one. */
  --bt-idle:             var(--text-muted);
  --bt-idle-bg:          var(--surface-hover);
  --bt-idle-border:      var(--border-strong);
  --bt-running:          var(--accent);
  --bt-running-bg:       var(--accent-subtle-bg);
  --bt-running-border:   var(--accent-border);
  --bt-completed:        var(--positive);
  --bt-completed-bg:     var(--positive-bg);
  --bt-completed-border: var(--positive-border);
  --bt-failed:           var(--negative);
  --bt-failed-bg:        var(--negative-bg);
  --bt-failed-border:    var(--negative-border);
  --bt-warn:             var(--warning);
  --bt-warn-bg:          var(--warning-bg);
  --bt-warn-border:      var(--warning-border);

  /* Card nesting depth — expressed as luminance steps, not as tint steps. */
  --bt-lvl1-bg:     var(--surface-1);
  --bt-lvl1-border: var(--border);
  --bt-lvl2-bg:     var(--surface-2);
  --bt-lvl2-border: var(--border);
  --bt-lvl3-bg:     var(--surface-3);
  --bt-lvl3-border: var(--border);

  /* Progress / timeline — the fill is orange because progress is the thing
     you are watching; the track and the glow are not. */
  --bt-progress-track:   rgba(255, 255, 255, 0.08);
  --bt-progress-fill:    linear-gradient(90deg, var(--accent), var(--accent-hover));
  --bt-progress-shimmer: linear-gradient(90deg, var(--accent), var(--accent-hover), var(--accent));
  --bt-step-idle:        var(--border-strong);
  --bt-step-active:      var(--accent);
  --bt-step-done:        var(--positive);

  /* Trade table */
  --bt-th-bg:        var(--surface-1);
  --bt-th-text:      var(--text-muted);
  --bt-row-bg:       transparent;
  --bt-row-alt-bg:   rgba(255, 255, 255, 0.02);
  --bt-row-hover-bg: var(--surface-hover);
  --bt-row-border:   var(--border);

  /* A/B comparison — A is the brand line, B is the neutral comparison line. */
  --bt-strat-a:        var(--series-1);
  --bt-strat-a-bg:     var(--accent-subtle-bg);
  --bt-strat-a-border: var(--accent-border);
  --bt-strat-b:        var(--series-2);
  --bt-strat-b-text:   var(--series-2-text);
  --bt-strat-b-bg:     var(--info-bg);
  --bt-strat-b-border: var(--info-border);

  /* ══ 11 · SPACING ════════════════════════════════════════════════════════
     One 4px grid. Nine steps, and the step number IS the multiplier —
     --space-6 = 6 × 4px = 24px — which is also Tailwind's own numbering, so
     `gap-6`, `var(--space-6)` and `S[6]` all mean the same 24px. One mental
     model instead of a translation table. This replaces 49 hand-picked
     values between 1px and 160px that had no relationship to each other.

        1 ·  4px  icon↔label, pill inset, label↔field
        2 ·  8px  tight stack, dot↔text, metric-grid gap
        3 · 12px  control inner gutter, dense card gap
        4 · 16px  dense card padding, default stack
        6 · 24px  editorial card padding, card-grid gap
        8 · 32px  separation between blocks
       12 · 48px  editorial section rhythm — phone
       16 · 64px  editorial section rhythm — desktop
       24 · 96px  hero band / page opening

     Below 4px is NOT spacing, it is optical alignment: a 2px pill inset, the
     2px rail of a segmented control, a 1px nudge onto a hairline. Those stay
     literal, and they are the only numbers under 4 a style object may hold. */
  --space-1:   4px;
  --space-2:   8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-6:  24px;
  --space-8:  32px;
  --space-12: 48px;
  --space-16: 64px;
  --space-24: 96px;

  /* ── Density — the two modes ──────────────────────────────────────────
     Spacing is not a per-page opinion, it is a property of the CONTEXT a
     component renders in. Every variable in this group is re-declared by
     .omnis-editorial below, so ONE <Card> is compact on the dashboard and
     generous on the pricing page — no prop, no fork, no media query at the
     call site, and a modal portalled to <body> still inherits the default.

       DENSE (these values, carried by :root) — dashboard, markets, builder,
       strategies, bots, experiments, risk, journal, marketplace, reports,
       logs, admin and every data table. A trader expects to see many rows
       without scrolling; that density is a feature, not a defect.

       EDITORIAL (.omnis-editorial) — home, pricing, about, trust, legal,
       account, support, onboarding, auth. The content is prose and
       conversion, so it gets air, a bounded measure and no grid texture. */
  --gutter:      var(--space-4);  /* page side padding                     */
  --section-y:   var(--space-6);  /* rhythm between top-level blocks       */
  --stack:       var(--space-3);  /* rhythm inside one block               */
  --grid-gap:    var(--space-3);  /* between cards of one grid             */
  --pad-card:    var(--space-4);  /* card body inset                       */
  --pad-card-y:  var(--space-3);  /* card header vertical inset            */
  --pad-field-x: var(--space-3);  /* field horizontal inset                */
  --container:   var(--container-wide);

  /* ── Control heights ──────────────────────────────────────────────────
     A control's height is a touch target, not a spacing decision. Fixing it
     here and centring the label with flex is what lets horizontal padding
     stay on the 4px grid instead of being reverse-engineered from a font
     metric — which is where 6/9/10/13/18/26px paddings came from. 44px is
     the WCAG 2.5.5 (AAA) target size, and every rung is a multiple of 4. */
  --control-sm:    28px;
  --control-md:    36px;
  --control-lg:    44px;
  --control-field: 44px;

  /* ── Containers ───────────────────────────────────────────────────────
     Five widths and nothing between them. Pages used to invent their own —
     1360 / 1280 / 1180 / 1100 / 1080 / 980 / 900 / 880 — so two sections of
     the SAME page could not line up with each other. */
  --container-wide:   1280px;         /* workspace shell, wide editorial   */
  --container-body:   1080px;         /* editorial default: cards, grids   */
  --container-narrow:  880px;         /* focused: FAQ, CTA, wide tables    */
  --container-prose: var(--measure);  /* long-form copy, bounded measure   */
  --container-form:    440px;         /* auth card, single-column forms    */
}
