/* ==========================================================================
   SteinTecDev — CINEMA
   --------------------------------------------------------------------------
   Layout and state for the 3D stages and the motion layer. Loaded after
   home.css / styles.css. Everything lives in the same cascade layers as the
   rest of the system, so nothing here needs !important either.

   Progressive enhancement, in three states (see fx-boot.js):
     html[data-fx="on"]       full effects
     html[data-fx="reduced"]  system asks for less motion: stages stay still
     html[data-fx="off"]      the plain static page
   ========================================================================== */

/* Micro-labels were set to clear 4.5:1 on the plain page surface. Over a lit
   3D picture the surface behind them is no longer pure black, so the faintest
   ink steps up one stop wherever effects can be on. (:root:not() outranks the
   plain :root in core.css without needing !important.) */
@layer base {
  :root:not([data-fx="off"]) {
    --ink-faint: var(--n-60);
  }
}

@layer components {
  /* ============================================================== STAGES =
     A stage is a region whose backdrop is a live WebGL canvas. The static
     poster underneath is what you see until the canvas is ready — and
     forever, if WebGL is unavailable or effects are off.
     ====================================================================== */
  .stage {
    position: relative;
    isolation: isolate;
    pointer-events: none;
  }

  .stage__canvas {
    position: absolute;
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    display: block;
    opacity: 0;
    transition: opacity 1.4s var(--ease-out);
    pointer-events: none;
  }

  .stage.is-live .stage__canvas {
    opacity: 1;
  }

  /* The element whose rectangle the 3D subject is composed into. It also
     takes the drag. `pan-y` keeps vertical page scrolling alive on touch. */
  [data-stage-anchor] {
    touch-action: pan-y;
  }

  html:is([data-fx="on"], [data-fx="reduced"]):not(.no-3d) [data-stage-anchor] {
    cursor: grab;
  }

  [data-stage-anchor].is-dragging {
    cursor: grabbing;
  }

  html[data-fx="off"] .stage__canvas {
    display: none;
  }

  /* Over the lit hero the transparent navigation sits on shafts of light. A
     soft fade of the page colour from the top edge keeps its small type at a
     steady contrast; it dissolves as the bar turns glassy on scroll. */
  html:not([data-fx="off"]) .nav::before {
    content: "";
    position: absolute;
    inset: 0 0 -1.5rem;
    z-index: -1;
    pointer-events: none;
    background: linear-gradient(
      to bottom,
      color-mix(in oklab, var(--surface-deep) 78%, transparent),
      transparent
    );
    transition: opacity var(--dur-3) var(--ease);
  }

  html:not([data-fx="off"]) .nav.is-scrolled::before {
    opacity: 0;
  }

  /* The section rail floats over every scene. A soft pool of darkness behind
     it (not a box — it fades out on every side) keeps its numerals at a steady
     contrast whatever is lit behind them. */
  .rail::before {
    content: "";
    position: absolute;
    inset: -1.4rem -1.4rem -1.4rem -0.9rem;
    z-index: -1;
    pointer-events: none;
    background: radial-gradient(
      closest-side,
      color-mix(in oklab, var(--surface-deep) 86%, transparent) 55%,
      transparent
    );
  }

  /* ============================================================ POSTER =
     The brand mark, large, as the still frame of the hero. */
  .poster-mark {
    inline-size: min(100%, 17rem);
    block-size: auto;
    margin: auto;
    transition: opacity 0.3s var(--ease);
  }

  .poster-mark .pm-ink {
    stroke: var(--ink);
  }

  .poster-mark .pm-accent {
    stroke: var(--accent);
  }

  /* Once the 3D hero is live the poster steps aside. */
  .hero:has(.stage.is-live) .poster-mark {
    opacity: 0;
  }

  /* ================================================================ HERO */
  .hero {
    isolation: isolate;
    overflow: clip;
  }

  .stage--hero {
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    /* about one screen of picture; the rest of the hero is the credit strip,
       which sits on the plain page colour */
    block-size: min(100%, max(100vh, 46rem));
    block-size: min(100%, max(100svh, 46rem));
    z-index: -1;
    /* the picture dissolves into the page instead of ending on an edge */
    mask-image: linear-gradient(to bottom, #000 0%, #000 78%, transparent 100%);
  }

  /* Keeps the headline legible over the lit scene. */
  .hero:has(.stage.is-live)::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background: linear-gradient(
      90deg,
      color-mix(in oklab, var(--surface-deep) 78%, transparent) 0%,
      color-mix(in oklab, var(--surface-deep) 52%, transparent) 34%,
      transparent 62%
    );
  }

  .hero:has(.stage.is-live)::before {
    opacity: 0;
  }

  /* ============================================================ SHOWCASE =
     The apps stage: a full-bleed 3D picture between the section head and
     the cards. The picture dissolves into the section on all four sides. */
  .showcase {
    position: relative;
    isolation: isolate;
    block-size: clamp(31rem, 82vh, 54rem);
    block-size: clamp(31rem, 82svh, 54rem);
    margin-block-end: clamp(2rem, 4vw, 3.5rem);
  }

  .stage--apps {
    position: absolute;
    inset: 0;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 14%, #000 86%, transparent 100%),
      linear-gradient(to right, transparent 0, #000 9%, #000 91%, transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image: linear-gradient(to bottom, transparent 0, #000 14%, #000 86%, transparent 100%),
      linear-gradient(to right, transparent 0, #000 9%, #000 91%, transparent 100%);
    mask-composite: intersect;
  }

  .showcase__hit {
    position: absolute;
    inset-block: 0;
    inset-inline: 0;
    margin-inline: auto;
    /* leaves the bottom strip to the picker so it never steals a drag */
    inset-block-end: 4.75rem;
    padding-inline: 0;
  }

  .showcase__poster {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: clamp(1.5rem, 6vw, 5rem);
    transition: opacity 0.3s var(--ease);
  }

  .showcase__poster img {
    inline-size: clamp(3rem, 8vw, 5.5rem);
    block-size: auto;
    opacity: 0.55;
  }

  .stage.is-live .showcase__poster {
    opacity: 0;
  }

  .showcase__bar {
    position: absolute;
    inset-inline: 0;
    inset-block-end: 0;
    margin-inline: auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3) var(--sp-5);
    padding-block-end: 0.25rem;
    pointer-events: none;
  }

  .showcase__hint {
    font-family: var(--face-mono);
    font-size: var(--t-2xs);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-faint);
  }

  .picker {
    display: inline-flex;
    gap: 0.5rem;
    pointer-events: auto;
  }

  .picker__btn {
    display: inline-flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.6rem 0.95rem 0.6rem 0.7rem;
    border: 1px solid var(--hair-strong);
    border-radius: var(--r-pill);
    background: color-mix(in oklab, var(--surface-deep) 78%, transparent);
    color: var(--ink-2);
    font-size: var(--t-ui);
    font-weight: 500;
    white-space: nowrap;
    transition: border-color var(--dur-2) var(--ease),
      background-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease);
  }

  .picker__btn img {
    inline-size: 1.5rem;
    block-size: 1.5rem;
    opacity: 0.8;
    transition: opacity var(--dur-2) var(--ease);
  }

  .picker__ver {
    font-size: var(--t-2xs);
    color: var(--ink-faint);
  }

  .picker__btn:hover {
    border-color: var(--hair-bright);
    color: var(--ink);
  }

  /* the global focus ring squares off the corners; keep the pill a pill */
  .picker__btn:focus-visible,
  .fxbtn:focus-visible {
    border-radius: var(--r-pill);
  }

  .picker__btn:hover img {
    opacity: 1;
  }

  .picker__btn[aria-pressed="true"] {
    border-color: var(--accent-hair);
    background: color-mix(in oklab, var(--accent) 12%, var(--surface-deep));
    color: var(--ink);
  }

  .picker__btn[aria-pressed="true"] img {
    opacity: 1;
  }

  @media (max-width: 40rem) {
    .picker {
      inline-size: 100%;
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .picker__btn {
      flex-direction: column;
      gap: 0.3rem;
      padding: 0.6rem 0.4rem;
      border-radius: var(--r-md);
      font-size: var(--t-xs);
      white-space: normal;
      text-align: center;
    }

    .picker__ver {
      display: none;
    }

    .showcase__hint {
      display: none;
    }

    .showcase__hit {
      inset-block-end: 6.25rem;
    }
  }

  /* the card that matches the phone on stage */
  .app.is-linked {
    border-color: var(--accent-hair);
    box-shadow: var(--edge), var(--lift-2),
      0 0 0 1px color-mix(in oklab, var(--accent) 22%, transparent);
  }

  .product-detail__row.is-linked .product-detail__visual {
    border-color: var(--accent-hair);
  }

  /* No stage → no showcase. The cards carry all the information. */
  html:not([data-fx]) .showcase,
  html[data-fx="off"] .showcase,
  html.no-3d .showcase,
  .showcase:has(.stage.is-failed) {
    display: none;
  }

  /* ============================================================ MANIFEST =
     Rules on the left, the stones on the right. The stage is sticky, so the
     stones stay in view while the four rules scroll past; fx.js marks the
     rule on the reading line and the matching stone lights up.
     The 3D layout only applies while the stage can exist (effects on, WebGL2
     present, JavaScript running) — otherwise the rules keep their original
     full-width layout.
     ====================================================================== */
  html:is([data-fx="on"], [data-fx="reduced"]):not(.no-3d) .manifest {
    display: grid;
    gap: clamp(1.5rem, 4vw, 3.5rem);
    grid-template-columns: minmax(0, 1fr);
  }

  html:is([data-fx="on"], [data-fx="reduced"]):not(.no-3d) .manifest__stage {
    position: relative;
    order: -1;
    block-size: clamp(15rem, 42vh, 22rem);
    block-size: clamp(15rem, 42svh, 22rem);
  }

  html:is([data-fx="on"], [data-fx="reduced"]):not(.no-3d) .stage--stones {
    position: absolute;
    inset: 0;
    -webkit-mask-image: radial-gradient(ellipse 54% 54% at 50% 50%, #000 42%, transparent 100%);
    mask-image: radial-gradient(ellipse 54% 54% at 50% 50%, #000 42%, transparent 100%);
  }

  .manifest__hit {
    position: absolute;
    inset: 0;
  }

  html:not([data-fx]) .manifest__stage,
  html[data-fx="off"] .manifest__stage,
  html.no-3d .manifest__stage {
    display: none;
  }

  @media (min-width: 62rem) {
    html:is([data-fx="on"], [data-fx="reduced"]):not(.no-3d) .manifest {
      grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr);
      align-items: start;
    }

    html:is([data-fx="on"], [data-fx="reduced"]):not(.no-3d) .manifest__stage {
      order: 0;
      position: sticky;
      inset-block-start: 6rem;
      block-size: min(38rem, calc(100vh - 8rem));
      block-size: min(38rem, calc(100svh - 8rem));
    }

    /* the rules lose their aside column — the stones are the aside now */
    html:is([data-fx="on"], [data-fx="reduced"]):not(.no-3d) .manifest .rule-item {
      grid-template-columns: 3.25rem minmax(0, 1fr);
      gap: var(--sp-4);
    }

    html:is([data-fx="on"], [data-fx="reduced"]):not(.no-3d) .manifest .rule-item__body {
      grid-template-columns: minmax(0, 1fr);
      gap: var(--sp-3);
    }
  }

  /* the rule on the reading line */
  .rule-item::before {
    content: "";
    position: absolute;
    inset-block: -1px;
    inset-inline-start: -0.9rem;
    inline-size: 2px;
    background: var(--accent);
    scale: 1 0;
    transform-origin: top;
    transition: scale var(--dur-4) var(--ease-out);
  }

  .rule-item.is-active::before {
    scale: 1 1;
  }

  .rule-item .rule-item__n {
    transition: color var(--dur-3) var(--ease);
  }

  .rule-item.is-active .rule-item__n {
    color: var(--accent-hi);
  }

  /* ================================================================= CTA = */
  .cta {
    isolation: isolate;
  }

  .stage--finale {
    position: absolute;
    inset: 0;
    z-index: -1;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 22%, #000 100%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 22%, #000 100%);
  }

  .cta__mark {
    display: grid;
    place-items: center;
    block-size: clamp(15rem, 42vh, 24rem);
    block-size: clamp(15rem, 42svh, 24rem);
    margin-block-end: var(--sp-6);
  }

  .cta:has(.stage.is-live) .poster-mark {
    opacity: 0;
  }

  .cta:has(.stage.is-live)::before {
    opacity: 0;
  }

  /* ========================================================== FX TOGGLE =
     In the navigation, next to the language switcher: a small, honest
     off-switch for everything in this file. Icon only on the desktop bar,
     icon + label in the mobile drawer. */
  .fxbtn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    block-size: 2rem;
    min-inline-size: 2rem;
    padding-inline: 0.55rem;
    border: 1px solid var(--hair-strong);
    border-radius: var(--r-pill);
    background: transparent;
    color: var(--ink-3);
    font-size: var(--t-xs);
    transition: color var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease);
  }

  .fxbtn:hover {
    color: var(--ink);
    border-color: var(--hair-bright);
  }

  .fxbtn[aria-pressed="true"] {
    color: var(--accent);
    border-color: var(--accent-hair);
  }

  .fxbtn__label {
    display: none;
  }

  .fxbtn__slash {
    opacity: 0;
    transition: opacity var(--dur-2) var(--ease);
  }

  .fxbtn[aria-pressed="false"] .fxbtn__slash {
    opacity: 1;
  }

  @media (max-width: 56rem) {
    .fxbtn {
      inline-size: 100%;
      block-size: auto;
      justify-content: flex-start;
      padding: 0.7rem 0.85rem;
      border: 0;
      border-radius: var(--r-sm);
      font-size: var(--t-md);
    }

    .fxbtn__label {
      display: inline;
    }
  }

  /* ========================================================== CARD LIGHT =
     Cards follow the pointer: a soft light under the cursor and a few
     degrees of tilt. Fine pointers only; fx.js sets the variables.
     `transform` is free for this because the lifts use `translate`. */
  @media (hover: hover) and (pointer: fine) {
    .fx-card {
      isolation: isolate;
    }

    .quicknav__item.fx-card {
      position: relative;
    }

    .fx-card::after {
      content: "";
      position: absolute;
      inset: 0;
      z-index: -1;
      border-radius: inherit;
      pointer-events: none;
      background: radial-gradient(
        340px circle at var(--mx, 50%) var(--my, 50%),
        color-mix(in oklab, var(--accent) 13%, transparent),
        transparent 62%
      );
      opacity: var(--glow, 0);
    }

    html:not([data-fx="off"]):not([data-fx="reduced"]) .fx-card {
      transform: perspective(1100px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
    }

    html[data-fx="off"] .fx-card::after {
      display: none;
    }
  }

  /* ========================================================= WORD REVEAL =
     Headings rise out of a mask, word by word. fx.js wraps the words; the
     markup stays an ordinary heading until then (and for ever, if effects
     are off). */
  .fx-words .w {
    display: inline-block;
    overflow: clip;
    vertical-align: top;
    padding: 0.08em 0.1em 0.16em;
    margin: -0.08em -0.1em -0.16em;
  }

  .fx-words .wi {
    display: inline-block;
    translate: 0 112%;
    transition: translate 1s var(--ease-out);
    transition-delay: calc(var(--i, 0) * 55ms);
  }

  .fx-words.fx-in .wi {
    translate: 0 0;
  }

  /* A subtitle, not a label: it appears once the intro has played, stays for a
     few seconds, and is gone. */
  .hero__hit {
    position: relative;
  }

  .hero__hint {
    position: absolute;
    inset-inline-end: 0;
    inset-block-end: 0.25rem;
    font-family: var(--face-mono);
    font-size: var(--t-2xs);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-faint);
    opacity: 0;
    pointer-events: none;
  }

  /* stacked layout: the mark fills the box down to its reflection, so the
     hint moves to the empty top corner instead */
  @media (max-width: 67.99rem) {
    .hero__hint {
      inset-block: 0.25rem auto;
    }
  }

  html:not([data-fx="off"]) .hero:has(.stage.is-live) .hero__hint {
    animation: hint-in-out 11s var(--ease) 2.4s both;
  }

  @keyframes hint-in-out {
    0% {
      opacity: 0;
    }
    8%,
    80% {
      opacity: 1;
    }
    100% {
      opacity: 0;
    }
  }

  /* ======================================================== HERO SCROLL CUE */
  .hero__scroll {
    display: none;
  }

  @media (min-width: 68rem) {
    html:is([data-fx="on"], [data-fx="reduced"]) .hero__scroll {
      position: absolute;
      inset-inline-start: 50%;
      inset-block-start: calc(max(100vh, 46rem) - 5.25rem);
      inset-block-start: calc(max(100svh, 46rem) - 5.25rem);
      translate: -50% 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 0.6rem;
      font-family: var(--face-mono);
      font-size: var(--t-2xs);
      letter-spacing: 0.18em;
      text-transform: uppercase;
      color: var(--ink-faint);
      pointer-events: none;
    }

    .hero__scroll i {
      display: block;
      inline-size: 1px;
      block-size: 2.75rem;
      background: linear-gradient(to bottom, var(--hair-bright), transparent);
      position: relative;
      overflow: hidden;
    }

    .hero__scroll i::after {
      content: "";
      position: absolute;
      inset-inline: 0;
      inset-block-start: 0;
      block-size: 40%;
      background: linear-gradient(to bottom, transparent, var(--accent));
      animation: scroll-cue 2.4s var(--ease-in-out) infinite;
    }

    @keyframes scroll-cue {
      0% {
        translate: 0 -110%;
      }
      70%,
      100% {
        translate: 0 260%;
      }
    }
  }

  /* keeps the headline and the button legible over the sun's glow */
  .cta:has(.stage.is-live)::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background: linear-gradient(
      to bottom,
      transparent 24%,
      color-mix(in oklab, var(--surface-deep) 84%, transparent) 50%
    );
  }

  /* ===================================================== WERKSTATT METERS =
     The depth gauges charge up one segment at a time when their card
     scrolls in. Without effects they are simply filled, as before. */
  html[data-fx="on"] .cap .meter i {
    transition: background-color 0.5s var(--ease), box-shadow 0.6s var(--ease);
    transition-delay: calc(var(--seg, 0) * 110ms + 320ms);
  }

  html[data-fx="on"] .meter i:nth-child(2) { --seg: 1; }
  html[data-fx="on"] .meter i:nth-child(3) { --seg: 2; }
  html[data-fx="on"] .meter i:nth-child(4) { --seg: 3; }
  html[data-fx="on"] .meter i:nth-child(5) { --seg: 4; }

  @media screen {
    html[data-fx="on"] .cap:not(.is-in) .meter i {
      background: var(--hair-bright);
      box-shadow: none;
    }
  }

  html[data-fx="on"] .cap.is-in .meter[data-level] i:nth-child(-n + 3) {
    box-shadow: 0 0 7px color-mix(in oklab, var(--accent) 55%, transparent);
  }

  html[data-fx="on"] .cap.is-in .meter[data-level="4"] i:nth-child(4),
  html[data-fx="on"] .cap.is-in .meter[data-level="5"] i:nth-child(4),
  html[data-fx="on"] .cap.is-in .meter[data-level="5"] i:nth-child(5) {
    box-shadow: 0 0 7px color-mix(in oklab, var(--accent) 55%, transparent);
  }

  /* ======================================================== ROWS SCRUB IN =
     The log, the status board and the figures ease in with the scroll
     position (scroll-driven animation, no scripting). Where the browser has
     no support they are just there. */
  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      html[data-fx="on"] .log__entry,
      html[data-fx="on"] .now__item,
      html[data-fx="on"] .figure {
        animation: row-in linear both;
        animation-timeline: view();
        animation-range: entry 4% entry 55%;
      }

      @keyframes row-in {
        from {
          opacity: 0;
          translate: 0 1.25rem;
        }
      }
    }
  }

  /* ====================================================== HERO PARALLAX =
     Pure CSS, no scripting: as the opening shot scrolls away, the headline
     block drifts up a little slower than the page. */
  @supports (animation-timeline: scroll()) {
    @media (prefers-reduced-motion: no-preference) {
      html[data-fx="on"] .hero__lede {
        animation: lede-drift linear both;
        animation-timeline: scroll(root block);
        animation-range: 0px 90svh;
      }

      @keyframes lede-drift {
        to {
          translate: 0 -3.5rem;
        }
      }
    }
  }

  /* ===================================================================
     PRINT — the 3D layer has no place on paper. */
  @media print {
    .stage,
    .showcase,
    .manifest__stage,
    .hero__scroll,
    .fxbtn,
    .poster-mark {
      display: none;
    }

    /* headings that have not been scrolled to yet must still print */
    .fx-words .wi {
      translate: none;
    }
  }
}

/* ============================================================ PAGE CHANGES =
   Same-document view transitions between the pages that carry this file:
   the outgoing page dims and recedes, the next one settles in. Browsers
   without support simply navigate as before. Outside the cascade layers
   because at-rules for view transitions cannot live inside one. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }

  ::view-transition-old(root) {
    animation: 240ms cubic-bezier(0.32, 0.72, 0, 1) both vt-out;
  }

  ::view-transition-new(root) {
    animation: 520ms cubic-bezier(0.16, 1, 0.3, 1) both vt-in;
  }

  html[data-fx="off"]::view-transition-old(root),
  html[data-fx="off"]::view-transition-new(root) {
    animation: none;
  }
}

@keyframes vt-out {
  to {
    opacity: 0;
    scale: 0.985;
  }
}

@keyframes vt-in {
  from {
    opacity: 0;
    scale: 1.012;
  }
}
