/* ==========================================================================
   CVR — homepage.css
   Homepage-only sections: hero, statement, areas of expertise, news, projects.
   Tokens and the shared chrome live in base.css, which must be linked first.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Hero
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-content: space-between;
  padding: calc(var(--space-13) + var(--space-4)) var(--space-5) var(--space-7);
  isolation: isolate;
  overflow: hidden;
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -1;
}

.hero__media img,
.hero__media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom, rgba(1, 20, 18, 0.55) 0%, rgba(1, 20, 18, 0) 32%),
    linear-gradient(to top, rgba(1, 43, 38, 0.65) 0%, rgba(1, 43, 38, 0) 45%);
}

.hero__body {
  display: grid;
  gap: var(--space-9);
}

.hero__title {
  color: var(--color-lime);
  max-width: 18ch;
  margin-block: -0.22em;
}

.hero__aside {
  display: grid;
  justify-items: start;
  gap: var(--space-4);
  max-width: 30ch;
}

.hero__caption {
  color: var(--color-white);
}

.hero__tabs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-9);
}

.hero-tab {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: var(--space-4) var(--space-3);
  padding: var(--space-4);
  min-height: 104px;
  text-align: left;
  color: var(--color-white);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: var(--radius-md);
  background: rgba(1, 25, 22, 0.42);
  backdrop-filter: blur(8px);
  transition: background-color var(--duration-base) var(--ease),
              border-color var(--duration-base) var(--ease),
              color var(--duration-base) var(--ease);
}

.hero-tab__index {
  grid-column: 1;
  align-self: start;
}

.hero-tab__arrow {
  grid-column: 2;
  grid-row: 1;
  font-size: var(--size-body-01);
  line-height: 1;
}

.hero-tab__label {
  grid-column: 1 / -1;
  color: rgba(255, 255, 255, 0.75);
  align-self: end;
}

.hero-tab:hover {
  border-color: var(--color-lime);
}

.hero-tab.is-active {
  background: rgba(190, 208, 32, 0.82);
  border-color: var(--color-lime);
  color: var(--color-ink);
}

.hero-tab.is-active .hero-tab__label {
  color: rgba(1, 43, 38, 0.85);
}

@media (min-width: 768px) {
  .hero__body {
    grid-template-columns: 1fr auto;
    align-items: start;
    gap: var(--space-10);
  }
  .hero__aside {
    justify-self: end;
    text-align: left;
  }
}

@media (min-width: 1024px) {
  .hero {
    min-height: clamp(600px, 76svh, 820px);
    padding: var(--space-13) var(--space-9) var(--space-9);
  }
  .is-pinned .hero {
    min-height: 100svh;
  }
  .hero__tabs {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    justify-self: end;
    width: min(100%, 46%);
    margin-top: 0;
  }
  .hero-tab {
    min-height: 116px;
  }
}


/* --------------------------------------------------------------------------
   Hero zoom-through stage

   The mechanic mirrors GSAP's ScrollTrigger image-zoom: a pinned stage with the
   hero as the foreground layer and the statement behind it. As the stage is
   pinned, the hero scales and travels toward the camera through a perspective,
   then fades, uncovering the statement.

   Two differences from the GSAP version: the pin is `position: sticky` rather
   than a JS-managed pin, and scroll progress is written to custom properties
   (--zoom / --copy / --reveal) that CSS interpolates, so no library is needed.

   All of this is gated behind `.is-pinned`, which js/main.js adds only at
   >=1024px with motion allowed. Without it, hero and statement are ordinary
   stacked sections.
   -------------------------------------------------------------------------- */

.hero-stack {
  position: relative;
}

.hero-stack.is-pinned {
  height: 300svh;
}

.is-pinned .hero-stack__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  overflow: hidden;
  perspective: 500px;
}

.is-pinned .hero-stack__stage > * {
  grid-area: 1 / 1;
  min-height: 100svh;
}

/* Foreground: the hero flies through the camera and dissolves */
.is-pinned .hero {
  z-index: 2;
  transform-origin: center center;
  transform:
    translateZ(calc(var(--zoom, 0) * 350px))
    scale(calc(1 + var(--zoom, 0) * 1));
  opacity: calc(1 - var(--reveal, 0));
  will-change: transform, opacity;
}

.is-pinned.is-revealed .hero {
  pointer-events: none;
}

/* Hero copy clears out first so the zoom reads cleanly */
.is-pinned .hero__body,
.is-pinned .hero__tabs {
  opacity: calc(1 - var(--copy, 0));
  transform: translateY(calc(var(--copy, 0) * -40px));
}

/* The statement copy fades in against scroll position — the autoAlpha scrub from
   BrianCross's pen. `visibility` is part of it, not decoration: at opacity 0 the
   text must also be out of the accessibility tree and unfocusable, which is what
   GSAP's autoAlpha does and a bare opacity tween does not. */
.is-pinned .statement .container {
  opacity: var(--text, 1);
  visibility: hidden;
}

.is-pinned.is-text-visible .statement .container {
  visibility: visible;
}

/* Behind: the statement drifts up in scale as it is uncovered */
.is-pinned .statement {
  z-index: 1;
  display: grid;
  align-content: center;
  padding-block: var(--space-9);
  transform: scale(calc(1 + var(--zoom, 0) * 0.1));
  transform-origin: center center;
  will-change: transform;
}

/* --------------------------------------------------------------------------
   Statement
   -------------------------------------------------------------------------- */

.statement {
  background: var(--color-ink);
  text-align: center;
}

.statement__text {
  color: var(--color-lime);
  max-width: 24ch;
  margin-inline: auto;
  line-height: 1.6;
  text-wrap: balance;
}

.statement__action {
  margin-top: var(--space-9);
}

@media (min-width: 1024px) {
  .statement__text {
    max-width: 26ch;
  }
  .statement__action {
    margin-top: var(--space-11);
  }
}



/* --------------------------------------------------------------------------
   Areas of expertise — pinned panel navigator

   Ported from phillip-gimmi's GSAP section navigator, with one change of intent:
   that pen hijacks the wheel to step between full-screen sections. A mid-page
   section that steals scroll traps the reader, so the same three mechanics are
   driven by scroll position through a pinned stack instead:

   1. Double-wrapper clip reveal — the mask slides up from +100% while the inner
      wrapper slides down from -100%. The two cancel, so the panel's content stays
      still on screen while a window opens over it.
   2. Counter-parallax — the incoming image rides in from +10%, the outgoing one
      drifts to -10%. The media is 140% tall so neither ever exposes an edge.
   3. Character stagger — each heading character rises and fades on its own delay
      (--d, randomised once at init), eased with power2.out in calc().

   Gated behind `.is-pinned`, added by js/main.js only at >=1024px with motion
   allowed. Otherwise the three panels are ordinary stacked sections.
   -------------------------------------------------------------------------- */

.expertise-stack {
  position: relative;
}

.expertise-stack.is-pinned {
  height: 340svh;
}

.is-pinned .expertise-stack__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  overflow: hidden;
}

.is-pinned .expertise {
  grid-area: 1 / 1;
  height: 100svh;
}

.is-pinned .expertise[data-panel="0"] { z-index: 1; }
.is-pinned .expertise[data-panel="1"] { z-index: 2; }
.is-pinned .expertise[data-panel="2"] { z-index: 3; }

.is-pinned .expertise__mask,
.is-pinned .expertise__inner {
  height: 100%;
}

/* 1. The clip reveal — mask and inner cancel each other out */
.is-pinned .expertise__mask {
  transform: translateY(calc((1 - var(--p, 1)) * 100%));
  will-change: transform;
}

.is-pinned .expertise__inner {
  transform: translateY(calc((1 - var(--p, 1)) * -100%));
  min-height: 0;
  height: 100%;
  will-change: transform;
}

/* 2. Counter-parallax, with overhang so no edge is ever exposed */
.is-pinned .expertise__media {
  top: -20%;
  height: 140%;
  transform: translateY(calc((1 - var(--p, 1)) * 10% - var(--out, 0) * 10%));
  transition: none;
}

.is-pinned .expertise:hover .expertise__media {
  transform: translateY(calc((1 - var(--p, 1)) * 10% - var(--out, 0) * 10%));
}

/* 3. Per-character heading rise. --cp is the character's own progress, eased
      with power2.out expanded as 2t - t^2 so it can live in calc(). */
.expertise__char {
  display: inline-block;
  white-space: pre;
}

.is-pinned .expertise__char {
  --cp: clamp(0, calc((var(--p, 1) - var(--d, 0)) / 0.65), 1);
  --ease-cp: calc(2 * var(--cp) - var(--cp) * var(--cp));
  opacity: var(--ease-cp);
  transform: translateY(calc((1 - var(--ease-cp)) * 150%));
  will-change: transform, opacity;
}


.section-head {
  display: grid;
  gap: var(--space-5);
  align-items: center;
  margin-bottom: var(--space-9);
}

@media (min-width: 768px) {
  .section-head {
    grid-template-columns: 1fr auto;
    gap: var(--space-7);
  }
}

/* --------------------------------------------------------------------------
   News
   -------------------------------------------------------------------------- */

.news__title,
.projects__title {
  color: var(--color-lime);
  margin: 0;
}

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
}

.filter {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--size-body-03);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--color-white);
  transition: color var(--duration-fast) var(--ease);
}

.filter:hover {
  color: var(--color-lime);
}

.filter.is-active {
  color: var(--color-lime);
}


.news__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  margin-top: var(--space-9);
}
