/* ============================================================
   CINEMATIC OPENING — fixed film backdrop · hero card · sticker burst
   Replaces the whole old HERO block. Two parts:
     1. a single FIXED film-strip layer at z-index 0, behind everything;
     2. an 180svh intro whose sticky stage bursts stickers out of the
        middle (desktop) or out of a camera lens (mobile).
   All hidden start states are gated on .js — no-JS gets the finished
   composition, never a blank screen.
   ============================================================ */

/* main has to own a stacking context above the backdrop, or the fixed
   film strip paints over the page instead of behind it. */
main{position:relative;z-index:1}

/* Linen at four alphas — one source of truth for every scrim in the
   opening. color-mix keeps --linen authoritative; the rgba() line is the
   fallback for engines without it. */
.hero,.intro{
  --veil-0:rgba(246,242,235,0);
  --veil-c:rgba(246,242,235,.9);
  --veil-9:rgba(246,242,235,.94);
  --veil-1:rgba(246,242,235,1);
}
@supports (color:color-mix(in srgb,#000,#fff)){
  .hero,.intro{
    --veil-0:color-mix(in srgb,var(--linen) 0%,transparent);
    --veil-c:color-mix(in srgb,var(--linen) 90%,transparent);
    --veil-9:color-mix(in srgb,var(--linen) 94%,transparent);
    --veil-1:var(--linen);
  }
}

/* ---------- 1. FIXED FILM-STRIP BACKDROP ------------------ */
.backdrop{
  position:fixed;inset:0;z-index:0;
  display:flex;justify-content:center;align-items:flex-start;
  overflow:hidden;      /* the -4deg tilt must never reach the scrollbar */
  pointer-events:none;  /* scenery, not a click target */
}
.strip-v{
  position:relative;
  width:min(880px,94vw);
  margin-top:-16vh;     /* the strip runs off the top and bottom of the window */
  padding:clamp(18px,2vw,30px) clamp(42px,4.6vw,64px);
  display:flex;flex-direction:column;
  gap:clamp(20px,2.6vw,38px);
  background:var(--espresso);
  transform:rotate(-4deg);
  box-shadow:0 26px 70px rgba(23,23,23,.34);
}
/* sprocket holes — one repeating gradient per edge, linen punched out of
   the espresso body. background-size sets the pitch, the gradient stop the
   hole height. */
.strip-v::before,.strip-v::after{
  content:"";position:absolute;top:0;bottom:0;
  width:clamp(16px,1.6vw,24px);border-radius:4px;
  background-image:linear-gradient(to bottom,var(--linen) 0 26px,transparent 26px 62px);
  background-size:100% 62px;
}
.strip-v::before{left:clamp(11px,1.2vw,17px)}
.strip-v::after {right:clamp(11px,1.2vw,17px)}

/* From 1000px up the strip moves out of the centre and into the right half —
   the hero copy owns the left. Below that the copy sits over the film and the
   strip stays centred (see the hero's own breakpoint). */
@media (min-width:1000px){
  /* The padding pulls the strip IN from the right edge and the width grows it
     leftward, so together they close the dead band that used to sit between
     the copy and the film on wide screens. Both were retuned at once: padding
     alone would have shrunk the film against the right gutter, width alone
     would have run it off the screen. */
  .backdrop{justify-content:flex-end;align-items:center;padding-right:clamp(1rem,6vw,7.5rem)}
  .strip-v{
    width:min(50vw,660px);
    /* ~120° off horizontal instead of the near-vertical 94°, so the strip cuts
       across the right half as a diagonal and the first frame (red sweater)
       drops into view instead of running off the top of the window */
    transform:rotate(-30deg);
    margin-top:0;
  }
}

.film-frame{display:block;background:var(--stone);padding:clamp(6px,.7vw,11px)}
/* In colour, deliberately — the hero strip is the one that should pop. The
   intro strip (.strip-h) stays black-and-white; do not unify these. */
.strip-v .film-frame img{
  width:100%;height:clamp(230px,46vh,520px);
  object-fit:cover;object-position:center 32%;
  filter:saturate(1.06) contrast(1.05);
}

/* ---------- 2. HERO — a window onto the strip -------------- */
.hero{
  position:relative;z-index:1;
  /* named rather than inlined into the shorthand because the mobile hero sizes
     its first row against them — see the max-width:999px block */
  --hero-pad-t:clamp(4.5rem,9.5vh,7rem);
  --hero-pad-b:clamp(2.75rem,6vh,4.5rem);
  display:grid;place-items:center;
  min-height:100svh;
  padding:var(--hero-pad-t) var(--gutter) var(--hero-pad-b);
  overflow:clip;
  background:transparent;   /* the fixed backdrop shows through */
}
/* Three washes, in one layer:
   1. a centre scrim so the title reads as a main-title card burned onto the
      film — dark enough for linen type over any frame, including the blown-out
      white studio backdrops;
   2. a top wash to linen so the nav's espresso links stay legible;
   3. a bottom wash to solid linen so the hero dissolves into the intro.
   The card itself carries no fill — hiding the film would defeat the point. */
.hero__veil{
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    radial-gradient(128% 76% at 50% 50%,
      rgba(23,23,23,.66) 0%,rgba(23,23,23,.5) 42%,rgba(23,23,23,0) 76%),
    linear-gradient(to bottom,var(--veil-9) 0,var(--veil-0) 15%),
    linear-gradient(to top,   var(--veil-1) 0,var(--veil-0) 22%);
}
/* Split layout: no centre scrim (the copy is on clean linen now, not on the
   film), and a left-to-right linen fade that guarantees the copy column stays
   clear however far the tilted strip creeps in. */
@media (min-width:1000px){
  .hero__veil{
    background:
      /* holds near-solid linen through the nav's own band before falling off,
         so the espresso links never sit on a bright frame */
      linear-gradient(to bottom,
        var(--veil-1) 0,var(--veil-9) 7%,var(--veil-0) 22%),
      linear-gradient(to top,   var(--veil-1) 0,var(--veil-0) 18%),
      /* Anchored to the copy column, not to a percentage of the viewport.
         Percentages drifted: the column's right edge is 54% of a 1000px screen
         but 38% of a 1920px one, so one pair of stops either left thin text
         over the film at the narrow end or washed the film out at the wide end.
         Solid linen runs to exactly where the copy stops, then clears within
         6rem — which also lets the film sit closer than the old 34/58 did. */
      linear-gradient(to right,
        var(--veil-1) 0,
        var(--veil-1) calc(var(--hero-inset) + var(--hero-card)),
        var(--veil-0)  calc(var(--hero-inset) + var(--hero-card) + 6rem));
  }
}

/* the title card — an intertitle laid over the film, no fill of its own */
.hero__card{
  position:relative;z-index:2;
  width:min(640px,90vw);
  padding:clamp(1.6rem,3.4vw,2.4rem) clamp(1.2rem,3.6vw,2.6rem);
  text-align:center;
  color:var(--linen);
  text-shadow:0 1px 26px rgba(23,23,23,.55);
}
/* the stitch motif, echoing .btn::after and .stitch */
.hero__card::after{
  content:"";position:absolute;inset:0;
  border:1px dashed rgba(181,146,120,.55);pointer-events:none;
}
/* micro-labels and the italic accent invert onto the film */
.hero__card .label{color:rgba(246,242,235,.72)}
.hero__card .annot{color:var(--linen)}
.hero__card .stitch{border-bottom-color:rgba(181,146,120,.9)}
/* the CTA inverts too — linen on film, filling to espresso on hover */
.hero__card .btn{
  background:transparent;color:var(--linen);
  border-color:rgba(246,242,235,.6);text-shadow:none;
}
.hero__card .btn:hover,.hero__card .btn:focus-visible{
  background:var(--linen);color:var(--espresso);border-color:var(--linen);
}

.hero__eyebrow{margin-bottom:clamp(.7rem,1.5vw,1.1rem)}
.hero__name{
  font-family:var(--name);font-weight:900;
  font-size:clamp(3.4rem,15vw,7.4rem);
  line-height:.86;letter-spacing:-.005em;
  text-transform:uppercase;
  /* Fraunces is a variable face; ask for the soft, wonky instance explicitly so
     it still renders as intended if the static instance above ever 404s */
  font-variation-settings:"opsz" 144,"SOFT" 100,"WONK" 1;
}
.hero__name .line > span{display:block}

.hero__accent{margin-top:clamp(.8rem,1.7vw,1.2rem)}
.hero__headline{
  margin-top:clamp(.85rem,1.9vw,1.45rem);
  font-family:var(--display);font-weight:300;
  font-size:var(--t-head);line-height:1.06;letter-spacing:-.004em;
}
.hero__tagline{
  margin:var(--s-2) auto 0;max-width:44ch;
  font-size:var(--t-lead);font-weight:300;line-height:1.68;
}
.hero__actions{
  display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:var(--s-3);
  margin-top:clamp(1.3rem,2.2vw,1.9rem);
}
.hero__meta{margin-top:clamp(.9rem,1.7vw,1.3rem)}

/* ---- HERO ENTRANCE: the take begins ----
   Cinematic load sequence. The film strip uses @keyframes (it sits outside
   the data-reveal system); the card enhances the existing reveal with
   hero-specific overrides — bigger throws, more elastic easing, and a
   rotation settle on the slate. All gated on .js; reduced-motion kills
   everything via the existing blanket rules in reduced-motion.css.

   Body background is --linen. With the strip at opacity 0 the visitor sees
   a linen page; as the strip fades in the film materialises THROUGH the
   existing veil gradients, so it appears correctly graded from the start —
   like a projector warming up on a screen that was always there. */

/* Film strip: materialises from a gentle zoom */
.js .strip-v{
  animation:hero-film 2.2s var(--ease) .2s both;
}
@keyframes hero-film{
  from{opacity:0;transform:rotate(-4deg) scale(1.1) translateY(-2%)}
}
@media (min-width:1000px){
  @keyframes hero-film{
    from{opacity:0;transform:rotate(-30deg) scale(1.08)}
  }
}

/* The name: a grander line reveal — bigger throw, slower, more elastic.
   140% translateY (vs the base 110%) makes the letters sweep up rather than
   peek; the overshoot easing gives the settle a film-reel click. Needs an
   explicit .is-in because the hero-scoped selector outranks the base
   .rv-lines.is-in rule by cascade order. */
.hero__name .line > span{
  transition:transform 1.4s cubic-bezier(.22,1.15,.36,1) var(--d,0s);
}
.js .hero__name .line > span{transform:translateY(140%)}
.hero__name.is-in .line > span{transform:none}

/* Accent badge: scale-up on entry for a "stamp" landing */
.hero__accent[data-reveal]{
  transition:opacity .9s var(--ease-soft) var(--d,0s),
             transform 1.1s cubic-bezier(.22,1.1,.36,1) var(--d,0s);
}
.js .hero__accent[data-reveal]{transform:translateY(16px) scale(.92)}
.hero__accent[data-reveal].is-in{opacity:1;transform:none}

/* Headline: cinematic line reveal, slightly less grand than the name */
.hero__headline .line > span{
  transition:transform 1.2s cubic-bezier(.22,1.1,.36,1) var(--d,0s);
}
.js .hero__headline .line > span{transform:translateY(125%)}
.hero__headline.is-in .line > span{transform:none}

/* Tagline / actions / meta: longer, gentler settle */
.hero__tagline[data-reveal],
.hero__actions[data-reveal],
.hero__meta[data-reveal]{
  transition:opacity 1s var(--ease-soft) var(--d,0s),
             transform 1.2s var(--ease) var(--d,0s);
}

/* Slate: the clapper drops in with a rotation, like the stick landing */
.hero .slate__in[data-reveal]{
  transition:opacity 1.1s var(--ease-soft) var(--d,0s),
             transform 1.4s cubic-bezier(.22,1.2,.36,1) var(--d,0s);
}
.js .hero .slate__in[data-reveal]{transform:translateY(32px) rotate(-3deg)}
.hero .slate__in[data-reveal].is-in{opacity:1;transform:none}

/* ---- drifting petals ----
   Scenery: pointer-events:none, and they sit at z-index 1 — above the veil and
   the film, below the copy card (z 2) — so they can never end up floating over
   the wordmark. Each carries its own drift vector and duration inline so no two
   share a rhythm; a single shared animation would read as a carousel. */
.hero__petals{position:absolute;inset:0;z-index:1;pointer-events:none;overflow:hidden}
.hero__petal{position:absolute;width:var(--w);opacity:var(--op,.9)}
/* positioned here, not inline, because it needs a different resting spot per
   breakpoint: clear of the CTA on desktop, clear of the meta line on mobile */
.hero__petal--lead{left:2%;top:79%}
.hero__petal img{
  width:100%;height:auto;
  filter:drop-shadow(0 10px 20px color-mix(in srgb,var(--espresso) 20%,transparent));
}
.js .hero__petal{
  will-change:transform;
  animation:tz-drift var(--dur,20s) ease-in-out var(--delay,0s) infinite;
}
@keyframes tz-drift{
  0%,100%{transform:translate3d(0,0,0) rotate(var(--r,0deg))}
  34%    {transform:translate3d(var(--dx,14px),var(--dy,-22px),0) rotate(calc(var(--r,0deg) + 9deg))}
  67%    {transform:translate3d(calc(var(--dx,14px) * -.55),calc(var(--dy,-22px) * -.4),0) rotate(calc(var(--r,0deg) - 7deg))}
}

.hero__scroll{
  position:absolute;left:50%;bottom:clamp(1.1rem,3vh,2.2rem);z-index:3;
  color:var(--ink-soft);   /* sits in the bottom linen wash, so it stays dark */
  animation:tz-bob 2.4s var(--ease-soft) infinite;
}
@keyframes tz-bob{
  0%,100%{transform:translate(-50%,0)}
  50%    {transform:translate(-50%,9px)}
}

@media (max-width:999px){
  .hero__actions .btn{width:100%;max-width:20rem}
  .hero__petal--wide{display:none}
  .hero__petal--lead{left:auto;right:4%;top:87%}

  /* TWO BEATS, NOT ONE SCREEN.
     Below 1000px the slate is in normal flow under the CTA, so the hero holds
     the title card AND the board in one centred stack — 867px of it on a
     375x667 phone. Centred in a 100svh box that overflows, the fold landed
     *across* the slate (162px of the board cut off on a 667px screen) and the
     "Scroll" cue — the one thing telling a visitor the page continues — sat
     167px below the fold at first paint.
     Shrinking the composition to fit was the other option and it cannot get
     there: trimming padding, gaps and the board recovers ~130 of the 200px
     needed, and the rest would have to come out of the wordmark.
     So the hero stops pretending to be one screen: row 1 is never shorter than
     one viewport and holds the card, row 2 is the cue, row 3 is the slate, and
     the fold falls in the gap BETWEEN the cue and the board. `minmax(...,auto)`
     is the whole mechanism — row 1 still grows past a screen if the card ever
     needs it, and rows 2 and 3 follow it down. */
  .hero{
    --hero-cue:2.4rem;        /* row 2: the "Scroll" cue's own band */
    grid-template-rows:
      minmax(calc(100svh - var(--hero-pad-t) - var(--hero-pad-b) - var(--hero-cue)),auto)
      var(--hero-cue);
    align-content:start;
  }
  /* Placed explicitly: the cue is the LAST child in the markup (it belongs at
     the end of the hero for a screen reader) but the SECOND band on screen. */
  .hero__card {grid-row:1}
  .hero__scroll{grid-row:2}
  /* The cue joins the grid rather than hanging off the hero's bottom edge.
     Pinned absolutely it had no good anchor: to the hero's bottom it sat a
     screen and a half down, and to a flat `100svh` it rode up over the card's
     last line at sizes where the card alone is taller than one screen (999x700
     — a narrow desktop window, still inside this query). A row of its own
     always follows the card, whatever the card does. */
  .hero__scroll{
    position:relative;left:auto;bottom:auto;
    align-self:center;
    /* the in-flow bob — tz-bob translates -50% to undo `left:50%`, which is
       exactly what this variant no longer needs */
    animation-name:tz-bob-y;
  }
  /* The slate (clapper board) is hidden on mobile — the social links live in
     the footer and collaborate sections, so the slate is purely a desktop
     compositional device filling the dead band between copy and film. */
  .hero .slate{display:none}
  /* The centre scrim is what buys linen type over the film, and it is anchored
     to the hero's middle — which is now the gap between the two beats, not the
     title. Re-anchor it to the middle of the first screen so the card keeps its
     contrast. The top and bottom washes are unchanged. */
  .hero__veil{
    background:
      radial-gradient(128% 76% at 50% calc(50svh - var(--hero-pad-b) / 2),
        rgba(23,23,23,.66) 0%,rgba(23,23,23,.5) 42%,rgba(23,23,23,0) 76%),
      linear-gradient(to bottom,var(--veil-9) 0,var(--veil-0) 15%),
      linear-gradient(to top,   var(--veil-1) 0,var(--veil-0) 22%);
  }
}

/* ---------- 2b. HERO, split layout (>=1000px) --------------
   Copy left on clean linen, colour film right. Because the copy is no longer
   sitting on photography, everything that existed to survive that — the linen
   type, the text shadow, the dashed intertitle frame, the outlined button —
   reverts to the normal on-linen treatment. */
@media (min-width:1000px){
  /* one inset drives both the copy column and the scroll hint, so they stay
     on the same left edge if it is ever retuned */
  .hero{
    --hero-inset:clamp(3.5rem,8vw,10rem);
    /* the copy column's width, named because the veil's fade is anchored to
       where this column ends — keep them in sync by keeping them one token */
    --hero-card:min(46vw,580px);
    justify-items:start;align-content:center;
    padding-left:var(--hero-inset);
  }
  .hero__card{
    width:var(--hero-card);
    padding:0;
    text-align:left;
    color:var(--espresso);
    text-shadow:none;
  }
  .hero__card::after{content:none}
  .hero__card .label{color:var(--ink-soft)}
  .hero__card .annot{color:var(--espresso)}
  .hero__card .stitch{border-bottom-color:var(--clay)}
  .hero__card .btn{
    background:var(--espresso);color:var(--ivory);
    border-color:var(--espresso);
  }
  .hero__card .btn:hover,.hero__card .btn:focus-visible{
    background:transparent;color:var(--espresso);border-color:var(--espresso);
  }
  .hero__name{font-size:clamp(3.6rem,7.6vw,6.6rem)}
  .hero__tagline{margin-inline:0;max-width:34ch}
  .hero__actions{justify-content:flex-start}
  /* the hint belongs under the copy, not under the whole viewport */
  .hero__scroll{left:var(--hero-inset);transform:none;animation-name:tz-bob-left}
  @keyframes tz-bob-left{
    0%,100%{transform:translateY(0)}
    50%    {transform:translateY(9px)}
  }
}

/* ---------- 2c. THE SLATE — socials on a clapper board -----
   Two shapes stacked: a hinged clap stick that rests open at -13deg, and the
   board it closes onto. Both carry the same repeating-linear-gradient teeth, so
   the closed position lines the stripes up into one continuous bar — that is
   what sells it as a clapper rather than as a striped box.

   Below 1000px it is a WIDE slate in normal flow under the CTA, three cells
   across. From 1000px up it becomes a NARROW slate absolutely positioned in the
   dead band between the copy column and the film. Those are the only two states;
   there is no in-between size that fits both the copy and the tilted strip.

   The tilt lives on .slate, never on .slate__in — see the markup comment. */
.slate{
  position:relative;z-index:3;
  width:min(330px,86vw);
  margin-top:clamp(2.2rem,6vw,2.8rem);
  transform:rotate(-2.4deg);
  filter:drop-shadow(0 16px 30px rgba(23,23,23,.32));
}

/* the hinged stick. transform-origin sits on the hinge pin, not on the corner,
   so it swings around the rivet the way a real one does. */
.slate__clap{
  position:relative;display:block;
  height:clamp(17px,1.7vw,22px);
  margin-bottom:2px;
  border-radius:2px 2px 0 0;
  background-color:var(--espresso);
  background-image:repeating-linear-gradient(64deg,
    var(--linen) 0 15px,var(--espresso) 15px 32px);
  transform-origin:5% 100%;
  /* Rests nearly closed by default. The stick pivots on its LEFT end, so how far
     the right end swings up is width x sin(angle) — on the wide mobile slate the
     13deg the desktop one rests at threw the right end 79px up, straight across
     "Miami · NYC — available worldwide". It opens to 13deg from 1000px, where the
     slate is narrow and there is a hover to open it with. */
  transform:rotate(-5deg);
  /* the overshoot IS the clap — an ease-out here reads as a lid closing */
  transition:transform .42s cubic-bezier(.2,1.5,.42,1);
}
.slate__clap::after{
  content:"";position:absolute;left:4%;bottom:-2px;
  width:7px;height:7px;border-radius:50%;
  background:var(--clay);
}
/* focus-within, not just hover: the clap has to answer a keyboard visitor too */
.slate:hover .slate__clap,
.slate:focus-within .slate__clap{transform:rotate(-1deg)}

.slate__board{
  position:relative;
  padding:clamp(1.15rem,1.9vw,1.5rem) clamp(.85rem,1.3vw,1.1rem) clamp(.7rem,1vw,.9rem);
  border-radius:0 0 2px 2px;
  background:var(--espresso);
  color:var(--linen);
}
/* the fixed lower stick the hinged one lands on */
.slate__board::before{
  content:"";position:absolute;left:0;right:0;top:0;
  height:clamp(9px,.95vw,12px);
  background-color:var(--espresso);
  background-image:repeating-linear-gradient(64deg,
    var(--linen) 0 15px,var(--espresso) 15px 32px);
}

/* "Prod." sits ON ITS OWN LINE above the name, not beside it. Beside it, the
   pair needs ~210px at the desktop slate's type sizes and the board gives it
   209 — it wrapped "Zapata" onto a second line at exactly the width the slate
   spends most of its life at. */
.slate__head{
  padding-bottom:.5rem;
  border-bottom:1px solid rgba(246,242,235,.26);
}
.slate__prod{
  display:block;
  font-family:var(--util);font-size:var(--t-micro);font-weight:500;
  letter-spacing:.22em;text-transform:uppercase;
  color:rgba(246,242,235,.5);
}
.slate__name{
  display:block;margin-top:.1rem;
  font-family:var(--display);font-weight:400;
  font-size:clamp(.95rem,1.4vw,1.2rem);line-height:1.2;
  letter-spacing:.12em;text-transform:uppercase;
  color:var(--linen);
}

/* Below 1000px: the two handles share a row and the email takes the full width
   under them. Three equal cells was tried first and broke the email mid-word —
   "tanyicollab@gmail.com" needs ~145px and a third of this board is ~110. */
.slate__rows{display:flex;flex-wrap:wrap;margin:0;padding:0;list-style:none}
.slate__row{flex:1 1 44%;min-width:0}
.slate__row:nth-child(2){border-left:1px dashed rgba(246,242,235,.2)}
.slate__row:nth-child(3){flex:1 1 100%;border-top:1px dashed rgba(246,242,235,.2)}
/* Label above value rather than beside it: "tanyicollab@gmail.com" beside its
   own label does not survive the narrow desktop slate at 1000px. */
.slate__link{
  display:block;padding:.6rem .55rem;
  text-decoration:none;color:var(--linen);
}
.slate__row:nth-child(1) .slate__link{padding-left:0}
.slate__row:nth-child(2) .slate__link{padding-right:0}
.slate__row:nth-child(3) .slate__link{padding-inline:0}
.slate__k{
  display:block;
  font-family:var(--util);font-size:var(--t-micro);font-weight:500;
  letter-spacing:.18em;text-transform:uppercase;
  color:var(--clay);
}
.slate__v{
  display:inline-block;margin-top:.15rem;
  font-family:var(--util);font-size:clamp(.68rem,.82vw,.78rem);
  line-height:1.4;letter-spacing:.005em;
  color:rgba(246,242,235,.94);
  border-bottom:1px solid transparent;
  transition:color .4s var(--ease-soft),border-color .4s var(--ease-soft);
  /* the email is the long one; let it break rather than widen the board */
  overflow-wrap:anywhere;
}
.slate__link:hover .slate__v,
.slate__link:focus-visible .slate__v{color:var(--linen);border-bottom-color:var(--clay)}

.slate__foot{
  display:flex;justify-content:space-between;gap:.5em;
  margin-top:.55rem;padding-top:.5rem;
  border-top:1px solid rgba(246,242,235,.26);
  font-family:var(--util);font-size:var(--t-micro);font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;
  color:rgba(246,242,235,.46);
}

/* ---- the desktop slate: narrow, stacked, in the dead band ----
   Anchored to `--hero-inset + --hero-card` — the copy column's right edge, the
   same pair of tokens the veil's linen fade uses — plus a small viewport-scaled
   nudge right. Anchoring to a percentage of the viewport was tried on the veil
   and drifted; this keeps the slate at a fixed distance from where the copy
   actually stops, at every width. If either token is retuned, this follows. */
/* top is 58%, not 50%. The film's edge is a -30deg diagonal, so the linen
   wedge left of it is a triangle that widens as it goes DOWN: at the vertical
   centre the wedge is ~0px at the slate's top corner and the board sat across
   the red-sweater frame. 58% drops it into the wide part of the wedge, where
   only its right edge grazes the film — which is the intended read, a slate
   held up at the edge of shot. Retune this together with .strip-v's rotation;
   they are the same geometry seen from two sides.

   Scoped to `.hero`, not to `.slate` alone. The narrow stacked slate exists to
   fit ONE space — the wedge left of the tilted film strip in this hero. A hero
   that has no such wedge (index-v2.html) reuses the same markup and gets the
   wide flow layout above at every width, with no rules to undo.

   ONE `@media` opener here, not two — a duplicated opener left this block
   unclosed and swallowed every rule below it (the whole intro burst, including
   its own `max-width:999px` mobile block) into a desktop-only query. */
@media (min-width:1000px){
  .hero .slate{
    position:absolute;top:58%;
    left:calc(var(--hero-inset) + var(--hero-card) + clamp(0px,.6vw,16px));
    width:clamp(206px,17vw,258px);
    margin-top:0;
    transform:translate(-50%,-50%) rotate(-3.2deg);
  }
  .hero .slate__clap{transform:rotate(-13deg)}
  .hero .slate__rows{flex-direction:column;flex-wrap:nowrap}
  .hero .slate__row,
  .hero .slate__row:nth-child(3){flex:0 0 auto;border-left:0;border-top:0}
  .hero .slate__row + .slate__row{border-top:1px dashed rgba(246,242,235,.2)}
  .hero .slate__link,
  .hero .slate__row:nth-child(1) .slate__link,
  .hero .slate__row:nth-child(2) .slate__link,
  .hero .slate__row:nth-child(3) .slate__link{padding:.5rem 0}
}

/* ---------- 3. INTRO — the sticker burst ------------------- */
.intro-wrap{
  position:relative;
  height:180vh;         /* fallback */
  height:180svh;        /* p = scroll progress through this wrapper */
  background:var(--linen);
}
.intro{
  position:sticky;top:0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  width:100%;min-height:100svh;
  padding:clamp(3rem,7vh,5rem) var(--gutter);
  text-align:center;
  overflow:hidden;      /* stickers overhang every edge — this is what keeps
                           the page free of a horizontal scrollbar */
  background:var(--linen);
}

/* horizontal film strip — same construction as .strip-v, turned 90deg */
.strip-h{
  position:relative;z-index:2;
  display:inline-flex;gap:clamp(6px,.7vw,11px);
  padding:clamp(19px,2vw,28px) clamp(8px,1vw,14px);
  background:var(--espresso);
  box-shadow:0 18px 44px rgba(23,23,23,.3);
  transform-origin:center;
}
.strip-h::before,.strip-h::after{
  content:"";position:absolute;left:0;right:0;height:13px;
  background-image:linear-gradient(to right,var(--linen) 0 13px,transparent 13px 32px);
  background-size:32px 100%;background-position:9px 0;
}
.strip-h::before{top:6px}
.strip-h::after {bottom:6px}
.strip-h .film-frame{padding:5px}
.strip-h .film-frame img{
  width:clamp(74px,7.4vw,110px);height:clamp(94px,9.3vw,138px);
  object-fit:cover;filter:grayscale(1) contrast(1.2);
}
.js .intro .strip-h{transform:scale(.72)}

/* the flying stickers.
   --x/--x2/--y/--y2 are the resting inset on desktop, --m* the mobile set;
   --r/--mr the resting rotation (JS reads the matching data-r/data-mr).
   Only left/top/right/bottom and width live in CSS — every transform is
   written by the scroll handler. */
.intro__fly{
  position:absolute;z-index:4;
  left:var(--x,auto);right:var(--x2,auto);
  top:var(--y,auto);bottom:var(--y2,auto);
  width:var(--w);
  transform:rotate(var(--r,0deg));
  pointer-events:none;
}
.intro__fly img{
  width:100%;height:auto;
  filter:drop-shadow(0 8px 16px rgba(23,23,23,.18));
}
.js .intro__fly{opacity:0;will-change:transform,opacity}

/* CTA + supporting line — fade and slide in over the back half of p */
.intro__cta{
  position:relative;z-index:5;
  display:inline-block;margin-top:clamp(1.6rem,3.4vw,2.6rem);
  padding:.24em .8em .3em;
  font-family:var(--display);font-style:italic;font-weight:300;
  font-size:clamp(1.9rem,4.6vw,3.3rem);line-height:1.1;letter-spacing:.01em;
  color:var(--espresso);text-decoration:none;
  border-bottom:1px dashed var(--clay);
  transition:color .5s var(--ease-soft),border-color .5s var(--ease-soft);
}
.intro__cta:hover,.intro__cta:focus-visible{color:var(--clay);border-color:var(--espresso)}
/* a scroll cue, not a stat line — the arrow does the whole job */
.intro__more{
  position:relative;z-index:5;
  display:flex;justify-content:center;
  margin-top:clamp(1.1rem,2.6vw,1.8rem);
}
.intro__more svg{
  width:clamp(20px,2.4vw,28px);height:auto;
  fill:none;stroke:var(--clay);stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;
  animation:tz-bob-y 2.4s var(--ease-soft) infinite;
}
@keyframes tz-bob-y{
  0%,100%{transform:translateY(0)}
  50%    {transform:translateY(8px)}
}
.js .intro__cta,.js .intro__more{opacity:0;will-change:transform,opacity}

/* ---------- 4. MOBILE — a production out of the lens ------- */
/* Below 1000px the film strip gives way to the camera: it sits low and
   centred, and every sticker is emitted from its lens rather than from the
   middle of the section. */
.intro__cam{display:none}

@media (min-width:1000px){
  .intro__flash{display:none}
}

@media (max-width:999px){
  .strip-h{display:none}
  .intro__fly[data-only="desk"]{display:none}

  .intro{justify-content:flex-end;padding-bottom:clamp(2.5rem,8vh,4rem)}

  .intro__cam{
    display:block;position:absolute;z-index:6;
    left:50%;top:58%;
    width:clamp(150px,44vw,230px);
    transform:translate(-50%,-50%);
    will-change:transform;
  }
  .intro__cam img{
    width:100%;height:auto;
    filter:drop-shadow(0 14px 26px rgba(23,23,23,.26));
  }

  /* one shutter beat: a linen bloom out of the lens as the burst starts.
     --fx/--fy are set from the measured lens position. */
  .intro__flash{
    position:absolute;inset:0;z-index:7;pointer-events:none;opacity:0;
    background:radial-gradient(circle at var(--fx,50%) var(--fy,58%),
      var(--veil-1) 0,var(--veil-9) 24%,var(--veil-0) 62%);
    will-change:opacity;
  }

  .intro__fly{
    left:var(--mx,auto);right:var(--mx2,auto);
    top:var(--my,auto);bottom:var(--my2,auto);
    width:var(--mw,var(--w));
    transform:rotate(var(--mr,var(--r,0deg)));
  }
  .intro__cta{margin-top:clamp(1.2rem,4vw,2rem)}
}


