@import '../tokens.css?v=a555c920';
@import './project-colours.css?v=28cf698b';

/* ── Rafa & Nicky — the story ───────────────────────────────────────────
 * The Rafa & Nicky EXTENSION of the shared story structure
 * (css/page/story.css: cover, spine, spreads, plates, statements, spec,
 * numbers, list, performances, interlude, close). This file carries only
 * what is Rafa & Nicky's: the funk on the cover, the signature's size,
 * the paper grounds, the cull, the session and its mini-site specimen.
 * Page: ux-case-studies/rafa-nicky.html.
 * Driver: js/page/story.js (shared).
 *
 * Client colours come from project-colours.css. Components borrowed
 * from case-study.css (the compare, the personas, the card compare, the
 * token sync, the signature progression, the site embed, the lightbox)
 * keep their own styles; this file only places them. */

/* ── Cover — the flower photograph under the funk ───────────────────── */
/* The funk — the client site's liquid oil-slick, the same blob field and
 * veil the case study's hover card uses (case-study.css .cs-cc-fx),
 * multiplied onto the photograph and running from load. */
.st-funk {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  isolation: isolate;
  mix-blend-mode: multiply;
  background: var(--rn-funk);
}

.st-funk .cs-cc-blob {
  animation-play-state: running;
}

/* The signature — the brand's one gesture — is the h1. */
.st-cover-title svg {
  width: min(340px, 64vw, 46dvh);
}

/* ── The client's paper — the plates' and the interlude's ground ────── */
.st-plate--paper {
  --st-plate-ground: var(--rn-paper);
}

.st-interlude {
  --st-ground: var(--rn-paper);
  --st-ground-ink: var(--color-plane-dark);
}

/* ── Borrowed components, placed ────────────────────────────────────── */
.st-chapter > .cs-logo-signature {
  margin: var(--space-16) 0 0;
}

.st-chapter > .cs-compare {
  border-radius: var(--radius-sharp);
}

.st-chapter > .cs-site-embed {
  width: 100%;
}

/* ── The cull — the editor's contact sheet, pinned ─────────────────────
 * Ported from the deck extension (rafa-nicky-showcase.css, "the pitch"):
 * twelve frames gridded on the shadow ground; the .st-cell--out frames
 * fade to black in a stagger (--i orders the wave) while the plane is
 * pinned, leaving the selects lit. The pin and the named view timeline
 * (--st-perf, the pinned dwell = 150dvh of scroll) are the shared
 * performance recipe in story.css. Engines without scroll-timeline
 * support land on the animations' end states — the edit done. */
.st-grid {
  width: 100%;
  height: 100%;
  /* The one dark plane — the sheet, the gaps, the culled frames: all
   * the same black, so nothing can seam. */
  background: var(--color-plane-dark);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  /* Film spacing: frames nearly touch within a roll; the rolls get a
   * wider band between them. */
  column-gap: var(--space-3);
  row-gap: var(--space-1);
  /* Square rows derived from the full-bleed width; taller than the
   * viewport in total, centred, so the sheet bleeds evenly off the top
   * and bottom: four rolls of film. */
  grid-template-rows: repeat(3, calc((100vw - 3 * var(--space-3)) / 4));
  align-content: center;
}

.st-cell {
  position: relative;
}

/* Square film frames, aligned in their strips. Black under the tile
 * kills any subpixel light line between photo and rebate. */
.st-tile {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: var(--color-plane-dark);
  transform: translateY(var(--colshift, 0px));
  animation: st-drift linear both;
  animation-timeline: --st-perf;
  animation-range: contain 0% contain 100%;
}

/* Each ROLL sits at its own vertical offset (--colshift) — staggered
 * strips, like cut rolls laid on the scanner — and drifts as one strip
 * (--drift), at its own pace and direction, through the dwell. Optical
 * pixels, as in the deck. */
.st-cell:nth-child(4n + 1) { --drift: -6px; --colshift: -28px; }
.st-cell:nth-child(4n + 2) { --drift: 9px;  --colshift: 22px; }
.st-cell:nth-child(4n + 3) { --drift: -9px; --colshift: -12px; }
.st-cell:nth-child(4n)     { --drift: 6px;  --colshift: 34px; }

/* Absolute so the photo can never stretch the square. */
.st-tile img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Per-cell crop anchor (--pos inline) — biased up by default so
   * portrait faces never clip. */
  object-position: var(--pos, 50% 22%);
  display: block;
}

/* Film rebate over every frame — the roll's own border, darken-blended
 * so its white interior vanishes and only the black frame edge prints.
 * Oversized 3% to crop the scan's white fringe; every third frame flips
 * 180° so the roll doesn't repeat. */
.st-tile::after {
  content: "";
  position: absolute;
  inset: 0;
  background: url('../../media/case-studies/rafa-nicky/rebate.png') center / 103% 103%;
  mix-blend-mode: darken;
  pointer-events: none;
}

.st-cell:nth-child(3n) .st-tile::after {
  transform: rotate(180deg);
}

@keyframes st-drift {
  from { translate: 0 calc(-1 * var(--drift, 6px)); }
  to { translate: 0 var(--drift, 6px); }
}

@keyframes st-cull {
  to { opacity: 0; }
}

/* A few frames stop short of black — sporadic residue of the edit. */
@keyframes st-cull-ghost {
  to { opacity: 0.1; }
}

.st-cell--out .st-tile {
  animation: st-drift linear both, st-cull linear both;
  animation-timeline: --st-perf, --st-perf;
  /* Front-loaded wave: first fade lands at 50%, then the spacing and
   * the fade windows stretch — fast at the start, slower to finish,
   * settled black by ~99%. */
  animation-range: contain 0% contain 100%, contain calc(46% + var(--i) * 4%) contain calc(50% + var(--i) * 4.5%);
}

.st-cell--ghost .st-tile {
  animation-name: st-drift, st-cull-ghost;
}

/* The statement (the shared .st-perf-text) rides the same timeline: it
 * arrives as the first frames go dark, so the words land on the edit. */
.st-cull-text {
  animation: st-cull-text linear both;
  animation-timeline: --st-perf;
  animation-range: contain 40% contain 60%;
}

@keyframes st-cull-text {
  from { opacity: 0; translate: 0 var(--space-4); }
  to { opacity: 1; translate: 0 0; }
}

/* ── The session — the handoff, performed in three beats ───────────────
 * The chapter's one idea is that the client adds a shoot without me, so
 * the plane tells it as a story while it is pinned: the new frames fly
 * into a folder; the ask types itself and Claude works (a thinking beat,
 * then the steps tick); a browser window rises out of the terminal with
 * the new page on the site — those same frames, on the client's paper —
 * and the statement lands last. Same pin and named-view-timeline recipe
 * as the cull (story.css); every beat is a [data-beat] with its own
 * window on the dwell, --a to --b.
 *
 * Base styles are the END state and keyframes start from the beginning,
 * so an engine without scroll timelines shows the finished composition.
 *
 * The plane is media-locked black in both themes; its ink is the one
 * theme-agnostic white, dimmed by opacity or mixed to a hairline, never a
 * second grey. The assistant's dot is the client's coral. The browser
 * window is a SPECIMEN of the client's site — its paper, its near-black
 * and its Space Grotesk at fixed sizes (case-study.css registers the
 * face for exactly this), like the card compare's label. */
.st-session {
  box-sizing: border-box;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: var(--space-10);
  align-items: center;
  padding: var(--page-gutter);
}

.st-session-side {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-8);
  height: 100%;
  /* Clear the floating header band, with room above the folder for the
   * frames' flight in. */
  padding-top: var(--space-20);
}

/* ── Beat one: the folder ─────────────────────────────────────────── */
.st-folder-label,
.st-term-bar,
.st-site-bar {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-weight-caption);
  opacity: 0.55;
}

.st-folder-label {
  color: var(--color-text-on-image);
  margin-bottom: var(--space-3);
}

/* A folder drawn in the hairline: the body, and a tab on its top edge. */
.st-folder {
  position: relative;
  aspect-ratio: 3 / 2;
  border: 1px solid color-mix(in srgb, var(--color-text-on-image) 18%, transparent);
  border-radius: var(--radius-sharp);
}

.st-folder::before {
  content: "";
  position: absolute;
  left: -1px;
  bottom: 100%;
  width: 28%;
  height: var(--space-5);
  border: 1px solid color-mix(in srgb, var(--color-text-on-image) 18%, transparent);
  border-bottom: 0;
  border-radius: var(--radius-sharp) var(--radius-sharp) 0 0;
}

/* The frames, fanned at rest inside the folder (--n orders the fan). */
.st-file {
  position: absolute;
  top: calc(10% + var(--n) * 3%);
  left: calc(8% + var(--n) * 22%);
  width: 40%;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius-sharp);
  background: var(--color-plane-dark);
  rotate: calc(var(--n) * 2deg - 2deg);
}

.st-file img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 30%);
  display: block;
}

/* ── The statement — in the side column's flow, not on the gutter ─── */
.st-session-text {
  margin: 0;
  max-width: 22ch;
  font-size: var(--font-size-display-l);
  line-height: var(--line-height-display-l);
  letter-spacing: var(--letter-spacing-display-l);
  font-weight: var(--font-weight-display-l);
  color: var(--color-text-on-image);
}

.st-session-text small {
  display: block;
  margin-top: var(--space-3);
  max-width: 44ch;
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-weight-body);
}

/* ── Beats two and three share the stage: the terminal, then the site
 * window rising over it. ─────────────────────────────────────────── */
.st-stage {
  position: relative;
}

/* The terminal — built from the tokens, not a screenshot. Base state is
 * the end state: dimmed and set back beneath the risen window. */
.st-term {
  border: 1px solid color-mix(in srgb, var(--color-text-on-image) 18%, transparent);
  border-radius: var(--radius-sharp);
  opacity: 0.18;
  scale: 0.96;
  translate: 0 -6%;
}

.st-term-bar {
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid color-mix(in srgb, var(--color-text-on-image) 18%, transparent);
  text-align: center;
  color: var(--color-text-on-image);
}

.st-term-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-6) var(--space-5);
}

.st-term-line {
  display: grid;
  grid-template-columns: var(--space-6) minmax(0, 1fr);
  column-gap: var(--space-2);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--font-size-small);
  line-height: var(--line-height-small);
  letter-spacing: var(--letter-spacing-small);
  font-weight: var(--font-weight-small);
  color: var(--color-text-on-image);
}

.st-term-line i {
  font-style: normal;
  opacity: 0.55;
}

.st-term-line--ask {
  font-weight: var(--font-weight-small-emphasized);
}

/* The assistant's dot — the client's coral (a project colour, from
 * css/page/project-colours.css). */
.st-term-line--ai i {
  color: var(--rn-coral);
  opacity: 1;
}

.st-term-line--think {
  opacity: 0.55;
}

/* The thinking beat: three dots that pulse in time while the line is
 * current. The pulse is a breathing loop (off the duration scale, with
 * the site's other loops); the scroll timeline gates it — the dots hold
 * once the answer arrives. */
.st-term-dots {
  display: inline-block;
  min-width: 3ch;
  text-align: left;
  animation: st-dots steps(4, end) infinite;
  animation-duration: 1200ms; /* breathing loop, off the scale */
}

@keyframes st-dots {
  from { clip-path: inset(0 100% 0 0); }
  to { clip-path: inset(0 -1ch 0 0); }
}

.st-term-line--step {
  padding-left: var(--space-6);
  opacity: 0.8;
}

.st-term-line--done {
  margin-top: var(--space-2);
}

.st-term-line--done i {
  opacity: 1;
}

/* The site window — rises out of the terminal on the third beat. A
 * specimen of the client's project page: paper, near-black, Space
 * Grotesk at the page's own fixed sizes; does not theme. */
.st-site {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  /* Its own 16:10 window, anchored to the stage's top — taller than the
   * terminal it rises over, so the page shows its gallery above the fold. */
  aspect-ratio: 16 / 10;
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-sharp);
  overflow: hidden;
  background: var(--rn-paper);
  color: var(--color-plane-dark);
}

.st-site-bar {
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid color-mix(in srgb, var(--color-plane-dark) 12%, transparent);
  color: var(--color-plane-dark);
  text-align: center;
}

.st-site-page {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  padding: var(--space-5) var(--space-6) 0;
  font-family: "Space Grotesk", sans-serif; /* specimen: the client's face */
}

.st-site-head {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  margin-bottom: var(--space-5);
}

.st-site-mark {
  width: var(--space-8);
  height: auto;
  fill: currentColor;
}

.st-site-head-title,
.st-site-menu {
  font-size: 12px;       /* specimen: the client's header size */
  line-height: 16px;
  letter-spacing: 0.06em;
  font-weight: 500;
}

.st-site-menu {
  text-align: right;
}

.st-site-crumb,
.st-site-sub {
  margin: 0;
  font-size: 12px;       /* specimen: the client's small size */
  line-height: 19px;
  font-weight: 400;
  opacity: 0.6;
}

.st-site-title {
  margin: 0;
  font-size: 32px;       /* specimen: the client's project title */
  line-height: 40px;
  font-weight: 500;
}

.st-site-gallery {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-4);
}

/* Square crops, anchored high, so the faces sit above the window's fold. */
.st-site-gallery img {
  width: 100%;
  height: auto; /* over the intrinsic height attribute, so the ratio rules */
  aspect-ratio: 1;
  object-fit: cover;
  object-position: var(--pos, 50% 18%);
  display: block;
}

/* ── The choreography ─────────────────────────────────────────────── */
/* Every beat rides the shared [data-beat] recipe (story.css); these
 * give the beats that do more than arrive their own keyframes. */

/* The frames fly in from where they were dropped (--fx, --fy, --fr) and
 * settle into the fan. */
.st-file[data-beat] {
  animation-name: st-file;
}

/* The ask types itself: a left-to-right wipe (st-type, shared). */
.st-term-line--ask[data-beat] {
  animation-name: st-type;
}

/* The terminal sets back as the window rises. */
.st-term[data-beat] {
  animation-name: st-recede;
}

/* The window rises out of the terminal (st-rise, shared). */
.st-site[data-beat] {
  animation-name: st-rise;
}

@keyframes st-file {
  from { opacity: 0; translate: var(--fx, 0) var(--fy, -60%); rotate: var(--fr, 0deg); scale: 1.12; }
  to { opacity: 1; translate: 0 0; rotate: calc(var(--n) * 2deg - 2deg); scale: 1; }
}

@keyframes st-recede {
  from { opacity: 1; scale: 1; translate: 0 0; }
  to { opacity: 0.18; scale: 0.96; translate: 0 -6%; }
}

/* ── Widths ─────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  /* The session unpins and stacks — folder, terminal, site, statement —
   * as a tall black card; the beats ride the card's own passage through
   * the viewport instead of a pinned dwell (a wrapped terminal and the
   * window cannot share one phone screen). */
  .st-session-slot {
    height: auto;
  }

  .st-session {
    position: relative;
    height: auto;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
    padding-top: var(--space-16);
  }

  .st-session-side {
    display: contents;
  }

  .st-session-folder { order: 1; }
  .st-stage { order: 2; }
  .st-session-text { order: 3; }

  .st-term-body {
    gap: var(--space-2);
    padding: var(--space-5) var(--space-4);
  }

  .st-term-line {
    font-size: var(--font-size-caption);
    line-height: var(--line-height-caption);
    letter-spacing: var(--letter-spacing-caption);
    font-weight: var(--font-weight-caption);
  }

  .st-term-line--ask {
    font-weight: var(--font-weight-caption-emphasized);
  }

  /* The window sits below the terminal rather than over it; the
   * terminal keeps its ink. */
  .st-stage {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
  }

  .st-site {
    position: relative;
    inset: auto;
    aspect-ratio: 1; /* square: the gallery clears the window's fold on a phone */
  }

  .st-term,
  .st-term[data-beat] {
    opacity: 1;
    scale: 1;
    translate: 0 0;
    animation-name: none;
  }

  .st-session [data-beat] {
    animation-range: cover calc(var(--a) * 0.6%) cover calc(var(--b) * 0.6%);
  }
}

/* ≤768, with scroll timelines and motion on: the plane pins as it does
 * on desktop and shows ONE beat at a time — the folder, then the
 * terminal in its place, then the window in its place — the statement
 * beneath. Folder and terminal leave as the next beat arrives. Without
 * scroll-timeline support, or under reduced motion, the stacked card
 * above stands. */
@media (max-width: 768px) {
  @supports (animation-timeline: view()) {
    :root:not([data-motion="reduce"]) .st-session-slot {
      height: 300dvh;
    }

    :root:not([data-motion="reduce"]) .st-session {
      position: sticky;
      top: 0;
      height: 100dvh;
      min-height: 0;
      display: grid;
      grid-template-columns: minmax(0, 1fr);
      grid-template-rows: minmax(0, 1fr) auto;
      align-items: center;
      gap: var(--space-6);
      padding-top: var(--space-16);
    }

    :root:not([data-motion="reduce"]) .st-session-folder,
    :root:not([data-motion="reduce"]) .st-stage {
      grid-area: 1 / 1;
      align-self: center;
    }

    :root:not([data-motion="reduce"]) .st-session-text {
      grid-area: 2 / 1;
    }

    /* Terminal and window share the cell; the window is in flow here. */
    :root:not([data-motion="reduce"]) .st-stage {
      display: grid;
      align-items: center;
      gap: 0;
    }

    :root:not([data-motion="reduce"]) .st-term,
    :root:not([data-motion="reduce"]) .st-site {
      grid-area: 1 / 1;
    }

    /* Beats keep their desktop windows on the pinned dwell. */
    :root:not([data-motion="reduce"]) .st-session [data-beat] {
      animation-range: contain calc(var(--a) * 1%) contain calc(var(--b) * 1%);
    }

    /* The folder leaves as the terminal arrives; the terminal leaves as
     * the window rises (the window's own --a/--b start at 76). */
    :root:not([data-motion="reduce"]) .st-session-folder {
      animation: st-leave linear both;
      animation-timeline: --st-perf;
      animation-range: contain 26% contain 32%;
    }

    :root:not([data-motion="reduce"]) .st-term,
    :root:not([data-motion="reduce"]) .st-term[data-beat] {
      opacity: 1;
      scale: 1;
      translate: 0 0;
      animation-name: st-arrive, st-leave;
      animation-timing-function: linear, linear;
      /* The exit fills forwards only — filling backwards would hold the
       * terminal visible before its entrance. */
      animation-fill-mode: both, forwards;
      animation-timeline: --st-perf, --st-perf;
      animation-range: contain 26% contain 32%, contain 76% contain 84%;
    }
  }
}

@media (max-width: 640px) {
  /* Two rolls on a phone — six square frames each, bleeding top and
   * bottom like the desktop sheet. */
  .st-grid {
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: repeat(6, calc((100vw - var(--space-2)) / 2));
    column-gap: var(--space-2);
    row-gap: var(--space-1);
  }

  .st-session-text {
    font-size: var(--font-size-display-m);
    line-height: var(--line-height-display-m);
    letter-spacing: var(--letter-spacing-display-m);
    font-weight: var(--font-weight-display-m);
  }
}

/* ── Reduced motion — keyed off data-motion="reduce" on the root, which
 * the head script stamps from the OS preference or the visitor's switch.
 * The unpinning is the shared rule in story.css; these park each
 * performance on its end state. */
:root[data-motion="reduce"] .st-funk .cs-cc-blob {
  animation-play-state: paused;
}

/* The cull: the edit done, the selects lit, the statement in place. */
:root[data-motion="reduce"] .st-tile,
:root[data-motion="reduce"] .st-cull-text {
  animation: none;
}

:root[data-motion="reduce"] .st-cell--out .st-tile {
  opacity: 0;
}

:root[data-motion="reduce"] .st-cell--ghost .st-tile {
  opacity: 0.1;
}

/* The session: frames in the folder, the exchange complete, the window
 * beside it, the statement in place. */
:root[data-motion="reduce"] .st-session {
  height: auto;
  min-height: 100dvh;
}

:root[data-motion="reduce"] .st-term-dots {
  animation: none;
}

:root[data-motion="reduce"] .st-stage {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

:root[data-motion="reduce"] .st-site {
  position: relative;
  inset: auto;
  aspect-ratio: 16 / 10;
}

:root[data-motion="reduce"] .st-term {
  opacity: 1;
  scale: 1;
  translate: 0 0;
}
