/* ═══════════════════════════════════════════════════════════════
   Persona Design Tokens
   Single source of truth for all design values.

   Structure:
     1. Primitive tokens  — raw values (never used directly in components)
     2. Semantic tokens   — purpose-mapped aliases (what components reference)

   Usage:
     Every page loads this file. Change a value here to update the
     entire product — app, admin, marketing, auth, support.
   ═══════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════════
   PERSONA DESIGN SYSTEM  ·  persona-styles.css
   Single source of truth — fonts, design tokens, reset, shared utilities

   HOW TO USE
   ──────────
   Add as the FIRST <link> in every page's <head>:
     <link rel="stylesheet" href="persona-styles.css">

   Page-specific <style> blocks come AFTER this link and override as needed.
   Dark-theme pages set  data-theme="dark"  on <html>.

   TOKEN ALIASES
   ─────────────
   Several tokens have short-hand aliases kept for backwards compatibility
   across the different page groups built at different times:
     --bd  = --border      (support KB pages)
     --bds = --border-soft (support KB pages)
     --fd  = --font-display
     --fm  = --font-mono
     --muted = --text-muted (support KB pages)
     --faint = --text-faint (support KB pages)
     --r   = --radius-md
     --rs  = --radius-sm
     --sh  = --card-shadow
     --pl  = light purple tint
     --pm  = medium purple tint
     --tl  = light teal tint

   ═══════════════════════════════════════════════════════════════════════════ */


/* ─── Fonts ─────────────────────────────────────────────────────────────────
   Loads all four families used across the project in one request.
   Remove individual <link> font tags from every page — this handles them.
   ─────────────────────────────────────────────────────────────────────────── */
/* ═══════════════════════════════════════════════════════════════════════════
   TWO-TIER TOKEN MODEL  —  the customization surface (Phase 3)
   ───────────────────────────────────────────────────────────────────────────
   TIER 1 · PRIMITIVES  — the ONLY tokens a user (or the future customization
     system) edits. Raw brand hues + white-label text/bg + font roles + one
     radius + one elevation. Identical across sections; may differ by theme.
       brand:    --purple --orange --teal --red --yellow
       surface:  --text --bg
       type:     --font-display --font-body --font-mono
       shape:    --radius-md      elevation: --card-shadow

   TIER 2 · DERIVED     — computed FROM a primitive; never edited directly.
     Each is annotated below with `derive:` = the formula the customization
     system's deriver (Phase 8) recomputes when its primitive changes.
     Some (the opaque pastel variants and the -rgb channels) can't be expressed
     in pure CSS from a hex primitive, so they carry the formula as a contract
     for the JS deriver; alpha tints and gradient stops already trace via var().

   TIER 3 · SEMANTIC / SURFACE — bg/text/border/elevation roles. Reference
     primitives or neutral scales; may vary per theme/section.

   RULE: every non-primitive value must trace back to a primitive (see `derive:`
   notes). No free-floating brand values. Changing one primitive is intended to
   recolour every section.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── Light theme tokens (default) ──────────────────────────────────────────
   Used by: marketing website, support knowledge base (these never set
   data-theme, so they always match this bare :root block).
   App pages opt into a separate, slightly different light palette via
   data-theme="light" — see that block further down — and into the dark
   palette via data-theme="dark" (defined below; currently unreachable
   in the UI since no page exposes a working theme toggle, kept for
   forward compatibility).
   ─────────────────────────────────────────────────────────────────────────── */
:root {

  /* ── TIER 1 · PRIMITIVES (brand) — customization knobs ───────────────────── */
  --purple:        #7F00FF;   /* primary  */
  --orange:        #F45D01;   /* accent   */
  --teal:          #007a71;   /* success  */
  --red:           #c41f1f;   /* danger   */
  --yellow:        #b85c00;   /* warning  */

  /* ── TIER 2 · DERIVED brand variants (do not edit; derive from primitive) ── */
  /* Opaque variants are hand-tuned (not a plain mix), so the JS deriver owns
     their formula; alpha tints trace to the primitive today via -rgb/var(). */
  --purple-bright: #9933FF;                      /* derive: lighten(--purple)  ≈ mix 80% white */
  --purple-dim:    #e8d5ff;                      /* derive: tint(--purple)     hand-tuned pastel */
  /* Deliberately NOT redefined per theme (unlike --purple/--purple-bright,
     which both lighten to the same #bf80ff in dark mode — legible as TEXT on
     a dark background, but white text on a FILL of it fails contrast). For a
     solid badge/pill that needs to carry white text in either theme, this
     stays the light theme's own --purple value always. */
  --purple-solid-fill: #7F00FF;
  --orange-soft:   #f97c35;                      /* derive: lighten(--orange)  hand-tuned */
  --orange-dim:    #fff3ed;                      /* derive: tint(--orange)     hand-tuned pastel */
  --teal-dim:      rgba(var(--teal-rgb),.15);    /* derive: --teal @ 15% alpha */
  --teal-bright:   #6df0db;                      /* derive: lighten(--teal) — bright accent for teal gradients */
  --divider:       rgba(var(--purple-rgb),.09);  /* derive: --purple @ 9% — subtle purple-tinted border/divider */
  --red-soft:      rgba(var(--red-rgb),.1);      /* derive: --red  @ 10% alpha */
  --red-dim:       #fff0f0;                      /* derive: tint(--red)        hand-tuned pastel */
  --yellow-dim:    #fffbeb;                      /* derive: tint(--yellow)     hand-tuned pastel */
  --cta-text:      #ffffff;                      /* derive: on(--purple) — readable text on primary */

  /* Readable label text on a WASH OF ITS OWN HUE (chips, soft badges).
     A brand hue on a 12–16% tint of itself is often too light to clear WCAG AA at
     label sizes — teal and orange both failed. These are the same hue darkened just
     enough to pass; purple and red already clear it, so they simply alias the brand
     token. Dark mode re-aliases all four, where the lightened hues pass on their own.

     Re-measured 2026-07-27 across every REAL page (not just the portal's own tint
     specimen): several components sit on a paler wash than the specimen assumed
     (admin-tag at .12 opacity, pill-orange, pill-green, td-cat), so teal/orange
     needed to darken further, and purple-bright — used directly as text in table
     category labels — needed its own text-safe variant for the first time. */
  --purple-text:        var(--purple);           /* derive: on(tint(--purple)) — 5.0:1, passes as-is */
  --red-text:           var(--red);               /* derive: on(tint(--red))    — 4.8:1, passes as-is */
  --teal-text:          #096e5e;                  /* derive: darken(--teal)        — worst real case 4.56:1 */
  --orange-text:        #a14b03;                  /* derive: darken(--orange)      — worst real case 4.51:1 */
  --purple-bright-text: #7400e8;                  /* derive: darken(--purple-bright) — worst real case 4.51:1 */

  /* Danger SURFACE (filled destructive buttons) — deliberately separate from --red.
     --red must stay LIGHT in dark mode so red *text* is legible on dark; a filled
     button needs the opposite (dark enough to carry white text). One token cannot do
     both, so surfaces get their own. In light, --red already carries white at 5.9:1. */
  --danger-solid:      var(--red);               /* derive: surface(--red) — fill for destructive buttons */
  /* NOT var(--orange): white on #F45D01 is 3.27:1 and fails AA. A filled orange surface needs
     its own darker orange, exactly as a filled red one needs --danger-solid. White → 5.2:1. */
  --warning-solid:     #be4500;                  /* surface(--orange) — fill that carries white */
  --danger-solid-text: #ffffff;                  /* derive: on(--danger-solid) — 5.9:1 light / 5.0:1 dark */

  /* ── TIER 3 · SEMANTIC · toast palette — edit here to recolour all toasts ─── */
  /* Default to brand primitives; override any one to change that toast state
     everywhere (all pages read these via persona-styles.css / persona-shared.css). */
  --toast-success: var(--teal);     /* success/confirm */
  --toast-error:   var(--red);      /* error + destructive */
  --toast-warning: var(--warning-solid);   /* warning — the SOLID orange: white text needs it */
  --toast-info:    var(--purple);   /* info */

  /* Gradient stops — TIER 2 derived: already trace to primitives via var() */
  --g1:            var(--purple);   /* derive: = --purple (cascades automatically) */
  --g2:            var(--orange);   /* derive: = --orange (cascades automatically) */

  /* ── TIER 3 · SEMANTIC · brand gradients — single source for every gradient.
     Stops reference the color primitives above, so these adapt to light/dark
     automatically. Edit an angle or stop here to change it everywhere. ──────── */
  --grad-brand:        linear-gradient(135deg, var(--purple), var(--orange));
  --grad-brand-90:     linear-gradient(90deg, var(--purple), var(--orange));
  --grad-brand-180:    linear-gradient(180deg, var(--purple), var(--orange));
  --grad-brand-soft:   linear-gradient(90deg, var(--purple-bright), var(--orange-soft));
  --grad-brand-conic:  conic-gradient(from 180deg, var(--purple), var(--orange), var(--purple));
  --grad-purple:       linear-gradient(135deg, var(--purple), var(--purple-bright));
  --grad-purple-90:    linear-gradient(90deg, var(--purple), var(--purple-bright));
  --grad-orange:       linear-gradient(135deg, var(--orange), var(--orange-soft));
  --grad-purple-glow:  radial-gradient(circle, var(--purple) 0%, transparent 70%);
  --grad-orange-glow:  radial-gradient(circle, var(--orange) 0%, transparent 70%);
  --grad-teal:         linear-gradient(90deg, var(--teal), var(--teal-bright));
  /* Scene / media-placeholder fills — purple-hued, fixed (stay dark/pale in both themes).
     Single source for thumbnail & avatar placeholder backgrounds. */
  --grad-scene:        linear-gradient(160deg, #3a2a72, #160f30);
  --grad-scene-light:  linear-gradient(160deg, #e3d9f9, #cdbdf6);
  --grad-skeleton:     linear-gradient(90deg, var(--text-faint), var(--text-muted));
  --grad-fade-bottom:  linear-gradient(to bottom, transparent 80%, var(--bg) 100%);

  /* ── TIER 1 · surface primitive + TIER 3 semantic text roles ────────────── */
  /* --text is a customization knob; muted/faint derive: shades of --text */
  --text:          #18141f;
  --text-muted:    #5c5868;
  /* Was #6f6c77 — several page-level <style> blocks had their own near-duplicate faint
     greys (#726b85, #8b81a0, #b0a8c0 — footer sub-copy, table meta, format labels)
     that were really this same role, just drifted apart and under AA on their palest
     real background (a lavender-tinted card, 4.37:1). Consolidated onto this one
     token, darkened to clear 4.5:1 on the palest case measured (4.51:1). */
  --text-faint:    #6f6389;

  /* ── TIER 1 · --bg knob + TIER 3 semantic surfaces (derive: shades of --bg) ─ */
  --bg:            #ffffff;
  --bg-alt:        rgba(0,0,0,.028);
  --bg-card:       #ffffff;
  --bg-raised:     rgba(0,0,0,.03);
  --bg-deep:       #f5f4fb;
  --bg-tint:       #f5f3ff;
  --panel:         #ffffff;
  --panel-strong:  rgba(0,0,0,.04);

  /* ── TIER 3 · borders (semantic; neutral alpha over --bg) ────────────────── */
  --border:        rgba(0,0,0,.09);
  --border-soft:   rgba(0,0,0,.06);
  /* WCAG 1.4.11 (non-text contrast, 3:1) needs a border strong enough on its own to
     mark a UI component's edge — --border's .09 is ~1.2:1, nowhere close. Only for
     inputs/buttons/selects whose border IS their boundary; decorative dividers and
     card edges keep --border/--border-soft as before. Calibrated against a plain
     white background — verified 3.0:1+ with tools/border-contrast-check.js. */
  --border-strong: rgba(0,0,0,.45);

  /* ── TIER 1 · --card-shadow elevation knob + TIER 3 shadow roles ─────────── */
  --card-shadow:   0 1px 3px rgba(0,0,0,.06), 0 6px 18px -8px rgba(0,0,0,.09);
  --shadow-lg:     0 8px 32px -8px rgba(var(--black-rgb),.18), 0 2px 8px rgba(var(--black-rgb),.08);
  --shadow-dialog: 0 24px 64px -12px rgba(0,0,0,.22), 0 0 0 1px rgba(0,0,0,.04);

  /* ── TIER 1 · PRIMITIVES (type) — customization knobs ────────────────────── */
  --font-display:  'Unbounded', sans-serif;
  --font-body:     'Inter', sans-serif;
  --font-mono:     'IBM Plex Mono', monospace;
  --font-hebrew:         'Heebo', sans-serif;   /* Hebrew BODY face */
  --font-hebrew-display: 'Heebo', sans-serif;   /* Hebrew DISPLAY face — see the note below */

  /* ── TIER 1 · --radius-md knob; sm/lg/xl derive: steps off --radius-md ────── */
  --radius-sm:   8px;
  --radius-md:  14px;
  --radius-lg:  20px;
  --radius-xl:  24px;

  /* Layout helpers --------------------------------------------------------- */
  --sw:          230px;  /* sidebar width  — admin portal        */
  --tw:          210px;  /* TOC width      — support KB          */

  /* Create-flow / video player specific ------------------------------------ */
  --preview-max-h: 520px;
  --ratio-w:       9;
  --ratio-h:       16;

  /* ── Backwards-compat aliases ───────────────────────────────────────────── */
  --blob-op:       0.55;

  /* RGB channels for rgba() usage — TIER 2 derived (channels of each primitive).
     CSS can't split a hex primitive into a "r, g, b" list, so these are the one
     place the customization deriver MUST recompute in JS when a primitive
     changes (channels of --purple, --orange, ...). Keep in sync with primitives. */
  --purple-rgb:    127, 0, 255;    /* derive: channels(--purple) */
  --orange-rgb:    244, 93, 1;     /* derive: channels(--orange) */
  --teal-rgb:      0, 122, 113;    /* derive: channels(--teal)   */
  /* The one flat wash for "this is selected/active" behind existing text —
     replaces ~15 one-off rgba(var(--purple-rgb), 0.0X) literals that had each
     picked their own opacity independently. Verified >13:1 text contrast in
     both themes at this strength; not a contrast-driven value, a consistency
     one — .1 was already the de facto majority before consolidating. */
  --selected-fill: rgba(var(--purple-rgb), 0.1);
  --red-rgb:       196, 31, 31;    /* derive: channels(--red)    */
  --yellow-rgb:    184, 92, 0;     /* derive: channels(--yellow) */
  --white-rgb:     255, 255, 255;
  --black-rgb:     0, 0, 0;
  --ink-rgb:       20, 20, 30;
  --text-rgb:      24, 20, 31;     /* derive: channels(--text) */
  --bg-rgb:        255, 255, 255;  /* derive: channels(--bg)   */
}


/* ─── Dark theme tokens ──────────────────────────────────────────────────────
   Used by: app pages (dashboard, videos, characters, create-flow,
            login, signup, upgrade)
   Activate by adding  data-theme="dark"  to <html>.
   ─────────────────────────────────────────────────────────────────────────── */
:root[data-theme="dark"] {

  /* Brand ------------------------------------------------------------------ */
  --purple:        #bf80ff;
  --purple-bright: #bf80ff;
  --purple-dim:    #2d1060;

  --orange:        #ff7a2e;
  --orange-soft:   #ffb38a;
  --orange-dim:    rgba(var(--orange-rgb),.15);

  --teal:          #48e0c2;
  --teal-dim:      rgba(var(--teal-rgb),.12);

  --red:           #ff5c5c;
  --red-soft:      rgba(var(--red-rgb),.15);
  --red-dim:       rgba(var(--red-rgb),.15);

  --yellow:        #f0b429;
  --yellow-dim:    rgba(var(--yellow-rgb),.15);

  /* On dark, the lightened hues already clear AA on their own tints (5.3–8.5:1),
     so the on-tint text colours are simply the brand hues — no darkening needed. */
  --teal-text:          var(--teal);
  --orange-text:        var(--orange);
  --purple-bright-text: var(--purple-bright);

  /* --red lightens to #ff5c5c here so red TEXT stays legible — but white on that
     is only 3.0:1. Filled destructive buttons therefore use their own darker red. */
  --danger-solid:  #d92222;
  /* White on --orange (#F45D01) is 3.27:1 — it FAILS AA. The warning toast therefore used DARK
     text, and was the only toast in the product that did: it looked like a mistake because it
     was solving a real problem the wrong way. Same trap as --red: one hue cannot be both a light
     text colour AND a fill that carries white. This is the fill. White on it → 5.2:1. */
  --warning-solid: #be4500;

  --g1:            var(--purple);
  --g2:            var(--orange);

  /* Text ------------------------------------------------------------------- */
  --text:          #f3f1fb;
  --text-muted:    #a59cc9;
  /* Was #716898 — only 3.3–3.8:1 on the dark surfaces it actually sits on (bg, panel,
     panel-strong), i.e. below AA for normal text. It carries real copy (video-card meta,
     share URLs, character meta), so it was a live dark-mode a11y failure; the design portal
     surfaced it the first time those components were previewed. Lightened to the minimum
     that clears 4.5:1 everywhere (worst case 4.70:1 on --panel-strong) while staying visibly
     fainter than --text-muted (6.5–7.6:1).
     Was then #8a82b0 — a second real page (not a portal specimen) put it on a slightly
     darker surface than the portal had checked (4.18:1). Lightened again to clear
     4.5:1 on that case too. */
  --text-faint:    #9088b4;

  /* Surfaces --------------------------------------------------------------- */
  --bg:            #0d0a20;
  --bg-alt:        #090718;
  --bg-card:       rgba(var(--white-rgb),.05);
  --bg-raised:     rgba(var(--white-rgb),.04);
  --bg-deep:       #090718;
  --panel:         rgba(var(--white-rgb),.045);
  --panel-strong:  rgba(var(--white-rgb),.07);

  /* Borders ---------------------------------------------------------------- */
  --border:        rgba(var(--white-rgb),.09);
  --border-soft:   rgba(var(--white-rgb),.06);
  /* See the light-theme --border-strong note above — same reasoning, calibrated
     against this theme's own dark background. */
  --border-strong: rgba(var(--white-rgb),.38);

  /* App-page-specific (no light-theme equivalent needed elsewhere) --------- */
  --blob-op:       0.35;
  --cta-text:      #1a0d05;

  /* Shadows ---------------------------------------------------------------- */
  --card-shadow:   none;
  --shadow-lg:     0 8px 32px -8px rgba(var(--black-rgb),.18), 0 2px 8px rgba(var(--black-rgb),.08);
  --shadow-dialog: 0 24px 64px -12px rgba(var(--black-rgb),.55), 0 0 0 1px rgba(var(--white-rgb),.06);

  /* RGB channels — dark theme values --------------------------------------- */
  --purple-rgb:    191, 128, 255;
  --selected-fill: rgba(var(--purple-rgb), 0.1);
  --orange-rgb:    255, 122, 46;
  --teal-rgb:      72, 224, 194;
  --red-rgb:       255, 92, 92;
  --yellow-rgb:    240, 180, 41;
  --text-rgb:      243, 241, 251;
  --bg-rgb:        13, 10, 32;
}


/* ─── App-page light theme ───────────────────────────────────────────────────
   Used by: app pages (dashboard, videos, characters, create-flow, account,
   admin, login, signup, etc) when in light mode — opted into via
   data-theme="light" on <html>. Distinct from the bare :root block above
   (marketing/support), which uses a slightly different palette (e.g. --bg).
   ─────────────────────────────────────────────────────────────────────────── */
:root[data-theme="light"] {
  /* White on --orange is 3.27:1 — it fails AA. Filled orange surfaces (the warning toast)
     need a darker orange, exactly as filled red ones need --danger-solid. White → 5.2:1. */
  --warning-solid: #be4500;
  --bg:            #ffffff;
  --bg-deep:       #f5f4fb;
  --bg-tint:       #f5f3ff;
  --panel:         #ffffff;
  /* Was #f3f4f8 — OPAQUE, while the same token is translucent in the base and dark themes.
     An opaque tint cannot blend with what is behind it, so anything using --panel-strong on a
     TRANSLUCENT surface (the wizard's sticky footer, dialogs, the top bars) rendered differently
     depending on what happened to be scrolled underneath: the back button's hover fill read as a
     grey pill on one backdrop and a white pill on another. Same rule, different appearance.
     rgba(black,.045) over white is the same colour it always was — and now it behaves like a
     tint everywhere else does. */
  --panel-strong:  rgba(var(--ink-rgb),.05);
  --border:        rgba(var(--ink-rgb),0.1);
  --border-soft:   rgba(var(--ink-rgb),0.07);
  /* See the light-theme --border-strong note above — same reasoning, calibrated
     against this bare-:root default (marketing/support pages before a theme loads). */
  --border-strong: rgba(var(--ink-rgb),0.5);
  
  
  
  
  --orange-soft:   #f97c35;
  
  
  --red-soft:      #fca5a5;
  --text:          #18141f;
  --text-muted:    #5c5868;
  /* See the bare :root --text-faint note above — same consolidation, same value. */
  --text-faint:    #6f6389;
  --blob-op:       0.05;
  --cta-text:      #fff;
  --card-shadow:   0 1px 2px rgba(var(--ink-rgb),0.05), 0 10px 26px -14px rgba(var(--ink-rgb),0.14);
  
  /* RGB channels for rgba() usage */
  --purple-rgb:    127, 0, 255;
  --selected-fill: rgba(var(--purple-rgb), 0.1);
  --orange-rgb:    244, 93, 1;
  --teal-rgb:      0, 122, 113;
  --red-rgb:       196, 31, 31;
  --yellow-rgb:    184, 92, 0;
  --white-rgb:     255, 255, 255;
  --black-rgb:     0, 0, 0;
  --ink-rgb:       20, 20, 30;
  --text-rgb:      24, 20, 31;
  --bg-rgb:        255, 255, 255;
}


/* ─── Base reset ─────────────────────────────────────────────────────────────
   Applied once here — remove equivalent blocks from page <style> tags.
   ─────────────────────────────────────────────────────────────────────────── */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

/* ============================================================
   RTL: mirror the directional gradients.

   A gradient angle is PHYSICAL. `linear-gradient(135deg, purple, orange)` runs
   top-left → bottom-right in every language, so in Hebrew the brand gradient ran the
   wrong way across the CTA, the sidebar's active item and the brand mark. Nothing
   caught it: the RTL gate checks position and text direction, not paint direction.

   Mirroring an angle about the vertical axis is `360 - angle`. 180deg is vertical and
   is therefore unchanged. Done here, on the tokens, so every consumer inherits it —
   rather than chasing each rule that happens to use one.
   ============================================================ */
html[dir="rtl"]{
  --grad-brand:        linear-gradient(225deg, var(--purple), var(--orange));
  --grad-brand-90:     linear-gradient(270deg, var(--purple), var(--orange));
  --grad-brand-soft:   linear-gradient(270deg, var(--purple-bright), var(--orange-soft));
  --grad-purple:       linear-gradient(225deg, var(--purple), var(--purple-bright));
  --grad-purple-90:    linear-gradient(270deg, var(--purple), var(--purple-bright));
  --grad-orange:       linear-gradient(225deg, var(--orange), var(--orange-soft));
  --grad-teal:         linear-gradient(270deg, var(--teal), var(--teal-bright));
  --grad-scene:        linear-gradient(200deg, #3a2a72, #160f30);
  --grad-scene-light:  linear-gradient(200deg, #e3d9f9, #cdbdf6);
  --grad-skeleton:     linear-gradient(270deg, var(--text-faint), var(--text-muted));
}

/* ============================================================
   Hebrew typography — THE ONE PLACE.

   This override used to live in FIVE files (persona-admin.css, persona-app.css,
   persona-auth.css, 404.html, persona-upgrade.html), copy-pasted. A page that loaded none of
   those sheets never got the Hebrew face at all — persona-portal.html did not even load the
   font. The token was single-sourced; the thing that overrode the token was not.

   To change the Hebrew font: edit --font-hebrew above. That is the whole job.
   ============================================================ */
html[lang="he"]{
  --font-display: var(--font-hebrew-display);
  --font-body:    var(--font-hebrew);
}

/* ── A NOTE ON THE HEBREW DISPLAY FACE ────────────────────────────────────────
   Unbounded — the display face that gives this brand its character — has NO HEBREW GLYPHS.
   So Hebrew headings fall back to the body face, and heading and body become the same font.
   The brand flattens: a Hebrew reader sees a plainer, more generic identity than an English
   reader does, and nothing in the codebase says so.

   --font-hebrew-display is currently set to Heebo, which changes nothing — it just makes the
   decision visible instead of accidental. To give Hebrew its own display voice, change that
   ONE line. Candidates, all SIL Open Font Licence, all free for commercial use:

     'Rubik'         geometric, wide weight range (300–900), the closest in spirit to Unbounded
     'Secular One'   bold and confident, but a single weight — headings only
     'Suez One'      a display serif with real personality; single weight
     'Assistant'     clean and neutral — safe, but adds little contrast against Heebo

   Whichever you pick, add it to tools/fetch-fonts.sh so it is downloaded and self-hosted with
   the rest. This is now a one-line experiment — that is the point of a single source.
   ──────────────────────────────────────────────────────────────────────────── */
