/* Design tokens — lifted from the Figma "HB / homepage" frame (1512px reference width).
   Everything visual (color, radius, shadow, spacing) lives here; typography.css builds
   the type scale on top, global.css / sections.css consume both. */

:root {
  /* Palette — Figma uses hard-coded hexes, no variables; these are the ones that repeat */
  --ink: #141414;          /* headings, primary buttons, body on light */
  --ink-2: #222222;        /* big stat numbers */
  --text: #585858;         /* descriptive body copy */
  /* WCAG 1.4.3: 5.4:1 contrast on --scene-bg (#e9e9ec), where eyebrows/.small/.caption/
     team role sit, and 6.57:1 on white. Do not lighten past this. */
  --muted: #5c5d62;        /* eyebrows, captions, inactive tabs */
  /* Quiet states that still pass WCAG 1.4.3 on white -- the lightest greys of the family
     that do, so do not lighten past them. --quiet: small text (an unselected tab on
     phones), 4.67:1 against the 4.5 it needs. --quiet-lg: large text only, 44px and up (an
     unselected row in "what we do"), 3.15:1 against 3. */
  --quiet: #72747a;
  --quiet-lg: #8f9197;
  --line: #acafb7;         /* hairlines, scroll track, waveform / ring ticks (unplayed) */
  --bar-light: #e3e5eb;
  /* The site's one grey for a surface: the plate an image or a mark stands on before its
     own picture wipes down over it (works tiles, team portraits, the quote portrait, the
     "what we do" slot, the case pages' plates and panels). #e1e1e5 (L* 89.6) till
     2026-10-08, then lighter and a touch less blue, by ask (L* 92.6). The covers and the
     logo's curtain are this grey too -- and since 12 Oct the scene's ground (--scene-bg):
     one grey for the site. */
  --plate-grey: #e9e9ec;
  --error: #c9463d;        /* an invalid form field: its rule, its label and its message */
  /* Opaque white curtain behind the closing section. */
  --footer-bg: #ffffff;
  --white: #ffffff;
  /* The ground a cut-out portrait stands on: the scene's own grey, lit from above so the
     head reads against it instead of floating on white */
  --ava-ground: radial-gradient(120% 108% at 50% 112%, #d6d8dd 0%, #e7e8eb 52%, #f5f5f7 100%); /* (lighter with --plate-grey, 2026-10-08) */

  /* The scene's ground as it shows, on both paths (scene-ground.js undoes the desktop's tone
     mapping for it), and the page ground under the canvas (what shows until the first WebGL
     frame): the plates' grey. (#d8dadf till 9 Oct -- the desktop showing its raw #e9edff
     mapped to L* 90.3, the phones and the case pages' copies 87 --; #e6e7eb till 12 Oct.) */
  --scene-bg: var(--plate-grey);
  /* The scene's ground is lit, not flat: a lighter middle falling off to a darker rim
     (scene-ground.js reads these at start). glow/edge: ground mixed toward white/black;
     mid: where the plain ground sits; at/size: the light's rest point and radii as screen
     shares -- each chapter moves the canvas light onto its subject (hand, EQ, globe). */
  --scene-glow: 15%; /* (60% / 10% till 9 Oct: what the desktop's mapping flattened to ~15 / 3) */
  --scene-edge: 4%;
  --scene-glow-mid: 45%;
  --scene-glow-at: 50% 38%;
  --scene-glow-size: 55% 60%; /* tight spot so the light's travel across chapters reads */
  /* Every grey surface -- a picture's plate, a mark's ground, a panel, the covers and the
     logo's curtain -- is the one grey lit one way: from above, like the avatars' ground,
     softer than the canvas (small boxes on white). (The marks' grounds were lit in the
     scene's shape, --scene-field, and read as a second grey beside the covers: 9 Oct.) */
  --field-glow: 30%;
  --field-edge: 4%;
  --plate-lit: radial-gradient(130% 120% at 50% 0%,
    color-mix(in srgb, var(--plate-grey), white var(--field-glow)) 0%,
    var(--plate-grey) var(--scene-glow-mid),
    color-mix(in srgb, var(--plate-grey), black var(--field-edge)) 100%);
  /* The shade laid over a picture that white text stands on (a case hero, css/case.css) */
  --plate-dark: #151619;
  --blue: #0055ff;        /* the story's particle blue (dune-story.js pointColor): the office callouts */
  --ink-soft: #3a3b40;    /* between --ink and --muted: 9.2:1 on --scene-bg (--muted is 5.4:1); phones' small text on the scene (mobile.css) */
  /* Dividers on white / grey */
  --hairline: rgba(20, 20, 20, 0.12);
  --hairline-soft: rgba(20, 20, 20, 0.1);
  --field-rule: rgba(20, 20, 20, 0.18);       /* a form field's underline... */
  --field-rule-hover: rgba(20, 20, 20, 0.34); /* ...under the cursor */
  /* Light text on a picture's dark shade (a works tile's caption, a case hero's overline) */
  --on-photo: rgba(255, 255, 255, 0.85); /* (a case hero's overline too) */
  /* "Online": the contact pill's dot, and its pulse */
  --live: #34c759;
  --live-glow: rgba(52, 199, 89, 0.45);
  /* The page map (global.css .scroll-progress) is drawn in a difference blend, so its
     colours are written inverted: the track reads light grey on the light page (and over a
     case hero's picture, its own light track reads dark), the labels a mid grey */
  --map-track: #1c1c1c;
  --map-track-on-photo: #c4c5c9;
  --map-label: #5a5c66;

  /* Scale unit: 1 Figma px at the 1512 reference width. Sizes tracking the mock
     proportionally are written as calc(N * var(--u)); fixed UI (buttons, pills) stays in
     px. --u stops growing at --u-to (a 27" screen), so sizes are max(floor, calc(N * --u))
     with no ceiling of their own -- the one ceiling lives here. */
  --u-to: 2560;
  --u: min(calc(100vw / 1512), calc(var(--u-to) * 1px / 1512));
  /* Height unit for the hero (mock frame is 982 tall): on short laptop screens the
     width-based scale alone leaves the 3-line title eating half the viewport. */
  --uh: calc(100vh / 982);
  /* UI unit: 1px up to the 1512 mock, then grows at ~half the content rate, capping at
     1.35px at --u-to (content is x1.69 there) -- chrome (e.g. the menu drawer) that keeps
     up with a big screen without swelling alongside the headlines.
     0.000334 = 0.35 / (2560 - 1512). */
  --ui: max(1px, calc(1px + (min(100vw, 2560px) - 1512px) * 0.000334));
  /* Small-print unit: same ramp, gentler -- 1.1px at 2560 (0.0000954 = 0.1 / 1048). Used by
     overlines, button labels, captions, footer links, services numbers/copy so they stay
     small print but don't vanish on a big screen. Boxes like pill heights/header skip it. */
  --ui-label: max(1px, calc(1px + (min(100vw, 2560px) - 1512px) * 0.0000954));
  /* Header unit: the same ramp to 1.2px at 2560 (0.000191 = 0.2 / 1048) -- the header keeps
     up with a big screen a step behind the rest of the chrome. */
  --ui-head: max(1px, calc(1px + (min(100vw, 2560px) - 1512px) * 0.000191));

  /* A case hero without a picture (a service's page): this tall (case.css .cb-hero; phones:
     mobile.css) */
  --case-hero-h: min(76svh, max(480px, calc(720 * var(--u))));

  /* Page gutters — 80px in the mock (hero, testimonials pill), 120px for the wide
     stats/why-us blocks. Full-bleed cards and the footer set their own. */
  --page-x: max(24px, calc(80 * var(--u)));
  --page-x-lg: max(32px, calc(120 * var(--u)));

  /* Tracking, by role -- one value per tier, so headlines read as one family:
     display: the big statements (hero, works, team, contact, the closing why-us phrase);
     head: mid-size headlines set in running lines (why-us phrases, the quote, stats, the
     service rows, project titles). body: running copy and small print (0.02em).
     Capitals have two steps and no others (there were five: 0.1 / 0.12 / 0.14 / 0.18 / 0.2):
     caps: overlines, names, buttons, the page map's labels -- set wide, the site's label voice;
     caps-tight: capitals inside a field or a pin (the form's floated label and its error,
     the office pins), where 0.2em would not fit. Every button is caps (0.2em, 9 Oct). */
  --track-display: -0.03em;
  --track-head: -0.02em;
  --track-body: 0.02em; /* body copy, captions, small print */
  --track-caps: 0.2em;
  --track-caps-tight: 0.12em;
  --track-chrome: -0.01em; /* the logo's word, the drawer's links: set a hair tight */
  /* The eyebrow's slash (typography.css .eyebrow::before, and every label set like one):
     its air to the word, its size against it, and its lift to the capitals' middle */
  --slash-gap: 1.3em;
  --slash-fs: 0.82em;
  --slash-y: 0.05em;
  /* An eyebrow to its title (the hero, why us) */
  --gap-eyebrow: 24px;

  /* Type sizes -- the steps of typography.css, and the UI sizes that repeat across the
     site. A step scales with the content (--u) above its floor; UI text with the chrome
     (--ui) or with the small print (--ui-label). Leading and tracking stay with the step
     (typography.css), where they are tuned to it. Phones have their own few (mobile.css). */
  --fs-display-2: max(64px, calc(160 * var(--u)));  /* team marquee */
  --fs-display-3: max(56px, calc(160 * var(--u)));  /* Signature works */
  --fs-display-4: max(52px, calc(132 * var(--u)));  /* What we do */
  --fs-display-5: max(48px, calc(120 * var(--u)));  /* Built on trust */
  --fs-display-6: max(48px, min(calc(120 * var(--u)), calc(120 * var(--uh)))); /* hero: the smaller of width and height */
  --fs-display-8: max(40px, calc(96 * var(--u)));   /* Let's talk */
  --fs-h3: max(28px, calc(48 * var(--u)));          /* featured quote */
  --fs-h4: max(28px, calc(40 * var(--u)));          /* stats */
  --fs-h5: max(24px, calc(36 * var(--u)));          /* project title */
  --fs-h6: max(22px, calc(32 * var(--u)));          /* service title, tabs, a case's label */
  --fs-list-xl: max(34px, calc(69 * var(--u)));     /* the stack's headline */
  --fs-list-lg: max(28px, calc(58 * var(--u)));     /* services rows */
  --fs-list: max(24px, calc(44 * var(--u)));        /* integrations rows, a case's pinned points */
  --fs-body: max(18px, calc(18 * var(--u)));
  --fs-body-sm: max(16px, calc(16 * var(--u)));
  --fs-small-lg: calc(18 * var(--ui-label));        /* the footer's description, a team member */
  --fs-small: calc(14 * var(--ui-label));           /* captions, small print, nav, a note */
  --fs-btn: calc(13 * var(--ui-label));             /* the pills' labels */
  --fs-cap: calc(12 * var(--ui-label));             /* overlines, labels, numbers */
  --fs-micro: calc(11 * var(--ui-label));           /* the page map's labels */
  --fs-tag: calc(16 * var(--ui));                   /* a tagline: "what we do", a case's point */
  --fs-copy: calc(14 * var(--ui));                  /* copy beside a mark, the drawer's small text */

  /* Radii */
  --r-pill: 999px;
  --r-round: 50%;         /* a circle: dots, discs, round buttons */
  --wordmark-fs: max(64px, calc(255 * var(--u))); /* footer wordmark; the rule's pull-up is a share of it */
  --sheet-r: 0px;         /* the content sheet's bottom corners, where it rests on the footer */

  /* Shadows */
  --shadow-btn: 0 4px 8px rgba(20, 20, 20, 0.07);
  --shadow-btn-hover: 0 12px 24px rgba(20, 20, 20, 0.13), 0 4px 9px rgba(20, 20, 20, 0.07);
  --shadow-btn-active: 0 5px 12px rgba(20, 20, 20, 0.11), 0 2px 5px rgba(20, 20, 20, 0.06);
  /* White pills on the white sheet (.plate) need more separation than the page default */
  --shadow-plate: 0 10px 24px rgba(20, 20, 20, 0.1), 0 3px 8px rgba(20, 20, 20, 0.06);
  --shadow-plate-hover: 0 16px 32px rgba(20, 20, 20, 0.14), 0 6px 12px rgba(20, 20, 20, 0.08);
  --shadow-plate-active: 0 6px 14px rgba(20, 20, 20, 0.12), 0 2px 6px rgba(20, 20, 20, 0.08);

  /* One hover for every button: it lifts and its shadow deepens by a relative step. These
     three say where a button rests/goes; a surface needing a different pair (white plate,
     drawer card) redefines them for its own subtree instead of using absolute values,
     which would vanish against a larger base shadow. */
  --lift-rest: var(--shadow-btn);
  --lift-hover: var(--shadow-btn-hover);
  --lift-active: var(--shadow-btn-active);
  --lift-up: -3px;   /* how far it rises... */
  --lift-down: -1px; /* ...and where it sits while pressed */
  /* Figma's 4-layer float shadow (124px offset / 83px blur) painted a visible band
     over the waveform; a tighter stack reads the same and stays under the pill. */
  --shadow-float:
    0 16px 40px rgba(0, 0, 0, 0.1),
    0 4px 12px rgba(0, 0, 0, 0.06),
    0 1px 3px rgba(0, 0, 0, 0.05);

  /* Display-text fill: a black→transparent sweep over a solid ink layer, so the glyphs
     run #000 at the top-left to #141414 at the bottom-right (very subtle, per mock). */
  --text-gradient:
    linear-gradient(157deg, #000 8.3%, rgba(102, 102, 102, 0) 81.9%),
    linear-gradient(90deg, var(--ink), var(--ink));

  /* Motion — one token per curve used more than once; one-off curves stay literal at their
     rule instead of being named. Scroll-driven JS animators keep their own smoothstep /
     easeOutCubic math, not these tokens. Reduced motion is handled per rule
     (`prefers-reduced-motion` blocks; the line masks, the drawer and the buttons in
     global.css, M11). */
  --ease-out: cubic-bezier(0.2, 0.75, 0.15, 1);      /* everything that arrives or settles: the line masks, the drawer, the pills, the hero's exit (one curve: M1, MOTION.md) */
  --ease-in: cubic-bezier(0.5, 0, 0.75, 0.3);        /* leaving: text pulled up out of its mask */
  --ease-io: cubic-bezier(0.45, 0, 0.25, 1);         /* the long clip-path wipes (a picture arriving over its grey plate) */

  /* Durations — the four the page actually repeats. Anything else (0.34s, 0.45s...)
     is tuned to its own gesture and stays written out: never round a duration to a token. */
  --dur-fast: 0.2s;
  --dur-base: 0.3s;
  --dur-slow: 0.5s;
  --dur-reveal: 0.7s;
  --cta-follow: 0.3s; /* a call's button after its title has started to rise (landing and case, MOTION.md) */
  /* The motion system's moves (MOTION.md, "Vocabulary"); script reads these too (js/motion.js) */
  --dur-wipe: 1.1s;        /* picture-wipe: a picture over its grey plate (1.6s -> 1.1s, 13 Oct: the works tiles unfolded too long; = --dur-wipe-panel) */
  --dur-wipe-panel: 1.1s;  /* panel-wipe: a panel or plate arriving */
  --dur-wipe-inline: 0.6s; /* picture-wipe of a portrait or mark standing in a line of typed text: the typing moves on */
  --dur-cover: 0.55s;      /* grey-cover: the grey coming up over the screen (script: case-leave, case, reload-curtain) */
  --dur-curtain: 0.55s;    /* curtain-lift: that grey leaving after a logo reload */
  --dur-drop: 0.55s;       /* line-drop: a line back under its mask */
  --dur-exit: 0.4s;        /* line-exit: a line up and out of its mask */
  --dur-accordion: 0.4s;   /* accordion: a body opening under its title (case points, footer columns) */
  --exit-stagger: 0.6;     /* a leaving line's delay, of its rising one */
  --back-stagger: 0;       /* the way back up (html.scroll-up): a line or block coming in again, its delay x this -- 0: all at once */
  --dur-swap-out: 0.32s;   /* a label's swap: the leaving line, quick and alone... */
  --dur-swap-in: 0.55s;    /* ...the arriving one... */
  --dur-swap-gap: 0.34s;   /* ...once the line is clear */
  --dur-disc: 0.45s;       /* the why-us player's disc turning into its ring (script: why-cursor) */
  --rise-mask: 110%;       /* a line under its mask; out above it: */
  --rise-mask-out: -110%;
  --rise-block: 16px;      /* block-rise: a block below its place; above it on the way out: */
  --rise-block-out: -16px;

  /* Header: 72px on the mock, growing on a big screen (--ui-head, x1.2 at 2560) */
  --header-h: calc(72 * var(--ui-head));
  /* The close's overline from the screen's top on a narrow screen (sections.css <= 768) */
  --overline-top-narrow: 112px;

  /* z-index ladder */
  --z-canvas: 0;
  --z-footer: 8;           /* the footer's layer: under the page sheet, which rides up off it */
  --z-tint: 8;             /* the white closing in round the hero (global.css .page-tint): under the sheet */
  --z-page: 10;
  --z-header: 100;
  --z-menu: 500;           /* the menu pop-up and its blurred backdrop: over everything but --z-top */
  --z-top: 1000;           /* over everything: the cursor (+1) and the phones' rotate note (+2) */

  /* Hero pin: extra scroll the hero stays on screen for (main.js reads it from .hero-stage
     height, ui.js too). A fallback only: dune-story.css sets the real pin (720vh, 200vh
     under reduced motion). */
  --hero-pin: 160vh;
}

/* Karaoke fill fraction on why-us words (sections.css) — registered so it transitions */
@property --f {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}

/* Scroll-written scalars that only the element they are set on reads (ui.js): --shrink on
   .page-tint and .why-sticky, --leave on the sticky screen's children and the player.
   Non-inherited, so a new value restyles those elements and not everything inside them --
   written every frame of a handover, an inherited one re-did every word of every phrase. */
@property --shrink {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}
@property --leave {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}
