/*
  OPULENT AURA — SITE ROLE LAYER v1.0
  Produced by Hudur, Digital Presence Directorate · 2026-07-31

  ══════════════════════════════════════════════════════════════════════════════
  THIS FILE IS THE SEAM. IT IS THE ONLY FILE IN THE SITE PERMITTED TO NAME
  A --oa-* TOKEN, AND THE ONLY FILE PERMITTED TO WRITE A COLOUR OR A LENGTH.
  ══════════════════════════════════════════════════════════════════════════════

  Haneen's brief, queue item 4, verbatim: «A neutral visual layer built so that the
  design system, when it arrives from Wasm through Nasaq, applies as tokens — not as
  a rewrite. This is the single most important structural decision in your build.»

  So the build is three layers, and the direction of dependency is one-way:

      vendor/tokens-v0_2.css     Nasaq's, VENDORED BYTE-IDENTICAL, never edited by me
              │                  (sha256 cc245d6d…ea67d, verified against his copy)
              ▼
      roles.css   ← THIS FILE.   Maps his tokens onto the roles a SITE needs.
              │                  Names --oa-*. Writes literals ONLY where marked.
              ▼
      structure.css              Layout, rhythm, states. Names ONLY --site-*.
                                 Contains no hex, no colour, no font name, no
                                 hand-typed size. Enforced by check.mjs, not by care.

  WHAT THIS BUYS, CONCRETELY. When Wasm rules and Nasaq issues tokens v0.3:
    · the vendored token file is REPLACED wholesale — a copy, not a merge;
    · this file is re-checked, and changes only where a role's SOURCE moved;
    · structure.css is not opened at all.
  The skin changes; the building does not. That is the whole design.

  AND THE HONEST LIMIT, because this house does not let a mechanism claim more than
  it does: this separates VALUES from STRUCTURE. It does not survive a design system
  that changes the STRUCTURE — a new page shape, a different entry sequence, a nav
  that is not a nav. Nothing token-shaped can. What it guarantees is that a colour,
  a size, a face, a rhythm or a motion ruling lands as a swap. That is the class of
  change a design system actually issues, which is why it is worth building for.

  ── SOURCE TAGS, Nasaq's discipline, carried ──
    [NSQ]  the value is his token, passed through untouched — I add no number
    [W]    Web Surface Standard v1.0 (Mi'yar)
    [HDR]  a pick of mine, inside a cited band, with the band named
    [GEN]  generated from the declared ratio — never hand-typed
  A value here with no tag is a defect, same rule as his file.
*/

:root {

  /* ═══════════════════════════════════════════════════════════════════
     COLOUR ROLES — every one is a pass-through. I introduce no colour.
     ═══════════════════════════════════════════════════════════════════
     The six are closed [C6] and the grounds are ruled to three [W5]. Note what
     has NO role below and therefore cannot be reached from structure.css:
       · no gold ground role          → X2
       · no Crystal Silence ground    → PC2 (1.3 ΔE from Clive Christian's canvas)
       · no #FFFFFF role              → X3
       · no gold-on-light text role   → X11 (1.93 on linen, 2.58 on ash — both fail)
     An absence here is the enforcement. A role that does not exist cannot be used,
     and unlike a lint rule that holds whatever a browser does. */

  --site-ink-on-dark:        var(--oa-silence);      /* [NSQ] PASS 15.44 / 4.5 — ×3.43 */
  --site-ink-on-light:       var(--oa-obsidian);     /* [NSQ] linen ×2.72 · ash ×3.64 */
  --site-ink-soft-on-light:  var(--oa-sandalwood);   /* [NSQ] linen ×2.04 · ash ×2.74 */
  --site-ink-muted-on-dark:  var(--oa-text-muted);   /* [NSQ] PASS  5.95 / 4.5 — ×1.32 */
  --site-accent:             var(--oa-gold);         /* [NSQ] on dark PASS 6.35 — ×1.41.
                                                        TEXT USE IS DARK-GROUND ONLY. On a
                                                        light ground this role is a LINE,
                                                        never a letterform [X11]. */

  /* --site-ink-faint is deliberately NOT defined. Nasaq's .oa-text-faint-large carries
     a ×1.04 margin and welds itself to a ≥24px floor; his own note says thin margins are
     how this house gets hurt. A site with five type sizes has no use for it that a muted
     role does not serve at ×1.32. Not forbidden — declined, and the reason recorded. */


  /* ═══════════════════════════════════════════════════════════════════
     TYPE — TWO ROLES, NEVER A FACE NAME. [RD2]
     ═══════════════════════════════════════════════════════════════════ */
  --site-face-identity: var(--oa-role-identity);   /* [NSQ] Cormorant → Amiri → Georgia */
  --site-face-support:  var(--oa-role-support);    /* [NSQ] Arsenal  → Almarai → Georgia */

  /* ── THE SCALE. One ratio, every size generated from it. [W2] ──
     Nasaq's file declares r = 1.25 and generates five sizes in calc(). I take his five
     unchanged and GENERATE the two the site needs beyond them by the same ratio — which
     is precisely the case W2 was written for: «can it produce the sixth size?» It can,
     so I do not invent one.

       caption   12.80px   [NSQ]  clears its 12px floor ×1.067
       body      16.00px   [NSQ]  the base
       title     20.00px   [NSQ]
       heading   25.00px   [NSQ]
       display   31.25px   [NSQ]
       hero      39.06px   [GEN]  display × r
       hero-lg   48.83px   [GEN]  display × r²

     ⚠️ T5 CAPS A SURFACE AT FIVE DISTINCT SIZES IN TOTAL. Seven exist here; this is an
     INVENTORY, never a set to use entirely. No page may reach into more than five of
     them, and no page does — check.mjs counts per page per breakpoint and fails the
     build, so the cap is arithmetic rather than vigilance. */
  --site-size-caption: var(--oa-size-caption);   /* [NSQ] */
  --site-size-body:    var(--oa-size-body);      /* [NSQ] */
  --site-size-title:   var(--oa-size-title);     /* [NSQ] */
  --site-size-heading: var(--oa-size-heading);   /* [NSQ] */
  --site-size-display: var(--oa-size-display);   /* [NSQ] */
  --site-size-hero:    calc(var(--oa-size-display) * var(--oa-scale-ratio));  /* [GEN] 39.06 */
  --site-size-hero-lg: calc(var(--site-size-hero) * var(--oa-scale-ratio));   /* [GEN] 48.83 */

  /* THE MARK'S RENDERED SIZE — and it is derived from the RULED TIER FLOOR, not from
     the type scale, because it does not answer the same question the type scale answers.

     W4 selects WHICH ARTWORK FILE IS LEGAL by rendered pixel size, and the ranges touch
     with no gap. The homepage serves the T1 master, so its rendered size must stay at or
     above the T1 floor — and `--oa-mark-band-hero` IS that floor, ratified, in the
     vendored token file.

     Written first as a flat `132px` in structure.css. SEAM-1d rejected it and was right,
     but the fix is better than the rule required: tying the size to the floor makes the
     tier invariant hold BY CONSTRUCTION. A flat 132 stays 132 if the ratified floor ever
     moves to 150, and the page would then be serving hero art below its own tier with
     nothing raising an error. Derived, it cannot.

     1.32 is a ratio, not a length: comfortably inside T1 rather than sitting on its
     edge, so no plausible change lands the mark on a boundary. [GEN] 132px at floor 100. */
  --site-size-mark-hero: calc(var(--oa-mark-band-hero) * 1.32);

  --site-leading-body:    var(--oa-leading-body);     /* [NSQ] 1.6, inside W2's 1.4–1.8 */
  --site-leading-display: var(--oa-leading-display);  /* [NSQ] 1.2, inside W2's ≤1.3    */
  --site-measure-min:     var(--oa-measure-min);      /* [NSQ] 45ch */
  --site-measure-max:     var(--oa-measure-max);      /* [NSQ] 75ch */
  --site-track-wordmark:  var(--oa-track-wordmark);   /* [NSQ] +0.12em. No negative token
                                                         exists anywhere — X7 by absence */
  --site-track-label:     var(--oa-track-label);      /* [NSQ] +0.25em */


  /* ═══════════════════════════════════════════════════════════════════
     SPACE — the declared module, and nothing that is not a multiple of it. [W1]
     ═══════════════════════════════════════════════════════════════════
     M = 8px [NSQ]. W1: every margin, gutter, gap and section padding is an integer
     multiple of M, ±2px, and outer margins are ≥2M at every breakpoint.

     I expose the steps as named roles rather than letting structure.css multiply,
     because a multiplication in a layout file is where a 13px eventually appears.
     Every step below is M × an integer, and check.mjs re-derives that from the file
     instead of trusting this comment. */
  --site-m:   var(--oa-m);                    /* [NSQ]  8px — the module itself */
  --site-s1:  var(--oa-m);                    /* [NSQ]  ×1   8px */
  --site-s2:  var(--oa-m2);                   /* [NSQ]  ×2  16px */
  --site-s3:  var(--oa-m3);                   /* [NSQ]  ×3  24px */
  --site-s4:  var(--oa-m4);                   /* [NSQ]  ×4  32px */
  --site-s6:  var(--oa-m6);                   /* [NSQ]  ×6  48px */
  --site-s8:  var(--oa-m8);                   /* [NSQ]  ×8  64px */
  --site-s12: calc(var(--oa-m) * 12);         /* [GEN]  ×12  96px */
  --site-s16: calc(var(--oa-m) * 16);         /* [GEN]  ×16 128px */
  --site-s24: calc(var(--oa-m) * 24);         /* [GEN]  ×24 192px — fold separation.
                                                 The large steps are what buys W3's
                                                 ≥50% bare ground; silence is spacing. */
  --site-margin-min: var(--oa-margin-min);    /* [NSQ] ≥2M outer, every breakpoint [W1] */
  --site-tolerance:  var(--oa-space-tolerance);/* [NSQ] ±2px screen */

  /* Measure and column. [HDR] inside W1's «≤12 columns» — the site declares a single
     centred text column and no grid at all, because nine short routes have nothing to
     put in twelve columns. Declaring a grid we do not use would be a decoration. */
  /* ⚠️ CORRECTED AT THE RENDER PASS, and the correction is worth more than the value.
     This was 90M = 720px, carrying the comment «inside the 45–75ch band at body size».
     IT WAS NOT. Measured in the browser at Arsenal 400 16px, one ch is 8.995px, so the
     column rendered at 76.6ch AND WOULD HAVE REACHED ~80ch at its own maximum — a W2
     reject on every body page in the site.

     I had written «verified rendered, not assumed» beside a number I had not yet
     rendered. That is the defect this house names most often: a value stated in one unit
     (px) governing in another (ch), with a confidence word attached that its evidence did
     not support. Nasaq's file warns of exactly this class, Mi'yar withdrew a floor for
     exactly this reason, and I reproduced it inside a comment claiming the opposite.
     [measured: px · governs: ch → MISMATCH, resolved by measuring in the governing unit]

     80M = 640px = 71.2ch at the measured ch width. Inside the band with room, rather
     than sitting on the 75 edge — an edge is where the failure lives, and the band's
     ceiling is not a target. The px value remains a module multiple so W1 still holds.
     RE-MEASURED AFTER THE CHANGE, not inferred from the arithmetic. */
  --site-column-max: calc(var(--oa-m) * 80);  /* [GEN] 640px = 71.2ch measured */
  --site-shell-max:  calc(var(--oa-m) * 150); /* [GEN] 1200px outer shell */


  /* ═══════════════════════════════════════════════════════════════════
     MOTION — restraint, quantified. [W3]
     ═══════════════════════════════════════════════════════════════════
     200–600ms, ONE easing family site-wide, and prefers-reduced-motion fully honoured.
     The site uses the floor of the band, on state changes only. There is no entrance
     animation, no scroll effect, no parallax, and no autoplaying anything — W3's harm
     clause governs and it is broader than its own list: «nothing on the page may compete
     with the object for attention.» A page that moves when nobody touched it competes. */
  --site-motion:      var(--oa-motion-min);   /* [NSQ] 200ms — the floor, deliberately */
  --site-motion-ease: var(--oa-motion-ease);  /* [NSQ] the one easing family */

  /* Hairlines. A 1px rule is not a module multiple and is not meant to be — W1 governs
     margins, gutters, gaps and section padding, and a border is none of those. Recorded
     because an unexplained 1px in a module-checked file reads as a violation. [HDR] */
  --site-hairline: 1px;

  /* ── FOCUS RING — IMPROVISED, THEN REPLACED BY THE RULED TOKENS. [NSQ] ──
     This is the seam doing the job it exists for, in the direction that matters.

     I had built a focus ring out of my own parts: a 1px hairline in currentColor at an
     8px offset. It was invented — `focus-visible` was ABSENT from the token file, and
     Nasaq predicted exactly this in NSQ-A06: «if a category is missing, Hudur will
     improvise, and improvise PLAUSIBLY — values consistent with the palette and outside
     the system. Nobody notices until my tokens arrive and disagree.» Mine disagreed on
     every count: half the width, four times the offset, and no ruled colour at all.

     He has since shipped the tokens. **Mine are deleted, not reconciled** — a value of
     mine surviving beside a ruled one is how two systems start.

     The ring is ground-dependent, so the role resolves per ground below. Those two rules
     ADD to his ground classes from this file; his file is not edited. */
  --site-focus-width:  var(--oa-focus-width);    /* [NSQ] 2px  — mine was 1px */
  --site-focus-offset: var(--oa-focus-offset);   /* [NSQ] 2px  — mine was 8px */

  /* Collection item height, taken as the positive requirement he wrote it as: AUTO, so
     a row of unequal heights is handled rather than clipped. [NSQ]
     «A prohibition is read at review. A positive requirement is read at build.» */
  --site-collection-item-height: var(--oa-collection-item-height);   /* [NSQ] auto */
}

/* The focus colour is a property of the GROUND, not of the element — gold reads on
   Obsidian at 6.35:1, obsidian reads on the light grounds at 12.23 and 16.40. Declared
   against his ground classes so any focusable element inherits the correct one without
   knowing which page it is on. */
.oa-ground-dark  { --site-focus: var(--oa-focus-on-dark); }
.oa-ground-linen { --site-focus: var(--oa-focus-on-light); }
.oa-ground-ash   { --site-focus: var(--oa-focus-on-light); }


/* ═══════════════════════════════════════════════════════════════════════
   REDUCED MOTION — «all motion optional» [W3], taken literally.
   ═══════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  :root { --site-motion: 0ms; }
}


/* ═══════════════════════════════════════════════════════════════════════
   THE ARABIC SEAM — W9, held open, costing nothing.
   ═══════════════════════════════════════════════════════════════════════
   RULED: English is the launch language; Arabic is service, not content; THERE IS NO
   ARABIC SITE. So nothing here renders Arabic and no Arabic page is built.

   What IS built is the seam, because W9 requires the readiness to be structural and
   because the cost of holding it open is zero:
     · type is addressed by ROLE, so an Arabic character resolves to Amiri/Almarai
       through the same declaration, with no new token invented — which is the whole
       of RD2 and the reason it cannot be broken by adding a script;
     · the two Arabic families are DECLARED in fonts.css and referenced by no rule, so
       no browser fetches them and the W6 weight budget is untouched. Declared-but-unused
       @font-face costs nothing at request time — CLAIMED BY SPECIFICATION HERE, and
       CONFIRMED BY OBSERVATION in the render pass, where the network panel is the
       instrument. Until that pass ran this line said DESIGNED, not OBSERVED;
     · the layout below is written in logical properties throughout — inline-start,
       margin-inline, padding-block — so `dir="rtl"` mirrors the grid with no LTR
       exception anywhere, which is what W9 demands and what a left/right build cannot
       retrofit.
   Numerals style (Arabic-Indic vs Latin) is NOT decided here. W9 requires it recorded
   before the first Arabic page ships, and no Arabic page ships. Left to its owner. */


/* ═══════════════════════════════════════════════════════════════════════
   WHAT THIS FILE CANNOT ENFORCE — stated here, not buried. Nasaq's rule.
   ═══════════════════════════════════════════════════════════════════════
   C1 dark share · C2/C3 gold area · W3's ≥50% bare ground per fold · P-series on any
   photograph · W6's three live numbers. Every one is an AREA or a RUNTIME measurement
   over rendered output, and no token layer reaches them. A fully role-compliant page
   can still be a cluttered page. Those belong to the render pass and to the acceptance
   protocol Mi'yar wrote for Haneen (W10), and they are listed in the handover as the
   checks a machine did NOT make.

   Produced by: Hudur — Digital Presence Directorate
   ═══════════════════════════════════════════════════════════════════════ */
