/* ============================================================
   Site / Portfolio styles
   Fixed-camera technique:
   - body is normal flow with a tall .scroll-track to generate scroll length
   - .stage is position:fixed at 100vh; JS reads window.scrollY
   - scenes are absolutely positioned inside .stage and animated via
     CSS custom properties driven by JS (--p1, --p2, --p3 in 0..1)
   ============================================================ */

.site {
  background: var(--bg);
  color: var(--ink);
}

.stage {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  z-index: 1;
}

.scroll-track {
  /* total page travel â€” 3 segments of 100vh each */
  height: 300vh;
}

/* ============================================================
   HEADER (persistent, on top of all scenes)
   ============================================================ */
/* 100% fixed â€” visible on every scene, every scroll position */
.hdr {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  padding-top: var(--s-16);    /* Figma: 16px margin from top */
  pointer-events: none;        /* let scenes receive most clicks */
  mix-blend-mode: difference;
}
.hdr * { pointer-events: auto; } /* re-enable for links/buttons */
/* 3 zones matching Figma frame 73:
   Name  â†’ cols 1â€“3   (width 458)
   Badge â†’ cols 4â€“5   (width 308)
   Right â†’ cols 10â€“12 (width 458) */
.hdr-grid { align-items: start; }
.hdr-id      { grid-column: 1 / span 3; display: inline-block; }
.hdr-id:hover {color: var(--bg);}
.hdr-status  { grid-column: 4 / span 2; }
.hdr-right   { grid-column: 10 / span 3; }

.hdr-status .dot,
.hdr .dot {
  display: inline-block;
  width: calc(6 * var(--u)); height: calc(6 * var(--u));
  border-radius: 50%; background: var(--accent);
  margin-right: calc(10 * var(--u));
  vertical-align: middle;
  transform: translateY(calc(-2 * var(--u)));
  /* Gentle pulse â€” scale breath + soft outward ring (Figma orange) */
  animation: dot-pulse 2.2s ease-in-out infinite;
}
@keyframes dot-pulse {
  0%, 100% {
    transform: translateY(calc(-2 * var(--u))) scale(1);
    box-shadow: 0 0 0 0 rgb(39 46 255 / 55%);
  }
  60% {
    transform: translateY(calc(-2 * var(--u))) scale(0.92);
    box-shadow: 0 0 0 calc(7 * var(--u)) rgba(255, 165, 39, 0);
  }
  100% {
    transform: translateY(calc(-2 * var(--u))) scale(1);
    box-shadow: 0 0 0 0 rgba(255, 165, 39, 0);
  }
}

.hdr-right {display: flex;flex-direction: column;align-items: flex-start;gap: calc(40 * var(--u));color: var(--mute-2);}
.hdr-menu  { display: flex; gap: calc(32 * var(--u)); align-items: baseline; width: 100%; }
.hdr-menu a:hover, .hdr-menu button:hover { color: var(--ink-3); }
.hdr-lang  {font-size: var(--t-16);margin-left: auto;color: var(--mute-2);}

/* ============================================================
   Tagline + social â€” per-word reveal + scroll exit
   Each word is wrapped in <span class="word"><span class="word-inner">word</span></span>.
   â€¢ .word has overflow:hidden â€” acts as a mask
   â€¢ .word-inner starts translateY(100%) opacity .4 â€” hidden below
   â€¢ Reveals to translateY(0) opacity 1 (staggered via --i)
   â€¢ On scroll (--p1: 0â†’1), each word lifts to translateY(-18vw) and fades
   ============================================================ */
.hdr-tag {
  color: var(--ink-3);
  width: 100%;
  max-width: calc(348 * var(--u));
  font-weight: 500;
  letter-spacing: -0.5%;
}

.hdr-social {
  display: flex; flex-direction: column;
  gap: calc(4 * var(--u));
}
.hdr-social a {color: var(--ink-3);}
.hdr-social a:hover { color: var(--ink-3); }

/* Word mask wrappers */
.hdr-tag .word,
.hdr-social .word {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  line-height: inherit;
  /* allow ascenders/descenders without clipping */
  padding-bottom: calc(2 * var(--u));
  margin-bottom: calc(-2 * var(--u));
}

.hdr-tag .word-inner,
.hdr-social .word-inner {
  display: inline-block;
  will-change: transform, opacity;
  /* Words stay visible from load through the whole site.
     --hdr-exit (set by app.js) only rises at the very end of .article-above,
     so the exit slide happens exactly when the pink panel takes over. */
  transform: translateY(calc(var(--hdr-exit, 0) * -18 * var(--u)));
  opacity: calc(1 - var(--hdr-exit, 0));
  /* One-shot reveal animation runs on page load with stagger */
  animation: word-reveal 700ms cubic-bezier(0.22, 1, 0.36, 1) backwards;
  animation-delay: calc(var(--i, 0) * 35ms + 400ms);
}

@keyframes word-reveal {
  from {
    transform: translateY(100%);
    opacity: 0.4;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

/* ============================================================
   SCROLL TAG â€” Figma scroll instance: x=1438, y=1024, 76Ã—24
   In 1920Ã—1080 frame: 406px from right edge, 32px from bottom.
   Anchored to right/bottom so it stays visible at any viewport.
   Visible from the very first frame (no opacity gating).
   ============================================================ */
.scroll-tag {
  position: fixed;                  /* stays inside viewport even on narrow */
  right:  calc(406 * var(--u));     /* 1920 âˆ’ 1438 âˆ’ 76 = 406 */
  bottom: calc(32 * var(--u));      /* 1080 âˆ’ 1024 âˆ’ 24 = 32 */
  z-index: 21;                      /* above header (20) for blend mode */
  font-size: var(--t-16);
  font-weight: 500;
  line-height: calc(24 / 16);
  letter-spacing: var(--tr-tight);
  color: var(--ink-3);
  pointer-events: none;
  transition: opacity .35s ease-out;
  mix-blend-mode: difference;
}
/* Once the user moves past the hero into Selected Work, the cue is no
   longer needed â€” fade it out. */
.site[data-stage="work"] .scroll-tag,
.site[data-stage="end"]  .scroll-tag {
  opacity: 0;
}

/* ============================================================
   SCENES base â€” absolute, full-screen, stacked
   ============================================================ */
.scene {
  position: absolute;
  inset: 0;
  width: 100vw;
  height: 100vh;
  will-change: transform, opacity, clip-path;
}

/* â”€â”€â”€â”€â”€â”€â”€â”€â”€ Scene 1: Hero with L/G logo â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Animation strategy:
   --p1 = progress 0..1 of intro (0..100vh scroll)
       0    : logo small, outline only (stroke draw-in)
       0.5  : logo filled, settled
       1.0  : logo masked away (clip-path closes from bottom)
*/
.scene--hero {
  display: block;
}

/* ===== Two-layer logo intro (copied from portfolio-lg) =====
   outline + fill stacked. Each layer has its own clip-path animated
   from inset(100% 0 0 0) â†’ inset(0 0 0 0) (bottomâ†’top reveal).
   Stage 1: outline reveals  â€” delay 480ms,  dur 640ms,  ease (0.72,0,0.28,1)
   Stage 2: fill reveals over outline â€” delay 1120ms, dur 1120ms, ease (0.76,0,0.24,1)
   The outer container's clip-path is reserved for the scroll-driven exit.
   ============================================================ */

.hero-logo {
  position: absolute;
  left:  var(--s-16);
  /* Figma: logo bottom edge at y=1062 within 1080-tall frame â†’ 18px gap below */
  bottom: calc(18 * var(--u));
  width:  calc(940 * var(--u));
  /* logo-fill.svg aspect ratio matches portfolio-lg reference (888/424) */
  aspect-ratio: 888 / 424;
  height: auto;
  transform-origin: bottom left;
  will-change: clip-path;
}

.hero-logo__outline,
.hero-logo__fill {
  position: absolute;
  inset: 0;
  clip-path: inset(100% 0 0 0);
  will-change: clip-path;
  overflow: hidden;
}

.hero-logo__outline img,
.hero-logo__fill img {
  width: 100%; height: 100%;
  object-fit: fill;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

/* â”€â”€ Intro timeline kicks in once JS sets data-intro="play" â”€â”€ */
.site[data-intro="play"] .hero-logo__outline {
  animation: lg-reveal 640ms cubic-bezier(0.72, 0, 0.28, 1) 480ms forwards;
}
.site[data-intro="play"] .hero-logo__fill {
  animation: lg-reveal 1120ms cubic-bezier(0.76, 0, 0.24, 1) 1120ms forwards;
}
/* Final stable state â€” keeps logo visible even if anim was throttled */
.site[data-intro="done"] .hero-logo__outline,
.site[data-intro="done"] .hero-logo__fill {
  clip-path: inset(0 0 0 0);
  animation: none;
}

@keyframes lg-reveal {
  from { clip-path: inset(100% 0 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* â”€â”€â”€â”€â”€â”€â”€â”€â”€ Scene 2: Selected Work â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Visibility is handled per-element by the clip-mask reveal below,
   so the scene itself stays rendered â€” only pointer-events gate. */
.scene--work {
  pointer-events: none;
}
.site[data-stage="work"] .scene--work,
.site[data-stage="end"]  .scene--work {
  pointer-events: auto;
}

/* â”€â”€ Clip-mask reveal â€” Selected Work label + each case â”€â”€
   .reveal-mask clips; .reveal-inner rises in from BELOW (bottomâ†’top).
   Driven by --work-rv (a JS time tween, 0â†’1) â€” it AUTOPLAYS the whole
   reveal once the section enters view and reverses on exit. Each element
   offsets the global progress by --i so the label, then each case,
   surface one at a time. opacity 0.1 â†’ 1. */
.reveal-mask {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}
.reveal-inner {
  display: inline-block;
  /* per-element progress: each item starts slightly after the previous one
     and takes most of the global span (tight stagger â†’ fluid, not sequential).
     last item (i=9) finishes right at --work-rv = 1.0 */
  --rv: clamp(0, calc((var(--work-rv, 0) - var(--i, 0) * 0.035) / 0.69), 1);
  transform: translateY(calc((1 - var(--rv)) * 112%));
  opacity: calc(0.1 + 0.9 * var(--rv));
  /* micro-smoothing on top of the JS tween â€” absorbs tiny jitter and
     softens the final landing so it doesn't snap to translateY(0) */
  transition: transform 180ms cubic-bezier(0.16, 1, 0.3, 1),
              opacity   180ms ease-out;
}

/* Selected Work block â€” anchored bottom-left so the whole 3-row list always
   fits the viewport (Figma layout assumes 1920Ã—1080; on shorter heights the
   block stays glued to the bottom instead of being clipped).
   - Container left padding matches grid margin (16px)
   - Container bottom margin matches Figma scroll spacing (32px = 1080âˆ’1048)
   - List width = 1256px Figma (= col-span-8) */
.work-stack {
  position: absolute;
  left:   var(--s-16);
  bottom: calc(32 * var(--u));
  width:  calc(1256 * var(--u));
  max-width: calc(100vw - (2 * var(--s-16)));
  display: flex; flex-direction: column;
  gap: calc(80 * var(--u));   /* Figma: 756âˆ’628âˆ’48 = 80 */
  /* Parallax exit â€” when p3 (200â†’300vh) progresses, the whole stack
     lifts slightly and fades, handing the screen to the article. */
  transform: translateY(calc(var(--work-exit, 0) * -24 * var(--u)));
  opacity: calc(1 - var(--work-exit, 0));
}

.work-label {
  margin: 0;
  font-size: var(--t-48);
  line-height: 1;
  letter-spacing: var(--tr-tighter);
  color: var(--label);
  /* breathing room so the mask doesn't clip caps/diacritics */
  padding: calc(6 * var(--u)) 0;
  margin-top: calc(-6 * var(--u));
}

.work-list {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  column-gap: calc(32 * var(--u));
  row-gap: calc(8 * var(--u));
  font-size: var(--t-88);
  line-height: calc(92 / 88);
  letter-spacing: var(--tr-tightest);
  color: var(--ink-4);
}
.work-list li { display: inline-block; }

.work-item {
  color: var(--ink-4);
  transition: color .25s var(--ease-out);
  display: inline-block;
  position: relative;
  /* extra room so the mask doesn't clip descenders + underline */
  padding-bottom: calc(20 * var(--u));
  margin-bottom: calc(-20 * var(--u));
}
/* underline lives on the inner so it slides with the masked text */
.work-item .reveal-inner {
  text-decoration: underline;
  text-decoration-thickness: calc(2 * var(--u));
  text-underline-offset: calc(8 * var(--u));
}
.work-item span { text-decoration: none; display: inline-block; margin-left: calc(2 * var(--u)); }

/* When overlay is active, dim non-hovered items via mix-blend trick */
.scene--work.is-hovering .work-item { color: var(--white); opacity: .55; }
.scene--work.is-hovering .work-item.is-active { color: var(--white); opacity: 1; }

/* Inactive cases — no link, no hover interaction */
.work-item--inactive {
  cursor: default;
  pointer-events: none;
}
.work-item--inactive .reveal-inner { text-decoration: none; }

.scene--work.is-hovering .work-list,
.site.is-hovering .hdr,
.site.is-hovering .scroll-tag {
  mix-blend-mode: difference;
  /* Force text white so difference against dark photo inverts to light */
  color: var(--white);
}
.site.is-hovering .hdr * { color: inherit; }
.site.is-hovering .hdr-status .dot { background: var(--accent); }

/* ============================================================
   HOVER OVERLAY (bg image + blur + centered preview reveal)
   ============================================================ */
.work-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity .35s var(--ease-out);
  z-index: 1; /* under the text via stacking â€” text z-index will be higher */
}
.scene--work.is-hovering .work-overlay,
.scene--work.is-leaving  .work-overlay {
  /* keep the bg + blur visible while the mask is closing UPWARD,
     so the user actually sees the close motion (mask is 0.7s,
     opacity used to fade in 0.35s and hide it too early). */
  opacity: 1;
}

.overlay-bg {
  position: absolute; inset: 0;
  background-size: cover;
  background-position: center;
  background-image: var(--overlay-bg-url, none);
  transform: scale(1.04);
  transition: background-image .2s linear, transform 1.4s var(--ease-out);
}
.scene--work.is-hovering .overlay-bg,
.scene--work.is-leaving  .overlay-bg { transform: scale(1); }

.overlay-blur {
  position: absolute; inset: 0;
  background: rgba(255,255,255,0.01); /* #fff 1% */
  backdrop-filter: blur(var(--blur));
  -webkit-backdrop-filter: blur(var(--blur));
}

.overlay-mask {
  position: absolute;
  left: 50%; top: 50%;
  width: calc(540 * var(--u));
  height: var(--hover-img-h);
  max-height: var(--hover-img-h-max);
  transform: translate(-50%, -50%);
  overflow: hidden;
  /* Default: closed at the bottom (visible region at bottom edge, 0 height) */
  clip-path: inset(100% 0 0 0);
  transition: clip-path .7s var(--ease-out);
  border-radius: calc(2 * var(--u));
}
/* Hover out â€” mask closes UPWARD (bottom-inset grows 0 â†’ 100%) */
.scene--work.is-leaving .overlay-mask {
  clip-path: inset(0 0 100% 0);
}
/* Hover in â€” mask fully open (declared last so it wins if both classes present) */
.scene--work.is-hovering .overlay-mask {
  clip-path: inset(0 0 0 0);
}
/* Image stays fixed; only the mask's clip-path animates open/closed.
   The image does not "surf" with the reveal â€” it's revealed in place. */
.overlay-img {
  width: 100%; height: 100%;
  object-fit: cover;
}

/* keep text above overlay (overlay stays absolute, raise text via z-index) */
.work-stack { z-index: 2; }
.work-overlay { z-index: 1; }

/* ============================================================
   RESPONSIVE TWEAKS â€” keep proportions in vw, give floors on small
   ============================================================ */

@media (max-width: 1024px) {
  .hdr-menu { gap: calc(40 * var(--u)); }
  .hdr-tag { width: 100%; max-width: none; }
}

@media (max-width: 768px) {
  .hdr-grid { row-gap: calc(16 * var(--u)); }
  .hdr-right { align-items: flex-start; }
  .hdr-menu  { flex-wrap: wrap; gap: calc(20 * var(--u)); }
  .hdr-lang  { margin-left: 0; }
  .hdr-tag   { width: 100%; max-width: 100%; }

  .work-stack { gap: calc(40 * var(--u)); }
  .work-list  { font-size: calc(56 * var(--u)); gap: calc(8 * var(--u)) calc(16 * var(--u)); }

  .hero-logo { width: 92vw; height: calc(92vw * (450 / 942)); }
  .overlay-mask { width: 80vw; height: 50vw; }
}

@media (max-width: 480px) {
  .hdr { padding-top: calc(16 * var(--u)); }
  .work-stack { gap: calc(28 * var(--u)); }
  .work-list  { font-size: calc(40 * var(--u)); }
}

/* ============================================================
   PAGE TRANSITION OVERLAY â€” matches Figma Transition 0/1/2
   Layers: bg image (or #202028) + white 1% + backdrop-blur 240 +
   centered L/G logo with bottom-up clip reveal.
   States:
     default       : closed from top (inset 100% 0 0 0), pointer none
     .is-entering  : fully open (inset 0)
     .is-leaving   : closed from bottom (inset 0 0 100% 0)
   ============================================================ */

.page-trans {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  clip-path: inset(100% 0 0 0);
  transition: clip-path 1s cubic-bezier(0.76, 0, 0.24, 1);
  /* Default bg used when no image is provided (other-instance transition) */
  --trans-bg: #202028;
}
/* Pre-paint state for incoming navigation â€” overlay fully covers viewport
   from the very first frame, before JS runs (no flash of new page) */
html.page-incoming .page-trans {
  clip-path: inset(0 0 0 0);
}
html.page-incoming .page-trans__logo {
  clip-path: inset(0 0 0 0);
}
.page-trans.is-entering {
  pointer-events: auto;
  clip-path: inset(0 0 0 0);
}
.page-trans.is-leaving {
  /* Reveal new page from BOTTOM up (overlay retreats upward) */
  clip-path: inset(0 0 100% 0);
}

.page-trans__bg {
  position: absolute; inset: 0;
  background: var(--trans-bg);
  background-size: cover;
  background-position: center;
}
.page-trans__blur {
  position: absolute; inset: 0;
  background: rgba(255, 255, 255, 0.01);   /* Figma: #FFFFFF 1% */
  backdrop-filter: blur(calc(240 * var(--u)));
  -webkit-backdrop-filter: blur(calc(240 * var(--u)));
}
.page-trans__logo {
  position: absolute;
  left: 50%; top: 50%;
  width: calc(152 * var(--u));        /* Figma "beh" frame 152Ã—72 */
  aspect-ratio: 152 / 72;
  transform: translate(-50%, -50%);
  /* Logo enters with its own clip-reveal, delayed after the bg lands */
  clip-path: inset(100% 0 0 0);
  transition: clip-path 1.1s cubic-bezier(0.76, 0, 0.24, 1) .4s;
}
.page-trans.is-entering .page-trans__logo { clip-path: inset(0 0 0 0); }
.page-trans.is-leaving  .page-trans__logo {
  clip-path: inset(0 0 100% 0);
  transition-delay: 0s;
}
.page-trans__logo img {
  width: 100%; height: 100%;
  object-fit: contain;
  /* Logo SVG is dark by default â€” invert to white for the dark bg */
  filter: invert(1) brightness(1.4);
}

/* Honor reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
}
