/**
 * VIPER design tokens — the single source of truth for colour, type, space,
 * radius and motion. Generated from docs/design-system.md.
 *
 * Two rules govern everything here:
 *
 * 1. NO RAW VALUES IN COMPONENTS. Components reference tokens; only this file
 *    holds literals (.claude/rules/04-performance-and-frontend.md §19).
 *
 * 2. LIME IS NOT A BACKGROUND FOR WHITE TEXT. #B7C21C is a *light* colour.
 *    White on lime-500 is 1.90:1 — a WCAG AA failure by more than double.
 *    Near-black on lime is 10.11:1 and passes comfortably. This is enforced
 *    by tests/Feature/DesignTokenTest.php, not left to discipline.
 */

:root {
  /* ---------------------------------------------------------------- surfaces
     Ink is #0B0C0E, not pure black: OLED black-crush and gradient banding both
     need headroom, and the prototype's #08090A had none. */
  --ink:        #0B0C0E;
  --surface-1:  #111315;
  --surface-2:  #191B1D;
  --surface-3:  #222427;
  --surface-4:  #2D3134;
  --surface-5:  #3E4146;

  /* ------------------------------------------------------------------- text */
  --text-hi:    #F1F4F6;
  --text:       #D0D5D9;
  --text-mid:   #ACB2B9;
  --text-lo:    #8F969E;
  /* The lowest tier of MEANINGFUL text, and it has to clear 4.5:1.
     It was #6C727A and described as "disabled ONLY", but nothing was ever
     disabled with it — twelve rules used it for content: table headers, the
     labels in a customer's own facts list, reviewer names, search counts,
     chat status, the "(optional)" marker on a form field. All small text, all
     at 3.84:1, all failing AA. Raised rather than re-pointed so the hierarchy
     (faint < lo < mid < text < hi) survives. Asserted by ColourContrastTest. */
  --text-faint: #7E848C;

  /* ------------------------------------------------------------------- lime
     THE BRAND SEED, sampled pixel-exact from the real logo at
     vipersmartfilm.com (assets/images/viper-logo-mid-265x164.png) on
     2026-08-01 — 4,872 px of #BDC71E, the dominant non-white colour.

     Earlier work used #B7C21C, taken from the static prototype. The real mark
     is slightly brighter, which makes the contrast problem WORSE, not better:
     white on #BDC71E measures 1.85:1, against 1.90:1 for the prototype value.
     Both fail WCAG AA by more than double. Near-black is 10.68:1. */
  --lime-100: #F1F8C2;
  --lime-200: #E3EE85;
  --lime-300: #DAE726;
  --lime-400: #C7D320;
  --lime-500: #BDC71E;
  --lime-600: #98A10D;
  --lime-700: #777F0A;
  --lime-800: #525806;
  --lime-900: #2E3102;
  --lime-950: #181A01;
  --lime:     var(--lime-500);

  /* The ONLY colour permitted as text on a lime fill: 10.11:1.
     Slightly green-shifted near-black — sits better on lime than pure black. */
  --on-lime:  #0A0B05;

  /* ------------------------------------------------------------------ lines
     line-control is the sole indicator of a control's bounds, so it was
     SOLVED for 3:1 rather than picked by eye. Do not darken it. */
  --line:          #272A2E;
  --line-2:        #3A3E42;
  --line-control:  #7C8186;
  --line-strong:   var(--text-lo);

  /* ------------------------------------------------- paper (light surfaces)
     Invoices, warranty PDFs, printable documents. */
  --paper:             #FBFCFE;
  --paper-2:           #F4F5F7;
  --paper-3:           #EBEDEF;
  --paper-line:        #D5D8DB;
  --paper-line-strong: #7C8186;
  --paper-text-lo:     #595E64;
  --paper-text:        #31363B;
  --paper-text-hi:     #0E1216;

  /* Lime as TEXT on paper must be lime-800 or darker. lime-700 is 4.24 —
     genuinely failing. Do not round it up. */
  --lime-on-paper: var(--lime-800);


  /* ==================================================================== type
     THE SCALE THAT WAS SPECIFIED AND NEVER BUILT.
     `docs/design-system.md` §2.4 defines this ramp; until now nothing
     referenced it, so twelve components hand-wrote their own `clamp()` and
     six different heading sizes landed inside a 14px band at desktop. A scale
     nobody can reference is not a scale — it is a document.

     Fluid between a 360px phone and a 1440px desktop. The display sizes are
     deliberately far apart: a hero only reads as a hero when the next thing
     down is obviously smaller. */
  --fs-display-1: clamp(2.75rem, 1.6rem + 5.1vw, 6.5rem);   /* 44 → 104 */
  --fs-display-2: clamp(2rem, 1.3rem + 3.1vw, 4.5rem);      /* 32 → 72  */
  --fs-h1:        clamp(1.9rem, 1.4rem + 2.2vw, 3.25rem);   /* 30 → 52  */
  --fs-h2:        clamp(1.6rem, 1.25rem + 1.6vw, 2.5rem);   /* 26 → 40  */
  --fs-h3:        clamp(1.25rem, 1.1rem + 0.7vw, 1.625rem); /* 20 → 26  */
  --fs-h4:        1.125rem;
  --fs-lede:      clamp(1.0625rem, 1rem + 0.35vw, 1.25rem);
  --fs-body:      1rem;
  --fs-body-sm:   0.9375rem;
  --fs-caption:   0.8125rem;
  --fs-micro:     0.75rem;

  --lh-display: 0.98;
  --lh-head:    1.12;
  --lh-body:    1.62;

  --ls-display: -0.03em;
  --ls-head:    -0.02em;
  --ls-body:    0;
  --ls-label:   0.14em;

  /* ------------------------------------------------------------------ radii
     `.pic` referenced `var(--radius-lg, 14px)` and --radius-lg HAS NEVER
     EXISTED, so every editorial image on the site fell back to 14px — the
     value §4.2 names as the point where a surface starts reading as a consumer
     app rather than as an instrument. Eight different radii shipped. */
  --r-0:    0;
  --r-1:    2px;
  --r-2:    3px;
  --r-3:    6px;
  --r-4:    10px;
  --r-pill: 999px;
  /* Kept ONLY so the old fallback resolves to something deliberate while the
     remaining call sites are repointed. Do not use it in new rules. */
  --radius-lg: var(--r-3);

  --bw-1: 1px;
  --bw-2: 2px;
  --bw-3: 3px;

  /* ------------------------------------------------------------------ rake
     THE SIGNATURE, and the reason the site has no geometric identity: it was
     specified as "the thing the site is remembered by" and only its easing
     curve was ever built. 29° from vertical, 61° from horizontal — the same
     angle, read two ways, so a skewed edge and a specular sweep are visibly
     the same gesture.

     It mirrors for free: the sign flips under RTL below, so a rake leans into
     the reading direction in both languages without a single override. */
  --rake-skew: 29deg;
  --rake-grad: 61deg;
  --rake-drop: 2.2rem;
  --rake-cut: polygon(0 0, 100% 0, 100% calc(100% - var(--rake-drop)), 0 100%);

  /* ------------------------------------------------- instrument greys
     For measured facts — a price, a duration, a warranty term, a coverage
     figure. Separated from the text ramp on purpose: a number is not prose,
     and giving it its own quiet register is what lets the accent stop being
     the only way to make something look important. */
  --inst-300: #C3C8CD;
  --inst-400: #A7ADB4;
  --inst-500: #878E96;

  /* --------------------------------------------------------- section air
     One rhythm for every band. Ten sections at an identical fixed padding is
     what produces "evenly-spaced grey bands"; this scales with the viewport
     so a desktop page breathes and a phone does not waste a screen on margin. */
  --section-air: clamp(var(--space-10), 4.5vw + var(--space-8), var(--space-13));

  /* ---------------------------------------------------------------- type */
  /* The `Variable` names first, then the static names, then the system
     stack. The variable files are what this site actually ships (see
     public/css/fonts-en.css); the static names are there so a browser
     without variable-font support falls through to a real face rather
     than to nothing. */
  --font-display: 'Archivo Variable', 'Archivo', 'Arial Narrow', system-ui, sans-serif;
  --font-text:    'Inter Variable', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-ar:      'IBM Plex Sans Arabic', 'Almarai', system-ui, sans-serif;

  /* ---------------------------------------------------------------- space */
  --space-0: 0;      --space-1: 4px;    --space-2: 8px;    --space-3: 12px;
  --space-4: 16px;   --space-5: 20px;   --space-6: 24px;   --space-7: 32px;
  --space-8: 40px;   --space-9: 48px;   --space-10: 64px;  --space-11: 80px;
  --space-12: 96px;  --space-13: 120px; --space-14: 160px; --space-15: 200px;

  /* --------------------------------------------------------------- motion */
  --ease-rake:     cubic-bezier(.22, 1, .36, 1);
  --ease-standard: cubic-bezier(.32, .72, 0, 1);
  --ease-exit:     cubic-bezier(.4, 0, 1, 1);
  --ease-sym:      cubic-bezier(.6, 0, .4, 1);

  --dur-1: 90ms;   --dur-2: 140ms;  --dur-3: 220ms;
  --dur-4: 320ms;  --dur-5: 480ms;  --dur-6: 720ms;
  --dur-sweep: 2600ms;
}

/* ---------------------------------------------------------------- direction
   Arabic needs more leading and zero tracking. Latin condensed display faces
   have no Arabic equivalent, which is why the display face is Archivo
   (extended) rather than the prototype's Barlow Condensed. */
[dir='rtl'] {
  --font-display: var(--font-ar);
  --font-text:    var(--font-ar);
  --leading-adjust: 1.3;
  --tracking-display: 0;
}

[dir='ltr'] {
  --leading-adjust: 1;
  --tracking-display: -0.01em;
}

/* ------------------------------------------------------------ reduced motion
   Not a nicety. prefers-reduced-motion collapses every duration to
   instantaneous rather than merely shortening them. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-1: 0ms; --dur-2: 0ms; --dur-3: 0ms;
    --dur-4: 0ms; --dur-5: 0ms; --dur-6: 0ms;
    --dur-sweep: 0ms;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   MOTION — durations and curves
   Values from NN/g's measured guidance and Porsche's published motion tokens,
   not from taste. 100ms for feedback, 250ms for hover, 400ms for a reveal,
   600ms for a large surface. Past 500ms motion reads as lag rather than
   polish; nothing here except the ambient loop exceeds 800ms.

   DURATION FOLLOWS DISTANCE, never importance. A modal is not slower because
   it matters more — it is slower because it is bigger.
   ========================================================================== */
:root {
  --m-instant:  100ms;   /* checkbox, toggle, focus feedback */
  --m-short:    250ms;   /* hover, colour, small scale */
  --m-moderate: 400ms;   /* scroll reveal, tab, drawer */
  --m-long:     600ms;   /* modal, flyout */
  --m-hero:     800ms;   /* one hero reveal, once per page */

  /* The default `ease` keyword is itself a tell — every serious design system
     replaced it years ago. */
  --m-ease:      cubic-bezier(.4, 0, .2, 1);      /* hover, fade, switch */
  --m-ease-out:  cubic-bezier(.33, 1, .68, 1);    /* entering */
  --m-ease-in:   cubic-bezier(.32, 0, .67, 0);    /* leaving — use it shorter */
  --m-ease-hero: cubic-bezier(.16, 1, .3, 1);     /* long-tailed hero reveal */

  /* No overshoot anywhere. A bezier with a Y control point outside 0-1
     reads playful; a precision instrument settles, it does not wobble. */

  --m-stagger: 60ms;   /* capped at 8 siblings = 420ms, then no delay */
  --m-travel:  16px;   /* reveal distance. Never more than 24px — a long
                          slide is the clearest "template" signal there is. */
  --m-travel-sm: 8px;
  /* The ceiling named above, used where a whole section head arrives as one
     unit and the extra distance reads as weight rather than as a slide. */
  --m-travel-lg: 24px;

  /* How far a scroll-linked image drifts inside its own frame, as a share of
     its height. The image is scaled up by twice this so the frame never
     empties at either end of the range. */
  --m-drift: 2.2%;

  /* ONE number drives every mirrored transform in the system. A motion with
     a reading direction multiplies by it; a motion that follows gravity
     ignores it. This is why there is no RTL override sheet. */
  --flow: 1;
}

[dir='rtl'] { --flow: -1; }

/* The rake leans into the reading direction. One sign flip, no override sheet. */
[dir='rtl'] { --rake-skew: -29deg; }

/* Gradients take a PHYSICAL direction — `to right` does not mirror under
   `dir="rtl"` the way a logical property does. This is the gradient half of
   `--flow`: one token, flipped once, so an overlay that shields text always
   darkens the side the text is on. */
:root      { --grad-end: right; }
[dir='rtl'] { --grad-end: left; }

/*
 | ARABIC IS NEVER LETTER-SPACED, AND THIS IS THE ONE JUSTIFIED `!important`.
 |
 | Arabic is a joined script: tracking does not space letters, it BREAKS THE
 | LIGATURES that make a word legible. CSS Text Level 3 §7.2.1 does not say
 | "should" — it says a UA must not apply letter-spacing between characters of
 | a cursive script. Twenty-two rules in this codebase set tracking with no
 | `:lang(ar)` escape, and Arabic is the PRIMARY locale, so every one of them
 | was damaging the master language to style the secondary one.
 |
 | `!important` because the alternative is finding and mirroring all
 | twenty-two, and missing one is invisible to anyone who does not read Arabic.
 */
:lang(ar) {
  letter-spacing: 0 !important;
  --ls-display: 0;
  --ls-head: 0;
  --ls-label: 0;
}

/*
 | `text-transform: uppercase` IS A NO-OP IN ARABIC — the script has no case.
 | So every eyebrow, every label and every small-caps device silently
 | evaporates on the primary locale, leaving text that looks like body copy in
 | the wrong size. Weight carries the same job in a script that cannot shout.
 */
:lang(ar) .eyebrow,
:lang(ar) .stat dt,
:lang(ar) .step__n { font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  :root {
    --m-instant: 0ms; --m-short: 0ms; --m-moderate: 0ms;
    --m-long: 0ms; --m-hero: 0ms;
    /* Defence in depth: even if a reveal class were applied, zero travel and
       zero stagger make it a no-op rather than a half-broken animation. */
    --m-travel: 0px; --m-travel-sm: 0px; --m-travel-lg: 0px; --m-stagger: 0ms;
    --m-drift: 0%;
  }
}
