/* ═══════════════════════════════════════════════════════════════
   Ramié Fabrics — coming soon, option A4: Where It Lands
   The Kept Card, laid in a world that plays once and comes to rest.
   Authored against ../../DESIGN.md. Every colour and size is a token.
   The stationery lies over the client's footage. Every word sits on Dust
   Linen stock, never on the footage, so every ratio in DESIGN.md §2 holds.
   ═══════════════════════════════════════════════════════════════ */

@font-face{
  font-family:"Manrope";
  src:url(assets/fonts/manrope-var-latin.woff2) format("woff2");
  font-weight:200 800;
  font-style:normal;
  font-display:swap;
}

:root{
  /* Palette — pixel-verified against brand book p.18 (DESIGN.md §2) */
  --ground:#E9E1D8;      /* Dust Linen */
  --surface:#E0C3A6;     /* Bare Fabric */
  --ink:#35251C;         /* Shadow Moss */
  --accent:#6C1D35;      /* Evening Plum */
  --accent-ink:#763C32;  /* Aged Clay */
  --signal:#C65C47;      /* Papaya — surface and focus ring only, never text */
  --highlight:#C3BA6E;   /* Still Garden */
  --deep:#4F2339;        /* Storm Orchid */
  --olive:#584925;       /* Deep Olive */
  --lavender:#754356;    /* Quiet Lavender */

  /* Roles on this page */
  --table:var(--accent);   /* under the footage, and below the world band on narrow screens */
  --stock:var(--ground);   /* card and label: uncoated, felt-finish, never white */

  /* The world. Two cuts of one shot: a square crop on phones, the full
     16:9 frame from 600px up. Opening frame and resting frame of each. */
  --still-start:url(assets/footage/square-start.jpg);
  --still-rest:url(assets/footage/square-rest.jpg);
  --world-pos:50% 70%;          /* where the frame is anchored when cropped: keep the bench */
  --band:min(100vw, 56svh);     /* narrow screens: the world is a band at the head of the page */
  --dissolve:900ms;             /* the only change the world makes besides playing: a crossfade */

  /* Derived tones — mixes of palette colours, never new hues */
  --emboss-shade:color-mix(in srgb, var(--ink) 20%, var(--stock));
  /* The raking light on the relief's lit wall: the stock itself, lifted.
     A 2px edge only, never a ground, never behind text. */
  --emboss-light:color-mix(in srgb, var(--stock) 40%, white);
  --cast:color-mix(in srgb, var(--ink) 60%, transparent);

  /* Type scale — 1.25 ratio (DESIGN.md §3) */
  --step--1:clamp(0.78rem, 0.76rem + 0.1vw, 0.84rem);
  --step-0:clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  --step-1:clamp(1.25rem, 1.18rem + 0.35vw, 1.5rem);
  --step-2:clamp(1.56rem, 1.44rem + 0.6vw, 2rem);
  --step-3:clamp(1.95rem, 1.72rem + 1.1vw, 2.75rem);
  --step-4:clamp(2.44rem, 2.02rem + 2vw, 4rem);

  /* 8pt grid (DESIGN.md §4) */
  --space-1:4px; --space-2:8px; --space-3:16px; --space-4:24px;
  --space-5:32px; --space-6:48px; --space-7:64px; --space-8:96px;
  --space-9:128px; --space-10:192px;

  /* The table's margin, and the card's own furniture */
  --pad-x:var(--space-3);
  --pad-y:var(--space-3);
  /* The card lies over the foot of the world band by exactly its bite, so the
     footage fills the bracket-cut corners and stops where the straight edge starts. */
  --lap:var(--bite);
  --pad-top:calc(var(--band) - var(--lap));
  --pad-end:var(--space-8);
  --bite:var(--space-4);        /* radius of the concave bracket-cut corner */
  --stitch-in:var(--space-2);   /* stitch inset from the cut edge */
  --card-px:var(--space-4);
  --card-pt:var(--space-6);
  --card-pb:var(--space-6);
  --col-gap:var(--space-7);
  --label-w:15rem;
  /* The spread: half the screen, never so narrow that the line breaks to three. */
  --card-w:clamp(50rem, 50vw, 60rem);
  --cast-x:var(--space-1);      /* the shadow falls away from the footage's low sun, behind and left */

  --ease:cubic-bezier(.2,.6,.2,1);

  --font:"Manrope","Helvetica Neue",Arial,sans-serif;
}

@media (min-width:600px){
  :root{
    --still-start:url(assets/footage/wide-start.jpg);
    --still-rest:url(assets/footage/wide-rest.jpg);
    --world-pos:50% 75%;
    --band:min(56.25vw, 62svh);   /* the whole 16:9 frame where it fits */
  }
}
@media (min-width:768px){
  :root{
    --pad-x:var(--space-6); --pad-y:var(--space-6);
    --bite:var(--space-5); --stitch-in:var(--space-3);
    --card-px:var(--space-7); --card-pt:var(--space-7); --card-pb:var(--space-7);
  }
}
/* The spread — card and label side by side — needs room for both, so it
   opens at 1280. Below that the label lies under the card. */
@media (min-width:1280px){
  :root{
    --pad-y:var(--space-7); --pad-top:var(--pad-y); --pad-end:var(--pad-y);
    --card-pt:var(--space-7); --card-pb:var(--space-6);
    --col-gap:var(--space-6);
    /* Anchored left: the frame's right half, where the cloth flies and
       lands, falls in the open field beside the card. */
    --world-pos:0% 70%;
  }
}
@media (min-width:1440px){
  :root{
    --pad-x:var(--space-7);
    --label-w:16rem;
  }
}
/* Short desktop windows: tighten the card's vertical furniture so the
   spread still sits on one screen. */
@media (min-width:1280px) and (max-height:820px){
  :root{ --pad-y:var(--space-5); --card-pt:var(--space-6); --card-pb:var(--space-4) }
  :root{ --reply-gap:var(--space-5) }
  .reply{ padding-top:var(--space-4) }
}
/* Very short laptop windows (1366×657 and the like). */
@media (min-width:1280px) and (max-height:700px){
  :root{ --pad-y:var(--space-4); --card-pt:var(--space-5) }
}
/* Landscape phones: a lower band, so the first screen still carries words. */
@media (max-height:500px) and (orientation:landscape){
  :root{ --band:min(56.25vw, 46svh) }
}

*,*::before,*::after{ box-sizing:border-box }
html{ -webkit-text-size-adjust:100%; background:var(--table); color-scheme:light }

/* body stays transparent so the fixed world (z-index -1) paints above the
   root background and below everything else. */
body{
  margin:0;
  background:transparent;
  color:var(--ink);
  font-family:var(--font);
  font-size:var(--step-0);
  font-weight:300;
  line-height:1.6;
  font-synthesis-weight:none;
  -webkit-font-smoothing:antialiased;
}
p{ margin:0 }

/* ── The world ───────────────────────────────────────────────
   The client's footage. Three layers, one picture at a time:
   the .world background (first paint), the film, and the resting frame.
   The film and both stills share one crop (--world-pos, cover), so a
   hand-over between them is pixel-registered. No scrim, no tint.
   Narrow screens: a band at the head of the page, tall enough to hold the
   whole frame, so the bench the cloth lands on is never under the card.
   The spread (1280 and up): full-bleed and fixed, as in the Kept Card. */
.world{
  position:absolute; top:0; left:0; right:0; z-index:-1;
  height:var(--band);
  overflow:hidden;
  background:var(--table) var(--still-rest) var(--world-pos) / cover no-repeat;
}
.is-story .world{ background-image:var(--still-start) }
.world__film,
.world__rest{ position:absolute; inset:0; width:100%; height:100% }
.world__film{
  display:block;
  object-fit:cover; object-position:var(--world-pos);
}
.world__film[hidden]{ display:none }
.world__rest{
  background:var(--still-rest) var(--world-pos) / cover no-repeat;
  opacity:0;
  transition:opacity var(--dissolve) var(--ease);
}
.world.is-resting .world__rest{ opacity:1 }

/* At rest the world does not freeze. The footage's camera was still easing in
   when the cloth came down, so the resting frame carries that move on: a slow
   drift toward the cloth, out and back, 44 seconds each way. It begins
   from rest (ease-in-out), so it continues the film's own stop instead of
   starting a new motion. Only the world moves; the card never does. A return
   visit opens on the resting frame and drifts from the first paint. */
.world__rest{ transform-origin:var(--drift-origin, 62% 62%) }
.world.is-resting .world__rest,
html:not(.is-story) .world__rest{
  opacity:1;
  animation:drift 44s cubic-bezier(.45,0,.55,1) var(--dissolve) infinite alternate both;
}
@keyframes drift{
  from{ transform:none }
  to{ transform:scale(1.08) translate3d(-1.4%, -1%, 0) }
}

/* ── The table ───────────────────────────────────────────────
   Nothing on it but two pieces of stationery. */
.table{
  min-height:100vh; min-height:100svh;
  padding:var(--pad-top) var(--pad-x) var(--pad-end);
  padding-top:max(var(--pad-top), env(safe-area-inset-top));
  padding-bottom:max(var(--pad-end), env(safe-area-inset-bottom));
  padding-left:max(var(--pad-x), env(safe-area-inset-left));     /* landscape phones: clear the notch */
  padding-right:max(var(--pad-x), env(safe-area-inset-right));
  display:grid;
  align-content:start;
  gap:var(--space-5);
}

/* ── The card ────────────────────────────────────────────────
   p.23: concave bracket-cut corners on the stock and a dashed "stitch" rule
   following them inside all four edges. Square everywhere else. */
.card{
  position:relative;
  isolation:isolate;
  display:grid;
  grid-template-columns:minmax(0,1fr);
  align-content:start;
  padding:var(--card-pt) var(--card-px) var(--card-pb);
  background:var(--stock);   /* no-JS: a plain sheet with a straight stitch */
}
.card::before{
  content:""; position:absolute; z-index:-1;
  inset:var(--stitch-in);
  border:1.5px dashed var(--accent);
  pointer-events:none;
}
.card.is-cut{ background:transparent }
.card.is-cut::before{ display:none }

.card__cut{
  position:absolute; inset:0; z-index:-2;
  width:100%; height:100%;
  overflow:visible;
  pointer-events:none;
  /* The overlay doctrine: an opaque object with a soft shadow (p.05, p.20). */
  filter:drop-shadow(0 1px 1px var(--cast)) drop-shadow(var(--cast-x) 12px 28px var(--cast));
}
.card__stock{ fill:var(--stock) }
.card__stitch{
  fill:none;
  stroke:var(--accent);
  stroke-width:1.5;
  stroke-dasharray:7 5;
}

/* ── Marks ───────────────────────────────────────────────────
   Extracted artwork as alpha masks, recoloured. Never live text, never
   moved, scaled or transformed after layout, never animated. */
.mark{
  display:block; background-color:currentColor;
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  -webkit-mask-size:contain; mask-size:contain;
  -webkit-mask-position:left center; mask-position:left center;
}
.mark--wordmark{
  -webkit-mask-image:url(assets/marks/wordmark.png);
  mask-image:url(assets/marks/wordmark.png);
  /* Never below ~180px wide: the 1–2px hairlines disappear (BRAND-BRIEF §6). */
  width:min(100%, 34rem);
  min-width:240px;
  aspect-ratio:1600/398;
  margin-left:-3%;   /* the artwork's own side bearing, so the R sits on the text edge */
}

/* ── The greeting ──────────────────────────────────────── */
.card__mark{ margin:0 0 var(--space-5); color:var(--accent); font-size:inherit }
.card__line{
  max-width:17ch;
  font-size:var(--step-3); font-weight:200; line-height:1.15;
  letter-spacing:-.01em;
  color:var(--ink);
  text-wrap:balance;
}
.card__what{
  margin-top:var(--space-3);
  font-size:var(--step-0); font-weight:400;
  color:var(--accent-ink);
}

/* ── The reply slip ──────────────────────────────────────────
   The card's lower part, ruled off as a printed reply card is. Both faces
   share one grid cell, so success is a crossfade in place: never a slide. */
.reply{
  margin-top:var(--space-7);
  padding-top:var(--space-5);
  border-top:1px solid var(--accent-ink);
  display:grid;
}
.reply__face{ grid-area:1 / 1; transition:opacity 400ms var(--ease) }
.reply__face[hidden]{ display:none }
.reply__face.is-spent{ visibility:hidden }
.reply__ask{ display:grid; gap:var(--space-5) }

.reply__h{
  margin:0 0 var(--space-3);
  max-width:22ch;
  font-size:var(--step-1); font-weight:500; line-height:1.3;
  color:var(--accent);
  text-wrap:balance;
}
.reply__sub{ max-width:40ch; color:var(--ink); text-wrap:pretty }

.form{ display:grid; gap:var(--space-2); align-content:start }
.form__row{ display:grid }
.form__label{
  font-size:var(--step--1); font-weight:600;
  letter-spacing:.16em; text-transform:uppercase;
  color:var(--accent-ink);
}
/* A ruled line on the card: how a printed reply card asks to be filled in.
   Aged Clay on Dust Linen, 6.59:1 — well past the 3:1 a control boundary needs. */
.form__input{
  font:inherit; font-size:var(--step-1); font-weight:300;
  color:var(--ink);
  background:transparent;
  border:0; border-bottom:1.5px solid var(--accent-ink);
  border-radius:0;
  padding:var(--space-1) 0 var(--space-2);
  min-height:48px;
  width:100%;
  transition:border-color 200ms var(--ease);
}
.form__input:hover{ border-bottom-color:var(--ink) }
.form__input[aria-invalid="true"]{ border-bottom-color:var(--accent); box-shadow:0 1.5px 0 var(--accent) }
.form__input:read-only{ color:var(--accent-ink) }

.form__btn{
  margin-top:var(--space-3);
  font:inherit; font-size:var(--step--1); font-weight:700;
  letter-spacing:.16em; text-transform:uppercase;
  color:var(--stock); background:var(--accent);
  border:0; border-radius:0;
  min-height:48px;
  min-width:14rem;   /* "Joining…" must not shrink the button under the cursor */
  padding:var(--space-3) var(--space-5);
  justify-self:start;
  white-space:nowrap;
  cursor:pointer;
  touch-action:manipulation;
  -webkit-tap-highlight-color:transparent;   /* the ground change is the feedback */
  transition:background-color 200ms var(--ease);
}
.form__btn:active{ background:var(--deep) }
.form__btn:hover{ background:var(--deep) }
.form__btn[aria-disabled="true"]{ background:var(--deep); cursor:progress }

.form__msg{ min-height:1.6em; font-size:var(--step--1); font-weight:500; color:var(--accent) }
.form__msg[data-state="error"]{ font-weight:600 }

/* One line of plain terms under the form, and the way to the full notice. */
.form__note{ max-width:44ch; font-size:var(--step--1); color:var(--ink); text-wrap:pretty }
.form__note a{
  color:var(--accent-ink);
  text-decoration:underline; text-decoration-thickness:1px; text-underline-offset:.2em;
  touch-action:manipulation;
}
.form__note a:hover{ color:var(--accent) }
/* A thumb needs ~44px: grow the hit area, hand the room back, move nothing. */
@media (pointer:coarse){
  .form__note a{ display:inline-block; padding-block:12px; margin-block:-12px }
}

/* The honeypot: out of sight and out of the tab order, but still in the form. */
.form__trap{ position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); white-space:nowrap }

.reply__kept{ align-self:start }
.reply__done{
  margin-bottom:var(--space-3);
  font-size:var(--step--1); font-weight:600;
  letter-spacing:.16em; text-transform:uppercase;
  color:var(--accent-ink);
}
.reply__thanks{
  max-width:22ch;
  font-size:var(--step-3); font-weight:200; line-height:1.15;
  color:var(--accent);
  text-wrap:balance;
}
.reply.is-turning .reply__ask,
.reply__kept.is-entering{ opacity:0 }

/* ── The blind emboss ────────────────────────────────────────
   p.01, p.26: the mark with no ink, legible only because raking light catches
   its relief. Three copies of one mask in exact register — the lit wall up and
   left, the shaded wall down and right, the stock face on top. No box-shadow,
   no bevel filter, no gradient: those read as plastic. Static, always. */
.emboss{
  position:relative;
  display:block;
  aspect-ratio:566/760;
  width:min(62%, 15rem);
  margin:0 0 var(--space-6) auto;
  pointer-events:none;
  --e:1.5px;
}
.emboss > span{
  position:absolute; inset:0;
  -webkit-mask:url(assets/marks/monogram-oval.png) center / contain no-repeat;
          mask:url(assets/marks/monogram-oval.png) center / contain no-repeat;
}
/* inset: top right bottom left — each copy offset by --e, same size. */
.emboss > .emboss__light{ background:var(--emboss-light); inset:calc(var(--e) * -1) var(--e) var(--e) calc(var(--e) * -1) }
.emboss > .emboss__shade{ background:var(--emboss-shade); inset:var(--e) calc(var(--e) * -1) calc(var(--e) * -1) var(--e) }
.emboss > .emboss__face{ background:var(--stock) }

/* ── The label ───────────────────────────────────────────────
   p.16 / p.13. The square of the book's three label formats, pinked left and
   right with a fine tooth because it is a small piece, straight top and
   bottom. The lockup is its face; a hairline rules off its one line of data.
   The cut is finished, so it never animates. */
/* The footer is the object; its shadow is cast by the cut, not the box. */
.label{
  width:var(--label-w);
  max-width:100%;
  justify-self:end;   /* beside the card's right hand at every width */
  filter:drop-shadow(0 1px 1px var(--cast)) drop-shadow(var(--cast-x) 8px 20px var(--cast));
}
.label__cut{
  aspect-ratio:1;
  display:grid;
  place-items:center;
  padding:var(--space-5);
  background:var(--stock);
  color:var(--accent);
  clip-path:polygon(0% 0%, 100% 0%, calc(100% - 6px) 1.923%, 100% 3.846%, calc(100% - 6px) 5.769%, 100% 7.692%, calc(100% - 6px) 9.615%, 100% 11.538%, calc(100% - 6px) 13.462%, 100% 15.385%, calc(100% - 6px) 17.308%, 100% 19.231%, calc(100% - 6px) 21.154%, 100% 23.077%, calc(100% - 6px) 25.000%, 100% 26.923%, calc(100% - 6px) 28.846%, 100% 30.769%, calc(100% - 6px) 32.692%, 100% 34.615%, calc(100% - 6px) 36.538%, 100% 38.462%, calc(100% - 6px) 40.385%, 100% 42.308%, calc(100% - 6px) 44.231%, 100% 46.154%, calc(100% - 6px) 48.077%, 100% 50.000%, calc(100% - 6px) 51.923%, 100% 53.846%, calc(100% - 6px) 55.769%, 100% 57.692%, calc(100% - 6px) 59.615%, 100% 61.538%, calc(100% - 6px) 63.462%, 100% 65.385%, calc(100% - 6px) 67.308%, 100% 69.231%, calc(100% - 6px) 71.154%, 100% 73.077%, calc(100% - 6px) 75.000%, 100% 76.923%, calc(100% - 6px) 78.846%, 100% 80.769%, calc(100% - 6px) 82.692%, 100% 84.615%, calc(100% - 6px) 86.538%, 100% 88.462%, calc(100% - 6px) 90.385%, 100% 92.308%, calc(100% - 6px) 94.231%, 100% 96.154%, calc(100% - 6px) 98.077%, 100% 100.000%, 0% 100%, 6px 98.077%, 0% 96.154%, 6px 94.231%, 0% 92.308%, 6px 90.385%, 0% 88.462%, 6px 86.538%, 0% 84.615%, 6px 82.692%, 0% 80.769%, 6px 78.846%, 0% 76.923%, 6px 75.000%, 0% 73.077%, 6px 71.154%, 0% 69.231%, 6px 67.308%, 0% 65.385%, 6px 63.462%, 0% 61.538%, 6px 59.615%, 0% 57.692%, 6px 55.769%, 0% 53.846%, 6px 51.923%, 0% 50.000%, 6px 48.077%, 0% 46.154%, 6px 44.231%, 0% 42.308%, 6px 40.385%, 0% 38.462%, 6px 36.538%, 0% 34.615%, 6px 32.692%, 0% 30.769%, 6px 28.846%, 0% 26.923%, 6px 25.000%, 0% 23.077%, 6px 21.154%, 0% 19.231%, 6px 17.308%, 0% 15.385%, 6px 13.462%, 0% 11.538%, 6px 9.615%, 0% 7.692%, 6px 5.769%, 0% 3.846%, 6px 1.923%, 0% 0.000%);
}
.lockup{ width:100%; height:auto; overflow:visible; display:block }
.lockup text{ fill:currentColor; font-family:var(--font); text-anchor:middle }
.lockup__arc{ font-size:22px; font-weight:800; letter-spacing:.2em }
.lockup__line{ font-size:14px; font-weight:700; letter-spacing:.24em }


/* ── Phones ──────────────────────────────────────────────────
   The world band takes the head of the screen, so the card opens with its
   words: wordmark, line and origin sit above the fold under the cloth. The
   emboss moves into the card's void, before the reply slip. Same subgrid
   trick as the tablet grid below, so the emboss stays out of reading order. */
@supports (grid-template-rows:subgrid){
  @media (max-width:767px){
    .card{ grid-template-rows:repeat(5, auto) }
    .card__body{
      grid-column:1; grid-row:1 / -1;
      display:grid;
      grid-template-columns:subgrid;
      grid-template-rows:subgrid;
    }
    .card__mark{ grid-row:1 }
    .card__line{ grid-row:2 }
    .card__what{ grid-row:3 }
    .reply{ grid-row:5; margin-top:var(--space-6) }
    .emboss{
      grid-column:1; grid-row:4;
      width:min(44%, 10rem);
      margin:var(--space-5) 0 0 auto;
    }
  }
}

/* ── Tablet and up ───────────────────────────────────────────
   One grid for the whole card. <main> is a subgrid of it, so the words and the
   emboss share rows without the emboss ever entering the document's reading
   order: mark | emboss, then the line, then the void, then the reply slip. */
@media (min-width:768px){
  .card{
    /* The words take three quarters, so the line still sets on two lines
       beside the relief; the relief takes the rest. */
    grid-template-columns:minmax(0,3fr) minmax(0,1fr);
    grid-template-rows:auto auto auto 1fr auto;
    column-gap:var(--col-gap);
  }
  .card__body{
    grid-column:1 / -1; grid-row:1 / -1;
    display:grid;
    grid-template-columns:subgrid;
    grid-template-rows:subgrid;
  }
  .card__mark{ grid-column:1; grid-row:1; align-self:start; margin-bottom:var(--space-5) }
  .card__line{ grid-column:1; grid-row:2 }
  .card__what{ grid-column:1; grid-row:3 }
  .reply{ grid-column:1 / -1; grid-row:5; margin-top:var(--space-7) }
  .emboss{
    grid-column:2; grid-row:1 / 4;
    justify-self:end; align-self:start;
    width:min(100%, 12rem);
    margin:0;
  }
  .form__row{ grid-template-columns:minmax(0,1fr) auto; column-gap:var(--space-4); align-items:end }
  .form__row .form__btn{ margin-top:0 }
}

/* ── Desktop: the stationery spread ──────────────────────────
   The world goes full-bleed and fixed. The card lies on the left at a fixed
   width, the label at the far right, both down on one line at the foot, as
   if on the bench in the picture. The field between them is the world's:
   the cloth crosses it and lands there. */
@media (min-width:1280px){
  .world{ position:fixed; inset:0; height:auto }
  .table{
    grid-template-columns:minmax(0, var(--card-w)) minmax(var(--space-6), 1fr) var(--label-w);
    align-items:end;
    column-gap:0;
  }
  .card{
    grid-column:1;
    min-height:calc(100svh - 2 * var(--pad-y));
    /* The line ("Made to last, because you keep it.") needs ~24rem to set on
       two lines; the relief takes what is left. */
    grid-template-columns:minmax(0,7fr) minmax(0,4fr);
  }
  .card__mark{ align-self:start }
  .card__line,.card__what{ grid-column:1 }
  .reply{ margin-top:var(--reply-gap, var(--space-6)) }
  .reply__ask{
    grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    column-gap:var(--space-6);
    align-items:start;
  }
  .form__row{ grid-template-columns:minmax(0,1fr) }
  .form__row .form__btn{ margin-top:var(--space-3) }
  .emboss{
    grid-row:1 / 5;
    align-self:center;
    width:auto;
    height:min(100%, 36rem);
    max-width:100%;
    margin:0;
    --e:2px;
  }
  .label{ grid-column:3; justify-self:end }
}

/* ── The intro film ──────────────────────────────────────────
   Twenty seconds, from the early site (site/film.js). Every line sits on an
   opaque ground, so no contrast pair in DESIGN.md §2 ever depends on the
   cloth. It covers the page completely until it lifts. */
.is-intro{ overflow:hidden }                  /* the page waits under the film */
.is-intro .intro{ display:grid }
.intro{
  position:fixed; inset:0; z-index:10;
  place-items:center;
  background:var(--ground);
  transition:opacity var(--dissolve) var(--ease);
}
.intro.is-over{ opacity:0; pointer-events:none }
.intro__cloth{ position:absolute; inset:0; width:100%; height:100%; display:block }
.intro__source{ position:fixed; left:0; top:0; width:1px; height:1px; opacity:0; pointer-events:none }
.intro__veil{ position:absolute; inset:0; background:var(--ground); opacity:0 }
.intro__veil--accent{ background:var(--accent) }   /* a second layer, never a colour swap */
.intro__copy{
  position:relative;
  width:min(90vw, 62rem); min-height:12rem;
  padding-inline:var(--pad-x);
  text-align:center;
}
.intro__line{
  position:absolute; inset:0;
  margin:0;
  display:flex; flex-direction:column; gap:.15em;
  align-items:center; justify-content:center;
  font-size:var(--step-3); font-weight:200; line-height:1.25;
  color:var(--ink);
  text-wrap:balance;
  opacity:0;
}
.intro__line--invert{ color:var(--surface) }
/* Drawn on the card's own wordmark box (intro.js measures it). Opacity only:
   never its own layer, or the 1-2px hairlines resample and thin. */
.intro__mark{
  position:fixed; left:50%; top:50%;
  transform:translate(-50%,-50%);             /* fallback until registered */
  width:min(88vw, 34rem); min-width:0; margin:0;
  color:var(--accent);
  opacity:0;
}
.intro__mark.is-registered{ left:0; top:0; transform:none }
.intro__bar{
  position:absolute; left:0; right:0; bottom:0; height:1px;
  background:color-mix(in srgb, var(--ink) 12%, transparent);
}
.intro__bar span{
  display:block; height:100%; width:100%;
  background:var(--bar-ink, var(--accent-ink));
  transform:scaleX(0); transform-origin:left center;
  transition:background-color 400ms var(--ease);
}
/* Twenty seconds needs a visible way out, not just a gesture. */
.intro__skip{
  position:fixed; z-index:11;
  top:max(var(--space-4), env(safe-area-inset-top)); right:max(var(--pad-x), env(safe-area-inset-right));
  min-height:44px;
  font:inherit; font-size:var(--step--1); font-weight:600;
  letter-spacing:.16em; text-transform:uppercase;
  color:var(--ground); background:var(--ink);
  border:0; padding:var(--space-2) var(--space-4);
  cursor:pointer;
  touch-action:manipulation;
  transition:background-color 200ms var(--ease), opacity 300ms var(--ease);
}
.is-intro .intro__skip{ display:block }
.intro__skip:hover{ background:var(--accent) }
.intro__skip.is-over{ opacity:0; pointer-events:none }
@media (max-width:479px){
  .intro__line{ font-size:var(--step-2) }
}

/* ── Focus: always visible, Papaya at 3px, 2px offset (DESIGN.md §2) ── */
:where(a,button,input,[tabindex]):not([tabindex="-1"]):focus-visible{
  outline:3px solid var(--signal);
  outline-offset:2px;
}
.form__input:focus-visible{ outline-offset:4px }
[tabindex="-1"]:focus{ outline:none }

/* ── Motion ──────────────────────────────────────────────────
   Near-still. No entrance: the stationery is simply there. The marks, the
   stitch and the pinked edge never move. Only the world moves: one flight,
   once, slowing to a stop, then a still. Besides that, the reply slip's
   crossfade (above), the resting frame's dissolve, and the button's ground
   shifting between two palette colours on hover. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{ transition-duration:.01ms !important; animation-duration:.01ms !important; transition-delay:0s !important }
  /* Not shortened: removed. An infinite animation at .01ms would flicker. */
  .world__rest{ animation:none !important }
}

@media (forced-colors:active){
  .card,.label{ border:1px solid CanvasText }
  .mark{ background-color:CanvasText }
}
