/* WORK · LUMIN tunnel geometry. Colour palette + card visuals on top
   of a porthole-mask / scene-perspective / letter-scatter rig. */

:root {
  /* Warm tunnel colour. Started at #b88455 (sampled from lumin_index.jpg
     highlights, heavily desaturated). User read that as too grey/brown,
     so bumped saturation + lightness for a brighter amber while staying
     short of full candy-orange. To re-tune: hold the hue (~28°), move
     S/L; e.g. dial back to #c89060 if the new value reads too punchy. */
  --tunnel-warm: #d49764;
  --tunnel-warm-rgb: 212, 151, 100;
}

.s-work {
  --height: 100lvh;                          /* fallback; JS overrides to works.length * 50lvh */
  --color-primary: var(--tunnel-warm);       /* tunnel fill */
  --color-primary-rgb: var(--tunnel-warm-rgb);
  --color-secondary: var(--bg, #0c0e14);     /* deep blue-black */
  --color-shadow: rgba(var(--tunnel-warm-rgb), 0.45);
  --color-text: var(--text, #e8eaef);
  --color-grid: rgba(var(--tunnel-warm-rgb), 0.18);
  --scroll-progress: 0;
  /* Soft fades blend section's amber edges into the page bg. Top fade was
     here originally; bottom fade added so the docked LUMIN/icons don't
     slam into a hard amber-to-dark seam below them on mobile (where the
     section bottom can sit a long way under the centred icons). */
  --tunnel-fade: 24vh;

  position: relative;
  /* z-index must be > .about-reveal (z:5) so the fixed .s__inner LUMIN
     title isn't covered by the about-reveal stacking context as the user
     scrolls past the about section into the tunnel. */
  z-index: 6;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  height: var(--height);
  background: var(--color-primary);
  color: var(--color-text);
  overflow: hidden;
}

.s-work::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--tunnel-fade);
  background: linear-gradient(to bottom, var(--bg) 0%, transparent 100%);
  /* Sits ABOVE .s-work's amber bg but BELOW .s__inner (z:2) so LUMIN
     isn't covered as the section enters viewport. Mask-outer (z:3) still
     overlays LUMIN until the tunnel opens. */
  z-index: 1;
  pointer-events: none;
}

.s-work::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: var(--tunnel-fade);
  background: linear-gradient(to top, var(--bg) 0%, transparent 100%);
  z-index: 1;
  pointer-events: none;
}

.s__outer {
  position: relative;
  height: var(--height);
  clip-path: inset(0 -1rem);
}

/* Fixed dark scene visible through the porthole. Flat
   `var(--color-secondary)` fill plus a faint radial vignette so the
   LUMIN letters read like they're glowing from depth, not pasted onto
   a black wall. */
.s__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100lvh;
  min-height: 100dvh;
  /* Above the .s-work::before/after fade overlays (z:1) so LUMIN stays
     visible as the section enters the viewport. Stays below
     .s__mask-outer (z:3) so the amber mask still hides LUMIN until the
     tunnel opens. */
  z-index: 2;
  background:
    radial-gradient(
      ellipse 60% 50% at 50% 45%,
      #1a1f2b 0%,
      var(--color-secondary) 55%,
      #04060a 100%
    );
  color: var(--color-primary);
  transform: translate3d(0, calc(var(--scroll-progress) * -15%), 0);
  will-change: clip-path, transform;
}

@media only screen and (max-width: 767px) and (orientation: landscape),
       only screen and (max-width: 576px) {
  .s__inner { transform: none; }
}

.s__inner::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(
    ellipse 75% 60% at 50% 50%,
    transparent 55%,
    rgba(0, 0, 0, 0.45) 100%
  );
  z-index: 3;
}

/* GSAP ScrollTrigger pins this layer for the WORK scroll range (see
   work-orbit.js). position:sticky was releasing early and the amber frame
   drifted up, exposing a black strip above .s__inner at the bottom. */
.s__mask-outer {
  position: relative;
  left: 0;
  z-index: 3;
  width: 100%;
  height: 100lvh;
  min-height: 100dvh;
  overflow: hidden;
  pointer-events: none;
}

.s__mask {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  transform-origin: 50% 50%;
  will-change: scale, transform;
}

.s__mask__svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

/* No outline on the porthole paths — the amber/dark colour contrast
   carries the edge by itself. */
.s__mask__path-outer,
.js-mask-path-outer {
  fill: var(--color-primary);
  fill-rule: evenodd;
  stroke: none;
}

.s__mask__path-inner,
.js-mask-path-inner {
  fill: var(--color-primary);
  fill-rule: evenodd;
  stroke: none;
  /* Parallax on the inner mask path shifted the hole vs the outer fill and
     looked like the amber frame sliding up (black strip at the bottom). The
     work tunnel pins with scaleX-only opening; keep this path aligned. */
  transform: none;
}

.s__mask__path-lines,
.js-mask-path-lines {
  fill: none;
  stroke: var(--color-grid);
  stroke-width: 1px;
  opacity: 0.6;
}

/* Porthole ruler — narrow (~16.67%) but full viewport height. At
   100lvh the capsule touches y=0 and y=100vh at scale 1. JS opens
   the mask with scaleX only (scaleY stays 1) so the hole widens
   sideways without growing taller and eating the frame. */
.s__ruler {
  --width: min(16.6667%, 19.625rem);
  position: absolute;
  top: 0;
  left: calc(50% - var(--width) / 2);
  width: var(--width);
  height: 100lvh;
  pointer-events: none;
  visibility: hidden;
}

@media only screen and (max-width: 987px) {
  .s__ruler { --width: 33.3333%; }
}

@media only screen and (max-width: 767px) and (orientation: landscape),
       only screen and (max-width: 576px) {
  .s__ruler { --width: 50%; }
}

/* Title + scene share the same display type ramp. DM Sans 900
   (already loaded for the rest of the page) — similar weight /
   vertical proportions to a typical display face. */
.s__title,
.s__scene {
  font-family: "DM Sans", system-ui, sans-serif;
  font-weight: 900;
  font-size: min(18.75rem, 25lvh);
  line-height: 1;
  text-align: center;
  text-transform: uppercase;
}

.s__title__inner,
.s__scene__letter {
  line-height: 0.85;
}

.s__title {
  width: 0.7em;
  margin: 0;
  opacity: 0;
  word-break: break-all;
}

.s__title__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin: 0.075em 0 -0.125em;
}

.s__scene {
  --shadow: var(--color-shadow);
  --progress: 0.5;
  --state: 0;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  /* Tighter perspective = stronger 3D foreshortening of cards as they
     travel through the tunnel. Was 40rem (mild). Dropped to 26rem so
     cards near the side edges visibly fish-eye / lean into the centre,
     selling the "inside of a curved tunnel" read user asked for. */
  perspective: 26rem;
  will-change: transform;
}

/* --close lives on .s-work (root) so BOTH the ghost letters inside .s__scene
   AND the social icons at .s__inner level inherit it. 0 = tunnel state /
   column LUMIN; 1 = horizontal LUMIN row + icons fully bursted to corners.
   GSAP scrubs this at the end of the timeline. */
.s-work {
  --close: 0;
}

.s__canvas {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  transform: translate3d(0, calc(var(--scroll-progress) * -5%), 0);
  pointer-events: none;
}

@media only screen and (max-width: 767px) and (orientation: landscape),
       only screen and (max-width: 576px) {
  .s__canvas { transform: none; }
}

.s__scene__letter {
  --head: calc((var(--progress) - 0.5) * -2);
  --ahead: calc(var(--head) * var(--head));
  /* Per-ghost row-target offset, set inline by JS in setLetters(). All
     ghosts of letter[s] share the same --close-x / --close-y so the whole
     row collapses onto one horizontal slot (the "L" slot, "U" slot, etc.)
     when --close goes 1. Falls back to 0px if JS hasn't run. */
  --close-x: 0px;
  --close-y: 0px;
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  color: var(--color-primary);
  /* Soft halo — alpha pulled way down so the letters read as
     printed-on, not radioactive. */
  text-shadow:
    0 0 0.02em rgba(var(--tunnel-warm-rgb), 0.22),
    0 0 0.08em rgba(var(--tunnel-warm-rgb), 0.10);
  /* Two motion phases overlaid on one transform:
       (1) Tunnel spread/shimmer — multiplied by --state. During the WORK
           timeline GSAP scrubs --state 0→1 (open) then 1→0 (close),
           so this term opens and re-stacks ghosts to their column origin.
       (2) Row-dock translate — multiplied by --close. After --state has
           returned to 0, GSAP scrubs --close 0→1 and each row's ghosts
           translate to its target horizontal slot, all rows aligning at
           viewport centre Y. The sequencing ensures the two phases don't
           fight (their factors hit 0 cleanly between phases). */
  transform:
    rotateY(calc(var(--head) * -10deg * var(--state)))
    translate3d(
      calc(
        var(--head) * 50vw * var(--state)
        + var(--close-x) * var(--close, 0)
      ),
      calc(
        var(--iy) * 50% * var(--ahead) * var(--state)
        + var(--close-y) * var(--close, 0)
      ),
      0
    );
  pointer-events: none;
  will-change: transform;
}

.s__scene__letter::before {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
  color: var(--color-shadow);
  opacity: min(calc(var(--state) * 2), 1);
  transform: scale(1.05, 1.02)
    translate3d(calc(var(--head) * 0.1rem * var(--state) * var(--state)), 0, 0);
  transform-origin: calc(50% - var(--head) * 50%) -50%;
  content: attr(data-letter);
  text-shadow: 0 0 0.18em rgba(var(--tunnel-warm-rgb), 0.06);
  will-change: opacity, transform;
}

/* Card transform — cards fly along X with a deepening yaw
   (rotateY) toward viewer-centre + a parabolic z-pushback at the
   extremes. Stronger-than-default 3D concave read with more side
   distortion. To dial back, lower --tunnel-yaw / --tunnel-depth
   on .s__scene__work. */
a-work.s__scene__work {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 2;
  --progress: 1;
  --size: 0.9;
  --y: 0;
  /* Yaw at progress = ±1 (tunnel edges). 38deg ≈ "you can clearly see
     the card's side panel"; raises beyond the previous 20deg to sell
     the curved-wall feel. */
  --tunnel-yaw: 38deg;
  /* Z pushback at progress = ±1, in rem. Parabolic: progress² * depth,
     so cards barely recede at centre and push deepest at the edges,
     mimicking a concave wall the cards roll along. */
  --tunnel-depth: 9rem;
  /* Portrait pole shots: big enough to read, small enough not to clip on
     short viewports (height = width × 5/4). */
  width: clamp(240px, 30vw, 460px);
  transform:
    rotateY(calc(var(--progress) * var(--tunnel-yaw) * -1))
    translate3d(
      calc(var(--progress) * (50vw + 100%) - 50%),
      calc(var(--y) * 50% - 50%),
      calc(var(--progress) * var(--progress) * var(--tunnel-depth) * -1)
    )
    scale(var(--size));
  will-change: transform;
  pointer-events: none;
}

@media only screen and (max-width: 987px) {
  /* drop the scale() at narrower viewports so cards don't shrink further */
  a-work.s__scene__work {
    transform:
      rotateY(calc(var(--progress) * var(--tunnel-yaw) * -1))
      translate3d(
        calc(var(--progress) * (50vw + 100%) - 50%),
        calc(var(--y) * 50% - 50%),
        calc(var(--progress) * var(--progress) * var(--tunnel-depth) * -1)
      );
  }
}

@media only screen and (max-width: 767px) and (orientation: landscape),
       only screen and (max-width: 576px) {
  /* mobile: --y multiplier doubles (50% → 100%) so cards drift further
     up/down — compensates for the cards being smaller relative to viewport */
  a-work.s__scene__work {
    transform:
      rotateY(calc(var(--progress) * var(--tunnel-yaw) * -1))
      translate3d(
        calc(var(--progress) * (50vw + 100%) - 50%),
        calc(var(--y) * 100% - 50%),
        calc(var(--progress) * var(--progress) * var(--tunnel-depth) * -1)
      );
  }
}

a-work.s__scene__work.is-inview {
  pointer-events: auto;
}

/* ---------- card visual (ours, kept as-is) ---------- */

.s__work__link {
  display: block;
  color: var(--color-text);
  border-radius: 6px;
  overflow: hidden;
  background: rgba(20, 24, 32, 0.85);
  border: 1px solid rgba(var(--tunnel-warm-rgb), 0.18);
  text-decoration: none;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  transition: border-color 0.2s, transform 0.2s, box-shadow 0.2s;
}

.s__work__link:hover {
  border-color: var(--color-primary);
  box-shadow: 0 0 18px rgba(var(--tunnel-warm-rgb), 0.25);
}

.s__work__media {
  /* Portrait 4:5 — matches the native ratio of most pole shots; landscape
     images get centre-cropped. */
  aspect-ratio: 4 / 5;
  background: #111;
  overflow: hidden;
  position: relative;
}

.s__work__media img,
.s__work__media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

.s__work__media.is-placeholder::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, var(--color-primary), #111 55%);
  opacity: 0.5;
}

.s__work__caption {
  padding: 0.6rem 0.75rem;
  background: rgba(0, 0, 0, 0.88);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.s__work__title {
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.s__work__key {
  display: block;
  margin-top: 0.2rem;
  font-family: ui-monospace, monospace;
  font-size: 0.58rem;
  color: rgba(255, 255, 255, 0.4);
}

/* Old "Scroll — into LUMIN" hint removed at user request — the WORK
   tunnel reads as scroll-driven on its own and the hint just clutters
   the closed-capsule frame. Rule kept commented in case you ever want
   to bring a hint back; uncomment + restore the <p class="s__hint"> in
   index.html. */

/* ---------- tagline + icon-row eyebrow ---------- */
/* Both fade in with --close (0 → 1), same beat as the LUMIN row dock and
   the icon burst, so the final frame settles together. Positioned
   absolute relative to .s__inner, anchored at viewport centre and offset
   by vh values that clear the docked LUMIN row (font-size 25lvh ≈ 17vh
   visible) on both sides. */
.s__tagline {
  position: absolute;
  top: calc(50% + 14vh); /* just below the LUMIN row's bottom edge */
  left: 50%;
  transform: translateX(-50%);
  z-index: 4;
  margin: 0;
  width: min(86vw, 720px);
  text-align: center;
  font-family: "DM Sans", system-ui, sans-serif;
  font-size: clamp(0.7rem, 1.1vw, 0.85rem);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(var(--tunnel-warm-rgb), 0.78);
  opacity: var(--close, 0);
  transition: none;
  pointer-events: none;
  white-space: normal;
  line-height: 1.4;
}

.s__icons-eyebrow {
  position: absolute;
  top: calc(50% + 22vh); /* between tagline and bottom (small) icons */
  left: 50%;
  transform: translateX(-50%);
  z-index: 4;
  margin: 0;
  /* Reserve a slot for a future small-caps headline ("MOVE WITH ME" /
     "DROP IN" / etc). Kept empty + a fixed min-height so future copy
     drops in without re-jigging the icon Y offsets below. */
  min-height: 1rem;
  width: min(60vw, 360px);
  text-align: center;
  font-family: "DM Sans", system-ui, sans-serif;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: rgba(var(--tunnel-warm-rgb), 0.7);
  opacity: var(--close, 0);
  pointer-events: none;
}

/* ---------- social icons that burst from behind the LUMIN row ---------- */
/* Anchor at viewport centre (the LUMIN column origin). Each .s__icon below
   then translates by its own --icon-x / --icon-y multiplied by --close, so
   at --close=0 every icon stacks at centre (hidden behind the column LUMIN
   thanks to z-index < scene's 2) and at --close=1 they're at their corners.
   Container itself has 0×0 footprint so children can position from the
   exact centre point. */
.s__icons {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  z-index: 1; /* below .s__scene (z=2) so icons read as "behind" the letters */
  pointer-events: none;
}

/* Per-icon --size drives both width/height AND the centre-on-anchor margin.
   Two named variants:
     --lg  primary platforms (Instagram / YouTube). These get most of
           Lumin's traffic so they read as the headline buttons.
     --sm  secondary platforms (TikTok / Email). Smaller, lower visual
           weight — they live as supporting offers under the main two. */
.s__icon {
  --size: clamp(48px, 6vw, 72px);
  position: absolute;
  top: 0;
  left: 0;
  width: var(--size);
  height: var(--size);
  /* Negative margin centres the icon on the (top:0, left:0) anchor point
     so its centre — not its top-left corner — sits at viewport centre when
     --close=0. */
  margin: calc(var(--size) * -0.5) 0 0 calc(var(--size) * -0.5);
  /* Two-part transform: translate from centre to corner via --icon-x/y *
     --close, with a slight scale-up (0.55 → 1) so the burst has a "pop"
     beat instead of a flat slide. */
  transform:
    translate(
      calc(var(--icon-x) * var(--close, 0)),
      calc(var(--icon-y) * var(--close, 0))
    )
    scale(calc(0.55 + 0.45 * var(--close, 0)));
  opacity: var(--close, 0);
  pointer-events: auto;
  will-change: transform, opacity;
}

.s__icon--lg {
  --size: clamp(64px, 8vw, 96px);
}

.s__icon--sm {
  --size: clamp(40px, 4.6vw, 60px);
}

.s__icon__link {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: rgba(var(--tunnel-warm-rgb), 0.16);
  color: var(--color-primary);
  border: 1px solid rgba(var(--tunnel-warm-rgb), 0.45);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
  text-decoration: none;
}

.s__icon--lg .s__icon__link {
  /* Subtle amber halo on the primary icons so the size hierarchy isn't
     just "bigger" — IG/YT visibly glow vs TT/Email. */
  box-shadow:
    0 0 0 1px rgba(var(--tunnel-warm-rgb), 0.3),
    0 0 24px rgba(var(--tunnel-warm-rgb), 0.22);
}

.s__icon__link:hover {
  background: rgba(var(--tunnel-warm-rgb), 0.32);
  border-color: rgba(var(--tunnel-warm-rgb), 0.85);
  transform: scale(1.08);
}

.s__icon__svg {
  width: 52%;
  height: 52%;
  fill: currentColor;
}

.s__icon__name {
  /* Visually hidden — labels surface as aria-label on the link. Keep the
     element so screen readers can still anchor on it if needed. */
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.s__icon__caption {
  position: absolute;
  top: calc(100% + 0.55rem);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 18ch;
  text-align: center;
  font-family: "DM Sans", system-ui, sans-serif;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  line-height: 1.3;
  color: rgba(var(--tunnel-warm-rgb), 0.85);
  white-space: normal;
  pointer-events: none;
  /* Caption inherits the parent .s__icon's transform (which includes a
     scale 0.55 → 1), so it pops with the icon. That's intentional — the
     whole burst lands as one beat. */
}

.s__icon--sm .s__icon__caption {
  font-size: 0.62rem;
  max-width: 14ch;
  color: rgba(var(--tunnel-warm-rgb), 0.7);
}

/* Mobile: icons closer to centre + smaller because viewport is narrower
   and the docked LUMIN row already takes ~25lvh. We keep the size
   hierarchy (lg vs sm) but compress everything inward. */
@media only screen and (max-width: 640px) {
  .s__icon--lg { --size: clamp(56px, 14vw, 76px); }
  .s__icon--sm { --size: clamp(36px, 9vw, 48px); }
  .s__icons li.s__icon[style*="--icon-x: -30vw"] { --icon-x: -32vw; }
  .s__icons li.s__icon[style*="--icon-x: 30vw"]  { --icon-x: 32vw; }
  .s__icons li.s__icon[style*="--icon-x: -24vw"] { --icon-x: -28vw; }
  .s__icons li.s__icon[style*="--icon-x: 24vw"]  { --icon-x: 28vw; }
  .s__tagline {
    font-size: 0.62rem;
    letter-spacing: 0.14em;
    width: 92vw;
    top: calc(50% + 12vh);
  }
  .s__icons-eyebrow {
    top: calc(50% + 18vh);
  }
}

/* ---------- reduced motion / no GSAP fallback ---------- */

.s-work.is-reduced {
  width: auto;
  margin-left: 0;
  height: auto;
  background: var(--bg, #0c0e14);
  color: inherit;
}

.s-work.is-reduced::before,
.s-work.is-reduced::after { display: none; }
.s-work.is-reduced .s__outer { display: none; }

.work-fallback-wrap { display: none; }
.s-work.is-reduced .work-fallback-wrap { display: block; }

.work-fallback-heading {
  font-size: 1.75rem;
  margin: 0 0 1.25rem;
  text-align: center;
}

.work-fallback {
  list-style: none;
  margin: 0 auto;
  padding: 0 1.25rem 2rem;
  max-width: 640px;
  display: grid;
  gap: 0.65rem;
}

.work-fallback__link {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--border, #ddd);
  border-radius: 8px;
  background: var(--bg-elevated, #fff);
  text-decoration: none;
  color: inherit;
}

.work-fallback__link:hover { border-color: var(--accent, #e8292a); }
.work-fallback__title { font-weight: 600; }

.work-fallback__key {
  font-family: ui-monospace, monospace;
  font-size: 0.7rem;
  color: var(--text-muted, #888);
}

.work-fallback__link--feel     { border-left: 3px solid var(--feel, #4caf50); }
.work-fallback__link--ask      { border-left: 3px solid var(--ask, #2196f3); }
.work-fallback__link--evidence { border-left: 3px solid var(--evidence, #9c27b0); }
.work-fallback__link--recovery { border-left: 3px solid var(--recovery, #ff5722); }

/* Noir-red tunnel theme (.s-work--club) — performance.html only.
   Moved to css/performance.css. */
