/* =============================================================================
   SudaHub — design tokens
   RESKIN Phase 1 (docs/RESKIN.md §3.1, §3.2, §4.3). Supersedes the console
   palette; see CLAUDE.md's amended §4 and docs/REDESIGN_LOG.md.

   Three things are load-bearing here and easy to undo by accident:

   1. RAMPS ARE FOREGROUND-PROMINENCE, NOT ABSOLUTE LIGHTNESS.
      --color-neutral-100 is always the MOST prominent foreground and -900
      the least (a fill that nearly matches the ground). In dark that means
      100 is near-white; in light it is near-black. Same token, same job,
      both themes — which is what lets a component be written once.

   2. LIGHT IS CONTRAST-MATCHED TO DARK, NOT EYEBALLED. Every light step was
      solved numerically for the same contrast ratio its dark counterpart
      has against its own ground. So RESKIN §5's three rules — 400 is the
      Arabic floor, 500 is the secondary floor, 600 never carries text —
      hold in BOTH themes by construction rather than by luck.

   3. TYPE SIZES ARE LOCALE-AWARE AT THE TOKEN LEVEL (§4.3). Arabic never
      renders below 13px (12px for the tab label alone), because the
      mockup's 9.5/10.5px steps are unreadable in a script carrying
      diacritics above and descenders below. Do NOT patch this per
      component — change the token.
   ========================================================================== */

:root {
    /* ---- Brand, full chroma ----------------------------------------------
       LOGO MARK + at most ONE signature moment per screen. Never interface
       colour, never text, never a background for content. The muting is the
       whole point of the reskin: the old build ran these everywhere and
       drowned the news in them. */
    --brand-navy: #0B1229;
    --brand-blue: #1E99FF;
    --brand-cyan: #29B6F6;
    --brand-violet: #7B2FF7;
    --brand-magenta: #D946EF;
    --grad-brand: linear-gradient(135deg, #29B6F6 0%, #1E99FF 34%, #7B2FF7 74%, #D946EF 100%);

    /* ---- Spacing — 2.8px base (§2.1) -------------------------------------
       The tightness IS the design. Do not loosen back toward the old 4pt
       scale; nothing off-scale. */
    --space-1: 2.8px;
    --space-2: 5.6px;
    --space-3: 8.4px;
    --space-4: 11.2px;
    --space-6: 16.8px;
    --space-8: 22.4px;

    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 14px;
    --radius-pill: 100px;

    /* ---- Type faces (§3.2) ------------------------------------------------
       Inter has NO Arabic glyphs. Anything that may contain Arabic must
       resolve to --font-ar or it silently falls back and looks broken. */
    --font-latin: "Inter", system-ui, -apple-system, sans-serif;
    --font-brand: "Outfit", var(--font-latin);          /* wordmark only */
    --font-ar: "IBM Plex Sans Arabic", "Noto Sans Arabic", system-ui, sans-serif;
    --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", monospace;
    --font-reading: "Noto Naskh Arabic", "Amiri", Georgia, serif;

    /* Resolved UI face. Every component uses --font-ui; the [lang] rules
       below swap what it points at, so no component ever branches on locale. */
    --font-ui: var(--font-latin);

    /* Resolved LABEL face — eyebrows, taglines, pill text, field labels.
       Mono in Latin (that is the instrument voice), but the Arabic face in
       Arabic, because IBM Plex Mono HAS NO ARABIC GLYPHS. Asking it to
       render Arabic does not merely look off: every letter is forced into a
       fixed-width cell, the joins between letters break, and the result
       reads as though it were letter-spaced — the exact failure CLAUDE.md
       §10 forbids, and it is invisible in a Latin-only review.

       --font-mono stays raw for DIGITS AND LATIN CODES ONLY (countdowns,
       word counts, source tags like `01`). Those are Western numerals in
       both locales, so mono is always safe there. If a string can be
       translated, it wants --font-label, not --font-mono. */
    --font-label: var(--font-mono);

    /* ---- Type scale — Latin defaults; Arabic overrides below (§4.3) ---- */
    --fs-tab: 9.5px;        /* tab-bar label — the one 12px Arabic exception */
    --fs-micro: 9.5px;      /* eyebrows, uppercase tracked labels */
    --fs-meta: 10.5px;      /* timestamps, counts */
    --fs-caption: 11.5px;
    --fs-label: 12.5px;
    --fs-body-s: 13px;
    --fs-body: 14px;
    --fs-body-l: 15.5px;
    --fs-title-s: 17px;
    --fs-title: 19px;
    --fs-title-l: 21px;
    --fs-display: 27px;
    --fs-hero: 30px;

    --lh-tight: 1.2;
    --lh-snug: 1.35;
    --lh-body: 1.55;
    --lh-reading: 1.68;

    --fw-regular: 400;
    --fw-medium: 500;
    --fw-semibold: 600;
    --fw-bold: 700;
    /* Body weight floor. Arabic raises it — thin strokes vanish on dark. */
    --fw-body: var(--fw-regular);

    /* Tracking. Zeroed for Arabic below: letter-spacing breaks the
       connected script. Never hardcode a tracking value in a component. */
    --tracking-label: 0.16em;
    --tracking-tight: 0.1em;
    --tracking-wide: 0.2em;
    --tracking-heading: -0.01em;

    /* ---- Motion ---- */
    --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
    --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
    --ease-sheet: cubic-bezier(0.22, 0.9, 0.24, 1);
    --dur-micro: 100ms;
    --dur-base: 180ms;
    --dur-enter: 260ms;
    --dur-ambient: 24s;

    /* ---- Theme-invariant ----
       A scrim behind a badge on a cover, and a modal backdrop, both stay
       dark regardless of theme. RGB triplets so solid and alpha call sites
       share one definition. */
    --scrim-rgb: 10, 11, 18;
    --text-on-accent-rgb: 255, 255, 255;

    /* ---- Reader (kept) — the one brand-free zone -------------------------
       Fixed values independent of [data-theme]: a reader's light/sepia/dark
       choice is a reading preference, not the app's mode. */
    --reader-dark-bg: #12151C;
    --reader-dark-text: #D8DCE4;
    --reader-sepia-bg: #F4ECD8;
    --reader-sepia-text: #3A3125;
    --reader-light-bg: #FBFBF9;
    --reader-light-text: #1A1A1A;

    --reader-light-line: rgba(26, 26, 26, 0.10);
    --reader-light-line-strong: rgba(26, 26, 26, 0.20);
    --reader-light-surface: rgba(26, 26, 26, 0.05);
    --reader-light-shadow: rgba(26, 26, 26, 0.18);

    --reader-sepia-line: rgba(58, 49, 37, 0.14);
    --reader-sepia-line-strong: rgba(58, 49, 37, 0.26);
    --reader-sepia-surface: rgba(58, 49, 37, 0.07);
    --reader-sepia-shadow: rgba(58, 49, 37, 0.22);

    --reader-dark-line: rgba(216, 220, 228, 0.12);
    --reader-dark-line-strong: rgba(216, 220, 228, 0.22);
    --reader-dark-surface: rgba(216, 220, 228, 0.07);
    --reader-dark-shadow: rgba(0, 0, 0, 0.4);

    --reader-swatch-border: rgba(128, 128, 128, 0.3);
}

/* ================= LOCALE-AWARE TYPE (§4.3) =================
   Applied at :root when the document is Arabic (i18n.js sets lang + dir on
   <html>), and again on any subtree explicitly marked lang="ar" so a single
   Arabic string inside an English page — a book title, a source name — gets
   the same floor. */
:root:lang(ar),
[lang="ar"] {
    --font-ui: var(--font-ar);
    /* Mono cannot render Arabic — see the --font-label note above. */
    --font-label: var(--font-ar);

    /* Nothing below 13px. The tab label is the sole 12px exception. */
    --fs-tab: 12px;
    --fs-micro: 13px;
    --fs-meta: 13px;
    --fs-caption: 13px;
    --fs-label: 13.5px;
    --fs-body-s: 14px;
    --fs-body: 15px;
    --fs-body-l: 16.5px;
    --fs-title-s: 18px;
    --fs-title: 20px;
    --fs-title-l: 22px;
    --fs-display: 28px;
    --fs-hero: 31px;

    /* Arabic needs the room: diacritics above, descenders below. */
    --lh-tight: 1.4;
    --lh-snug: 1.6;
    --lh-body: 1.8;
    --lh-reading: 1.9;

    /* Thin Arabic strokes disappear on dark. 500 is the floor. */
    --fw-body: var(--fw-medium);

    /* §9/§13: "Arabic body ≥ 7:1 on dark; never --text-muted for Arabic
       body." The secondary floor (neutral-500, ~6.4:1) is correct for
       Latin and NOT enough for Arabic, whose finer strokes need more
       separation at the same size. Raising the alias here fixes every
       Arabic body use at once rather than per-component — and it can only
       ever increase contrast, so it is safe for Latin call sites that
       happen to be inside an Arabic subtree. */
    --text-muted: var(--color-neutral-400);
    --text-secondary: var(--color-neutral-300);

    /* Tracking breaks the connected script — all of it goes to zero. */
    --tracking-label: 0;
    --tracking-tight: 0;
    --tracking-wide: 0;
    --tracking-heading: 0;
}

/* Safety net, not the mechanism. The tokens above are how tracking and case
   are supposed to be controlled; this catches a component that hardcoded
   either anyway. Both are correctness bugs in Arabic, not style choices:
   uppercase is meaningless (Arabic has no case) and tracking severs the
   joins between letters. */
:lang(ar) {
    letter-spacing: 0 !important;
    text-transform: none !important;
}

/* ================= DARK ================= */
[data-theme="dark"] {
    --color-bg: #0F1424;
    --color-surface: #1A2036;
    --color-text: #E9EBF5;

    --color-accent: #8C7FD4;      /* 5.3:1 on bg */
    --color-accent-2: #A29CD8;

    --color-neutral-100: #f1f5ff;  /* 16.8:1 */
    --color-neutral-200: #e1e8f8;  /* 14.9:1 */
    --color-neutral-300: #cbd4e9;  /* 12.3:1 */
    --color-neutral-400: #aeb7cf;  /*  9.1:1 bg · 8.0:1 surface — Arabic body floor */
    --color-neutral-500: #8f98b0;  /*  6.4:1 bg · 5.6:1 surface — secondary floor */
    --color-neutral-600: #717a91;  /*  4.3:1 — DECORATION ONLY, never text */
    --color-neutral-700: #565d70;
    --color-neutral-800: #3d4250;
    --color-neutral-900: #282b32;

    --color-accent-100: #f5f4ff;
    --color-accent-200: #e7e5fe;
    --color-accent-300: #d2cefd;
    --color-accent-400: #b5abfc;
    --color-accent-500: #978ae0;
    --color-accent-600: #796cbf;
    --color-accent-700: #5d5294;
    --color-accent-800: #423a6a;
    --color-accent-900: #2b2741;

    --color-divider: color-mix(in srgb, var(--color-text) 16%, transparent);

    /* Elevation is a hairline, not a shadow (§2.1) — ambient darkness only
       at the top two levels. */
    --shadow-sm: 0 0 0 1px var(--color-neutral-800);
    --shadow-md: 0 0 0 1px var(--color-neutral-700), 0 6px 18px rgba(0, 0, 0, 0.55);
    --shadow-lg: 0 0 0 1px var(--color-neutral-500), 0 16px 40px rgba(0, 0, 0, 0.65);

    --glass-bg: color-mix(in srgb, var(--color-bg) 92%, transparent);
    --glass-border: var(--color-neutral-900);

    /* Phase 8 audit: --danger had to serve two incompatible jobs — TEXT on a
       dark ground (wants to be light) and a FILL under white text (wants to
       be dark). It failed the fill test at 3.58:1 and only scraped 4.50:1 as
       text on surface. Split, per §9's "white text ON a brand fill" table:
       -fill variants are for backgrounds carrying white text; the plain
       token is for coloured text. */
    --warning: #F5A524;
    --danger: #f7466e;        /* as text: 4.6:1 on bg AND surface */
    --danger-fill: #df2c5c;   /* white on it: 4.5:1 */
    --success: #2ED3A0;

    --line-grid: rgba(255, 255, 255, 0.030); /* faintest rule available; no longer a lattice */
}

/* ================= LIGHT =================
   Contrast-matched to dark, step for step. Not the dark recipe inverted:
   each value was solved for the same ratio against the light ground that
   its dark counterpart has against the dark ground. */
[data-theme="light"] {
    --color-bg: #f5f6fb;
    --color-surface: #FFFFFF;
    --color-text: #13151d;

    --color-accent: #695aac;      /* 5.3:1 on bg — matched to dark's 5.3:1 */
    --color-accent-2: #7a6cbf;

    --color-neutral-100: #13151d;  /* 16.9:1 */
    --color-neutral-200: #1c212d;  /* 14.9:1 */
    --color-neutral-300: #293040;  /* 12.2:1 */
    --color-neutral-400: #3c4357;  /*  9.1:1 bg · 9.9:1 surface — Arabic body floor */
    --color-neutral-500: #525b70;  /*  6.3:1 bg · 6.8:1 surface — secondary floor */
    --color-neutral-600: #6c748b;  /*  4.3:1 — DECORATION ONLY, never text */
    --color-neutral-700: #8d95a8;
    --color-neutral-800: #b2b9c9;
    --color-neutral-900: #d7dae4;

    --color-accent-100: #16151c;
    --color-accent-200: #221f31;
    --color-accent-300: #312c50;
    --color-accent-400: #483980;
    --color-accent-500: #5f50a1;
    --color-accent-600: #7a6cbf;
    --color-accent-700: #988fd6;
    --color-accent-800: #bbb4ee;
    --color-accent-900: #dad7fa;

    --color-divider: color-mix(in srgb, var(--color-text) 14%, transparent);

    /* In light, depth is shadow — the hairline stays but softens. */
    --shadow-sm: 0 0 0 1px var(--color-neutral-800);
    --shadow-md: 0 0 0 1px var(--color-neutral-800), 0 6px 18px rgba(19, 21, 29, 0.10);
    --shadow-lg: 0 0 0 1px var(--color-neutral-700), 0 16px 40px rgba(19, 21, 29, 0.16);

    --glass-bg: color-mix(in srgb, var(--color-bg) 88%, transparent);
    --glass-border: var(--color-neutral-900);

    --warning: #a55e00;       /* was #B26A00 — 4.24:1 failed AA on white */
    --danger: #c81e48;        /* 4.6:1 as text; also 5.6:1 with white on it */
    --danger-fill: #c81e48;
    --success: #007f59;       /* darkened to clear AA on both light grounds */

    --line-grid: rgba(19, 21, 29, 0.035);    /* faintest rule available; no longer a lattice */
}

/* ================= COMPATIBILITY ALIASES =================
   TEMPORARY — delete as each screen is ported (Phases 2–6).

   Every screen still on the console build reads --canvas / --text-primary /
   --line / etc. Re-pointing those at the new ramps here means Phase 1 does
   what it claims — the app adopts the new palette immediately — without
   touching a single screen, which is Phase 2+ work. Removing an alias
   before its screen is ported will visibly break that screen.

   Note --text-muted: it deliberately resolves to neutral-500, NOT 600.
   That is RESKIN §5's central fix — the old muted step was the app's
   workhorse for meta text at 4.1:1 and failed. Do not "restore" it. */
[data-theme] {
    --canvas: var(--color-bg);
    --surface: var(--color-surface);
    --surface-raised: var(--color-surface);
    --surface-overlay: var(--color-surface);
    --surface-sunken: var(--color-bg);

    --text-primary: var(--color-neutral-100);
    --text-secondary: var(--color-neutral-400);
    --text-muted: var(--color-neutral-500);
    --text-on-bright: var(--color-bg);

    --line-subtle: var(--color-neutral-900);
    --line: var(--color-neutral-800);
    --line-strong: var(--color-neutral-700);

    --accent: var(--color-accent);
    --accent-quiet: var(--color-accent-2);

    --rate-up: var(--success);
    --rate-down: var(--danger);
    --rate-flat: var(--color-neutral-500);

    --r-sm: var(--radius-sm);
    --r-md: var(--radius-md);
    --r-lg: var(--radius-lg);
    --r-xl: var(--radius-lg);
    --r-full: var(--radius-pill);

    --shadow-rgb: var(--scrim-rgb);
    --glass-border-top: var(--glass-border);
    --glass-shadow: var(--shadow-md);

    --grad-action: linear-gradient(120deg, var(--color-accent-600) 0%, var(--color-accent-500) 100%);
}

/* ================= GROUND =================
   The app's base surface and text. Lives here rather than on <body>'s class
   list because the Tailwind palette classes that used to do this job
   (bg-slate-950 et al) were colour literals in disguise and pinned the
   ground to one value regardless of theme. */
body {
    background: var(--color-bg);
    color: var(--color-text);
    font-family: var(--font-ui);
    font-size: var(--fs-body);
    font-weight: var(--fw-body);
    line-height: var(--lh-body);
    -webkit-font-smoothing: antialiased;
}

/* ================= BACKGROUND =================
   There is no lattice and no ambient layer any more (removed 2026-08-01).
   The background of the whole app is the flat --color-bg set on body above,
   exactly as in the mockup. CLAUDE.md §5.5 describes a 72px grid lattice as
   "the whole instrumentation texture"; that is superseded — over real content
   it read as noise rather than instrumentation.

   --line-grid is kept in the palette: it is still the right token for any
   deliberate rule or divider that wants to be fainter than --line-subtle. */

/* ================= GLASS =================
   Floating chrome only — header, tab bar, sheets. Never inside a scrolling
   list, tile or row; never nested in glass. */
.glass {
    background: var(--glass-bg);
    backdrop-filter: blur(14px) saturate(140%);
    -webkit-backdrop-filter: blur(14px) saturate(140%);
    border: 1px solid var(--glass-border);
}
@supports not (backdrop-filter: blur(1px)) {
    .glass {
        background: var(--color-surface);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
}

/* ================= ELEVATION ================= */
.elev-sm { box-shadow: var(--shadow-sm); }
.elev-md { box-shadow: var(--shadow-md); }
.elev-lg { box-shadow: var(--shadow-lg); }

/* Console-era aliases — see COMPATIBILITY note above. */
.elevation-1 { background: var(--color-surface); box-shadow: var(--shadow-sm); }
.elevation-2 { background: var(--color-surface); box-shadow: var(--shadow-md); }
.elevation-3 { background: var(--color-surface); box-shadow: var(--shadow-md); }
.elevation-4 { background: var(--color-surface); box-shadow: var(--shadow-lg); }

/* ================= CDN-INDEPENDENT STRUCTURAL UTILITIES =================
   §8 audit finding (2026-07-31): `.hidden { display: none }` was defined
   ONLY by the Tailwind CDN — nothing in this repo declared it. Verified by
   enumerating every stylesheet at runtime.

   That made an unreachable cdn.tailwindcss.com catastrophic rather than
   cosmetic: every hidden panel, sheet, backdrop, modal AND the onboarding
   overlay would render simultaneously, on top of each other. For an app
   whose premise is working on a bad Sudanese connection — and which ships
   to app stores as a bundled Capacitor build where reaching a CDN at boot
   is a liability, not a convenience — that is the single worst failure
   mode in the codebase.

   These re-declare the handful of utilities whose ABSENCE breaks the app
   rather than merely unstyling it. Identical values to Tailwind's, so
   nothing changes while the CDN is reachable.

   This is defensive, not a fix. The real fix is removing the runtime CDN
   dependency altogether — see the Phase 7 log entry. */
.hidden { display: none !important; }
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
}
.fixed { position: fixed; }
.relative { position: relative; }
.inset-0 { inset: 0; }
.min-h-screen { min-height: 100vh; }
.opacity-0 { opacity: 0; }
.overflow-hidden { overflow: hidden; }

/* Body prose that names the ramp step directly rather than going through
   --text-muted. Same §13 rule, same reason; listed explicitly because a
   blanket :lang(ar) * override would also raise labels and chrome that are
   correctly quieter. */
:lang(ar) .news-feed-foot,
:lang(ar) .arena-nominate-note,
:lang(ar) .you-version,
:lang(ar) .source-note,
:lang(ar) .sources-note,
:lang(ar) .news-card-body,
:lang(ar) .onboarding-sub,
:lang(ar) .onboarding-note,
:lang(ar) .shelf-note-source,
:lang(ar) .news-verify-sub {
    color: var(--color-neutral-400);
}
