/* ==========================================================================
   SiteYourStory — hero, story beats, process
   The page no longer opens on the film. It opens on a hero, states the idea,
   states the problem, and only then hands over to the cinematic sequence.
   Loaded after film.css.
   ========================================================================== */

/* --- Hero: the doorway ----------------------------------------------------
   A track and a sticky stage. The track is the scroll distance the shot is
   given; the stage is the frame you actually watch. Scroll walks the man
   down the corridor, then pushes through the door — and the light on the
   far side of it is section two, which is why the two never look like
   separate pages.

   Time is driven in assets/js/film.js. Everything here is transform and
   opacity only, so the composite stays on the GPU while a 1440p video is
   being seeked underneath it.
   ------------------------------------------------------------------------ */

.hero {
  position: relative;
  height: 250svh;          /* 100svh of stage + 200svh of travel */
  background: var(--bg-dark);
  z-index: 1;
}
.hero-stage {
  position: sticky;
  top: 0;
  height: 100svh;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  padding-block: clamp(120px, 18vh, 200px) clamp(72px, 11vh, 130px);
  background: var(--bg-dark);
}

/* The push. The origin is the doorway itself, at 51.4% across and 47.5%
   down — not eyeballed but measured, as the centroid of the brightest tier
   of pixels across the clip, where it holds steady from 7.3s to the end.

   object-position and transform-origin carry the same pair on purpose:
   object-position aligns the video's 51.4% point with the container's 51.4%
   point, so an origin of 51.4% is then the doorway in both coordinate
   systems. Let those two drift apart and the zoom pushes into the wall
   beside the door instead of through it. */
.hero-plate {
  position: absolute;
  inset: 0;
  transform-origin: 51.4% 47.5%;
  transform: scale(var(--zoom, 1)) translate3d(0, var(--drift, 0%), 0);
  will-change: transform;
}
.hero-plate video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 51.4% 47.5%;
  filter: contrast(1.05) saturate(1.02);
  /* A seek repaints the whole surface; keeping it on its own layer stops
     that repaint from dragging the type through with it. */
  transform: translateZ(0);
  backface-visibility: hidden;
}

/* Weighted low and left, where the words are; the corridor keeps its depth.
   Lighter than the old plate's veil — this frame is darker to begin with,
   and the doorway has to stay bright enough to be the subject. */
.hero-veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* The veil exists to hold the words. It leaves with them — otherwise the
     push into the doorway arrives with a dark floor still weighing down a
     frame that is supposed to be turning into light. */
  opacity: var(--veil, 1);
  background:
    linear-gradient(to top,
      rgba(6, 6, 9, 0.9) 0%,
      rgba(6, 6, 9, 0.6) 32%,
      rgba(6, 6, 9, 0.08) 66%,
      transparent 100%),
    linear-gradient(100deg,
      rgba(6, 6, 9, 0.68) 0%,
      rgba(6, 6, 9, 0.26) 44%,
      transparent 76%);
}

/* The bloom opens from the doorway; the wash finishes what the camera
   starts. The clip peaks at 143/255 — bright, but not white — so without
   these two the transition would be a fade, which is what we are avoiding. */
.hero-bloom {
  position: absolute;
  inset: -30%;
  z-index: 3;
  opacity: var(--o, 0);
  transform: scale(var(--s, 0.35));
  transform-origin: 51.4% 47.5%;
  will-change: transform, opacity;
  background: radial-gradient(circle at 51.4% 47.5%,
    #fffdf8 0%,
    #fdfaf2 26%,
    rgba(247, 243, 234, 0.72) 46%,
    rgba(244, 241, 234, 0) 68%);
}
.hero-wash {
  position: absolute;
  inset: 0;
  z-index: 4;
  opacity: var(--o, 0);
  background: #f5f2eb;     /* the exact ivory .dawn opens on */
  pointer-events: none;
}

.hero-in {
  position: relative;
  z-index: 2;
  width: 100%;
  opacity: var(--o, 1);
  transform: translate3d(0, var(--y, 0), 0);
}

.hero-eyebrow {
  font-size: clamp(0.66rem, 0.8vw, 0.8rem);
  font-weight: 600;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(242, 241, 238, 0.7);
  margin-bottom: clamp(18px, 3vh, 34px);
}
.hero-head {
  font-size: clamp(2.3rem, 6.4vw, 6.6rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.042em;
  color: #fff;
  max-width: 17ch;
}
/* Masked rise, one line after the other. */
.hero-line { display: block; overflow: hidden; }
.hero-line > span {
  display: block;
  transform: translateY(104%);
  animation: hero-rise 1.15s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.hero-line:nth-child(2) > span { animation-delay: 0.11s; }
@keyframes hero-rise { to { transform: none; } }

.hero-sub {
  margin-top: clamp(18px, 2.6vh, 30px);
  font-size: clamp(1rem, 1.25vw, 1.3rem);
  line-height: 1.55;
  max-width: 40ch;
  color: rgba(242, 241, 238, 0.82);
  opacity: 0;
  animation: hero-fade 1s ease 0.42s forwards;
}
.hero-cta {
  margin-top: clamp(26px, 4vh, 46px);
  display: flex;
  align-items: center;
  gap: clamp(18px, 2.4vw, 34px);
  flex-wrap: wrap;
  opacity: 0;
  animation: hero-fade 1s ease 0.56s forwards;
}
@keyframes hero-fade { to { opacity: 1; } }

.hero-alt {
  font-size: 0.88rem;
  font-weight: 600;
  color: #fff;
  padding-bottom: 5px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.32);
  transition: border-color 0.35s var(--ease);
}
.hero-alt:hover { border-bottom-color: var(--accent); }

.hero-cue {
  position: absolute;
  left: 50%;
  bottom: clamp(22px, 3.4vh, 40px);
  transform: translateX(-50%);
  z-index: 5;
  opacity: var(--o, 1);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.hero-cue::after {
  content: '';
  display: block;
  width: 1px;
  height: 26px;
  margin: 9px auto 0;
  background: linear-gradient(rgba(255, 255, 255, 0.5), transparent);
}

/* --- Story beats ---------------------------------------------------------
   The calm between the hero and the film. Type, space, one hairline that
   draws itself as the beat lands.
   ------------------------------------------------------------------------ */

/* --- Daylight -------------------------------------------------------------
   What is on the other side of the door. One field, not two sections: it
   opens on the same ivory the hero wash ends on — #f5f2eb, the same value
   in both files — so the boundary between them cannot be seen, then it
   darkens on its own to hand the film a black stage.

   Ink is charcoal rather than black. Pure black on warm ivory reads as a
   printing error; #191817 reads as ink.
   ------------------------------------------------------------------------ */
.dawn {
  position: relative;
  z-index: 2;
  background:
    linear-gradient(180deg,
      #f5f2eb 0%,
      #f3efe6 34%,
      #ece7db 58%,
      #b9b3a6 76%,
      #3a3833 90%,
      #0a0a0b 100%);
}
.dawn .beat-line,
.dawn .f-eyebrow { color: #191817; }
.dawn .beat-sub { color: rgba(25, 24, 23, 0.7); }
.dawn .beat-rule { background: rgba(25, 24, 23, 0.16); }
/* The gap's second half runs into the darkening tail, so its own copy stays
   in the light and the extra room below is left deliberately empty. */
.dawn .beat--gap { padding-bottom: clamp(190px, 30vh, 380px); }

.beat {
  position: relative;
  z-index: 2;
  background: transparent;
  padding-block: clamp(90px, 16vh, 200px);
}
.beat-line {
  font-size: clamp(1.5rem, 3.6vw, 3.6rem);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.034em;
  color: #fff;
  max-width: 22ch;
}
.beat-sub {
  margin-top: clamp(16px, 2.4vh, 28px);
  font-size: clamp(0.98rem, 1.16vw, 1.2rem);
  line-height: 1.6;
  max-width: 46ch;
  color: rgba(242, 241, 238, 0.72);
}
.beat-rule {
  display: block;
  height: 1px;
  margin-top: clamp(40px, 7vh, 90px);
  background: rgba(255, 255, 255, 0.16);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.beat-rule.is-in { transform: scaleX(1); }

[data-rise] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
[data-rise].is-in { opacity: 1; transform: none; }
[data-rise].is-in.d1 { transition-delay: 0.08s; }
[data-rise].is-in.d2 { transition-delay: 0.16s; }

/* --- Process -------------------------------------------------------------
   Four stages, each with a diagram that draws its own stroke as it arrives:
   scattered marks resolving into a grid, into structure, into something that
   ships. The motion is the graphic — there are no images here.
   ------------------------------------------------------------------------ */

.process {
  position: relative;
  z-index: 2;
  background: var(--bg-secondary);
  padding-block: clamp(84px, 14vh, 170px);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.process-title {
  font-size: clamp(1.5rem, 3vw, 3rem);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.034em;
  color: #fff;
  max-width: 22ch;
  margin-bottom: clamp(34px, 5vh, 60px);
}
.process-rail {
  position: relative;
  height: 1px;
  background: rgba(255, 255, 255, 0.14);
  margin-bottom: clamp(30px, 4.5vh, 54px);
  overflow: hidden;
}
.process-rail i {
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform-origin: left;
  transform: scaleX(0);
  transition: transform 0.25s linear;
}

.stage-list {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(22px, 3vw, 52px);
}
.stage {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.85s var(--ease), transform 0.85s var(--ease);
}
.stage.is-in { opacity: 1; transform: none; }
.stage:nth-child(2).is-in { transition-delay: 0.09s; }
.stage:nth-child(3).is-in { transition-delay: 0.18s; }
.stage:nth-child(4).is-in { transition-delay: 0.27s; }

.stage-art {
  display: block;
  width: clamp(64px, 6vw, 92px);
  margin-bottom: clamp(18px, 2.6vh, 28px);
  color: rgba(242, 241, 238, 0.42);
  transition: color 1s var(--ease) 0.2s;
}
.stage.is-in .stage-art { color: var(--accent-lift); }
.stage-art svg { width: 100%; height: auto; overflow: visible; }

/* Each diagram draws itself. 240 comfortably exceeds every path length here,
   so one value serves them all. */
.stage-art .dg > * {
  stroke-dasharray: 240;
  stroke-dashoffset: 240;
}
.stage.is-in .stage-art .dg > * {
  animation: draw 1.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.stage.is-in .stage-art .dg > *:nth-child(2) { animation-delay: 0.12s; }
.stage.is-in .stage-art .dg > *:nth-child(3) { animation-delay: 0.24s; }
.stage.is-in .stage-art .dg > *:nth-child(4) { animation-delay: 0.36s; }
@keyframes draw { to { stroke-dashoffset: 0; } }

.stage b {
  display: block;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  color: var(--accent-lift);
  margin-bottom: 12px;
}
.stage h3 {
  font-size: clamp(1.15rem, 1.9vw, 1.8rem);
  font-weight: 600;
  letter-spacing: -0.028em;
  color: #fff;
  margin-bottom: 10px;
}
.stage p {
  font-size: clamp(0.86rem, 0.98vw, 0.98rem);
  line-height: 1.55;
  color: rgba(242, 241, 238, 0.68);
}

@media (max-width: 900px) {
  .stage-list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 34px 20px; }
  .hero-head { max-width: 14ch; }
}
@media (max-width: 560px) {
  .stage-list { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-plate img { animation: none; transform: scale(1.03); }
  .hero-line > span,
  .hero-sub,
  .hero-cta { animation: none; opacity: 1; transform: none; }
  [data-rise],
  .stage { opacity: 1; transform: none; }
  .beat-rule { transform: scaleX(1); }
  .stage-art .dg > * { stroke-dashoffset: 0; }
}


/* --- Industry holds ------------------------------------------------------
   The film freezes on the room while the label reads. Three of them sit in
   clean footage and can be set at title-card scale. Two — Pilates and
   fitness — fall inside the stretch where the film carries its own centred
   lines, so those are set small and pinned low-left, clear of the film's
   type instead of fighting it.
   ------------------------------------------------------------------------ */

.ch--ind .f-ind {
  font-size: clamp(2.2rem, 5.6vw, 5.6rem);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.03em;
  color: #fff;
  text-shadow: 0 2px 34px rgba(0, 0, 0, 0.6);
}
.ch--ind .f-ind-note {
  margin-top: clamp(10px, 1.6vh, 20px);
  font-size: clamp(0.9rem, 1.06vw, 1.08rem);
  line-height: 1.55;
  max-width: 38ch;
  color: rgba(242, 241, 238, 0.82);
  text-shadow: 0 1px 18px rgba(0, 0, 0, 0.6);
}

/* The two that share the frame with the film's own burned-in lines. Small,
   low, left — and carrying their own local scrim, because the chapter scrim
   is lifted there so the film can speak. */
.ch--tag { justify-content: flex-end; }
.ch--tag .wrap {
  max-width: none;
}
.ch--tag .f-eyebrow { margin-bottom: 8px; }
.ch--tag .f-ind {
  font-size: clamp(1.5rem, 2.6vw, 2.5rem);
  font-weight: 600;
}
.ch--tag .f-ind-note {
  font-size: clamp(0.82rem, 0.95vw, 0.95rem);
  max-width: 30ch;
  color: rgba(242, 241, 238, 0.76);
}
/* A contained wash under the label only — it must not reach the centre of
   frame, where the film's own typography is. */
.ch--tag .wrap::before {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: min(56%, 620px);
  height: 46%;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(to top right,
    rgba(8, 8, 10, 0.9) 0%,
    rgba(8, 8, 10, 0.55) 42%,
    transparent 78%);
}
.ch--tag .wrap { position: relative; z-index: 1; }

@media (max-width: 900px) {
  .ch--ind .f-ind { font-size: clamp(1.7rem, 8vw, 2.6rem); }
  .ch--tag .f-ind { font-size: clamp(1.25rem, 6vw, 1.7rem); }
  .ch--tag .wrap::before { width: 100%; height: 54%; }
}


/* --- USP ------------------------------------------------------------------
   One statement, four places it does its work, one closing line. The list is
   set as type with a hairline per row — no icons, no cards.
   ------------------------------------------------------------------------ */

.usp {
  position: relative;
  z-index: 2;
  background: var(--bg-warm-deep);
  padding-block: clamp(90px, 15vh, 190px);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.usp-head {
  font-size: clamp(1.7rem, 3.9vw, 3.9rem);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -0.036em;
  color: #fff;
  max-width: 24ch;
}
.usp-list {
  list-style: none;
  margin-top: clamp(38px, 6vh, 74px);
  max-width: 62ch;
}
.usp-list li {
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  padding-block: clamp(14px, 2.2vh, 22px);
}
.usp-list li span {
  font-size: clamp(1rem, 1.5vw, 1.5rem);
  font-weight: 500;
  letter-spacing: -0.022em;
  color: rgba(242, 241, 238, 0.86);
}
.usp-list li:nth-child(2).is-in { transition-delay: 0.07s; }
.usp-list li:nth-child(3).is-in { transition-delay: 0.14s; }
.usp-list li:nth-child(4).is-in { transition-delay: 0.21s; }
.usp-close {
  margin-top: clamp(26px, 4vh, 48px);
  font-size: clamp(1.05rem, 1.7vw, 1.7rem);
  font-weight: 600;
  letter-spacing: -0.026em;
  color: #fff;
}

/* --- CTA background -------------------------------------------------------
   Reuses cta.mp4 — glass and steel settling around a warm core. Fetched only
   when the section is near, played muted and looped, and pushed well back so
   the form stays the most legible thing on the screen.
   ------------------------------------------------------------------------ */

.close { position: relative; overflow: hidden; }
.close-bg { position: absolute; inset: -4%; z-index: 0; }
.close-vid {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  opacity: 0;
  transform: scale(1.06);
  filter: saturate(0.85) brightness(0.72) blur(2px);
  transition: opacity 1.6s cubic-bezier(0.65, 0, 0.35, 1);
}
.close[data-bg='on'] .close-vid { opacity: 0.55; }
/* Weighted right, under the form, and along the bottom under the fine print. */
.close-wash {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(to right,
      rgba(10, 10, 11, 0.72) 0%,
      rgba(10, 10, 11, 0.5) 34%,
      rgba(10, 10, 11, 0.82) 62%,
      rgba(10, 10, 11, 0.92) 100%),
    linear-gradient(to bottom,
      rgba(10, 10, 11, 0.55) 0%,
      transparent 26%,
      rgba(10, 10, 11, 0.6) 100%);
}
.close > .wrap { position: relative; z-index: 2; }

/* --- Selected work: arrival ----------------------------------------------
   The ring builds itself as the section enters — cards come forward out of
   depth rather than being there already. Driven from coverflow.js so one
   system owns the transform; CSS here only carries the heading and rail.
   ------------------------------------------------------------------------ */
.cf-head,
.cf-foot {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.cf[data-entered='true'] .cf-head,
.cf[data-entered='true'] .cf-foot { opacity: 1; transform: none; }
.cf[data-entered='true'] .cf-foot { transition-delay: 0.12s; }

@media (prefers-reduced-motion: reduce) {
  .close-vid { display: none; }
  .cf-head, .cf-foot { opacity: 1; transform: none; }
}





/* --- Carousel: driven by the page ----------------------------------------
   The ring used to require a drag. It is now pinned, and scrolling the page
   walks it through the projects — drag stays as an optional override for
   anyone who reaches for it, but nothing requires it.
   ------------------------------------------------------------------------ */
.cf { position: relative; padding-block: 0; overflow: visible; }
.cf-pin {
  position: sticky;
  top: 0;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
  padding-block: clamp(96px, 14vh, 170px) clamp(48px, 8vh, 90px);
  background: transparent;
}
.cf-track { position: relative; width: 100%; pointer-events: none; }

/* The stage no longer advertises a grab handle as the primary affordance. */
.cf-stage { cursor: default; }
.cf-stage.is-dragging { cursor: grabbing; }


/* --- We build with heart -------------------------------------------------
   The site's own line, given a mark instead of an emoji. Two beats then a
   rest — the rhythm of an actual heartbeat rather than a steady pulse, which
   is what makes it read as organic instead of as a loading state.
   ------------------------------------------------------------------------ */

.heartline {
  display: flex;
  align-items: center;
  gap: 0.5em;
  margin-top: clamp(28px, 4vh, 46px);
  font-size: clamp(0.95rem, 1.1vw, 1.12rem);
  font-weight: 500;
  letter-spacing: -0.015em;
  color: rgba(242, 241, 238, 0.72);
}
.heartmark {
  display: inline-flex;
  width: 1.25em;
  color: var(--accent);
  transform-origin: 50% 62%;
  animation: heartbeat 3.4s cubic-bezier(0.4, 0, 0.3, 1) infinite;
  filter: drop-shadow(0 0 10px rgba(192, 57, 43, 0.45));
}
.heartmark svg { width: 100%; height: auto; }
@keyframes heartbeat {
  0%, 62%, 100% { transform: scale(1); }
  68%  { transform: scale(1.17); }
  74%  { transform: scale(1.02); }
  80%  { transform: scale(1.11); }
  87%  { transform: scale(1); }
}

/* --- Glass action --------------------------------------------------------
   Restrained: one translucent layer, one hairline, one highlight. It sits on
   the brand red rather than replacing it.
   ------------------------------------------------------------------------ */
.btn--glass {
  position: relative;
  background: color-mix(in oklab, var(--accent) 82%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.22);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.24) inset,
    0 18px 40px -18px rgba(192, 57, 43, 0.7);
  transition: background-color 0.4s var(--ease), transform 0.4s var(--ease),
    box-shadow 0.4s var(--ease);
}
.btn--glass:hover {
  background: var(--accent);
  transform: translateY(-2px);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.3) inset,
    0 24px 50px -18px rgba(192, 57, 43, 0.85);
}

@media (prefers-reduced-motion: reduce) {
  .heartmark { animation: none; }
  .btn--glass:hover { transform: none; }
}

/* --- Nav ink over the daylight -------------------------------------------
   The home nav is white type with no plate behind it, which is correct over
   a corridor and invisible over ivory. initDoor() flips data-ink from the
   real geometry of the light band, so this stays right at any viewport
   height rather than at one guessed scroll offset.
   ------------------------------------------------------------------------ */
.nav--film[data-ink='dark'] .logo {
  color: #191817;
  text-shadow: none;
}
.nav--film[data-ink='dark'] .nav-link { color: #191817; }
.nav--film[data-ink='dark'] .burger { border-color: rgba(25, 24, 23, 0.3); }
.nav--film[data-ink='dark'] .burger span { background: #191817; }

/* --- The doorway on smaller and slower ------------------------------------ */

/* Touch: the film runs itself, so the track only has to carry the push and
   the light. Less of it, because scrolling costs more on a phone. */
.hero[data-mode='play'] { height: 240svh; }

@media (max-width: 860px) {
  .hero { height: 250svh; }
  /* Portrait crops a 16:9 corridor hard, so the doorway stays the anchor
     — it is the whole point of the shot and must never be the part cut off. */
  .hero-plate video { object-position: 51.4% 47.5%; }
  .hero-bloom { inset: -50%; }
  .dawn .beat--gap { padding-bottom: clamp(150px, 24vh, 260px); }
}

/* Reduced motion: one composed frame, and the daylight is simply the next
   section. No push, no bloom, nothing that moves on its own. */
@media (prefers-reduced-motion: reduce) {
  .hero { height: auto; min-height: 100svh; }
  .hero-stage { position: relative; }
  .hero-plate { transform: none; }
  .hero-bloom,
  .hero-wash { display: none; }
  .hero-in { opacity: 1; transform: none; }
  .hero-cue { opacity: 1; }
}
