/* ─────────────────────────────────────────────────────────────
   Layer order, declared once. Later layers win without specificity
   fights; anything unlayered wins over everything (keep that rare).
   All Baseline 2026: nesting, @layer, container queries, :has().
   ───────────────────────────────────────────────────────────── */
@layer reset, tokens, base, layout, components, motion, utilities;

/* ── the one typeface ───────────────────────────────────────────
   Switzer Variable (Indian Type Foundry · Fontshare · ITF Free Font
   License, commercial web OK). Self-hosted; the only font on the site.
   @font-face sits outside layers on purpose — descriptors don't cascade. */
@font-face{
  font-family:'Switzer';
  src:url('fonts/Switzer-Variable.woff2') format('woff2');
  font-weight:100 900;
  font-style:normal;
  font-display:swap;
}
/* Metric-compatible fallback: Arial re-shaped to Switzer's vertical
   metrics, so the swap causes no layout shift. Numbers from the reference. */
@font-face{
  font-family:'Switzer Fallback';
  src:local('Arial');
  ascent-override:96.67%;
  descent-override:24.66%;
  line-gap-override:8.88%;
  size-adjust:101.38%;
}

/* The wordmark's resting height once shrunk = the three-line menu block.
   Registered as a <length> so main.js can read it back resolved in px
   when the menu itself is not on screen (narrow viewports). */
@property --brand-min-h{ syntax:'<length>'; inherits:true; initial-value:72px }

@layer reset {
  *{ margin:0; padding:0; box-sizing:border-box }
  html, body{
    width:100%;
    /* house rule: no rubber-band bounce, ever */
    overscroll-behavior:none;
    /* the collage starts beyond the edges; clip (not hidden — hidden would break sticky) */
    overflow-x:clip;
  }
  img, video, canvas{ display:block; max-width:100% }
}

@layer tokens {
  /* ═══════════════════════════════════════════════════════════
     TYPE SYSTEM — see DESIGN.md for the full spec and the rules.
     Derived from drone.riotters.com, translated from its three
     breakpoints (mobile / 768 / 1200) into fluid clamp() — no width
     media queries anywhere. One family, three weights, negative
     tracking that grows with size, hierarchy by size + weight + ink
     alpha. No second font, no uppercase eyebrows, no bold.
     ═══════════════════════════════════════════════════════════ */
  :root{
    --font-sans:'Switzer','Switzer Fallback','Inter Tight',-apple-system,BlinkMacSystemFont,'Helvetica Neue',Arial,sans-serif;

    /* — weights: only these three exist — */
    --w-display:300;   /* everything ≥18px: display, headings, paragraphs */
    --w-text:400;      /* labels, card titles, meta (≤20px)                */
    --w-ui:500;        /* nav + buttons at 14px — the only place for 500   */

    /* — sizes: min = reference mobile, max = reference desktop —
                                                     @390  @768  @1200 @1440 */
    --fs-display: clamp(80px, 16.667vw, 240px);  /* ghost word    80   128   200   240 */
    --fs-h1:      clamp(40px,  3.333vw,  48px);  /* statement     40    40    40    48 */
    --fs-h2:      clamp(24px,  4.667vw,  56px);  /* selector      24    36    56    56 */
    --fs-figure:  clamp(32px,  3.333vw,  48px);  /* stat numeral  32    32    40    48 */
    --fs-lead:    clamp(20px,  2.222vw,  32px);  /* lead para     20    20    27    32 */
    --fs-body-lg: clamp(18px,  2vw,      24px);  /* sub-headline  18    18    24    24 */
    --fs-body:    clamp(16px,  1.667vw,  20px);  /* body          16    16    20    20 */
    --fs-small:   14px;                          /* meta, captions — the ex-eyebrow tier */
    --fs-ui:      14px;                          /* nav, buttons (16px on coarse pointers, below) */

    /* — line-height: desktop values of the reference, unitless — */
    --lh-display:.92;    /* 220/240 */
    --lh-h1:1.125;       /* 54/48   */
    --lh-h2:1.15;        /* 64/56   */
    --lh-figure:1.15;    /* 56/48   */
    --lh-lead:1.3;       /* 40/32   */
    --lh-body-lg:1.33;   /* 32/24   */
    --lh-body:1.45;      /* 28/20   */
    --lh-small:1.7;      /* 24/14   */
    --lh-ui:1.7;         /* 24/14   */

    /* — tracking: never positive, tightens as size grows — */
    --ls-display:-.055em;  /* -13px @240 */
    --ls-h1:-.04em;        /* -1.9px @48 */
    --ls-h2:-.025em;       /* -1.4px @56 */
    --ls-figure:-.06em;    /* -2.8px @48 */
    --ls-lead:-.025em;     /* -0.8px @32 */
    --ls-body-lg:-.03em;   /* -0.8px @24 */
    --ls-body:-.02em;      /* -0.4px @20 */
    --ls-small:-.01em;
    --ls-ui:0;

    /* — measure (apply in layout, not baked into type classes) — */
    --measure-lead:480px;
    --measure-body:420px;
    --measure-h1:580px;   /* statement column: keeps clear of the centre, where the video stands */
    --container:1256px;
    --container-wide:1600px;

    /* — ink + alpha ladder. Palette is a placeholder from the reference;
         the bracelet brief will replace --ink / --bg / --ghost, the ladder stays. — */
    --ink:#24363f;
    --bg:#fff;
    --ink-88:color-mix(in srgb, var(--ink) 88%, transparent);  /* demoted paragraph      */
    --ink-64:color-mix(in srgb, var(--ink) 64%, transparent);  /* meta, second sentence  */
    --ink-48:color-mix(in srgb, var(--ink) 48%, transparent);  /* tertiary               */
    --ink-32:color-mix(in srgb, var(--ink) 32%, transparent);  /* inactive selector item */
    --ink-24:color-mix(in srgb, var(--ink) 24%, transparent);  /* disabled / hairline    */
    --ink-08:color-mix(in srgb, var(--ink)  8%, transparent);  /* dividers, faint fills  */
    --ghost:#ededed;   /* the 240px display word on --bg: present, barely there */

    /* — chrome / hero geometry — */
    --pad-x: max(16px, 2.7211vw);            /* 40px @1470 */
    --pad-y: max(16px, 2.1769vw);            /* 32px @1470 — the top line shared by wordmark, menu, burger */
    --fs-brand: min(var(--fs-display), 26svh); /* wordmark: display size, capped so short viewports keep room below */
    --brand-min-h: calc(3 * var(--fs-ui) * var(--lh-ui));   /* three UI lines ≈ 71px (82px on touch) */
    --shrink-end: .32;                       /* pre-JS estimate; main.js publishes the measured coefficient */
    --hero-runway: 80svh;                    /* scroll distance over which the hero dissolves and the brand shrinks */
    --sec2-runway: 220svh;                   /* screen 2: text in · collage in · hold · collage out · text out */

    /* — materials collage — */
    --mat-max: 180px;                        /* hard cap on the longer side */
    --mat-travel: 520px;                     /* parallax travel per unit speed (alp1ne) */
    --mat-rot: 4deg;                         /* parallax rotation per unit rot (alp1ne) */

    /* — motion — */
    --ease-expo: cubic-bezier(0.16, 1, 0.3, 1);   /* house; reference uses .22,1,.36,1 */
    /* the reveal contract (DESIGN.md §7): enter rises from below, exit lifts away — asymmetric on purpose */
    --in-y:12px;  --in-blur:8px;
    --out-y:12px; --out-blur:8px;
  }

  /* touch targets: buttons/nav read at 16px on coarse pointers (reference: text-ui) */
  @media (pointer: coarse){
    :root{ --fs-ui:16px }
  }
}

@layer base {
  body{
    font-family:var(--font-sans);
    font-weight:var(--w-display);
    font-size:var(--fs-body);
    line-height:var(--lh-body);
    letter-spacing:var(--ls-body);
    background:var(--bg);
    color:var(--ink);
    -webkit-font-smoothing:antialiased;
    text-rendering:optimizeLegibility;
  }
  h1, h2, h3, h4, p{ font:inherit; letter-spacing:inherit }
  b, strong{ font-weight:var(--w-ui) }   /* "bold" on this site means 500, never 700 */
  a{ color:inherit }
  button{ font:inherit; letter-spacing:inherit; color:inherit; background:none; border:0 }

  /* keyboard focus: visible, never on mouse clicks */
  :focus-visible{ outline:2px solid var(--ink); outline-offset:4px }
  :focus:not(:focus-visible){ outline:none }
}

@layer layout {
  /* ── the plate: fixed full-bleed video stage behind everything. Content
       (main, chrome) stacks above it; --x2 crossfades layer B over layer A
       at the handoff scroll; --stage-out dissolves the whole stage to white
       (blur gated behind .is-blurring); .is-covered stops compositing. ── */
  .stage{
    position:fixed;
    inset:0;
    z-index:0;
    background:var(--bg);
    opacity:calc(1 - var(--stage-out, 0));
  }
  .stage__layer{ position:absolute; inset:0 }
  .stage__b{ opacity:var(--x2, 0) }
  .stage video,
  .stage canvas{
    position:absolute;
    inset:0;
    width:100%;
    height:100%;
    object-fit:cover;
  }
  /* portrait phones: a 16:9 clip covering a 9:19 viewport crops to the model's
     torso and runs under the copy. Contain it in the lower part of the screen
     instead — the girl reads whole, the text above keeps its white.
     (The clip's own background is white, so the letterbox is invisible.) */
  @media (max-aspect-ratio: 3/4){
    .stage video,
    .stage canvas{ object-fit:contain; object-position:center 92% }
  }
  /* the frame-bank canvas reveals only on its first REAL paint (is-live) */
  canvas.plate{ opacity:0; transition:opacity 240ms linear }
  canvas.plate.is-live{ opacity:1 }
  .stage.is-blurring{ filter:blur(calc(var(--stage-out, 0) * 24px)) }
  .stage .is-covered,
  .stage.is-covered{ visibility:hidden }

  main{ position:relative; z-index:1 }   /* content composites above the plate */

  /* ── fixed chrome: only what survives the hero — the wordmark and the burger.
       Shares --pad-y / --pad-x with .hero__stage, so wordmark, menu and burger
       sit on one top line without any coordination. ── */
  .chrome{
    position:fixed;
    inset:0;
    z-index:10;
    padding:var(--pad-y) var(--pad-x);
    display:flex;
    justify-content:space-between;
    align-items:flex-start;
    pointer-events:none;   /* re-enabled per interactive element */
  }

  /* The wordmark. Sized like the display tier, capped by the viewport height.
     Shrinks with scroll via scale() from the corner — never font-size, so the
     shrink costs no layout and no paint. --shrink is written by main.js. */
  .brand{
    pointer-events:auto;
    display:block;
    font-size:var(--fs-brand);
    line-height:var(--lh-display);
    letter-spacing:var(--ls-display);
    font-weight:var(--w-display);
    text-decoration:none;
    white-space:nowrap;
    transform-origin:top left;
    transform:scale(var(--shrink, 1));
    will-change:transform;
  }

  /* The initial: Playball, the one second-face gesture on the site. Its "O"
     sits a touch larger than the grotesque and tucks under the "r"; the
     vertical nudge lines its bowl up with the x-height of "rla". */
  .brand__initial{
    font-family:'Playball', 'Switzer', cursive;
    font-weight:400;
    letter-spacing:0;
    font-size:1.04em;
    line-height:0;              /* contributes nothing to the line box; the brand keeps its own leading */
    margin-right:-.06em;
    display:inline-block;
    translate:0 -.01em;
  }

  /* 44px hit area around a 36×1.5px two-line glyph; the negative margins pull
     the glyph's optical centre onto the menu's first line. */
  .burger{
    pointer-events:auto;
    width:44px;
    height:44px;
    margin:-10px -4px 0 0;
    display:grid;
    align-content:center;
    justify-items:end;
    gap:8px;
    cursor:pointer;
    touch-action:manipulation;
  }

  /* ── hero: pinned for --hero-runway of scroll while it dissolves ── */
  .hero{ height:calc(100svh + var(--hero-runway)) }
  .hero__stage{
    position:sticky;
    top:0;
    min-height:100svh;
    padding:var(--pad-y) var(--pad-x);
    display:grid;
    grid-template-columns:minmax(0, 7fr) minmax(0, 3fr);   /* menu starts at 70% of the width */
    grid-template-rows:38svh minmax(0, 1fr) auto;          /* lede lands on the 38svh line, CTA on the floor */
  }
  .hero__nav{
    grid-area:1 / 2;
    align-self:start;      /* content-height box: main.js measures it as the brand's shrink target */
    display:grid;
    justify-items:start;
  }
  .hero__lede{
    grid-area:2 / 1;
    align-self:start;
    max-width:var(--measure-body);
  }
  .hero__cta{
    grid-area:3 / 1;
    justify-self:start;
    align-self:end;
  }

  /* ── screen 2: statement left, centred in the free field (always clear of
       the shrunk wordmark), materials list pinned to the bottom-right ── */
  .sec2{ height:calc(100svh + var(--sec2-runway)) }
  .sec2__stage{
    position:sticky;
    top:0;
    min-height:100svh;
    padding:var(--pad-y) var(--pad-x);
    display:grid;
    grid-template-columns:minmax(0, 3fr) minmax(0, 2fr);   /* statement 60%: five-ish lines at 64px */
    grid-template-rows:minmax(0, 1fr) auto;
    column-gap:var(--pad-x);
  }
  .sec2__statement{
    grid-area:1 / 1;
    align-self:center;
    position:relative;
    z-index:2;                 /* the text layer: collage items sit below (1) or above (3) it */
    max-width:var(--measure-h1);
    padding-top:calc(var(--brand-min-h) + var(--pad-y));   /* keep the centre below the resting wordmark */
  }
  .specs{
    grid-area:2 / 2;
    justify-self:end;
    align-self:end;
    position:relative;
    z-index:2;
    width:min(100%, var(--measure-lead));
    list-style:none;
  }

  /* ── materials collage: absolute cut-outs on the stage, positioned by %,
       back/front straddling the text (alp1ne collage). One line per item —
       this is the composition; retune here. The centre corridor (≈40–60%)
       is kept free for the video. Motion is written inline by main.js. ── */
  .mats{ position:absolute; inset:0; pointer-events:none }
  .mat{
    position:absolute;
    left:var(--x);
    top:var(--y);
    width:min(var(--w), var(--mat-max));
    height:auto;
    will-change:transform;
    /* mirroring goes in `transform`; main.js writes translate / rotate / scale
       as individual properties, which compose with it instead of overwriting it */
  }
  .mat--back { z-index:1 }
  .mat--front{ z-index:3 }
  [data-mat="driftwood"]{ --x:4%;  --y:74%; --w:180px }   /* bottom-left, front, from the left  */
  [data-mat="cotton"]   { --x:27%; --y:6%;  --w:170px }   /* under the first line, back, from above (the PNG has a lot of air) */
  [data-mat="driftwood-2"]{ --x:84%; --y:30%; --w:180px; transform:scale(-1, -1) }   /* right edge, front, from the right — the same branch, flipped both ways */
  [data-mat="fern"]     { --x:66%; --y:40%; --w:135px; transform:scaleX(-1) }        /* above the list, back, from below — mirrored to face the text */
  [data-mat="granite"]  { --x:76%; --y:5%;  --w:160px }   /* under the burger, front, from above */

  /* ── shop: flows normally. hellohello's 16-column grid — row A
       [medium 2:3][small 1:1][gap][large 1:1], row B [small][gap][tall 2:3][square] ── */
  .shop{ padding:calc(var(--pad-y) * 4) var(--pad-x) }
  .shop__head{
    display:grid;
    grid-template-columns:minmax(0, 7fr) minmax(0, 3fr);   /* same split as the hero: lede on the menu line */
    column-gap:var(--pad-x);
    align-items:end;
    margin-bottom:64px;
  }
  .shop__title{ grid-column:1; margin-left:-.04em }         /* optical: the S's side bearing vs the edge */
  .shop__lede{ grid-column:2; max-width:var(--measure-body); padding-bottom:.4em }
  .grid{
    display:grid;
    grid-template-columns:repeat(16, minmax(0, 1fr));
    column-gap:24px;
    row-gap:clamp(64px, 9vw, 136px);
    align-items:start;
    list-style:none;
  }
  .card--a1{ grid-column:1 / span 3; grid-row:1 }
  .card--a2{ grid-column:4 / span 3; grid-row:1 }
  .card--a3{ grid-column:9 / -1;     grid-row:1 }
  .card--b1{ grid-column:1 / span 3; grid-row:2 }
  .card--b2{ grid-column:6 / span 3; grid-row:2 }
  .card--b3{ grid-column:9 / span 3; grid-row:2 }
  .card__media{ margin:0; aspect-ratio:1; background:var(--ink-08); overflow:hidden }
  .card--a1 .card__media,
  .card--b2 .card__media{ aspect-ratio:2 / 3 }
  .card__media img{ display:block; width:100%; height:100%; object-fit:cover }

  @media (max-width: 767px){
    .shop__head{ grid-template-columns:minmax(0, 1fr); row-gap:24px; align-items:start; margin-bottom:40px }
    .shop__lede{ grid-column:1 }
    .grid{ grid-template-columns:repeat(2, minmax(0, 1fr)); column-gap:16px; row-gap:40px }
    .card--a1, .card--a2, .card--b1, .card--b2, .card--b3{ grid-column:auto / span 1; grid-row:auto }
    .card--a3{ grid-column:1 / -1; grid-row:auto }

    .hero__stage{
      grid-template-columns:minmax(0, 1fr);
      grid-template-rows:30svh minmax(0, 1fr) auto;
    }
    .hero__nav{ display:none }    /* the burger is the navigation here */
    .hero__lede{ grid-area:2 / 1 }
    .hero__cta{ grid-area:3 / 1 }

    /* screen 2 flows instead of pinning: a 48px statement plus the list is
       taller than a phone viewport. main.js notices (stage is not sticky)
       and reveals lines by their own position in the viewport instead. */
    .sec2{ height:auto }
    .sec2__stage{
      position:relative;         /* not static: .mats needs a positioned ancestor */
      min-height:0;
      padding-block:calc(var(--pad-y) * 3) calc(var(--pad-y) * 2);
      grid-template-columns:minmax(0, 1fr);
      grid-template-rows:auto 220px auto;   /* statement · collage band · list */
      row-gap:32px;
    }
    .sec2__statement{ grid-area:1 / 1; padding-top:0 }
    .specs{ grid-area:3 / 1; width:100% }

    /* collage on phones: no free margins beside the text, so the cut-outs get
       a band of their own between the statement and the list — they fly into
       it from the sides and never touch a line of type. Two thirds the size. */
    .mats{ grid-area:2 / 1; position:relative; inset:auto; height:220px }
    .mat{ width:calc(min(var(--w), var(--mat-max)) * .66) }
    [data-mat="driftwood"]{ --x:-4%; --y:58% }
    [data-mat="granite"]  { --x:34%; --y:4%  }
    [data-mat="fern"]     { --x:26%; --y:42% }
    [data-mat="cotton"]   { --x:64%; --y:50% }
    [data-mat="driftwood-2"]{ --x:62%; --y:0% }
  }

  /* Tablet portrait keeps the desktop composition, but gives navigation and
     copy enough room at iPad widths instead of squeezing both into 30%. */
  @media (min-width: 768px) and (max-width: 1024px){
    .hero__stage,
    .shop__head{ grid-template-columns:minmax(0, 3fr) minmax(240px, 2fr) }
    .sec2__stage{ grid-template-columns:minmax(0, 11fr) minmax(280px, 9fr) }
    .grid{ column-gap:18px; row-gap:clamp(56px, 8vw, 88px) }
  }
}

@layer components {
  /* ── type roles — one class per row of the scale (DESIGN.md) ──
     Each sets exactly size / line-height / tracking / weight. Color and
     measure are applied separately (.is-* modifiers, layout tokens). */
  .t-display{ font-size:var(--fs-display); line-height:var(--lh-display); letter-spacing:var(--ls-display); font-weight:var(--w-display) }
  .t-h1     { font-size:var(--fs-h1);      line-height:var(--lh-h1);      letter-spacing:var(--ls-h1);      font-weight:var(--w-display); text-wrap:balance }
  .t-h2     { font-size:var(--fs-h2);      line-height:var(--lh-h2);      letter-spacing:var(--ls-h2);      font-weight:var(--w-display); text-wrap:balance }
  .t-figure { font-size:var(--fs-figure);  line-height:var(--lh-figure);  letter-spacing:var(--ls-figure);  font-weight:var(--w-display); white-space:nowrap }
  .t-lead   { font-size:var(--fs-lead);    line-height:var(--lh-lead);    letter-spacing:var(--ls-lead);    font-weight:var(--w-display); text-wrap:pretty }
  .t-body-lg{ font-size:var(--fs-body-lg); line-height:var(--lh-body-lg); letter-spacing:var(--ls-body-lg); font-weight:var(--w-display); text-wrap:pretty }
  .t-body   { font-size:var(--fs-body);    line-height:var(--lh-body);    letter-spacing:var(--ls-body);    font-weight:var(--w-display); text-wrap:pretty }
  .t-title  { font-size:var(--fs-body);    line-height:var(--lh-body);    letter-spacing:var(--ls-body);    font-weight:var(--w-text) }   /* card title: body size, 400 */
  .t-small  { font-size:var(--fs-small);   line-height:var(--lh-small);   letter-spacing:var(--ls-small);   font-weight:var(--w-text) }
  .t-ui     { font-size:var(--fs-ui);      line-height:var(--lh-ui);      letter-spacing:var(--ls-ui);      font-weight:var(--w-ui) }

  /* ── hierarchy by ink, not by color or caps ── */
  .is-ghost  { color:var(--ghost) }     /* section display word */
  .is-demoted{ color:var(--ink-64) }    /* second half of a two-tone sentence, meta */
  .is-muted  { color:var(--ink-32) }    /* inactive item in a selector list */

  /* ── nav links: hover dims through color, never opacity — opacity belongs
       to the reveal contract and a transition on it would lag the scroll ── */
  .hero__nav a{
    text-decoration:none;
    touch-action:manipulation;
    transition:color 200ms var(--ease-expo);
  }

  /* ── the one button: flat ink-08 slab, inverts on hover, gives on press ── */
  .btn{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    min-height:44px;
    padding:10px 24px;
    background:var(--ink-08);
    border-radius:4px;
    text-decoration:none;
    cursor:pointer;
    touch-action:manipulation;
    transition:background-color 200ms var(--ease-expo), color 200ms var(--ease-expo), scale 160ms ease-out;
  }
  .btn:active{ scale:.97 }

  /* ── materials list: hairline rows, hierarchy by ink only ── */
  .specs__row{
    display:flex;
    justify-content:space-between;
    align-items:baseline;
    gap:24px;
    padding:12px 0;
    border-top:1px solid var(--ink-24);
  }
  .specs__row:last-child{ border-bottom:1px solid var(--ink-24) }
  .specs__row span:last-child{ text-align:right }

  /* words produced by the line splitter: transforms need a box (inline gets none) */
  .word{ display:inline-block }

  /* ── product card: photo → name | price → tag, all flush left. Sentence case, ink hierarchy. ── */
  .card__link{ display:block; text-decoration:none; touch-action:manipulation; transition:scale 160ms ease-out }
  .card__link:active{ scale:.985 }
  .card__row{ display:flex; justify-content:space-between; gap:16px; margin-top:12px }
  .card__tag{ margin-top:4px }
  .card__media img{ transition:scale 700ms var(--ease-expo) }

  .burger span{ display:block; width:36px; height:1.5px; background:currentColor }
  .burger{ transition:color 200ms var(--ease-expo), scale 160ms ease-out }
  .burger:active{ scale:.97 }

  @media (hover: hover) and (pointer: fine){
    .hero__nav a:hover{ color:var(--ink-64) }
    .btn:hover{ background:var(--ink); color:var(--bg) }
    .burger:hover{ color:var(--ink-64) }
    .card__link:hover .card__media img{ scale:1.03 }
  }
}

@layer motion {
  /* ─────────────────────────────────────────────────────────────
     THE REVEAL CONTRACT — every text and element on the site enters
     and leaves through this. main.js writes two 0→1 custom properties
     ON THE ELEMENT (never :root):
       --in   0→1  appear : rises from below --in-y, blur --in-blur→0, fades in
       --out  0→1  vanish : lifts up --out-y, blur 0→--out-blur, fades out
     Both compose, so a scroll that begins mid-entrance folds into it.
     No CSS transitions here on purpose: type tracks the finger raw.
     .is-anim gates the blur (a permanent blur(0) forces a render
     surface for nothing); .is-hidden drops the element out of
     hit-testing and the accessibility tree once it is invisible.
     Rested state IS the design: with JS off both vars fall back to 1/0.
     ───────────────────────────────────────────────────────────── */
  [data-reveal]{
    opacity:calc(var(--in, 1) * (1 - var(--out, 0)));
    translate:0 calc((1 - var(--in, 1)) * var(--in-y) - var(--out, 0) * var(--out-y));
  }
  [data-reveal].is-anim{
    filter:blur(calc((1 - var(--in, 1)) * var(--in-blur) + var(--out, 0) * var(--out-blur)));
  }
  [data-reveal].is-hidden{ visibility:hidden }

  /* hidden until a driver writes --in — only while JS runs.
     data-reveal        → enters at load, in --i order (time-driven)
     data-reveal="scroll" → enters with its section's scroll progress */
  .js [data-reveal]{ --in:0 }

  /* the wordmark develops into focus in place: blur only, no travel (house title entrance) */
  .brand[data-reveal]{ --in-y:0px; --in-blur:14px }

  /* collage items take only opacity / blur / hidden from the contract; their
     translate / rotate / scale are written inline by main.js (fly-in + parallax) */
  .mat[data-reveal]{ translate:none; --out-blur:0px }   /* the collage leaves by opacity and lift only, no blur */

  /* the catalogue enters at a larger amplitude: 12px of rise vanishes on a
     684px card, 40px reads; the display title gets the wordmark's blur */
  .card[data-reveal]{ --in-y:40px; --in-blur:12px }
  .shop__title[data-reveal]{ --in-y:24px; --in-blur:14px }
}

@layer utilities {
  .sr-only{
    position:absolute; width:1px; height:1px; margin:-1px; padding:0;
    overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
  }
}

@media (prefers-reduced-motion: reduce){
  /* keep the fades, drop the travel and the blur */
  [data-reveal]{ translate:none }
  [data-reveal].is-anim{ filter:none }
  .btn, .burger, .card__link, .card__media img{ transition:none }
  .stage.is-blurring{ filter:none }   /* the whiteout stays a plain fade */
  canvas.plate{ transition:none }
}
