/* ═══════════════════════════════════════
   InvestJournal design tokens — the single --ij-* source.
   Light on :root; dark under .ij-theme-dark (MudBlazor swaps --mud-palette-*
   with NO theme class, so both MainLayouts add ij-theme-dark themselves).
   Palette values mirror StockGrade.ComponentLibrary/Theme/AppPalette{Light,Dark}.cs
   and are guarded by ThemeTokensDriftTests — change them there first.
   Load order: this file BEFORE app-layout.css in every host.
═══════════════════════════════════════ */

:root {

    /* ── Motion ──────────────────────────────────────────
       Durations are the values already in use, named. Curves replace the
       UA default `ease`, which 121 of 130 transitions were relying on either
       explicitly or by omission.

       Honest scope note: at 120-150ms on a colour or opacity fade the curve
       is not perceptible. These exist so motion is DELIBERATE and so future
       spatial animation has something correct to reach for -- not because
       every call site below feels different. */
    --ij-duration-instant: 100ms;
    --ij-duration-fast: 120ms;
    --ij-duration-base: 150ms;
    --ij-duration-slow: 200ms;
    --ij-duration-slower: 300ms;
    --ij-duration-page: 350ms;

    /* Standard: state changes (colour, background, border, shadow). */
    --ij-ease-standard: cubic-bezier(0.2, 0, 0, 1);
    /* No enter/exit curves here yet: nothing uses one. The 28 keyframe
       `animation` declarations are deliberately NOT migrated -- 8 of them
       loop (typing dots, pulses, cycles), where ease-in-out/linear is
       correct and a spring would be wrong. Add a curve when a call site
       needs it. */
    /* Spring: spatial movement only (transform). cubic-bezier here is the
       fallback; the real spring is swapped in below where linear() exists. */
    --ij-ease-spring: cubic-bezier(0.16, 1, 0.3, 1);

    /* Brand palette (mirrors AppPaletteLight) */
    --ij-bg: #ffffff;
    --ij-surface: #ffffff;
    --ij-bg-gray: #f5f5f7;
    --ij-text-primary: #1d1d1f;
    --ij-text-secondary: #5f5f63;
    --ij-primary: #0071e3;
    --ij-divider: #d2d2d7;

    /* #1819 — WCAG 1.4.11: the boundary that identifies a FIELD needs 3:1 against its
       ground, which the hairline divider (1.51:1 on white, 1.38:1 on the grey field
       ground) never met. Deliberately separate from --ij-divider: a rule between rows
       is decoration and stays quiet; a field's edge is information. #8a8a8e is the
       LIGHTEST value that clears 3:1 on BOTH grounds (3.44 on white, 3.16 on #f5f5f7),
       so the design gets the softest border the criterion allows. */
    --ij-field-border: #8a8a8e;

    /* #1743 §1. --ij-primary is a FILL colour first (white sits on it), so it cannot
       also be darkened far enough to serve as text on a light tint. Split the roles,
       exactly as --ij-lp-purple / --ij-lp-purple-text already do:
         --ij-primary-text  blue TEXT on a surface or a light tint. #0071e3 is 4.70:1
            on white but only 3.87:1 on .ij-dcf-ai-btn's own primary-18% gradient —
            #0063c8 is 4.52:1 on that worst real ground.
         --ij-primary-fill  a FILLED control carrying white label text. In DARK the
            #2997ff fill left white at 3.02:1, below the 4.5:1 a 14px/500 label needs
            (.ij-btn-primary, .wlc-btn-create); #0a72d4 restores 4.80:1. */
    --ij-primary-text: #0063c8;
    --ij-primary-fill: #0071e3;

    /* #1817 — the TEXT half of three more accents, same split as --ij-primary-text.
       Each is painted on a 12–14% tint of ITSELF, which lifts the ground enough that
       the base value lands at 3.2–4.1:1. Measured grounds, light theme:
         --ij-info-text   #147ce5 on its own 14% tint (#d6e4f4) was 3.22:1
         --ij-loss-text   #c62828 on its own 14% tint (#eed8da) was 4.14:1
         --ij-event-text  #4a5ae8 on --ij-event-tint  (#d7e1fa) was 4.09:1
         --ij-lp-clay-text #d97757 on white was 3.12:1 (the /connect eyebrow, which
            also hard-coded the DARK clay into the light theme).
       The FILL values keep their brightness; only text moves. */
    --ij-info-text: #0f62b5;
    --ij-loss-text: #b32424;
    --ij-event-text: #4353d6;
    --ij-lp-clay-text: #b06046;


    /* #1814 — the keyboard focus ring (WCAG 2.4.7). Declared ONCE, here, and only as
       an alias of --mud-palette-primary: MudBlazor swaps that on :root in both themes,
       so this follows the theme everywhere — including the app bar, which sits outside
       both ij-theme-dark wrappers and would freeze a light --ij-* value into dark.
       #0071e3 is 4.7:1 on white, #2997ff 7.2:1 on black: both clear the 3:1 that
       1.4.11 asks of a focus indicator. Do NOT re-declare under .ij-theme-dark. */
    --ij-focus-ring: var(--mud-palette-primary);

    /* #1774. The same split, for red — the one role that never got it. The danger
       button painted --mud-palette-error, which is a TEXT red first: bright enough in
       dark to read on a black ground, and therefore too light to carry white. The
       Archive confirmation's 14px/500 label measured 3.43:1 on it in dark, against
       the 4.5 it needs — found only once the walk could finally open that dialog.
         --ij-danger-fill  a FILLED destructive control carrying white label text.
            Light keeps the palette red (#c62828, 5.62:1); dark deepens #ff3f5f to
            #de1e3e (4.82:1), which also keeps the .88-opacity hover at 4.51:1.
       Text red stays where it was (--ij-loss / --mud-palette-error). */
    --ij-danger-fill: #c62828;

    /* #1743 follow-up. Amber has the same two-role problem the blue had, and a
       worse version of it: as a FILL it must stay bright to read as a warning,
       but as TEXT on a light ground #f59e0b measures 2.15:1 on white and 1.64:1
       on the stacked grey the DCF editor nests — the lowest real-text ratio
       found anywhere in this audit, and one the audit itself never listed.
       ~10 call sites paint warning as text: the Pro chip, the DCF verdict, the
       calendar, the desk, the portfolio margin-of-safety cell.
       #895806 is 4.60:1 on that worst ground. It is a deep amber rather than the
       bright one, which is the unavoidable cost of amber-on-white at AA — the
       FILL keeps the bright value, so a warning still looks like a warning. */
    --ij-warning-text: #895806;

    /* Gain / loss (#524). Deliberately NOT Success/Error: a loss is not an error
       state — that separation is about MEANING and still holds. Light aliases the
       DARKENED palette values because the base Success/Error were only ~3.4:1 on
       white and failed at 13px tabular; #1743 §1 has since pulled the bases down to
       those same darkened values, so the two now coincide in light. Numeric text
       only; never a row background, never the allocation bar, never a filled chip. */
    --ij-gain: #157a30;
    --ij-loss: #c62828;

    /* Naming three values the shipped CSS already hardcodes (#524). */
    /* #1743 §1 — the single biggest cause in the HIG audit (16 of 85 failures).
       Hint text had TWO ways of being too faint: this token at #a1a1a6 (1.93:1 on a
       lifted ground), and ~40 call sites painting --mud-palette-text-disabled, an
       ALPHA colour meant for genuinely disabled controls — rgba(0,0,0,0.25) measured
       1.83:1 against a 4.5:1 requirement, in both themes. Those call sites now point
       here, and this token carries roughly the greys secondary vacated, pulled down
       far enough to survive STACKED tints: the DCF editor nests a 5% read-only row
       inside a 12% field group, which composites to #e9e9ea, not the #f8f8f9 a single
       5% layer would give. #6e6e73 measured 4.18:1 there in the browser; #67676b is
       4.64:1. --mud-palette-text-disabled keeps its alpha and its real job (disabled
       controls, which WCAG and Apple both exempt). The hierarchy is compressed, not
       lost — three AA-passing greys cannot be far apart on white, so secondary and
       tertiary now differ by ~8 sRGB units in light. That is the honest cost of AA. */
    --ij-text-tertiary: #67676b;
    --ij-field: var(--ij-bg-gray);
    --ij-primary-tint: rgba(0, 113, 227, 0.08);

    /* Verdict marks — the valuation's own signal (the portfolio row's value
       cell). Deliberately NOT the gain/loss pair: red and green mean P&L and
       nothing else, which is the whole reason the verdict moved off the figure
       and onto a dot.

       --ij-lp-teal is a LANDING accent, tuned against near-black. On the light
       card ground (#f5f5f7) a 6px dot of it measures 1.71:1, under WCAG 1.4.11's
       3:1 for a non-text mark — so light darkens it, for exactly the reason
       --ij-gain darkens Success above. Clay already clears 3:1 on both grounds
       (3.58 light / 5.79 dark) and is aliased as-is.

       All three are RE-DECLARED under .ij-theme-dark, never left to inherit —
       see the --ij-field note below: a property holding var(...) is substituted
       where it is DECLARED, so a :root alias freezes the light value into dark. */
    --ij-verdict-under: #0d9488;
    --ij-verdict-over: var(--ij-lp-clay);
    --ij-verdict-fair: var(--ij-text-tertiary);

    /* Landing-page accents (moved from app-layout.css). Purple/pink/teal are
       theme-invariant; clay (the Claude mark) gets a dark override below. */
    --ij-lp-purple: #a855f7;
    /* Text-safe purple. #a855f7 measures 3.96:1 on the light surface (#ffffff) —
       below AA's 4.5:1 — so it may paint a decorative fill (a 3px spine, a glow,
       a gradient) but never small text. #9333ea is 5.38:1 on white. In DARK the
       base purple already clears AA (4.91:1 on #0d0d0d), so this token is
       re-declared under .ij-theme-dark back to #a855f7 rather than left to
       inherit — a value declared only on :root freezes the light colour into the
       dark theme (the --ij-field trap below). Guarded by ThemeContrastTests. */
    --ij-lp-purple-text: #9333ea;

    /* Earnings-calendar indigo (#1503, Canvas-20): a Scheduled report on the calendar,
       the StockView chip, the desk's Earnings-ahead eyebrow. Not a MudBlazor palette
       slot — a design accent like --ij-lp-purple. Dark is the design's #7e8fff; light
       is the same hue pulled down to 5.4:1 on white for 11.5px chip text (ThemeContrastTests). */
    --ij-event: #4a5ae8;
    --ij-event-tint: rgba(74, 90, 232, 0.12);
    --ij-lp-pink: #ec4899;
    --ij-lp-teal: #2dd4bf;
    --ij-lp-clay: #c96442;

    /* Type (mirrors AppTypography.Default) */
    --ij-font-family: -apple-system, "SF Pro Display", "Helvetica Neue", Arial, sans-serif;
    --ij-font-size-body: 0.9375rem;
    --ij-line-height-body: 1.47;

    /* Shape & chrome (mirrors AppLayoutProperties) */
    --ij-radius: 4px;
    --ij-appbar-height: 64px;

    /* Spacing steps — the 4px grid the app already uses */
    --ij-space-1: 4px;
    --ij-space-2: 8px;
    --ij-space-3: 12px;
    --ij-space-4: 16px;
    /* The ladder skipped 5 while nothing asked for it, but four rules in
       portfolio.css already did — two with a `, 20px` fallback and two without,
       and those two silently resolved to nothing. The summary strip's mobile
       `gap: var(--ij-space-5)` therefore computed to `normal`, i.e. ZERO, which
       ran the CAD and USD blocks into one another at 375px. Defining the step is
       the fix; it is on the same 4px grid as its neighbours. */
    --ij-space-5: 20px;
    --ij-space-6: 24px;
    --ij-space-8: 32px;
}

.ij-theme-dark {
    /* Brand palette (mirrors AppPaletteDark) */
    --ij-bg: #000000;
    --ij-surface: #0d0d0d;
    --ij-bg-gray: #161617;
    --ij-text-primary: #f5f5f7;
    --ij-text-secondary: #939398;
    --ij-primary: #2997ff;
    --ij-divider: #333336;

    /* #1819 — dark counterpart, re-declared rather than inherited (the --ij-field trap).
       #68686d is the lightest that clears 3:1 on every dark ground: 3.51 on the surface,
       3.26 on the filled field, 3.79 on black. */
    --ij-field-border: #68686d;

    /* #1743 §1 — see the :root note. Blue text already clears AA on every dark
       ground it lands on (5.12:1 worst, on the AI button's own tint), so the TEXT
       token keeps the base primary; only the FILL is deepened, to put white back
       above 4.5:1 on .ij-btn-primary / .wlc-btn-create. */
    --ij-primary-text: #2997ff;
    --ij-primary-fill: #0a72d4;

    /* #1817 — dark counterparts. Re-declared, never inherited: a value declared only on
       :root freezes the LIGHT colour into dark (the --ij-field trap above). On a dark
       ground the accents are already bright enough, so these mostly restate the base —
       except the info badge, which sat at 4.44:1 on its own tint. */
    --ij-info-text: #4e88ff;
    --ij-loss-text: #ff3f5f;
    --ij-event-text: #7e8fff;
    --ij-lp-clay-text: #d97757;

    /* #1774 — see :root. Deepened so white clears 4.5 on the dark destructive fill. */
    --ij-danger-fill: #de1e3e;

    /* Dark needs no separate value: on a dark ground the bright amber is already
       7.79:1 at its worst (the verdict button's own 18% tint). Re-declared rather
       than inherited — a value declared only on :root freezes the LIGHT colour
       into the dark theme (the --ij-field trap below). */
    --ij-warning-text: #ffb545;

    /* Dark aliases the BASE palette values — on black they clear contrast
       without darkening (mirrors AppPaletteDark Success/Error). */
    --ij-gain: #3dcb6c;
    --ij-loss: #ff3f5f;

    /* #1743 §1 — dark counterpart. #5a5a60 was 2.16:1 on a lifted ground; #86868b is
       4.68:1 on the dialog surface and 5.06:1 on pure black, while staying visibly
       dimmer than the new --ij-text-secondary (#939398). */
    --ij-text-tertiary: #86868b;
    --ij-primary-tint: rgba(41, 151, 255, 0.14);

    /* MUST be re-declared here, not left to inherit from :root. A custom
       property holding var(--ij-bg-gray) is substituted where it is DECLARED,
       so the :root copy froze the LIGHT gray and carried it into dark — filled
       fields rendered near-white on black (19A caught it; portfolio.css has
       used --ij-field since #524). */
    --ij-field: var(--ij-bg-gray);

    /* #1817 — 4.39:1 on its own 12% tint (#201629); #aa58f7 is 4.52:1. */
    --ij-lp-purple-text: #aa58f7;
    --ij-event: #7e8fff;
    --ij-event-tint: rgba(126, 143, 255, 0.14);
    --ij-lp-clay: #d97757;

    /* Verdict marks, dark. RE-DECLARED, never inherited — the --ij-field trap:
       a property holding var(...) is substituted where it is DECLARED, so the
       :root copies of `over` and `fair` hold the LIGHT clay and the LIGHT
       tertiary and would carry them into dark. `under` goes back to the
       full-strength accent: on the dark card ground it measures 9.71:1, so
       there is nothing to darken away from. */
    --ij-verdict-under: var(--ij-lp-teal);
    --ij-verdict-over: var(--ij-lp-clay);
    --ij-verdict-fair: var(--ij-text-tertiary);
}

/* ═══════════════════════════════════════
   Motion, continued.
   A real spring needs linear(); browsers without it keep the cubic-bezier
   fallback declared above. Verified present in the device WebView
   (WebKit 26.2) before being relied on.
═══════════════════════════════════════ */
@supports (transition-timing-function: linear(0, 1)) {
    :root {
        --ij-ease-spring: linear(0.0000 0.0%, 0.0658 4.2%, 0.2149 8.3%, 0.3929 12.5%, 0.5659 16.7%, 0.7152 20.8%, 0.8333 25.0%, 0.9197 29.2%, 0.9779 33.3%, 1.0133 37.5%, 1.0316 41.7%, 1.0381 45.8%, 1.0372 50.0%, 1.0322 54.2%, 1.0255 58.3%, 1.0187 62.5%, 1.0126 66.7%, 1.0077 70.8%, 1.0040 75.0%, 1.0015 79.2%, 0.9998 83.3%, 0.9990 87.5%, 0.9986 91.7%, 0.9985 95.8%, 1.0000 100.0%);
    }
}

/* Respect the OS setting. Durations collapse rather than curves changing, so
   a transition still "completes" and any transitionend listener still fires. */
@media (prefers-reduced-motion: reduce) {
    :root {
        --ij-duration-instant: 1ms;
        --ij-duration-fast: 1ms;
        --ij-duration-base: 1ms;
        --ij-duration-slow: 1ms;
        --ij-duration-slower: 1ms;
        --ij-duration-page: 1ms;
    }
}
