/* ---------------------------------------------------------------------------
 * hero-cinema.css — the scroll-driven cinematic hero.
 *
 * Repo-owned, copied into site/ by deploy.sh:apply_hero() and injected into
 * index.html. NEVER edit the copy in site/ — it is wiped on every deploy.
 *
 * Companion files: hero/hero-cinema.html (the markup and the copy) and
 * hero/hero-cinema.js (the drive loop, the canvas, the text split).
 *
 * The direction, said out loud: the signature element is the automation graph
 * itself. George ships n8n workflows and AI agents, so the hero does not show a
 * stock "AI" clip; it builds one of his pipelines while you scroll. Nodes land,
 * edges draw, then payloads run the graph and it settles into a pipeline that
 * keeps humming. Everything else on the stage stays quiet so the graph reads.
 *
 * The accent (#ff8300) appears in rare doses only: the live payloads, the ring
 * on the node that is currently working, the primary button, focus rings.
 *
 * Two visual modes, and the five gates that pick between them are duplicated
 * character for character in hero-cinema.js:
 *   1. (max-width: 720px)
 *   2. (orientation: portrait) and (max-width: 1024px)
 *   3. (orientation: portrait) and (pointer: coarse)
 *   4. (orientation: landscape) and (pointer: coarse) and (max-height: 560px)
 *   5. (prefers-reduced-motion: reduce)
 * Any match ships the composed static hero instead of the scrub. The static
 * hero is a designed layout, not an apology: the finished graph, the headline,
 * both calls to action.
 * ------------------------------------------------------------------------- */

.gc {
  /* Height IS the scroll distance. Scroll range is this minus the 100vh sticky
   * stage, so 300vh of travel across three bands: roughly a 108vh plateau for
   * the opening beat and 96vh each for the other two, which is what the flick
   * test wanted. At 340vh every beat came in under the 80vh floor. */
  position: relative;
  height: 400vh;
  /* The stage paints the environment; the section itself must not clip the
   * sticky child. */
  isolation: isolate;
}

.gc__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  min-height: 520px;
  overflow: hidden;
  display: grid;
  align-items: center;
}

/* The environment layer. One canvas behind everything, so scrolling the hero
 * feels like moving through a place rather than past a picture. */
.gc__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* Its own compositor layer so its repaints never drag the page. */
  transform: translateZ(0);
  will-change: transform;
}

/* The veil. --gc-veil is written by hero-cinema.js from the page's own resolved
 * background, so it tracks the light/dark theme switch instead of guessing. */
.gc__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(
      to right,
      var(--gc-veil, rgba(13, 12, 11, 0.92)) 0%,
      var(--gc-veil, rgba(13, 12, 11, 0.92)) 30%,
      var(--gc-veil-soft, rgba(13, 12, 11, 0.4)) 56%,
      transparent 78%
    ),
    /* A floor under the scroll cue and the buttons, so neither ever lands on a
     * bright node. */
    linear-gradient(to top, var(--gc-veil-soft, rgba(13, 12, 11, 0.4)), transparent 26%);
}

/* --------------------------------------------------------------------------
 * The text lane. It owns the left of the frame; the graph is composed to live
 * on the right, so the action lane stays clear at every width.
 * ------------------------------------------------------------------------ */
.gc__lane {
  position: relative;
  z-index: 2;
  width: min(100%, 1320px);
  margin: 0 auto;
  padding: 0 clamp(20px, 5vw, 72px);
  /* Two rows: every band stacks in the first one (only one is legible at a
   * time), the buttons own the second. Stacking the buttons in the same cell as
   * the bands put them under the settle's subline. */
  display: grid;
  grid-template-rows: auto auto;
  row-gap: clamp(28px, 4vh, 48px);
}

/* Bands stack in the same grid cell: only one is ever legible at a time, and
 * the crossfade between them is what makes the hero read as one shot. */
.gc__band {
  grid-area: 1 / 1;
  align-self: end;
  max-width: 40rem;
}

.gc__band {
  opacity: 0; /* JS owns this; 0 keeps the pre-JS frame from stacking all three */
  pointer-events: none;
}

/* Deliberately NOT pointer-events: auto. The bands and the button row share one
 * grid cell, so a headline character span would otherwise sit over the buttons
 * and swallow the click. Nothing inside a band is interactive, so the band stays
 * transparent to the pointer for its whole life; the class is kept as the hook
 * for anything that needs to know which beat is being read. */

.gc__title,
.gc .ga-lede__title {
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 500;
  /* Bigger than the rest of the site's headings on purpose. This is the one
   * full-viewport moment on the page, and at the page's normal 38px cap the
   * type read as a paragraph floating on a wide dark frame. 1440px -> 52px. */
  font-size: clamp(1.75rem, 1.1rem + 2.2vw, 3.25rem);
  line-height: 1.3;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin: 0;
  max-width: 19ch;
  text-wrap: balance;
}

/* The rule above the headline grows with it. */
.gc .ga-lede__title::before {
  width: 60px;
  margin-bottom: 28px;
}

.gc__sub {
  font-size: clamp(0.9375rem, 0.87rem + 0.3vw, 1.0625rem);
  line-height: 1.6;
  color: var(--body);
  max-width: 48ch;
  margin: 20px 0 0;
}

/* Band 1 hosts the relocated <h1> block. It already carries its own type from
 * hero-motion.css; here it only needs its outer spacing neutralised. */
.gc__band--lede .ga-lede {
  padding: 0;
  margin: 0;
  max-width: none;
  /* The scroll drive owns its visibility now, so the fade-in transition that
   * hero-motion.css gives it must not fight the band opacity. */
  opacity: 1;
  transform: none;
  transition: none;
}

/* --------------------------------------------------------------------------
 * Text choreography. Every entrance is transform and opacity only, scrubbed off
 * the band's --k (0 to 1 assembly progress written by the drive loop), and
 * fully reversible. Band 1 is the one designed exception: it also has a
 * one-time load ramp so the hero opens with words already on screen.
 * ------------------------------------------------------------------------ */
.gc__title[data-split] .w {
  display: inline-block;
  white-space: pre;
}

.gc__title[data-split] .c {
  display: inline-block;
  will-change: transform, opacity;
}

/* (b) Grid snap-align — characters slide into place in reading order. It echoes
 * what the graph is doing under them: nodes snapping onto their grid. */
.gc__band[data-entrance="grid"] .c,
.gc__band--lede .ga-lede__title .c {
  --kc: clamp(0, (var(--k, 0) - var(--th, 0)) * 2.6, 1);
  opacity: var(--kc);
  transform: translateX(calc((1 - var(--kc)) * var(--jx, 0px)));
}

/* (d) Word-punch with overshoot — echoes each payload landing on a node. */
.gc__band[data-entrance="punch"] .w {
  --kc: clamp(0, (var(--k, 0) - var(--th, 0)) * 3, 1);
  --ks: clamp(0, (var(--k, 0) - var(--th, 0) - var(--sd, 0.1)) * var(--ss, 3.2), 1);
  opacity: var(--kc);
  transform: scale(calc(0.6 + (0.4 + var(--ov, 0.12)) * var(--kc) - var(--ov, 0.12) * var(--ks)));
}

.gc__band[data-entrance="punch"] .w.em {
  --ov: 0.24;
  --sd: 0.16;
  --ss: 2.6;
}

/* (e) Word-by-word rise into a staged settle — the ending entrance. Headline
 * words rise, then the subline, then the buttons. Three arrivals, one band. */
.gc__band[data-entrance="rise"] .w {
  --kc: clamp(0, (var(--k, 0) - var(--th, 0)) * 2.8, 1);
  opacity: var(--kc);
  transform: translateY(calc((1 - var(--kc)) * 18px));
}

.gc__band[data-entrance="rise"] .gc__sub {
  --ks: clamp(0, (var(--k, 0) - 0.66) * 4, 1);
  opacity: var(--ks);
  transform: translateY(calc((1 - var(--ks)) * 10px));
}

/* The subline of the other bands rides the band's own assembly. */
.gc__band[data-entrance="punch"] .gc__sub,
.gc__band--lede .ga-lede__sub {
  --ks: clamp(0, (var(--k, 0) - 0.5) * 3, 1);
  opacity: var(--ks);
  transform: translateY(calc((1 - var(--ks)) * 10px));
}

/* --------------------------------------------------------------------------
 * The calls to action. They live outside the bands so the static hero can show
 * them beside band 1; in the scrub they arrive last, on band 3's tail (--kb).
 * ------------------------------------------------------------------------ */
.gc__cta {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  grid-row: 2;
  grid-column: 1;
  align-self: start;
  max-width: 40rem;
  z-index: 3;
  opacity: var(--kb, 0);
  transform: translateY(calc((1 - var(--kb, 0)) * 12px));
  pointer-events: none;
}

.gc__cta[data-live="1"] {
  pointer-events: auto;
}

.gc__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 15px 26px;
  border-radius: 8px;
  font-size: 15px;
  font-weight: 500;
  text-decoration: none;
  border: 1px solid var(--cardBorder);
  background: var(--card);
  color: var(--ink);
  transition: transform 200ms var(--ga-ease-out), background-color 200ms ease,
    border-color 200ms ease;
}

.gc__btn--primary {
  background: var(--accentBtn);
  border-color: transparent;
  color: #141413;
}

.gc__btn:hover {
  transform: translateY(-2px);
}

.gc__btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
 * The scroll cue. It says the hero is scrubbed, then gets out of the way; JS
 * writes --cue from progress. Hidden entirely on short screens, where it has
 * no room and would collide with the buttons.
 * ------------------------------------------------------------------------ */
.gc__cue {
  position: absolute;
  z-index: 2;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  opacity: var(--cue, 1);
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--body);
  pointer-events: none;
}

.gc__cue-line {
  width: 1px;
  height: 34px;
  background: linear-gradient(to bottom, var(--accent), transparent);
  transform-origin: top center;
  animation: gc-cue 2.8s var(--ga-ease-in-out) -0.9s infinite;
}

@keyframes gc-cue {
  0%, 100% { transform: scaleY(0.35); opacity: 0.45; }
  45%      { transform: scaleY(1);    opacity: 1; }
}

@media (max-height: 620px) {
  .gc__cue { display: none; }
}

/* --------------------------------------------------------------------------
 * The static hero. The five gates, character for character identical to the
 * GATES array in hero-cinema.js.
 * ------------------------------------------------------------------------ */
@media (max-width: 720px),
  (orientation: portrait) and (max-width: 1024px),
  (orientation: portrait) and (pointer: coarse),
  (orientation: landscape) and (pointer: coarse) and (max-height: 560px),
  (prefers-reduced-motion: reduce) {
  .gc {
    height: auto;
  }

  .gc__stage {
    position: relative;
    height: auto;
    min-height: 0;
    padding: clamp(32px, 8vw, 64px) 0 clamp(40px, 9vw, 72px);
  }

  .gc__lane {
    display: block;
  }

  /* Only the composed frame: the headline block and the buttons. */
  .gc__band:not(.gc__band--lede) {
    display: none;
  }

  .gc__band--lede {
    opacity: 1 !important;
    max-width: none;
  }

  .gc__band--lede .c,
  .gc__band--lede .w,
  .gc__band--lede .ga-lede__sub {
    opacity: 1 !important;
    transform: none !important;
  }

  .gc__cta {
    margin: 32px 0 0;
    opacity: 1 !important;
    transform: none !important;
    pointer-events: auto;
  }

  .gc__cue {
    display: none;
  }

  /* The graph still paints, once, in its finished state — behind a flat veil
   * rather than the left-to-right gradient the wide layout uses. */
  .gc__scrim {
    background: var(--gc-veil, rgba(13, 12, 11, 0.9));
  }

  .gc__canvas {
    opacity: 0.85;
  }
}

/* Reduced motion, honoured completely: no cue pulse, no button travel. */
@media (prefers-reduced-motion: reduce) {
  .gc__cue-line { animation: none; }
  .gc__btn { transition: none; }
  .gc__btn:hover { transform: none; }
}

/* Without scripting there is no split, no drive loop and no canvas paint, so
 * the block must simply read as a normal hero. */
@media (scripting: none) {
  .gc { height: auto; }
  .gc__stage { position: relative; height: auto; padding: 48px 0; }
  .gc__lane { display: block; }
  .gc__band { opacity: 1; }
  .gc__band + .gc__band { margin-top: 28px; }
  .gc__cta { opacity: 1; transform: none; pointer-events: auto; margin: 32px 0 0; }
  .gc__canvas, .gc__cue { display: none; }
}

/* Touch targets, added without changing layout (the buttons are already
 * inline-flex and centred, so nothing shifts). */
@media (pointer: coarse) {
  .gc__btn { min-height: 44px; }
}
