/* FYF foundation — merged from the Front Yard Fantasy design system.
   Order matters: fonts, then tokens, then base element styles.
   Font URLs rewritten to ./fonts/ for this bundle. */

/* Vendored from the FYF design system (design_handoff_frank_landing_pages/
   design-system/fyf-foundation.css). Three edits from source, all recorded in
   docs/superpowers/specs/2026-08-18-fyf-design-system-integration-design.md:
   the Borough Grotesk Shaded @font-face rule is dropped (that face is not
   used and not licensed here), the url()s are Propshaft-relative, and every
   :root is written as .fyf.

   Clash Display and General Sans were dropped in the original vendoring and
   restored on 2026-08-25 with the entrant-card work -- until then
   --font-subhead and --font-body resolved to system fallbacks on every .fyf
   page.

   The .fyf scope exists ONLY to keep this off the editor, admin, and snapshot
   surfaces while they still run on design_tokens.css. The variable names are
   the design system's real names, unprefixed, so the app-wide migration is
   `s/\.fyf\b/:root/` plus dropping the descendant prefix on the base rules.
   Do not rename a variable to make it "marketing-specific". */


/* ============ tokens/fonts.css ============ */
/* FYF webfonts. Files live in /assets/fonts. Paths are relative to this file. */
@font-face{font-family:"Borough Grotesk";src:url("/assets/BoroughGroteskPro-Regular-7053434b.otf") format("opentype");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"VC Henrietta Condensed";src:url("/assets/VCHenriettaCondensed-Medium-fd7e96bd.otf") format("opentype");font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:"General Sans";src:url("/assets/GeneralSans-Medium-d1433eaa.otf") format("opentype");font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:"Clash Display";src:url("/assets/ClashDisplay-Bold-1e28a732.otf") format("opentype");font-weight:700;font-style:normal;font-display:swap}


/* ============ tokens/colors.css ============ */
/* FYF palette — hexes taken verbatim from FYF-Brand Guidelines.pdf (V1, Spring 2023, p.6).
   Foundations: charcoal, green, cream, white. Additions: yellow, blue, pink, orange, red. */
.fyf{
  /* ---- Foundations ---- */
  --fyf-charcoal:#30202d;
  --fyf-cream:#fadcc2;
  --fyf-white:#f6f6f4;
  --fyf-green:#1f7031;

  /* ---- Colorful additions ---- */
  --fyf-yellow:#faca16;
  --fyf-blue:#5197d2;
  --fyf-pink:#f046a0;
  --fyf-orange:#ff7142;
  /* 2026-08-19 landing revision: the loss/demoted colour moves from pink to a
     dedicated red so a "you lost this matchup" state isn't the same hue as
     the brand's default accent. Hex verbatim from the revised design mock,
     not the brand guidelines PDF -- this pair is new since that document. */
  --fyf-red:#c9342b;
  --fyf-red-deep:#992116;
  /* Interim soft pink for large field fills (user: full --fyf-pink reads
     "blinding" as a hero background). Same hue family, less neon, and
     charcoal text on it measures 5.54:1 (AA pass) vs 4.44:1 on the full
     pink. Candidate for the app-wide saturation pass, not gospel. */
  --fyf-pink-soft:#f06fae;

  /* Charcoal ramp — for body text, rules and disabled states only. Never a new brand color. */
  --fyf-charcoal-80:#5a4855;
  --fyf-charcoal-60:#84737f;
  --fyf-charcoal-30:#c3b9c0;
  --fyf-charcoal-12:#e6e0e4;

  /* Cream ramp */
  --fyf-cream-tint:#fdeee2;
  --fyf-cream-deep:#f2c9a6;

  /* Deepened accents — only for small text on a light field, and for press states */
  --fyf-yellow-deep:#d9a800;
  --fyf-orange-deep:#e0501f;
  --fyf-pink-deep:#c82478;
  --fyf-blue-deep:#3576ac;
  --fyf-green-deep:#155321;

  /* Card stock — the trading-card material, alongside turf and chalk. Not new brand hues:
     cream pushed warm and dry so cardboard reads as cardboard next to paper white. */
  --fyf-stock:#e9cba7;
  --fyf-stock-tint:#f4dfc4;
  --fyf-stock-edge:#c9a67e;

  /* Turf & chalk — the photographic field */
  --fyf-turf:#3f6b2c;
  --fyf-turf-deep:#26421a;
  --fyf-chalk:#d9d5cb;

  /* `ink` is the legacy name for charcoal. Kept so older code keeps working — prefer --fyf-charcoal. */
  --fyf-ink:var(--fyf-charcoal);
  --fyf-ink-80:var(--fyf-charcoal-80);
  --fyf-ink-60:var(--fyf-charcoal-60);
  --fyf-ink-30:var(--fyf-charcoal-30);
  --fyf-ink-12:var(--fyf-charcoal-12);

  /* ---- Semantic aliases ---- */
  /* A layout is one solid field. Charcoal, cream, white and green carry most of them;
     an accent field is a deliberate, high-energy choice. */
  --surface-page:var(--fyf-cream);
  --surface-page-alt:var(--fyf-cream-tint);
  --surface-card:var(--fyf-white);
  --surface-inverse:var(--fyf-charcoal);
  --surface-field:var(--fyf-green);
  --surface-turf:var(--fyf-turf);

  --text-body:var(--fyf-charcoal);
  --text-muted:var(--fyf-charcoal-60);
  --text-inverse:var(--fyf-cream);
  --text-on-accent:var(--fyf-charcoal);
  --text-link:var(--fyf-pink-deep);
  --text-link-hover:var(--fyf-charcoal);

  --border-ink:var(--fyf-charcoal);
  --border-soft:var(--fyf-charcoal-12);

  --accent-primary:var(--fyf-pink);
  --accent-secondary:var(--fyf-blue);
  --accent-tertiary:var(--fyf-yellow);

  /* Status reuses brand colors — never a new hue */
  --status-win:var(--fyf-green);
  --status-loss:var(--fyf-pink);
  --status-warn:var(--fyf-yellow);
  --status-info:var(--fyf-blue);
  --status-live:var(--fyf-orange);
}


/* ============ tokens/typography.css ============ */
/* Four faces, four jobs — per FYF-Brand Guidelines.pdf p.5. Do not reassign them. */
.fyf{
  /* SPICE — Borough Grotesk Pro. Vintage sports-card energy. Uppercase, huge, with a hard
     charcoal offset shadow. One or two words per layout: "MOVE", "JOIN THE CLUB", "WEEKDAYS AT 7AM". */
  --font-spice:"Borough Grotesk","Arial Narrow",sans-serif;
  /* The shadow-only companion cut of the spice face. See tokens/fonts.css. */
  --font-spice-shaded:"Borough Grotesk Shaded","Borough Grotesk",sans-serif;

  /* HEADER — VC Henrietta Condensed Medium. Fun and approachable, and it does the talking:
     every headline and subject line. SENTENCE CASE, never uppercase. */
  --font-header:"VC Henrietta Condensed","Georgia",serif;

  /* SUBHEAD — Clash Display Bold. Punchy uppercase micro-copy: eyebrows, meta bars, nav,
     table headers, badges, timestamps. Always tracked out. Also the numeral face. */
  --font-subhead:"Clash Display","General Sans",sans-serif;

  /* BODY — General Sans Medium/Bold. Legible and modern; grounds the stack. */
  --font-body:"General Sans",system-ui,sans-serif;

  --font-mono:ui-monospace,SFMono-Regular,Menlo,monospace;

  /* Legacy aliases — older code refers to these. They now resolve to the correct roles. */
  --font-display:var(--font-header);
  --font-display-shaded:var(--font-spice-shaded);
  --font-condensed:var(--font-subhead);
  --font-display-alt:var(--font-subhead);

  /* Spice scale — always the largest thing on the layout */
  --text-spice-hero:clamp(72px,11vw,200px);
  --text-spice-1:132px;
  --text-spice-2:88px;

  /* Header scale */
  --text-hero:clamp(40px,6vw,88px);
  --text-display-1:72px;
  --text-display-2:52px;
  --text-display-3:38px;

  /* Text scale */
  --text-xl:24px;
  --text-lg:20px;
  --text-md:16px;
  --text-sm:14px;
  --text-xs:12px;
  --text-eyebrow:13px;

  /* Line heights — spice is set solid, headers very tight, body open */
  --leading-spice:0.86;
  --leading-display:1.02;
  --leading-heading:1.06;
  --leading-body:1.55;
  --leading-tight:1.2;

  /* Tracking — spice sets TIGHT: glyphs nearly touch at display sizes (see p.12 marquee) */
  --tracking-spice:-0.025em;
  --tracking-display:-0.005em;
  --tracking-eyebrow:0.14em;
  --tracking-label:0.08em;
  --tracking-body:0em;

  --weight-body:500;
  --weight-display:500;
}


/* ============ tokens/spacing.css ============ */
.fyf{
  /* 4px base, with a 6px half-step because the brand's sticker outlines sit on odd numbers */
  --space-0:0px;
  --space-1:4px;
  --space-2:8px;
  --space-3:12px;
  --space-4:16px;
  --space-5:20px;
  --space-6:24px;
  --space-8:32px;
  --space-10:40px;
  --space-12:48px;
  --space-16:64px;
  --space-20:80px;
  --space-24:96px;
  --space-32:128px;

  --gutter-inline:24px;
  --gutter-inline-lg:56px;
  --section-y:80px;
  --container-max:1200px;
  --container-narrow:720px;
}


/* ============ tokens/effects.css ============ */
.fyf{
  /* Radii — layouts are either a hard 90° corner (1:1 social tiles, meta bars, tables) or a
     generously rounded slab (the wide banner units). The middle of the ramp is not a container
     scale: --radius-xl is the framed-photograph corner, --radius-lg its small crops. */
  --radius-none:0px;
  --radius-sm:6px;
  --radius-md:10px;
  --radius-lg:18px;
  --radius-xl:28px;
  --radius-slab:40px;
  --radius-pill:999px;
  --radius-oval:50% / 50%;

  /* Strokes. The brand's keyline is the OVAL DEVICE and the patch/sticker outline —
     it is not a default on every container. Layouts are flat solid fields. */
  --stroke-hairline:1px;
  --stroke-chip:2px;
  --stroke-sticker:3px;
  --stroke-heavy:5px;
  --stroke-frame:10px; /* thick accent frame around a photograph */

  /* Hard offset shadows — no blur, ever. Reserved for SPICE type and, sparingly, for
     a control that needs to feel physical. Containers do not get one. */
  --shadow-hard-sm:3px 3px 0 var(--fyf-charcoal);
  --shadow-hard:5px 5px 0 var(--fyf-charcoal);
  --shadow-hard-lg:8px 8px 0 var(--fyf-charcoal);
  --shadow-hard-cream:5px 5px 0 var(--fyf-cream);
  --shadow-press:2px 2px 0 var(--fyf-charcoal);

  /* One soft shadow only, for a floating overlay on photography */
  --shadow-overlay:0 18px 40px -12px rgba(48,32,45,.45);

  /* The spice shadow — em-based so it scales with the type size. Tight against the glyph. */
  --text-shadow-hard:0.05em 0.05em 0 var(--fyf-charcoal);

  /* Motion — snappy, slightly overshooting. Nothing slow, nothing floaty. */
  --ease-snap:cubic-bezier(.2,.8,.2,1); /* @kind other */
  --ease-pop:cubic-bezier(.34,1.56,.64,1); /* @kind other */
  --dur-fast:120ms; /* @kind other */
  --dur-base:180ms; /* @kind other */
  --dur-slow:320ms; /* @kind other */

  /* ---- Ephemera & paper (asset library p.8) ----
     The tactile layer: paper fiber, grid paper, chalk hashes, tape strips and punched/torn
     edges. Feedback UI uses these INSTEAD of a keyline-plus-hard-shadow treatment, so a toast
     reads as a ticket stub and a dialog as a notice taped to a locker door. All textures are
     self-sizing: set them as background-image over a paper-colored field. */
  --texture-paper:repeating-linear-gradient(0deg,rgba(48,32,45,.038) 0 1px,transparent 1px 3px),repeating-linear-gradient(90deg,rgba(48,32,45,.028) 0 1px,transparent 1px 4px); /* @kind color */
  --texture-grid-paper:repeating-linear-gradient(0deg,rgba(81,151,210,.24) 0 1px,transparent 1px 11px),repeating-linear-gradient(90deg,rgba(81,151,210,.24) 0 1px,transparent 1px 11px); /* @kind color */
  --texture-chalk-hash:repeating-linear-gradient(90deg,rgba(217,213,203,.5) 0 2px,transparent 2px 15px); /* @kind color */

  /* ---- Card stock (trading cards) ----
     Coarser and drier than --texture-paper: cross-grain fiber plus an uneven press.
     Self-sizing; lay it over --fyf-stock. --texture-halftone is the retro dot screen and
     needs an explicit background-size (4–7px) at the point of use. */
  --texture-cardboard:repeating-linear-gradient(94deg,rgba(48,32,45,.055) 0 1px,transparent 1px 3px),repeating-linear-gradient(4deg,rgba(48,32,45,.04) 0 1px,transparent 1px 4px),radial-gradient(120% 90% at 12% 6%,rgba(255,255,255,.4),rgba(255,255,255,0) 62%),radial-gradient(90% 70% at 88% 96%,rgba(48,32,45,.09),rgba(48,32,45,0) 60%); /* @kind color */
  --texture-halftone:radial-gradient(rgba(48,32,45,.2) 1px,rgba(48,32,45,0) 1.5px); /* @kind color */

  /* The worn rim of a cut card — darkened edge and a hint of a bevel. Inset, on the stock. */
  --edge-card-wear:inset 0 0 0 1px rgba(48,32,45,.3),inset 0 0 26px rgba(48,32,45,.13),inset 0 2px 0 rgba(255,255,255,.4); /* @kind other */

  /* Tape strips — translucent so the field reads through, like real gaffer tape */
  --tape-pink:linear-gradient(180deg,rgba(240,70,160,.92),rgba(200,36,120,.8)); /* @kind color */
  --tape-orange:linear-gradient(180deg,rgba(255,113,66,.9),rgba(224,80,31,.78)); /* @kind color */
  --tape-cream:linear-gradient(180deg,rgba(250,220,194,.92),rgba(242,201,166,.8)); /* @kind color */

  /* Punched and torn edges */
  --mask-ticket:radial-gradient(circle 5px at 0% 50%,transparent 5px,#000 5.5px),radial-gradient(circle 5px at 100% 50%,transparent 5px,#000 5.5px); /* @kind other */
  --clip-torn-top:polygon(0 9px,5% 2px,11% 10px,17% 3px,24% 11px,31% 4px,38% 10px,45% 2px,52% 9px,59% 3px,66% 11px,73% 4px,80% 10px,87% 2px,94% 9px,100% 4px,100% 100%,0 100%); /* @kind other */

  /* Askew — paper is never perfectly square to the page. Keep it under 3°. */
  --tilt-sm:-1.2deg; /* @kind other */
  --tilt-md:1.8deg; /* @kind other */
  --tilt-lg:-2.6deg; /* @kind other */

  /* Protection scrim for text over turf or stadium photography.
     -turf is the heavy ramp: use it when copy runs over the lower half (social posts, stories).
     -turf-light keeps the grass reading as grass: use it when the lower area carries only
     one spice line with its own stroke and hard offset (YouTube thumbnails). */
  --scrim-turf:linear-gradient(180deg,rgba(38,66,26,0) 0%,rgba(38,66,26,.72) 70%,rgba(38,66,26,.9) 100%); /* @kind color */
  --scrim-turf-light:linear-gradient(180deg,rgba(38,66,26,0) 0%,rgba(38,66,26,.34) 62%,rgba(38,66,26,.62) 100%); /* @kind color */

  /* Turf photography sits behind everything and softens when upscaled past its native size.
     Apply to the turf layer itself (never to a parent — it would filter the artwork too). */
  --turf-crisp:contrast(1.1) saturate(1.06); /* @kind other */
}


/* ============ tokens/motion.css ============ */
/* ============================================================
   MOTION GRAMMAR
   Physical paper, thrown by a person. Every move has weight:
   it winds up, it arrives hot, it overshoots, it corrects.
   Nothing travels at a constant speed and nothing dissolves.

   Counted in frames at 30fps so an editor can match a cut.
   ============================================================ */
.fyf{
  /* Frame clock — always author in these, never in round numbers. */
  --f1:33ms;   /* @kind other */
  --f2:67ms;   /* @kind other */
  --f3:100ms;  /* @kind other */
  --f4:133ms;  /* @kind other */
  --f5:167ms;  /* @kind other */
  --f6:200ms;  /* @kind other */
  --f8:267ms;  /* @kind other */
  --f10:333ms; /* @kind other */
  --f12:400ms; /* @kind other */

  /* Video easings. --ease-snap / --ease-pop stay the interface pair;
     these four are the broadcast set, tuned for 1920-wide travel. */
  /* arrives hot, stops hard */
  --ease-slam:cubic-bezier(.06,.86,.16,1); /* @kind other */
  /* the windup before a move */
  --ease-load:cubic-bezier(.36,0,.7,.3); /* @kind other */
  /* accelerates out of frame */
  --ease-hurl:cubic-bezier(.7,.02,.98,.42); /* @kind other */
  /* lands and rebounds */
  --ease-settle:cubic-bezier(.16,1.3,.38,1); /* @kind other */
  /* stamp */
  --ease-strike:cubic-bezier(.2,1.5,.34,1); /* @kind other */

  /* Torn leading edges for full-frame bands. Oversize the band by 40px on the
     leading side so the teeth sit off-camera at rest. */
  --clip-torn-lead-r:polygon(0 0,100% 0,calc(100% - 9px) 6%,100% 12%,calc(100% - 15px) 19%,100% 25%,calc(100% - 7px) 32%,100% 39%,calc(100% - 16px) 46%,100% 53%,calc(100% - 8px) 60%,100% 66%,calc(100% - 13px) 73%,100% 80%,calc(100% - 6px) 87%,100% 93%,calc(100% - 11px) 100%,0 100%); /* @kind other */
  --clip-torn-lead-l:polygon(100% 0,0 0,9px 6%,0 12%,15px 19%,0 25%,7px 32%,0 39%,16px 46%,0 53%,8px 60%,0 66%,13px 73%,0 80%,6px 87%,0 93%,11px 100%,100% 100%); /* @kind other */

  /* Print grain for motion fills — keeps a flat ink field from reading as vector. */
  --texture-press:repeating-linear-gradient(0deg,rgba(250,220,194,.045) 0 1px,transparent 1px 3px),repeating-linear-gradient(90deg,rgba(48,32,45,.05) 0 1px,transparent 1px 4px); /* @kind color */
}

/* ---- ARRIVE · 8f -------------------------------------------------
   A slab thrown into frame. Overshoots 2%, corrects back through zero,
   sets. --dir 1|-1, --in-x travel, --rot the angle it lives at. */
@keyframes fyf-arrive{
  0%{opacity:0;transform:translate(calc(var(--dir,1) * var(--in-x,-118%)),var(--in-y,0)) rotate(var(--in-rot,-3.5deg))}
  6%{opacity:1}
  60%{transform:translate(calc(var(--dir,1) * 2.2%),0) rotate(calc(var(--rot,0deg) + 1.1deg))}
  78%{transform:translate(calc(var(--dir,1) * -.7%),0) rotate(calc(var(--rot,0deg) - .4deg))}
  91%{transform:translate(calc(var(--dir,1) * .2%),0) rotate(calc(var(--rot,0deg) + .12deg))}
  100%{opacity:1;transform:translate(0,0) rotate(var(--rot,0deg))}
}

/* ---- DEPART · 6f -------------------------------------------------
   Loads against the exit for 2 frames, then leaves. */
@keyframes fyf-depart{
  0%{opacity:1;transform:translate(0,0) rotate(var(--rot,0deg));animation-timing-function:var(--ease-load)}
  26%{transform:translate(calc(var(--dir,1) * 2.4%),0) rotate(calc(var(--rot,0deg) + 1.5deg));animation-timing-function:var(--ease-hurl)}
  99%{opacity:1}
  100%{opacity:1;transform:translate(calc(var(--dir,1) * var(--out-x,-124%)),var(--out-y,0)) rotate(var(--out-rot,-5deg))}
}

/* ---- STAMP · 6f --------------------------------------------------
   Struck onto the page. Two bounces, not one — a single overshoot is
   what makes a move read as software. */
@keyframes fyf-stamp{
  0%{opacity:0;transform:scale(var(--in-s,1.42)) rotate(calc(var(--rot,0deg) + var(--in-rot,2deg)))}
  5%{opacity:1}
  44%{transform:scale(.955) rotate(calc(var(--rot,0deg) - .8deg))}
  68%{transform:scale(1.018) rotate(calc(var(--rot,0deg) + .3deg))}
  86%{transform:scale(.994) rotate(var(--rot,0deg))}
  100%{opacity:1;transform:scale(1) rotate(var(--rot,0deg))}
}
@keyframes fyf-stamp-out{
  0%{opacity:1;transform:scale(1) rotate(var(--rot,0deg));animation-timing-function:var(--ease-load)}
  30%{transform:scale(1.06) rotate(calc(var(--rot,0deg) - 1.2deg));animation-timing-function:var(--ease-hurl)}
  99%{opacity:1;transform:scale(.72) rotate(calc(var(--rot,0deg) + 3deg))}
  100%{opacity:0;transform:scale(.72) rotate(calc(var(--rot,0deg) + 3deg))}
}

/* ---- PEEL · 7f ---------------------------------------------------
   Sticker pressed on from a corner, rocks past level, sets. */
@keyframes fyf-peel{
  0%{opacity:0;transform:rotate(calc(var(--rot,-8deg) - 38deg)) scale(.34)}
  5%{opacity:1}
  52%{transform:rotate(calc(var(--rot,-8deg) + 9deg)) scale(1.09)}
  74%{transform:rotate(calc(var(--rot,-8deg) - 3.4deg)) scale(.975)}
  89%{transform:rotate(calc(var(--rot,-8deg) + 1deg)) scale(1.006)}
  100%{opacity:1;transform:rotate(var(--rot,-8deg)) scale(1)}
}

/* ---- RISE · 7f ---------------------------------------------------
   Type climbing into a slab. Parent must clip. */
@keyframes fyf-rise{
  0%{opacity:0;transform:translateY(var(--in-y,74%)) rotate(var(--rot,0deg))}
  10%{opacity:1}
  64%{transform:translateY(-5%) rotate(var(--rot,0deg))}
  83%{transform:translateY(1.8%) rotate(var(--rot,0deg))}
  100%{opacity:1;transform:translateY(0) rotate(var(--rot,0deg))}
}

/* ---- WIPE · full transition --------------------------------------
   One band's whole life: in, hold, wind back, fling out. The 2-frame
   load at 62% is the difference between a stinger and a PowerPoint. */
@keyframes fyf-wipe{
  0%{transform:translateX(calc(var(--dir,1) * -114%)) rotate(var(--wipe-rot,-.8deg));animation-timing-function:var(--ease-slam)}
  33%{transform:translateX(calc(var(--dir,1) * 1.6%)) rotate(calc(var(--wipe-rot,-.8deg) * .3))}
  40%{transform:translateX(0) rotate(0deg);animation-timing-function:linear}
  55%{transform:translateX(0) rotate(0deg);animation-timing-function:var(--ease-load)}
  62%{transform:translateX(calc(var(--dir,1) * -2.2%)) rotate(calc(var(--wipe-rot,-.8deg) * -.35));animation-timing-function:var(--ease-hurl)}
  100%{transform:translateX(calc(var(--dir,1) * 120%)) rotate(calc(var(--wipe-rot,-.8deg) * -1))}
}

/* ---- SECONDARY ---------------------------------------------------
   Nothing lands alone. Tape corners and pennant tails carry the
   impact for 4 frames after the thing they're stuck to has stopped. */
@keyframes fyf-flutter{
  0%{transform:rotate(calc(var(--rot,-5deg) - 4deg)) translateY(-4px)}
  32%{transform:rotate(calc(var(--rot,-5deg) + 3.4deg)) translateY(1px)}
  60%{transform:rotate(calc(var(--rot,-5deg) - 1.5deg)) translateY(0)}
  82%{transform:rotate(calc(var(--rot,-5deg) + .5deg))}
  100%{transform:rotate(var(--rot,-5deg))}
}

/* ---- REGISTRATION ------------------------------------------------
   The rig, not the artwork. Sub-pixel-to-1px drift held on twos so it
   reads as a camera on a copy stand, never as an element floating.
   Always paired with animation-timing-function:steps(1,end). */
@keyframes fyf-reg{
  0%,100%{transform:translate(0,0)}
  17%{transform:translate(.7px,-1px)}
  34%{transform:translate(-1px,.4px)}
  51%{transform:translate(.3px,.9px)}
  68%{transform:translate(-.6px,-.5px)}
  85%{transform:translate(.9px,.2px)}
}

/* ---- MISREGISTER -------------------------------------------------
   Second-pass ink landing a beat late and never quite converging.
   The screenprint tell. Offsets are in px, not %, so they hold at any
   canvas size. */
@keyframes fyf-misregister{
  0%{opacity:0;transform:translate(var(--mis-x,26px),var(--mis-y,20px)) scale(1.035)}
  8%{opacity:1}
  55%{transform:translate(calc(var(--mis-x,26px) * -.22),calc(var(--mis-y,20px) * -.3)) scale(.998)}
  100%{opacity:1;transform:translate(calc(var(--mis-x,26px) * .2),calc(var(--mis-y,20px) * .22)) scale(1)}
}

/* ---- MATCHED EXITS + PRESS ----------------------------------------
   Every entrance gets a leave that loads against the exit first.
   peel-out lifts off the corner it was pressed on; rise-out drops the
   type back down; press-in/out are a squeegee pass — hard clip edge,
   never a fade. The -15% inset margins keep tape overhangs and hard
   shadows inside the reveal. */
@keyframes fyf-peel-out{
  0%{opacity:1;transform:rotate(var(--rot,0deg)) scale(1);animation-timing-function:var(--ease-load)}
  30%{transform:rotate(calc(var(--rot,0deg) - 6deg)) scale(1.04);animation-timing-function:var(--ease-hurl)}
  99%{opacity:1;transform:rotate(calc(var(--rot,0deg) + 30deg)) scale(.3) translateY(46px)}
  100%{opacity:0;transform:rotate(calc(var(--rot,0deg) + 30deg)) scale(.3) translateY(46px)}
}
@keyframes fyf-rise-out{
  0%{opacity:1;transform:translateY(0) rotate(var(--rot,0deg));animation-timing-function:var(--ease-load)}
  24%{transform:translateY(-6%) rotate(var(--rot,0deg));animation-timing-function:var(--ease-hurl)}
  90%{opacity:1}
  100%{opacity:0;transform:translateY(86%) rotate(var(--rot,0deg))}
}
@keyframes fyf-press-in{
  0%{clip-path:inset(-15% 115% -15% -15%);transform:rotate(var(--rot,0deg))}
  76%{clip-path:inset(-15% -15% -15% -15%);animation-timing-function:var(--ease-load)}
  88%{transform:rotate(calc(var(--rot,0deg) + .6deg)) scale(1.012);animation-timing-function:var(--ease-settle)}
  100%{clip-path:inset(-15% -15% -15% -15%);transform:rotate(var(--rot,0deg)) scale(1)}
}
@keyframes fyf-press-out{
  0%{opacity:1;clip-path:inset(-15% -15% -15% -15%);transform:rotate(var(--rot,0deg));animation-timing-function:var(--ease-load)}
  14%{transform:rotate(calc(var(--rot,0deg) - .8deg));animation-timing-function:var(--ease-hurl)}
  99%{opacity:1}
  100%{opacity:0;clip-path:inset(-15% -15% -15% 115%);transform:rotate(var(--rot,0deg))}
}

@media (prefers-reduced-motion:reduce){
  [data-fyf-reg]{animation:none !important}
}


/* ============ tokens/base.css ============ */
.fyf *,.fyf *::before,.fyf *::after{box-sizing:border-box}
.fyf body{margin:0;background:var(--surface-page);color:var(--text-body);font-family:var(--font-body);font-weight:var(--weight-body);font-size:var(--text-md);line-height:var(--leading-body);-webkit-font-smoothing:antialiased;text-wrap:pretty}
/* Headlines are VC Henrietta Condensed in SENTENCE CASE — never uppercase. */
.fyf h1,.fyf h2,.fyf h3,.fyf h4{font-family:var(--font-header);font-weight:var(--weight-display);line-height:var(--leading-display);letter-spacing:var(--tracking-display);margin:0}
.fyf h1{font-size:var(--text-display-1)}.fyf h2{font-size:var(--text-display-2)}.fyf h3{font-size:var(--text-display-3)}.fyf h4{font-size:var(--text-xl)}
.fyf p{margin:0 0 1em}
.fyf a{color:var(--text-link);text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:3px;transition:color var(--dur-fast) var(--ease-snap)}
.fyf a:hover{color:var(--text-link-hover)}
.fyf strong{font-weight:600;color:var(--fyf-charcoal)}
.fyf ::selection{background:var(--fyf-yellow);color:var(--fyf-charcoal)}
/* Eyebrow / meta bar: Clash Display Bold, uppercase, tracked. */
.fyf-eyebrow{font-family:var(--font-subhead);font-size:var(--text-eyebrow);font-weight:700;text-transform:uppercase;letter-spacing:var(--tracking-eyebrow);color:var(--text-muted)}
/* Spice: the loud accent word. Uppercase, hard charcoal offset, no blur. */
.fyf-spice{font-family:var(--font-spice);font-weight:400;text-transform:uppercase;line-height:var(--leading-spice);letter-spacing:var(--tracking-spice);-webkit-text-stroke:0.012em currentColor;text-shadow:var(--text-shadow-hard)}
/* The oval device: ring one word inside a subhead line. */
.fyf-oval{display:inline-block;border:2px solid currentColor;border-radius:50%;padding:.35em 1.1em;line-height:1.15}
