/* ============================================================
   AL BUTTER — design tokens
   Brand system v1: four colors, five shapes, one voice.
   ------------------------------------------------------------
   STATUS: LIVE. These are the real design-system values, no longer
   placeholders. Source: "Al Butter Design System" v1.

   RULES:
   1. No raw hex, rgb(), or hsl() in any other CSS file.
   2. No physical direction properties anywhere (margin-left,
      padding-right, left:, right:). Logical properties only —
      that is what makes RTL free.
   3. No raw cubic-bezier or duration outside this file.

   COLOR LAW — from the system, non-negotiable:
   · Ink is always Jam on light, Churn on dark. NEVER grey.
     Secondary text is Jam at 62% opacity; hairlines at 12%.
   · Melt Gold is a FILL, never type on a yellow field
     (1.4:1 — fails badly). It may carry text only at 21px+
     over Jam.
   · One Jam button per view. Melt Gold is the second action.
     Ghost is tertiary.

   BREAKPOINTS (cannot be custom properties), mirrored in
   assets/js/lang.js as window.Z_BP:
       md: 768px    lg: 1024px    xl: 1440px
   ============================================================ */

:root {
  /* ---- the four colors ------------------------------------ */
  --z-churn:  #F8F3C9;   /* default surface — the butter field */
  --z-gold:   #EFC44B;   /* the butter itself — fills only     */
  --z-jam:    #5C0E2C;   /* all ink, all primary buttons       */
  --z-creme:  #FDF3D3;   /* paper — cards, forms, long reading */
  /* The original, deeper butter field. The page surface moved to the paler
     #F8F3C9; the nav bar deliberately stays on this so it still reads as a
     distinct band against the page rather than dissolving into it. */
  --z-churn-deep: #F1E890;
  --z-paper:  #FFFDF6;   /* lightest paper, for stacked cards  */

  /* ---- ink: tints of Jam, never grey ---------------------
     Aug-2026 legibility pass. These alphas were set by eye and two of
     them did not survive being measured against the Churn ground:

       soft  .62 → 4.26:1   under the 4.5:1 body-text minimum
       faint .38 → 2.25:1   fails every threshold there is

     Small Arabic set in Palestine is where this showed up worst — a
     display face at 11px has thin joins to begin with, and at 2.25:1
     they simply stopped rendering as text. Re-derived so both clear
     4.5:1 with the gap between the two tiers preserved:

       soft  .74 → 6.00:1
       faint .66 → 4.77:1

     Hairlines are deliberately NOT raised: they are 1px dividers, not
     text, and 4.5:1 rules would turn every rule on the page into a
     border. */
  --z-ink:          var(--z-jam);
  --z-ink-soft:     rgb(92 14 44 / .74);   /* secondary text  */
  --z-ink-faint:    rgb(92 14 44 / .66);   /* captions, meta  */
  --z-hairline:     rgb(92 14 44 / .12);
  --z-hairline-strong: rgb(92 14 44 / .22);

  /* ---- ink on dark: tints of Churn, same law inverted ----
     Used by the footer and any Jam-surfaced block. Same pass: rev-faint
     was 3.73:1 on Jam, which is fine for a 1px rule and not fine for the
     footer's small print, which is what it was actually colouring. */
  --z-ink-rev:          var(--z-churn);
  --z-ink-rev-soft:     rgb(241 232 144 / .82);   /* 8.43:1 */
  --z-ink-rev-faint:    rgb(241 232 144 / .68);   /* 6.18:1 */
  --z-hairline-rev:     rgb(241 232 144 / .18);

  /* ---- plain white on dark ----
     The footer reads as white rather than tinted. Same .72/.48 steps as
     the Churn scale above so the hierarchy between body, muted and faint
     text is identical — only the hue changes. */
  --z-white:            #FFFFFF;
  --z-white-soft:       rgb(255 255 255 / .72);
  --z-white-faint:      rgb(255 255 255 / .52);

  /* ---- semantic aliases — SITE CODE CONSUMES THESE ------- */
  --z-bg:            var(--z-churn);
  --z-header-bg:     var(--z-churn-deep);
  --z-bg-alt:        var(--z-creme);
  --z-fg:            var(--z-ink);
  --z-fg-muted:      var(--z-ink-soft);
  --z-fg-faint:      var(--z-ink-faint);

  --z-card-bg:       var(--z-creme);
  --z-card-bg-alt:   var(--z-paper);
  --z-card-border:   var(--z-hairline);

  /* Primary = Jam pill with Churn text (10.7:1).
     Secondary = Melt Gold fill with Jam text (7.9:1). */
  --z-cta-bg:        var(--z-jam);
  --z-cta-fg:        var(--z-churn);
  --z-cta2-bg:       var(--z-gold);
  --z-cta2-fg:       var(--z-jam);

  --z-focus-ring:    var(--z-jam);
  --z-chart-fill:    var(--z-gold);
  --z-chart-ink:     var(--z-jam);
  --z-chart-track:   rgb(92 14 44 / .10);

  /* ---- type ----------------------------------------------
     Palestine is listed FIRST and is unicode-range-scoped to Arabic,
     so Arabic resolves to it and Latin falls through to Rubik. One
     stack, no language switch needed. */
  --z-font-display: "Palestine", "Rubik", system-ui, sans-serif;
  --z-font-body:    "Palestine", "Rubik", system-ui, sans-serif;
  /* Games segment ONLY. Baloo 2 ships Latin only, so Rubik is chained
     ahead of the generic — otherwise Arabic game titles drop to
     system-ui instead of the self-hosted Rubik Arabic. */
  --z-font-accent:  "Baloo 2", "Rubik", system-ui, sans-serif;
  --z-font-label:   "DM Mono", ui-monospace, monospace;

  /* Scale straight from the system spec.
     DISPLAY 64/900/-3.5% · H1 40/900/-2.5% · H2 28/700
     BODY 17/400/1.65 · SMALL 15/400 · LABEL 12 mono .2em

     LABEL and SMALL are one step up from the spec's 11/14. The spec was
     written for Latin; Palestine carries the Arabic and needs more size
     than Rubik does to hold its joins, and the label style compounds it
     by being uppercase, letterspaced and mono. 11px was the size at
     which the Arabic stopped being readable rather than merely small. */
  --z-text-label:   0.75rem;                                      /* 12 */
  --z-text-small:   0.9375rem;                                    /* 15 */

  --z-text-body:    1.0625rem;                                    /* 17 */
  --z-text-h2:      clamp(1.375rem, 1.15rem + 1vw, 1.75rem);      /* →28 */
  --z-text-h1:      clamp(1.875rem, 1.35rem + 2.6vw, 2.5rem);     /* →40 */
  --z-text-display: clamp(2.25rem, 1.3rem + 4.75vw, 4rem);        /* →64 */
  --z-text-stat:    clamp(2rem, 1.2rem + 4vw, 3.5rem);

  --z-tracking-display: -0.035em;   /* -3.5% */
  --z-tracking-h1:      -0.025em;   /* -2.5% */
  --z-tracking-label:    0.2em;
  /* Arabic set in Palestine reads about one step smaller than Rubik does
     at the same px, because the letterforms carry their meaning in thin
     joins and dots rather than in x-height. Rather than growing the whole
     scale for both languages, the two sizes small enough to be hurt by it
     get a per-language step. Everything above SMALL is large enough that
     the two faces read at parity, so the rest of the scale is untouched
     and the two languages stay visually matched. */
  --z-leading-tight: 1.05;
  /* Where the hero's gold accent rule sits within the line box. Latin
     sits high under the baseline; Arabic needs it pushed clear of the
     descenders and the tooth marks. */
  --z-accent-offset: 88%;
  --z-leading-snug:  1.25;
  --z-leading-body:  1.65;

  --z-weight-body:    400;
  --z-weight-medium:  500;
  --z-weight-bold:    700;
  --z-weight-black:   900;

  /* ---- space --------------------------------------------- */
  --z-space-1:  .25rem;
  --z-space-2:  .5rem;
  --z-space-3:  .75rem;
  --z-space-4:  1rem;
  --z-space-6:  1.5rem;
  --z-space-8:  2rem;
  --z-space-12: 3rem;
  --z-space-16: 4rem;
  --z-space-24: 6rem;

  --z-section-pad-block:    clamp(3.5rem, 9vh, 7rem);
  --z-container-max:        72rem;
  --z-container-narrow:     42rem;
  --z-container-pad-inline: clamp(1rem, 4vw, 2.5rem);

  /* ---- radii / shadows ----------------------------------
     "Pills only, 999px radius" for buttons. Cards stay one shape.
     The system lists SHADOW under logo DON'Ts, so elevation is
     carried by Jam borders and paper tone, not blur. */
  --z-radius-sm:   .5rem;
  --z-radius-md:   .875rem;
  --z-radius-lg:   1.5rem;
  --z-radius-xl:   2rem;
  --z-radius-full: 999px;

  /* ── the cut ──────────────────────────────────────────────
     The site's one corner language: a chamfer, not a radius. Two opposite
     corners sliced flat, like a pat cut with a blade. Applied to CONTROLS
     only — buttons, tabs, toggles, badges. Bars, dots, rails, the donut and
     the range thumb stay round, because those are geometry rather than
     things you press, and a chamfered progress bar reads as a rendering bug.

     --z-cut is the slice depth. It does NOT scale with the control: a fixed
     depth is what makes a 44px toggle and a 200px button look like the same
     decision rather than two different angles. */
  --z-cut: 13px;
  --z-cut-sm: 9px;      /* for controls under ~36px, where 13 eats the box */
  /* The cut itself, as a shape you can reuse. It was hand-copied into eight
     rules and was about to be copied into eight more, which is how a shape
     drifts out of sync with itself.

     TWO tokens, not one with a variable depth: a custom property resolves
     its inner var() at the element where it is DECLARED, so --z-cut is
     baked in here at :root and an element setting `--z-cut: 9px` further
     down gets the 13px polygon anyway. Verified, not assumed — the shallow
     controls came back measuring 13px. Hence an explicit second token. */
  --z-chamfer:
    polygon(var(--z-cut) 0, 100% 0, 100% calc(100% - var(--z-cut)),
            calc(100% - var(--z-cut)) 100%, 0 100%, 0 var(--z-cut));
  --z-chamfer-sm:
    polygon(var(--z-cut-sm) 0, 100% 0, 100% calc(100% - var(--z-cut-sm)),
            calc(100% - var(--z-cut-sm)) 100%, 0 100%, 0 var(--z-cut-sm));
  /* Roadmap station circle. Single-sourced because the pinned rail has to
     sit exactly on the circles' centre line (node ÷ 2) and pin-scroll.js
     measures the same box to place the rail's two ends. */
  --z-roadmap-node: 2.75rem;
  --z-roadmap-rail: 4px;
  /* Narrowest a station may get before three-across stops being readable.
     Below this the pinned layout is refused outright and the original
     vertical roadmap renders instead. */
  --z-roadmap-step-min: 15rem;

  --z-border-ink:  2px solid var(--z-jam);
  --z-hit-min:     44px;    /* "hit target never under 44px" */

  /* ---- horizontal pin-scrub (.process / .formats) --------
     --z-header-h is the single source for the sticky header's own height
     AND the offset the pinned sections stick below — previously the header
     hardcoded 4.25rem directly; consolidated here so the two can never
     drift apart. --z-format-card-min-w is the same 15rem .format-grid's
     unpinned CSS Grid already used, now named so the JS pin-eligibility
     threshold and the grid's own minmax() read the same number. */
  --z-header-h: 4.25rem;
  --z-format-card-min-w: 15rem;

  /* ---- motion -------------------------------------------- */
  --z-dur-fast:   140ms;
  --z-dur-base:   280ms;
  --z-dur-slow:   560ms;
  --z-dur-reveal: 720ms;
  /* "One shape, four states, one clock" — the PAT·MELT·SPREAD·BLOCK
     loop. Every stage is a quarter of this. */
  --z-dur-loop:   5200ms;
  --z-dur-scroll-cue: 2400ms;   /* the hero's falling scroll dot */
  --z-dur-marquee: 42s;         /* one full pass of the client logo strip */
  --z-dur-joystick: 1400ms;     /* the games-pointer icon's back-and-forth sway */

  --z-ease-out:    cubic-bezier(.16, 1, .3, 1);
  --z-ease-in-out: cubic-bezier(.65, 0, .35, 1);
  --z-ease-spread: cubic-bezier(.34, .01, .2, 1);

  --z-slide-x: 2rem;
  /* Sign for the pin-scrub translateX, not a distance. LTR: the track pans
     toward -X to reveal DOM-later content (which sits physically to the
     right, since flex-direction:row places item 1 at inline-start = left).
     Flipped under RTL below, next to --z-slide-x's own flip — same job,
     same pattern. JS only ever writes an unsigned magnitude; this token is
     the only place a direction sign enters the transform. */
  --z-pin-sign: -1;

  /* ---- z-index layers ----------------------------------- */
  --z-layer-base:    0;
  --z-layer-raised:  1;
  --z-layer-sticky:  10;
  --z-layer-header:  20;
  --z-layer-overlay: 30;
  --z-layer-modal:   40;
  --z-layer-toast:   50;
}

/* ── Arabic (§08) ──────────────────────────────────────────
   Arabic is not a translation layer, it is the primary language.
   Rubik carries both scripts, so nothing swaps except two things:

   1. Leading opens up — 1.75 against 1.65. Diacritics and descenders
      need the room. Type SIZE does not change.
   2. Display tracking goes to ZERO. The -3.5% that tightens Latin
      display collides connected Arabic letterforms. */
html[lang="ar"] {
  --z-leading-body: 1.75;
  --z-tracking-display: 0;
  --z-tracking-h1: 0;

  /* Palestine has deep descenders and high tooth marks, so the Latin
     display leading of 1.05 made consecutive lines collide outright.
     Arabic display gets real room. Type SIZE is unchanged — this is
     leading only, exactly like the 1.65 -> 1.75 body rule. */
  --z-leading-tight: 1.42;

  /* At 88% the accent rule ran THROUGH the glyphs rather than under
     them — Arabic sits lower in the line box than Latin. */
  --z-accent-offset: 112%;

  /* AMENDS the "type SIZE does not change" rule above, for the two
     smallest steps only. That rule holds wherever both scripts have room
     to resolve, and it stops holding at the bottom of the scale: Latin
     falls through to Rubik, a text face, while Arabic stays in Palestine,
     a display face whose joins and dots are the first thing to go. At
     LABEL — uppercase, letterspaced, mono — the Arabic was measurably
     harder to read than the Latin beside it at the identical px.
     BODY and up are untouched, so the two languages still sit at parity
     everywhere a reader compares them. */
  --z-text-label: 0.8125rem;   /* 13, against Latin's 12 */
  --z-text-small: 1rem;        /* 16, against Latin's 15 */

  /* LABELS IN ARABIC DO NOT GET THE MONO, AND DO NOT GET TRACKED.
     --z-font-label is DM Mono, whose unicode-range stops at U+00FF — it
     carries no Arabic at all. So every Arabic eyebrow fell past it to
     ui-monospace and rendered in a SYSTEM font, not a brand one. Measured
     on /ar/: the eyebrow set 265.2px wide, matching plain ui-monospace at
     265.4, against Palestine at 156 and Rubik at 164.8.

     The tracking was the worse half. 0.2em on a joined script prises the
     letterforms apart at exactly the size they can least afford it; Arabic
     is cursive and that is a typographic error rather than a preference.
     Both were invisible on the English pages, which is how they survived.

     Rubik Arabic is already downloaded on every page for the body text, so
     this costs nothing new. Latin labels keep DM Mono and its tracking
     untouched — including the wordmark's "albutter", which is Latin. */
  --z-font-label: "Rubik", system-ui, sans-serif;
  --z-tracking-label: 0;

  /* SECONDARY TEXT IN ARABIC MOVES TO RUBIK TOO.
     Palestine is a display face. It is doing real work at 60px in the
     headlines and it is the wordmark, but it was also set on every lede,
     paragraph, card description and form label — and a display face is the
     first thing to lose its joins and counters at 15-17px, which is
     exactly where most of the reading on this site happens.

     Only --z-font-body moves. Headings take --z-font-display (base.css
     h1-h4), the header wordmark and the big figures take it directly, so
     titles, الزبدة in the navigator and the numbers all stay Palestine.
     The split is now: Palestine for the things you look at, Rubik for the
     things you read. */
  --z-font-body: "Rubik", system-ui, sans-serif;
}

/* Palestine ships one weight. Without this the browser fakes the 700
   and 900 weights the Latin scale asks for, and synthetic bold on a
   connected script smears strokes into each other. Arabic renders at
   its true weight instead; hierarchy comes from SIZE, which the system
   already varies, and from the Jam/Gold surfaces. */
html[lang="ar"] {
  font-synthesis-weight: none;
  font-synthesis: none;   /* older syntax, same intent */
}

/* Direction-driven slide flip keeps keyframes free of physical props. */
html[dir="rtl"] {
  --z-slide-x: -2rem;
  /* Under RTL, flex-direction:row auto-reverses (Flexbox spec) — item 1
     lands at physical right, later items extend physically left. Revealing
     them means panning the track physically RIGHT, the opposite sign from
     LTR. */
  --z-pin-sign: 1;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --z-dur-fast:   0ms;
    --z-dur-base:   0ms;
    --z-dur-slow:   0ms;
    --z-dur-reveal: 0ms;
    --z-slide-x:    0rem;
  }
}
