/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   VOLURR AGI — TYPOGRAPHY CONTRACT              app/static/css/typography-contract.css
   Phase 3 · Step 1 · 2026-08-27 · CALENDAR PILOT ONLY

   OWNER DECISION: Candidate B — Latin UI = Inter, Arabic + Kurdish/Sorani = Vazirmatn,
   numeric/mono keeps its current stability (JetBrains Mono) and is bound to the central contract.

   WHY A CONTRACT AND NOT A FAMILY SWAP. Measured in Phase 3: VOLURR had FIVE competing font
   contracts for one idea — --gi-num (46 consumers, gold.css), --mono (33, news-terminal.css),
   --font-mono (30, tokens.css), plus 55 raw ui-monospace and 51 raw 'JetBrains Mono' — and the
   --vt-* contract Phase 1 declared for exactly this purpose had ZERO consumers. Swapping a family
   before unifying the contract means doing the work twice.

   SCOPE. The tokens below are declared globally because declaring a custom property changes
   nothing until something reads it. Everything that APPLIES them is scoped to #v-calendar.
   No other surface can move. Verified by the 36-cell before/after in
   docs/typography/evidence/phase3-calendar-pilot.json.

   SELF-HOSTED, AND THAT IS THE POINT. Production loads six families from fonts.googleapis.com
   today and self-hosts exactly one face. These seven files are the official upstream builds,
   unmodified, each with its OFL text and a PROVENANCE.json recording the source URL and sha256.
   They are VARIABLE: one file per family covers the whole 400..900 axis, which is what makes
   VOLURR's 550/620/650/750/820/850 real instead of rounded — measured, see the report.

   NOT A REDESIGN. No font-size, spacing, padding, margin, colour, radius, line-height, translation
   or bidi markup is touched here. Phase 1H's dir="ltr" and <bdi> fixes are untouched by
   construction: this file contains no direction, no unicode-bidi, and no [lang] selector.
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */

/* ── Inter · variable 400..900 · SIL OFL 1.1 · 133,324 bytes in 2 files ── */
@font-face{
  font-family:'Inter';
  font-style:normal;
  font-weight:400 900;
  font-display:swap;
  src:url('/static/fonts/inter/inter-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Inter';
  font-style:normal;
  font-weight:400 900;
  font-display:swap;
  src:url('/static/fonts/inter/inter-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── Vazirmatn · variable 400..900 · SIL OFL 1.1 · 102,692 bytes in 3 files ── */
@font-face{
  font-family:'Vazirmatn';
  font-style:normal;
  font-weight:400 900;
  font-display:swap;
  src:url('/static/fonts/vazirmatn/vazirmatn-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Vazirmatn';
  font-style:normal;
  font-weight:400 900;
  font-display:swap;
  src:url('/static/fonts/vazirmatn/vazirmatn-arabic.woff2') format('woff2');
  unicode-range:U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
@font-face{
  font-family:'Vazirmatn';
  font-style:normal;
  font-weight:400 900;
  font-display:swap;
  src:url('/static/fonts/vazirmatn/vazirmatn-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── JetBrains Mono · variable 400..800 · SIL OFL 1.1 · 43,056 bytes in 2 files ── */
/* font-display:block on the numeric face: a price must never paint in a fallback
   metric and then jump when the real face lands. */
@font-face{
  font-family:'JetBrains Mono';
  font-style:normal;
  font-weight:400 800;
  font-display:block;
  src:url('/static/fonts/jetbrains-mono/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'JetBrains Mono';
  font-style:normal;
  font-weight:400 800;
  font-display:block;
  src:url('/static/fonts/jetbrains-mono/jetbrains-mono-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* ── THE CONTRACT ─────────────────────────────────────────────────────────────────────────────
   Five names, each keeping an Arabic-capable tail. Phase 1's law holds: EXTEND a stack, never
   truncate it — a face that cannot draw a glyph must fall to one that can, never to the OS.

   Mixed content works PER CHARACTER with no language rule and no [lang] selector: Inter carries no
   Arabic glyph, so an Arabic character inside an English sentence falls to Vazirmatn on that
   character alone. Nothing here can fight the Phase 1H bidi fixes. */
:root{
  /* ── WAVE 0 · GLOBAL SCOPE = TODAY'S VALUES, BYTE FOR BYTE ────────────────────────────────
     Architecture convergence and typeface migration are DIFFERENT JOBS and this file now keeps
     them apart. Candidate B is certified on the calendar and nowhere else, so the global values
     below are exactly what the app renders today; only the NAMES are new.

     Why that separation is not pedantry. MEASURED on this machine: `ui-monospace` does not
     resolve on Windows at all - it measures 300px for "0123456789" at 60px, identical to a
     nonexistent family - so every legacy mono stack already falls through to JetBrains Mono, and
     the legacy and Candidate-B stacks are indistinguishable HERE (0 of 14 samples differ).
     But `ui-monospace` DOES resolve on macOS and iOS, to SF Mono. Pointing the legacy contracts
     at the JetBrains-led Candidate-B value would therefore be invisible on this machine and a
     real face change for every Apple reader - a change nothing in this environment can test.
     So the global value keeps ui-monospace in the lead and Candidate B stays scoped. */
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Cairo','Noto Kufi Arabic','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Cairo','Inter',system-ui,sans-serif;
  --volurr-font-numeric: ui-monospace,'SF Mono','JetBrains Mono',Menlo,Consolas,'Vazirmatn','Noto Kufi Arabic','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-mono:    ui-monospace,'SF Mono','JetBrains Mono',Menlo,Monaco,Consolas,'Vazirmatn','IBM Plex Sans Arabic',monospace;
  /* The news tape is the one surface whose mono stack carries 'Cascadia Mono', and that is NOT
     cosmetic drift: Cascadia exists on Windows and Monaco does not, so for any glyph JetBrains
     Mono lacks the two stacks land on different faces. Measured on real painted content -
     U+25AA the tape actually prints moves 7.0313px -> 6.6094px if Cascadia is dropped. Wave 0
     preserves it exactly; whether the terminal SHOULD keep Cascadia is a typeface decision. */
  --volurr-font-mono-terminal: ui-monospace,'SF Mono','JetBrains Mono','Cascadia Mono',Menlo,Consolas,'Vazirmatn','IBM Plex Sans Arabic',monospace;
}

/* ── PILOT APPLICATION — CALENDAR ONLY ────────────────────────────────────────────────────────
   The alias layer is scoped here rather than global on purpose. Aliasing --font-mono / --mono /
   --gi-num at :root would move gold, news-terminal and twenty other files in the same commit,
   which is exactly what Step 1 must not do. Inside #v-calendar the three legacy names resolve to
   the one contract; outside it they are untouched. */
#v-calendar{
  /* ── CANDIDATE B — certified at commit 4167073b, scoped, unchanged by Wave 0 ──────────────
     Wave 0 moved the GLOBAL values back to the incumbent stacks, so the calendar states its own
     Candidate B values here rather than inheriting them. The rendered result on this surface is
     byte-identical to what 4167073b certified. */
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;
  --volurr-font-numeric: 'JetBrains Mono','Inter','Vazirmatn','IBM Plex Sans Arabic',ui-monospace,monospace;
  --volurr-font-mono:    'JetBrains Mono',ui-monospace,'SF Mono',Menlo,Monaco,Consolas,'Vazirmatn','IBM Plex Sans Arabic',monospace;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --font-mono:   var(--volurr-font-mono);
  --mono:        var(--volurr-font-mono);
  --gi-num:      var(--volurr-font-numeric);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);
  --vt-mono:     var(--volurr-font-mono);

  font-family: var(--volurr-font-ui);
}

/* The calendar's own controls say `font:inherit` / `font-family:inherit` in 14 places, so they
   follow the line above. These two are the only hardcoded families inside news-calendar.css and
   they are re-pointed at the contract rather than rewritten in that file. */
#v-calendar .tgw-hint,
#v-calendar .tgw-prev code{ font-family: var(--volurr-font-mono); }

/* Form controls do not inherit a family from an ancestor — the browser gives them the OS UI font
   unless told otherwise. base.css already fixes this for html[lang="ar"]; inside the calendar the
   contract must cover every language, or a Kurdish <select> silently drops to the OS face. */
#v-calendar button,
#v-calendar input,
#v-calendar select,
#v-calendar textarea{ font-family: var(--volurr-font-ui); }


/* ── PHASE 3 STEP 2 — CONTAINER FIT, forced by the new metrics ────────────────────────────────
   These are LAYOUT fixes, not typography. They belong in news-calendar.css at migration time and
   are kept here only so the whole pilot stays one file and one rollback.

   The font contract is not the problem and is not touched: no size, no weight, no letter-spacing,
   no transform, no font-stretch. Two containers were dimensioned for the old metrics.

   ROOT CAUSE 1 — .cev-time is a FIXED 56px column (46px under 560px) with flex-shrink:0, and it
   holds .cev-t. When an event is time_tentative the label becomes t('tentative'), and the English
   string "Tentative" measures 63.09px at 13.5px/800 — it cannot fit 46px, over by 17.09px.
   MEASURED: 67 clipped elements in EVERY English cell, all 12. Arabic "مبدئي" and Kurdish "کاتی"
   are short and clipped ZERO, so this is an English-only overflow of a fixed box.

   Of the four options weighed — bigger fixed width, min-width, content-sized, grid track — a fixed
   bump was rejected because it only moves the ceiling and the next translation hits it again.
   min-width lets the column grow for the one label that needs it while every clock time (38.48px)
   still gets the same 56px, so the time column stays aligned exactly as before. */
#v-calendar .cev .cev-time{ width:auto; min-width:56px; }
@media(max-width:560px){ #v-calendar .cev .cev-time{ min-width:46px; } }

/* ROOT CAUSE 2 — a date is one unit and must never break across lines. .dayhdr .dd is styled in
   chart-panel.css with no white-space rule, so "2026-08-27" wrapped to two lines at 375px once
   Inter widened it. nowrap is the whole fix; .dn beside it already shrinks. */
#v-calendar .dayhdr .dd{ white-space:nowrap; }

/* DELIBERATELY NOT CHANGED, each with its measured reason:
   · .fcount  — its ancestor .frow.franges is overflow-x:auto / flex-wrap:nowrap, a deliberate
                horizontal scroll strip, and the document itself does not scroll (docScrollX
                false). Content scrolled out of a scroller is not "offscreen"; the metric was
                wrong, not the layout.
   · .fhint   — flex:1 1 100%, a hint on its own full-width line. A long English hint wrapping to
                two lines is what a hint does. Recorded as INTENTIONAL WRAP, not hidden as a pass.
   · .cev-name — flex:1 with min-width:0; it did not reproduce under the worst-case string at any
                width. Nothing to fix without inventing a defect. */

/* ── WAVE 1 · CANDIDATE B ON THE WHY SURFACE ───────────────────────────────────────────────
   `why` was chosen by measurement, after Settings turned out to be auth-gated and 97% of it
   unmeasurable. It renders fully for a guest in all three languages, carries mixed-script text
   so the migration is actually exercised, and why.css declares no font family of its own - its
   three font rules are all `font: inherit`. Nothing in the app sets font-family on #v-why, so
   this one block is the entire migration and why.css needs no edit at all.

   NUMERIC AND MONO ARE DELIBERATELY NOT OVERRIDDEN. The brief keeps them on today's rendering
   unless a measurement says otherwise, so they continue to resolve from the global Wave 0
   contract. Only latin, arabic and ui move.

   What this actually changes, measured before it was applied: the surface inherits
   `body{font-family:'Cairo','Inter',system-ui,sans-serif}`, so Arabic paints Cairo and the five
   Sorani letters Cairo lacks - ڕ ڵ ۆ ێ ە - fall through to IBM Plex Sans Arabic. Under Candidate B
   all thirteen come from one face. */
#v-why{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}

/* ── WAVE 1 · CANDIDATE B ON THE RELATIVE VALUE SURFACE ────────────────────────────────────
   relvalue was selected by measurement after `why` certified: 1771-1883 leaves per cell with
   perfect parity across en/ar/ku, zero baseline clipping, zero OS fallback, zero face ties, and
   576 mixed-script leaves, so the migration is genuinely exercised rather than trivially green.
   rvil.css already neutralises the global html[lang="ar"] button rule with eight
   `font-family: inherit` declarations, which is why one scoped block reaches the whole surface.

   NUMERIC AND MONO ARE NOT OVERRIDDEN. 95% of this surface is tabular-nums (61,068 of 64,224
   measured leaves) and it is the most numeric-heavy surface in the app, so numeric and mono keep
   resolving from the global Wave 0 contract. The `<code>` diagnostics stay on their mono face by
   design - correct adoption is semantic, not uniform. */
#v-relvalue{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}

/* ── WAVE 1 · CANDIDATE B ON THE ACADEMY SURFACE ───────────────────────────────────────────
   The cleanest baseline measured in this campaign: 148 leaves in every one of 36 cells, zero
   clipping, zero offscreen, zero document overflow, zero OS fallback and zero face ties, with 240
   mixed-script leaves so the migration is genuinely exercised. academy-page.css declares no font
   family of its own and already carries seven `font-family: inherit` neutralisers - its authors hit
   the user-agent button-font defect and fixed it, and said so at academy-page.css:487. That is why
   one scoped block reaches the whole surface.

   THE CANVAS AND SVG PATHS ARE OUT OF SCOPE, MEASURED RATHER THAN ASSUMED. academy.js has exactly
   one canvas and it draws no text at all - its only context call is drawImage, rasterising the
   certificate for download; there is no ctx.font or fillText in the file. The thirteen <text>
   occurrences are one progress-gated certificate that builds into #modalBox, outside #v-academy,
   with a deliberate single-family face per language (Georgia for Latin, Segoe UI / Noto Naskh
   Arabic for RTL). The page itself renders no SVG text: the nodes present in the DOM belong to
   #dCandles and measure 0x0. Numeric and mono stay on the global Wave 0 contract. */
#v-academy{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}

/* ── WAVE 1 · CANDIDATE B ON THE COMMUNITY / COPY SURFACE ──────────────────────────────────
   Measured before migrating, in the discriminating language: script adoption already 100% in ar
   and ku, all-visible-leaf 98.35%, and a runtime simulation clean on every axis - no new clipping,
   no new wraps in ar or ku, no bidi change, Sorani 0/13 to 13/13.

   TWO THINGS RECORDED RATHER THAN ABSORBED. This surface carries a PRE-EXISTING horizontal
   document overflow at 1366 in every language; the simulation shows it unchanged (true before,
   true after), so typography does not worsen it, and it stays a layout item rather than a
   typography one. And the leaves that appear to leave the viewport sit inside `.cmv-drow`, a
   container with overflow-x:auto whose scrollWidth already exceeded its clientWidth before this
   change - reachable by scrolling, not lost, the same shape as the relvalue tab strip.

   Numeric and mono keep resolving from the global Wave 0 contract. */
#v-copy{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}

/* ── #v-alerts ── WAVE 1 · Candidate B ────────────────────────────────────────────────────────────
   The alerts page owns no CSS file of its own: `grep -l v-alerts app/static/css/*.css` returns
   nothing, and its classes (.lalert, .la-ic, .la-d, .alsel) live in the shared news-calendar.css.
   So the whole migration is this block. news-calendar.css is NOT edited — it is shared with the
   calendar surface, which is already certified, and with news and deep, which are not yet migrated.

   THE CONTROL NEUTRALISER IS THE WHOLE POINT HERE. Measured before the change, English and Kurdish
   both read 18/18 script adoption while Arabic read 16/18 — the two missing leaves being
   <button.btn.albtn> and <button.btn> painting Cairo. That is base.css:101,
   `html[lang="ar"] button`, a DIRECT declaration on the control, which beats inheritance from the
   root no matter what the root says. Only Arabic exposes it, which is why Arabic is the language
   this campaign measures in.

   MEASURED, 6 cells (en/ar/ku x 375/1366): newClip 0, newVClip 0, newOff 0, bidi 0,
   docOverflow false->false in every cell, Sorani coverage 0/13 -> 13/13 in every cell.

   ONE NEW WRAP, CLASSIFIED, NOT WAVED THROUGH. In en/375 one leaf goes 1 -> 2 lines: the detail
   line of a signal alert ("Overall AGI Confidence 51% ..."), width unchanged at 255px, deterministic
   across repeat runs. It sits in `.lalert .la-d` (news-calendar.css:279), a -webkit-line-clamp:2
   box, so two lines is exactly the clamp and no text is lost. Recorded as boundary-adjacent:
   a future string one word longer would truncate there, with or without this change.

   RESIDUAL IS TWO LEAVES AND BOTH ARE CORRECT. `<span.la-ic>` carries `tp.e`, an emoji
   (alerts.js:137). No UI font should draw it and none does — OS-FALLBACK is the right answer, not a
   miss. Also present: 239 <option> elements with no box at all, inside the symbol <select>. They
   cannot clip and are excluded from geometry; the select itself is neutralised below, which is the
   only lever CSS has over them.

   Numeric and mono keep resolving from the global Wave 0 contract. */
#v-alerts{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated. A list of class names rots one sibling at a time — that is how
   .rv-ancset was missed on relvalue and .rv-fav after it. Every control on the surface, by element. */
#v-alerts button,
#v-alerts input,
#v-alerts select,
#v-alerts textarea{ font-family: var(--volurr-font-ui); }

/* ── #v-channel ── Candidate B (R6, 2026-10-04) ─────────────────────────────────────────────────────
   The Channel Studio («جهّز رسالة لقناتك», js/pages/channel-studio.js) took the slot of the Reports page, which the
   owner deleted. The contract that surface carried moves to its successor: the same Candidate B families, the same
   positional control rule. Its stylesheet (css/channel-studio.css) reads --volurr-font-ui and declares no family of
   its own. NOT YET MEASURED in this campaign's six-cell harness (en/ar/ku x 375/1366) — the Reports measurements
   recorded here before were of a page that no longer exists, so they were removed, not carried. */
#v-channel{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated: every control, by element. */
#v-channel button,
#v-channel input,
#v-channel select,
#v-channel textarea{ font-family: var(--volurr-font-ui); }

/* ── #v-deep ── WAVE 1 · Candidate B ──────────────────────────────────────────────────────────────
   #v-deep is an EXTRA_VIEW (shell.js:22), not a PAGES entry, and it owns no stylesheet. It borrows
   .nsrc from news-calendar.css and SRC_TIER from dashboard.js. Neither is edited.

   MEASURED, all six cells (en/ar/ku x 375/1366), and every cell is byte-identical to every other:
   newClip 0, newVClip 0, newWrap 0, newOff 0, bidi 0, docOverflow false->false, Sorani 0/13 -> 13/13.
   Not one geometric change anywhere on the surface. Script adoption is 7/7 = 100%.

   THE STRICT ALL-VISIBLE NUMBER IS 37/72 = 51.39% AND THAT IS NOT A MISS. Every one of the 35
   residual leaves is a symbol leaf the contract cannot and should not claim, and each is identified:
     20x  <div> holding "(green) 5 · (red) 3 · (white) 2"   - ai.js:1430, emoji-led source sentiment
      8x  <span.nsrc.t-tru>  the check mark U+2713          - SRC_TIER.trusted, dashboard.js:1849
      4x  <span.v.up|.down>  direction arrows               - dirArrow()
      1x  <span.nsrc.t-off>  U+1F6E1 shield                 - SRC_TIER.official
      1x  <span.nsrc.t-agg>  U+26A0 warning                 - SRC_TIER.aggregator
   UNKNOWN is zero.

   THE CHECK MARK IS THE INTERESTING ONE. It resolves to 'IBM Plex Sans Arabic', which looks like a
   non-adopting leaf but is the correct answer: U+2713 is in Dingbats, outside the unicode-range of
   both self-hosted Inter and Vazirmatn (see the @font-face blocks above), and IBM Plex is the only
   family in the chain that carries the glyph. Naming a face is not the same as failing to adopt one.

   AND B IS A CONSERVATIVE LOWER BOUND HERE. The resolver classifies a leaf by ONE sampled character,
   preferring a Latin letter, then an Arabic letter, then simply the first character -- and its Latin
   test is /[A-Za-z]/, which does not match digits. So a line reading "(green) 5 · (red) 3" is judged
   on its emoji even though its digits do paint in the contract face. That understates adoption on
   symbol-led mixed leaves and never overstates it, so it is left alone rather than changed midway
   through a campaign whose six earlier surfaces were all measured with the same rule.

   Numeric and mono keep resolving from the global Wave 0 contract. */
#v-deep{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated. A list of class names rots one sibling at a time - that is how
   .rv-ancset was missed on relvalue and .rv-fav after it. Every control, by element. */
#v-deep button,
#v-deep input,
#v-deep select,
#v-deep textarea{ font-family: var(--volurr-font-ui); }

/* ── #v-portfolio ── WAVE 1 · Candidate B ──────────────────────────────────────────────────────────
   The VR practice wallet. It owns no stylesheet -- `grep -l v-portfolio app/static/css/*.css` returns
   nothing -- so the whole migration is this block plus the control neutraliser. Nothing shared is
   edited.

   MEASURED, all six cells (en/ar/ku x 375/1366), and every cell is identical:
   matched coverage 13/13 = 100%, newClip 0, newVClip 0, newOff 0, bidi 0, docOverflow false->false,
   Sorani 0/13 -> 13/13. Script adoption 9/9 = 100%.

   ONE NEW WRAP, at en/1366 only: the page subtitle ("Practice wallet - virtual balance for...")
   goes 1 -> 2 lines in a container computing overflow:visible with auto height, width unchanged.
   Nothing can be truncated there. INTENTIONAL RESPONSIVE WRAP.

   RESIDUAL IS TWO LEAVES AND BOTH ARE CORRECT: <button.walc-eye> carries the balance-visibility
   emoji (portfolio.js:34) and <span.walact-ic> carries the deposit/withdraw arrows and the padlock
   (portfolio.js:40-42). No UI font should draw those and none does.

   WHAT WAS NOT MEASURED, AND IT MATTERS. Thirteen geometry-bearing leaves is the REAL mounted state
   for a visitor with no transactions -- which is what every new user sees -- but it is not the only
   state. A populated wallet renders `.waltx` rows (portfolio.js:47): `.waltx-ic` an emoji, `.waltx-t`
   and `.waltx-m` plain text, `.waltx-st` a status chip, and `.waltx-am` a dir="ltr" amount. Those
   rows inherit from this same root and from the same neutraliser, so the risk is low -- but low is
   not measured, and populating a wallet to raise the number would mean fabricating transactions to
   verify, which this campaign does not do.

   Numeric and mono keep resolving from the global Wave 0 contract. */
#v-portfolio{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated. A list of class names rots one sibling at a time - that is how
   .rv-ancset was missed on relvalue and .rv-fav after it. Every control, by element. */
#v-portfolio button,
#v-portfolio input,
#v-portfolio select,
#v-portfolio textarea{ font-family: var(--volurr-font-ui); }

/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   AR/KU TRACKING NORMALIZATION — the Phase 1H law, applied beyond the calendar pilot
   ───────────────────────────────────────────────────────────────────────────────────────────────
   WHY. `letter-spacing` and `text-transform` are Latin devices. In Arabic script positive tracking
   is inserted between every glyph pair and breaks the cursive join -- the word stops being one
   connected form and becomes a row of detached letters. `uppercase` is a no-op on a unicase script
   and a corruption risk on any Latin fragment sharing the element. news-calendar.css:605 established
   this for #v-calendar; these are the surfaces it could not reach.

   MEASURED with tools/typo/ar_tracking_audit.js, ar AND ku, 1366, per selector, counting
   pure / mixed / latin / inside-[dir=ltr]. A selector is reset ONLY when it is 100% pure
   Arabic-script in BOTH languages. Everything else is left alone and listed in the report.

     surface     selector                 pure ar / pure ku
     reports     .nsl                        6 / 6      ← the defect found in Run 1, now measured
     portfolio   .cpdemo                     1 / 1
     portfolio   .walc-bll                   1 / 1
     market      .mov-alive                  9 / 9
     market      .mov-pt                     6 / 6
     market      .mov-pill.down              6 / 6
     market      .mov-pill.up                5 / 5
     market      .mov-aval.mut               3 / 3
     market      .mov-pst.down               2 / 2
     market      .mov-age.warn               1 / 1
     market      .mov-tab.on                 1 / 1
     market      .mov-pst.amb                1 / 1
     news        .ngt-dom                   24 / 24
     news        .ngt-why                   24 / 24
     news        .ngt-tab                    2 / 2
     news        .ngt-break-tag              1 / 1
     dash        .tab                        5 / 5
     dash        .dn                         3 / 3
     dash        .tab.on                     1 / 1

   DELIBERATELY EXCLUDED, and why each:
     · every BARE TAG group -- i, u, b, span, h3, h2, em, p. The Phase 1H law forbids resetting a tag
       globally ("exactly the class of change that turns a fix into a regression somewhere nobody
       looked"), and dash alone has eleven of them. They need their owning component named first.
     · market `.mov-tab` -- pure 5 in Arabic but carries a LATIN leaf in Kurdish. Pure in one language
       is not pure.
     · news `.ngt-tag` (33 pure, 24 latin), `.ngt-mi`, `.ngt-lang` -- genuinely mixed; each needs its
       own before/after test.
     · dash `.chip`, `.state`, `.tag`, `.kwin` and friends -- they appear pure in Kurdish but do not
       appear at all in the Arabic pass. Absent is not the same as clean; excluded until measured in
       both.

   ENGLISH IS THE CONTROL ARM. The html[lang="ar"] / html[lang="ku"] gate means an English reader sees
   byte-identical output. Any measured English change is a FAIL of this rule, not a side effect.
   `:not([dir="ltr"])` protects any Latin island that appears inside these elements later.

   SCOPE. Every selector is prefixed with its surface root, so `.tab`, `.dn`, `.chip` and the other
   generic class names cannot reach a page they were not measured on. No shared stylesheet is edited:
   this lives in the contract file rather than in four peer-owned files.

   NOT CHANGED: font-family, font-size, font-weight, line-height, colour, spacing, layout, or any
   font-variant-numeric. Two properties only. */
html[lang="ar"] #v-portfolio :is(.cpdemo, .walc-bll):not([dir="ltr"]),
html[lang="ku"] #v-portfolio :is(.cpdemo, .walc-bll):not([dir="ltr"]),
html[lang="ar"] #v-market :is(.mov-alive, .mov-pt, .mov-pill, .mov-aval, .mov-pst, .mov-age, .mov-tab.on):not([dir="ltr"]),
html[lang="ku"] #v-market :is(.mov-alive, .mov-pt, .mov-pill, .mov-aval, .mov-pst, .mov-age, .mov-tab.on):not([dir="ltr"]),
html[lang="ar"] #v-news :is(.ngt-dom, .ngt-why, .ngt-tab, .ngt-break-tag):not([dir="ltr"]),
html[lang="ku"] #v-news :is(.ngt-dom, .ngt-why, .ngt-tab, .ngt-break-tag):not([dir="ltr"]),
html[lang="ar"] #v-dash :is(.tab, .dn):not([dir="ltr"]),
html[lang="ku"] #v-dash :is(.tab, .dn):not([dir="ltr"]){
  letter-spacing: 0;
  text-transform: none;
}

/* ── #v-ai ── WAVE 1 · Candidate B ─────────────────────────────────────────────────────────────────
   The AGI assistant surface. Owns no stylesheet; migration is this block plus the neutraliser.

   MEASURED, all six cells (en/ar/ku x 375/1366): newClip 0, newVClip 0, newLClamp 0, newOff 0,
   bidi 0, docOverflow false->false, Sorani 0/13 -> 13/13, script adoption 100% in every cell.
   All-visible adoption is 100% in four cells and 94.2-94.4% in the two English ones, where the
   residual is three symbol leaves.

   ONE CELL NEEDED THE SAME-STATE GATE. ai/ar/375 first read matched 15/22 = 68.2% -- the page grew
   while it was being measured, so the delta covered two thirds of it and was not evidence. Re-run
   with --settle=30 it reported STABLE at 15 leaves and then matched 22/22 = 100%, all deltas zero.
   The number was never wrong; the coverage behind it was too small to read.

   One new wrap at en/1366, in a container computing overflow:visible. Nothing truncates.

   Canvas: ai.js declares zero getContext and zero ctx.font, so the two canvases this surface hosts
   come from shared components and paint no text. No canvas typography path here. */
#v-ai{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated. A list of class names rots one sibling at a time - that is how
   .rv-ancset was missed on relvalue and .rv-fav after it. Every control, by element. */
#v-ai button,
#v-ai input,
#v-ai select,
#v-ai textarea{ font-family: var(--volurr-font-ui); }

/* ── #v-feedback ── WAVE 1 · Candidate B ───────────────────────────────────────────────────────────
   The improvement centre. Owns no stylesheet; migration is this block plus the neutraliser.

   MEASURED, all six cells (en/ar/ku x 375/1366), and every one identical with MATCHED COVERAGE
   82/82 = 100%: newClip 0, newVClip 0, newLClamp 0, newOff 0, bidi 0, docOverflow false->false,
   Sorani 0/13 -> 13/13. Script adoption 69/70 = 98.57%, all-visible 81/82 = 98.78%, residual ONE.

   Two and three new wraps in the English cells only, both in overflow:visible auto-height
   containers, so nothing can be truncated. The Arabic and Kurdish cells wrap nothing new at all.

   Twenty of the 102 DOM leaves carry no box and are excluded from geometry -- they cannot clip. */
#v-feedback{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated. A list of class names rots one sibling at a time - that is how
   .rv-ancset was missed on relvalue and .rv-fav after it. Every control, by element. */
#v-feedback button,
#v-feedback input,
#v-feedback select,
#v-feedback textarea{ font-family: var(--volurr-font-ui); }

/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   INTRINSIC-FIT LAW — labels wrap rather than lose text  (owner decision, Master Run 3)
   ───────────────────────────────────────────────────────────────────────────────────────────────
   THE OWNER'S RULING. Preserve full text. Do not shrink type, reduce weight, tighten tracking,
   shorten translations, or revert to Cairo. The layout adapts to the typography, not the reverse.

   THE SHARED STRUCTURAL CAUSE, and it is one sentence. Each of these labels declares
   `white-space:nowrap` while its container declares `min-width:0` -- "never wrap, and always be
   allowed to be narrower than your text". Those two together can only ever resolve as truncation.
   Cairo's narrower Latin hid the contradiction; Inter exposes it. The bug was always there.

   MEASURED before choosing, with tools/typo/fit_probe.js (intrinsic width of every real string, in
   every language, at 375/390/768/1024/1366/1920):

     mmap      .mi-cl-n     "Precious Metals"   box 78px    needs 87.4px   clips at 390 and 1366
     market    .mov-drv-l   "REAL YIELD 10Y"    box 80px    needs 85.6px   clips at 768 and 1920
     market    .mov-drv-l   "LABOR MARKET"      box 80px    needs 84.8px
     market    .mov-drv-l   "INFLATION EXP"     box 80px    needs 82.5px
     watchlist .wr-nm       "Dollar (DXY)"      box 78px    needs 82.0px   clips at 375

   WHY WRAPPING AND NOT WIDTH. Widening was tried first, on evidence, and costs columns:
   mmap's cluster grid is `repeat(auto-fit,minmax(90px,1fr))` inside a panel that is `grid-column:
   span 4` of twelve, so at 1366 it has only 320.7px and yields three 101.5px cards. Raising the
   floor to fit 87.4px resolves to TWO columns, not three -- trading a text defect for a density
   defect, which the brief forbids. Every container here is column-flex or a centred row, so the
   cost of wrapping is vertical, and vertical is the one resource that is free.

   `overflow-wrap:anywhere` rather than `break-word`: it also lowers the min-content contribution,
   so a grid track can still shrink instead of forcing the page to scroll. That matters at 375.

   NOT INCLUDED, DELIBERATELY. `.mov-drv-s` (driver descriptions, over by +99 to +226px) and
   `.mi-rib-note` (`max-width:54vw`) are long prose that is MEANT to ellipsis and always did. Making
   those wrap would add three or four lines to every card. A label and a sentence are not the same
   thing, and one law should not pretend they are.

   This rule is exactly what was simulated before it was written -- same four declarations, same
   three selectors -- so the verification tests what was proved rather than something adjacent. */
/* SPECIFICITY, checked rather than assumed -- and this nearly shipped broken. index.html loads
   typography-contract.css at position 12, but market.css at 18 and market-live.css at 20, so they
   come LATER in the cascade. `#v-market .mov-drv-l` (one id, one class) TIES with
   market-live.css:144 and loses on order, and the rule would have done nothing on market. The
   simulation could not reveal this: it injected a <style> into document.head, which always wins.
   So the market selector goes through the parent component class for one extra class of weight,
   and `.mi-cl-n` / `.wr-nm` are declared in market.css with a bare class, which the id prefix
   already outranks. Verified by reading computed white-space on all three after loading, not by
   counting selectors on paper. */
/* .mov-met .mov-drv-s IS ADDED ON EVIDENCE, .mov-drv .mov-drv-s IS NOT. Both are the same class,
   which is the trap. Measured at 1920 with both states settled at the same 238 leaves: the four
   descriptions inside .mov-drv clip in BOTH -- +89 to +198px under Cairo, +99 to +235px under
   Inter -- so they are long prose that was always meant to ellipsis. But the two status notes
   inside .mov-met, "VIX quote is 99d ago" and "Awaiting verified feed", FIT under Cairo and clip
   under Inter by +4.3px and +8.7px. Those are new, and they are short enough to wrap without
   turning a card into a paragraph. One class, two jobs; only the job that regressed is fixed. */
#v-mmap .mi-cl-n,
#v-market .mov-drv .mov-drv-l,
#v-market .mov-met .mov-drv-l,
#v-market .mov-met .mov-drv-s,
#v-watchlist .wr-nm{
  white-space: normal;
  overflow-wrap: anywhere;
  text-overflow: clip;
  overflow: visible;
}

/* -- #v-mmap -- WAVE 1 - Candidate B (Master Run 3, paired with the intrinsic-fit law above) */
#v-mmap{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated. A list of class names rots one sibling at a time - that is how
   .rv-ancset was missed on relvalue and .rv-fav after it. Every control, by element. */
#v-mmap button,
#v-mmap input,
#v-mmap select,
#v-mmap textarea{ font-family: var(--volurr-font-ui); }

/* -- #v-market -- WAVE 1 - Candidate B (Master Run 3, paired with the intrinsic-fit law above) */
#v-market{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated. A list of class names rots one sibling at a time - that is how
   .rv-ancset was missed on relvalue and .rv-fav after it. Every control, by element. */
#v-market button,
#v-market input,
#v-market select,
#v-market textarea{ font-family: var(--volurr-font-ui); }

/* -- #v-watchlist -- WAVE 1 - Candidate B (Master Run 3, paired with the intrinsic-fit law above) */
#v-watchlist{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated. A list of class names rots one sibling at a time - that is how
   .rv-ancset was missed on relvalue and .rv-fav after it. Every control, by element. */
#v-watchlist button,
#v-watchlist input,
#v-watchlist select,
#v-watchlist textarea{ font-family: var(--volurr-font-ui); }

/* -- #v-news -- WAVE 1 - Candidate B (Master Run 3) */
#v-news{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated. A list of class names rots one sibling at a time - that is how
   .rv-ancset was missed on relvalue and .rv-fav after it. Every control, by element. */
#v-news button,
#v-news input,
#v-news select,
#v-news textarea{ font-family: var(--volurr-font-ui); }

/* ── #v-news ── WAVE 1 · Candidate B + headline clamp  (Master Run 3) ──────────────────────────────
   THE DEFECT. news-terminal.css:537 clamps article headlines at two lines. Candidate B pushes two of
   them to three, and -webkit-line-clamp truncates by LINE COUNT -- scrollWidth equals clientWidth and
   there is no overflow-y signal, so neither the horizontal nor the vertical detector can see it. Text
   simply disappears. It was found only because a detector for line-clamp was built first.

   WHY RAISING THE CLAMP AND NOT SHRINKING THE FONT. The owner forbade shrinking, and measurement says
   raising is nearly free: `-webkit-line-clamp` is a MAXIMUM, not a fixed height. Headlines that fit in
   one or two lines are completely unaffected -- only the two that need a third line grow. And
   `.ngt-row` declares `min-height:56px`, a floor rather than a fixed height, so a taller row is a
   shape the component already supports.

   THE DESIGN ALREADY USES THREE. news-terminal.css:749 sets `-webkit-line-clamp:3` at the narrow
   breakpoint. Three-line headlines are not alien to this component; they are what it already does
   when the row stacks. This makes the desktop clamp agree with the narrow one rather than inventing
   a new value.

   Scoped through #v-news so it cannot reach another surface, and with the id prefix it outranks
   `.ngt-hl h3` regardless of stylesheet order -- which matters here, because news-terminal.css loads
   at position 15 and this file at 12. */
#v-news .ngt-hl h3{
  -webkit-line-clamp: 3;
  line-clamp: 3;
}

/* ── .mi-rib-note ── owner-approved responsive wrap  (Master Run 4) ────────────────────────────────
   OWNER RULING: preserve full text, the layout adapts. No truncation, no title-only recovery, no
   font shrink, no shortened server text, no negative tracking, no clamp.

   WHAT IT WAS. `overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:54vw` on a
   SERVER-SUPPLIED status note (mmap.js:359, intel.note). Measured thresholds before this change:

     width   incumbent (Cairo)      Candidate B (Inter)
      375    CLIPPED  +333.1        CLIPPED  +402
      768    CLIPPED  +121          CLIPPED  +190
     1024    fits (536.1 vs 553)    CLIPPED  +52      <- the new one
     1366    fits                   fits

   So it already lost text on every mobile and tablet width, and Inter moved that threshold past
   1024. A `title` was explicitly rejected as a remedy: it is unreachable on touch, which is exactly
   where the note truncates worst.

   WHAT IT IS NOW. The 54vw cap is KEPT -- it is the designer's horizontal intent and there is no
   reason to spend it -- and only the single-line assumption is removed. The note wraps inside that
   width and the ribbon grows downward. `.mi-rib-l` is `display:flex; align-items:center`, so a
   taller child simply makes the row taller; nothing else moves.

   No line count is imposed, per the ruling: at 1366 the note still fits on one line and nothing
   changes at all, while at 375 it becomes two or three lines and is finally readable in full. */
#v-mmap .mi-rib-note{
  white-space: normal;
  overflow-wrap: anywhere;
  overflow: visible;
  text-overflow: clip;
}

/* -- #v-gold -- WAVE 1 - Candidate B (Master Run 4) */
#v-gold{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated. A list of class names rots one sibling at a time - that is how
   .rv-ancset was missed on relvalue and .rv-fav after it. Every control, by element. */
#v-gold button,
#v-gold input,
#v-gold select,
#v-gold textarea{ font-family: var(--volurr-font-ui); }

/* (#v-gold ── the intrinsic-fit fix for the removed room's .gir-nhl headline lived here; the room was replaced by
   js/gold/room.js and removed 2026-10-02, and the Candidate B block above is what the new room reads.) */

/* -- #v-dash -- WAVE 1 - Candidate B (Master Run 5) */
#v-dash{
  --volurr-font-latin:   'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-arabic:  'Vazirmatn','IBM Plex Sans Arabic',sans-serif;
  --volurr-font-ui:      'Inter','Vazirmatn','IBM Plex Sans Arabic',system-ui,-apple-system,sans-serif;

  --font-latin:  var(--volurr-font-latin);
  --font-ar:     var(--volurr-font-arabic);
  --font-ku:     var(--volurr-font-arabic);
  --vt-latin:    var(--volurr-font-latin);
  --vt-arabic:   var(--volurr-font-arabic);
  --vt-ui:       var(--volurr-font-ui);

  font-family: var(--volurr-font-ui);
}
/* Positional, not enumerated. A list of class names rots one sibling at a time - that is how
   .rv-ancset was missed on relvalue and .rv-fav after it. Every control, by element. */
#v-dash button,
#v-dash input,
#v-dash select,
#v-dash textarea{ font-family: var(--volurr-font-ui); }

/* ── #v-dash ── WAVE 1 · Candidate B  (Master Run 5, owner decision resolved) ──────────────────────
   THE OWNER RULED that `#v-dash.agid{font-family:Archivo,…}` is LEGACY FUNCTIONAL UI DEBT and not a
   brand identity, because it sat on the ROOT and therefore owned functional Latin indiscriminately.

   CLASSIFIED BEFORE TOUCHING ANYTHING. All 97 `font:` shorthand declarations naming Archivo in
   dash-agi.css were extracted with their selectors: .klabel, .agid-tile .tl/.tv/.ts, .tsym, #top h1,
   .pill, .tabs, .chip, .badge, thead th, tbody td, .lmenu li, select, .hdr-title, .anid>b … every
   one is a label, control, tab, badge, table cell, heading or status field. FUNCTIONAL UI: 97.
   DISPLAY/BRAND: 0. UNKNOWN: 0.

   The brand mark is not among them and never was: it is the `<volurr-mark>` web component, and
   dash-agi.js:315 states outright "Nothing here draws the logo any more". So there was no display
   face to preserve here - only Latin text wearing a display face by inheritance.

   MIGRATED AT SOURCE, not overridden. Each of those 97 shorthands writes font-family directly onto
   the element (the `font:` shorthand always does), so no amount of root inheritance can reach them -
   a fact dash-agi.css:1301 had already recorded the hard way. Archivo was replaced with Inter in all
   97 shorthands and both font-family declarations, and the `'Vazirmatn','IBM Plex Sans Arabic'` tail
   was left intact in every one, so Arabic and Kurdish keep the face they already had correctly.
   Nine Archivo mentions remain in dash-agi.css and all nine are inside comments - the file documents
   its own history and that history stays readable.

   THIS BLOCK EXISTS FOR WHAT THE SOURCE EDIT CANNOT REACH. Measured after the replacement, the view
   root still reported `rootUi Cairo` and `Sorani 0/13`: `.agid` is applied to inner containers, not
   to `#v-dash` itself, so the root and any subtree outside `.agid` still inherited body's Cairo.

   AND THE SVG PATH, which §7 required proving rather than assuming: dash renders 22 SVG <text>
   nodes and no stylesheet gave them a family, so 18 resolved through a generic keyword. SVG text
   inherits font-family, so naming it here is enough - the same thing market.css:44 does for .fl-svg. */
#v-dash svg text{
  font-family: var(--volurr-font-ui);
}
