
/* ================================================================
   TrailaHero — ported verbatim from dist-package/TrailaHero.astro
   (28 Aug 2026). Both of the component's <style> blocks, concatenated
   and otherwise unmodified. All selectors are namespaced under
   .traila-hero, so no Astro scoping is required. Do NOT retune the
   band here: the look is locked (see dist-package/README.md).
   ================================================================ */

/* --- component <style> (was Astro-scoped) --- */
  /* Traila hero – warm greyscale + brand red, light theme only (G1-locked).
   * Adapted from ../src/style.css. Custom properties are scoped to
   * `.traila-hero` rather than `:root` so this component never leaks
   * variables into the host site's global scope.
   *
   * Raleway 300/400/700 is assumed loaded by the consuming site – see
   * README.md's "install steps" for the Google Fonts <link> tags. The
   * fallback stack below carries the layout if it isn't. */

  .traila-hero {
    --warm-50: #f7f4f2;
    --warm-200: #d8d3ce;
    --warm-400: #9e9790;
    --trace-dark: #565048;
    --ink: #2a2622;
    --red: #e52a12;

    position: relative;
    width: 100%;
    height: 100vh;
    min-height: 640px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--warm-50);
    color: var(--ink);
    font-family: "Raleway-Traila", "Segoe UI", system-ui, sans-serif;
    font-weight: 300;
    -webkit-font-smoothing: antialiased;
    box-sizing: border-box;
  }

  .traila-hero *,
  .traila-hero *::before,
  .traila-hero *::after {
    box-sizing: border-box;
  }

  .traila-hero__logo {
    position: absolute;
    top: 32px;
    left: 48px;
    z-index: 5;
    height: 40px;
    width: auto;
    display: block;
    pointer-events: none;
  }

  .traila-hero__stack {
    width: fit-content;
    max-width: 92vw;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }

  /* Wrapper carries layout (width/margin); typographic properties are set
   * here too and inherit down into whatever the `headline` slot supplies –
   * plain inheritance reaches slotted content reliably, but overriding a
   * slotted <h1>'s own UA-stylesheet font-size/weight/margin needs an
   * explicit :global() rule below, since Astro's scoping attribute is
   * never applied to elements the consuming page renders into the slot. */
  .traila-hero__headline {
    width: 100%;
    margin: 0 0 26px;
    font-family: "Raleway-Traila", "Segoe UI", system-ui, sans-serif;
    font-weight: 300;
    font-size: clamp(1.5rem, 2.6vw, 2.3rem);
    line-height: 1.2;
    letter-spacing: -0.01em;
    color: var(--ink);
  }

  .traila-hero__headline h1 { /* was :global(h1) in Astro; unwrapped for plain CSS */
    margin: 0;
    font: inherit;
    letter-spacing: inherit;
    color: inherit;
  }

  /* --- video card --- */

  .traila-hero__video-wrap {
    position: relative;
    height: min(50vh, calc(min(1040px, 86vw) * 0.5625));
    aspect-ratio: 16 / 9;
    overflow: hidden;
    border-radius: 0; /* squared 28.08 (Lucas: no rounded corners) */
    box-shadow:
      0 30px 80px rgba(26, 24, 22, 0.18),
      0 2px 10px rgba(26, 24, 22, 0.1);
    background: #e7e2dd;
  }

  .traila-hero__video,
  .traila-hero__canvas {
    display: block;
    width: 100%;
    height: 100%;
  }

  .traila-hero__video {
    object-fit: cover;
    border-radius: 0; /* squared 28.08 (Lucas: no rounded corners) */
  }

  .traila-hero__placeholder:not([hidden]) {
    display: block;
    position: absolute;
    inset: 0;
    border-radius: 0; /* squared 28.08 (Lucas: no rounded corners) */
    background: repeating-linear-gradient(
      135deg,
      #e2ddd7,
      #e2ddd7 12px,
      #dad4cd 12px,
      #dad4cd 24px
    );
  }

  /* --- spectrogram band, width-locked to the video card --- */

  .traila-hero__canvas-wrap {
    position: relative;
    width: 100%;
    /* Version-2 pick (Lucas, 26.08 late): the COMPOSITION solves at the
       gallery version's 2.83 reference aspect, but the BOX is cropped 16%
       at the top (Lucas 28.08, whitespace cut): 2.83/(1-0.16) = 3.37. The
       script's TOP_CROP shifts the camera target so the cut comes off the
       top. */
    aspect-ratio: 3.37;
    height: auto;
    margin-top: 14px;
    overflow: hidden; /* axes live INSIDE the band */
  }

  .traila-hero__canvas {
    position: absolute;
    inset: 0;
  }

  /* --- controls (audio toggle + caption) --- */

  .traila-hero__controls {
    width: 100%;
    margin-top: 14px;
    display: flex;
    align-items: baseline;
    gap: 16px;
  }

  .traila-hero__unmute {
    appearance: none;
    cursor: pointer;
    border: 1px solid var(--warm-200);
    background: rgba(247, 244, 242, 0.82);
    color: #4d4843;
    font-family: "Raleway-Traila", sans-serif;
    font-weight: 400;
    font-size: 0.85rem;
    letter-spacing: 0.02em;
    border-radius: 999px;
    padding: 10px 20px;
    white-space: nowrap;
    transition: border-color 160ms ease;
  }

  .traila-hero__unmute:hover,
  .traila-hero__unmute:focus-visible {
    border-color: var(--warm-400);
    outline: none;
  }

  .traila-hero__unmute[aria-pressed="true"] {
    border-color: var(--red);
    color: var(--ink);
  }

  .traila-hero__caption {
    margin: 0;
    font-family: "Raleway-Traila", sans-serif;
    font-weight: 300;
    font-size: 0.85rem;
    color: #716c67;
  }

  @media (max-width: 720px) {
    .traila-hero__logo {
      left: 20px;
      top: 20px;
      height: 32px;
    }

    .traila-hero__stack {
      max-width: calc(100vw - 32px);
    }

    .traila-hero__video-wrap {
      height: auto;
      width: calc(100vw - 32px);
    }

    /* No canvas-wrap height override: the band's aspect-ratio scales with
       the stack width on its own (v2 responsive behaviour). */

    .traila-hero__controls {
      flex-direction: column;
      align-items: flex-start;
      gap: 10px;
    }
  }

/* --- component <style is:global> --- */
  /* Axis overlay (built at runtime by src/axes.ts, so Astro's scoped-style
     attribute never reaches these elements) - every rule is namespaced
     under .traila-hero to keep it out of the host site's global scope.
     Mirrors the lab's style.css axis block. */

  .traila-hero .hero__axes {
    position: absolute;
    inset: 0;
    pointer-events: none;
    font-family: "Raleway-Traila", "Segoe UI", system-ui, sans-serif;
    font-weight: 400;
    font-size: 10px;
    letter-spacing: 0.03em;
    color: #9e9790;
    z-index: 4;
  }

  /* Frequency labels sit INSIDE the right edge, superimposed on the render:
     muted warm text with a soft pale shadow reads on both the light floor
     and the red crests. Hairline points outward to the edge. */
  .traila-hero .hero__axis-freq {
    position: absolute;
    right: 12px;
    /* Crisp type (28.08): even line-height so translateY(-50%) is whole-
       pixel (tops rounded in axes.ts) + ONE tight shadow — the 6px halo
       read as blur. */
    line-height: 12px;
    transform: translateY(-50%);
    white-space: nowrap;
    text-align: right;
    color: #716c67;
    text-shadow: 0 0 2px rgba(247, 244, 242, 0.95);
  }

  .traila-hero .hero__axis-freq::before {
    content: "";
    position: absolute;
    left: calc(100% + 4px);
    top: 50%;
    width: 8px;
    height: 1px;
    background: rgba(113, 108, 103, 0.5);
  }

  /* The squeal marker is the one datum that must read on ANY ground (the
     terrain skyline moves through its row) - a quiet pale chip guarantees
     it; the grey labels keep the lighter halo treatment. */
  .traila-hero .hero__axis-squeal {
    color: #e52a12;
    background: rgba(247, 244, 242, 0.85);
    padding: 1px 6px;
    border-radius: 3px;
    text-shadow: none;
  }

  .traila-hero .hero__axis-squeal::before {
    background: #e52a12;
    opacity: 0.7;
  }

  /* Time ticks: INSIDE the band's bottom edge - hairline rising from the
     edge, label just above it, superimposed on the render. */
  .traila-hero .hero__axis-time {
    position: absolute;
    left: 0;
    bottom: 0;
    width: 0;
  }

  .traila-hero .hero__axis-time i {
    position: absolute;
    left: 0;
    bottom: 0;
    width: 1px;
    height: 7px;
    background: rgba(113, 108, 103, 0.45);
  }

  .traila-hero .hero__axis-time span {
    position: absolute;
    left: 0;
    bottom: 10px;
    transform: translateX(-50%);
    color: #716c67;
    text-shadow:
      0 0 2px rgba(247, 244, 242, 0.95),
      0 0 6px rgba(247, 244, 242, 0.85);
  }

  .traila-hero .hero__axis-caption {
    position: absolute;
    font-size: 10px;
    color: #9e9790;
    text-shadow:
      0 0 2px rgba(247, 244, 242, 0.95),
      0 0 6px rgba(247, 244, 242, 0.85);
  }

  .traila-hero .hero__axis-caption--time {
    left: 12px;
    bottom: 8px;
  }

  .traila-hero .hero__axis-caption--freq {
    right: 12px;
    text-align: right;
    white-space: nowrap;
  }

  /* Ticks-on-dark (the ink-wash band is a solid dark mass at its bottom
     edge, so the time ticks read as light text with ONE tight shadow —
     the crisp-ticks fix, lab 27.08; axes.ts adds the modifier class when
     ticksDark is set). */
  .traila-hero .hero__axes--ticksdark .hero__axis-time span,
  .traila-hero .hero__axes--ticksdark .hero__axis-caption--time {
    color: #d8d3ce;
    text-shadow: 0 1px 1px rgba(16, 14, 13, 0.9);
  }

  .traila-hero .hero__axes--ticksdark .hero__axis-time i {
    background: rgba(216, 211, 206, 0.55);
  }

  /* Centre playhead (28.08, Lucas-confirmed): downward ink triangle at the
     Frequency-caption level (axes.ts sets top), whisper hairline running
     down from it. Ink, never red (red = squeal-data channel only). */
  .traila-hero .hero__axis-playhead {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 1px;
    background: linear-gradient(
      to bottom,
      rgba(42, 38, 34, 0.3),
      rgba(216, 211, 206, 0.5)
    );
  }

  .traila-hero .hero__axis-playhead i {
    position: absolute;
    top: 0;
    left: -5px; /* centre the 11px glyph on the 1px line */
    width: 0;
    height: 0;
    border-left: 5.5px solid transparent;
    border-right: 5.5px solid transparent;
    border-top: 7px solid rgba(42, 38, 34, 0.55);
  }

/* ================================================================
   PAGE INTEGRATION OVERRIDES — not part of the ported component.
   Everything above this line is dist-package/TrailaHero.astro verbatim;
   keep it that way so it stays diffable against the package.

   The component assumes it owns the whole viewport (its own absolutely
   positioned logo, height: 100vh). This page puts a 68px sticky header
   above it, so an untouched 100vh pushes the audio toggle and caption
   below the fold, which is exactly the control the hero exists to offer.
   Subtracting the header height is layout only: it changes nothing about
   the band's locked composition, which solves from its own aspect ratio.
   ================================================================ */
:root { --traila-header-h: 69px; } /* .site-header .wrap height 68px + 1px border */

/* The component is built to BE the viewport: height 100vh, its own absolutely
   positioned logo, overflow hidden. This page has a 68px sticky header above it
   and an eyebrow/sub/CTA band below it, so 100vh either pushes the audio toggle
   (the whole point of the hero) past the fold, or clips it inside the
   component's own overflow: hidden on short windows.

   Two rejected fixes, for the record:
     - height: calc(100vh - header) alone still clipped the controls at
       1280x720 and 1440x780, because the video is sized at 50vh of the FULL
       viewport.
     - also scaling the video to 50% of the hero made the video narrower than
       the headline's max-content width, so .traila-hero__stack's fit-content
       sized to the HEADLINE and the band came out wider than the video,
       breaking the package's core width lock.

   So: content-height hero. The video keeps the package's own sizing, the band
   stays locked to the video's width, nothing is clipped, and the section is as
   tall as it needs to be, which is what the rest of this page does anyway.
   Layout only. No change to the band's locked composition. */
.traila-hero {
  height: auto;
  min-height: 0;
  /* Tight vertical padding on purpose: the audio toggle is the hero's one
     interaction, so it wants to sit as close to the fold as the rhythm allows. */
  padding: clamp(1.25rem, 2.4vw, 2rem) 0 clamp(1.75rem, 3vw, 2.5rem);
}

/* Width lock. .traila-hero__stack is width: fit-content, and the headline is a
   width: 100% block whose MAX-CONTENT width still feeds that intrinsic sizing.
   Whenever the video comes out narrower than the headline's unwrapped text
   (50vh * 16/9 < headline max-content, e.g. 1280x720 and 1440x780) the stack
   sizes to the HEADLINE, and the band ends up 20-40px wider than the video.
   That breaks the package's core promise that the band is locked to the video's
   exact width. Pre-existing in the package, not introduced by the port.

   width: 0 takes the headline out of the parent's intrinsic width calculation;
   min-width: 100% gives it back its rendered width. The video is then the only
   thing that sizes the stack, at every viewport. */
.traila-hero__headline {
  width: 0;
  min-width: 100%;

  /* Lucas 28.08: the headline moved from the component's slot ABOVE the video
     to below the band, and went bold. It sits between the band and the audio
     toggle, so the band still lands directly beneath the video at its exact
     width, which is the composition the package locks. The component's own
     margin (0 0 26px) was sized for sitting above the video; below it, the
     space belongs on top instead. */
  font-weight: 700;
  margin: 22px 0 4px;
}


/* =====================================================================
   DARK / PURE-BLACK VARIANT (Lucas, 06.09). Everything above this line is
   the vendored light stylesheet, untouched, so it stays diffable.

   Pairs with hero-spectro-dark.js. Ship the two together: the ground here
   and the engine's `clear` tunable there must BOTH be #000. uClear is not
   just gl.clearColor, it is also the colour the terrain fogs toward at
   distance and the colour the floor mixes out of, so a mismatch shows up
   as a visible rectangle AND a terrain fading to the wrong colour.

   THE RED IS NOT INVERTED. Squeal caps and the "3.6 kHz · squeal" chip stay
   #E52A12: red comes from the bake's own squeal-ness channel, not the
   palette. Only the ground and the ink swap. On light, loud crests darken
   toward ink over warm-50; on black, they lighten toward warm grey.
   ===================================================================== */

.traila-hero {
  --warm-50: #000000;  /* = the engine's clear colour in hero-spectro-dark.js */
  --warm-200: #4d4843;
  --warm-400: #716c67;
  --ink: #f7f4f2;
}

/* The video card's placeholder tone: light in the original, which flashes
   pale before the first video frame paints on a black page. */
.traila-hero__video-wrap {
  background: #141212;
  box-shadow:
    0 30px 80px rgba(0, 0, 0, 0.7),
    0 2px 10px rgba(0, 0, 0, 0.45);
}

.traila-hero__unmute {
  background: rgba(0, 0, 0, 0.72);
  color: #d8d3ce;
}
.traila-hero__unmute[aria-pressed="true"] {
  color: var(--ink);
}

.traila-hero__caption {
  color: #9e9790;
}

/* --- axis layer ------------------------------------------------------ */
/* NOTE on :not(.hero__axis-squeal): axes.ts gives the squeal chip BOTH
   classes ("hero__axis-freq hero__axis-squeal"), so an unqualified
   .hero__axis-freq colour rule outranks the chip's own #e52a12 and turns
   the red squeal marker grey — the exact opposite of the brief. Exclude it. */

.traila-hero .hero__axis-freq:not(.hero__axis-squeal) {
  color: #bfb9b3;
  text-shadow: 0 0 2px rgba(0, 0, 0, 0.95);
}

.traila-hero .hero__axis-freq:not(.hero__axis-squeal)::before {
  background: rgba(191, 185, 179, 0.5);
}

/* Red text untouched; only the pill behind it inverts. */
.traila-hero .hero__axis-squeal {
  background: rgba(0, 0, 0, 0.85);
}

.traila-hero .hero__axis-caption {
  text-shadow: 0 0 2px rgba(0, 0, 0, 0.95);
}

/* Light runs the playhead ink-at-top into light-at-bottom. On black both
   ends are dark, so it stays light the whole way down. */
.traila-hero .hero__axis-playhead {
  background: linear-gradient(
    to bottom,
    rgba(216, 211, 206, 0.35),
    rgba(216, 211, 206, 0.6)
  );
}

.traila-hero .hero__axis-playhead i {
  border-top-color: rgba(216, 211, 206, 0.6);
}

/* =====================================================================
   OVERLAY (Lucas, 06.09): headline + controls sit ON the tram video, and
   "where it hurts" gets the Traila laser gradient back.

   Done with grid, no magic numbers. The stack's four children are laid on
   a single column: the VIDEO SPANS ROWS 1-3 while the headline takes row 2
   and the controls row 3, so both sit inside the video's own box, stacked,
   bottom-aligned by row 1 absorbing the slack as 1fr. The band keeps its
   own row underneath. Nothing here depends on measuring the headline or
   the controls, so it holds at any viewport and through text wrapping.
   ===================================================================== */

.traila-hero__stack {
  display: grid;
  grid-template-columns: 100%;
  grid-template-rows: 1fr auto auto auto;
}

.traila-hero__video-wrap  { grid-area: 1 / 1 / 4 / 2; }
.traila-hero__headline    { grid-area: 2 / 1 / 3 / 2; z-index: 3; }
.traila-hero__controls    { grid-area: 3 / 1 / 4 / 2; z-index: 3; }
.traila-hero__canvas-wrap { grid-area: 4 / 1 / 5 / 2; }

/* Inset from the video's edges. The component's own margins assumed the
   text sat outside the card. */
.traila-hero__headline {
  margin: 0;
  padding: 0 clamp(20px, 2.6vw, 40px) 12px;
}
.traila-hero__controls {
  margin: 0;
  padding: 0 clamp(20px, 2.6vw, 40px) clamp(18px, 2.2vw, 30px);
}

/* Scrim. The r7 footage is a WHITE tram: without this the headline and the
   caption sit on near-white and disappear. Bottom-weighted so the top of
   the frame stays clean. pointer-events off so the unmute button, which is
   above it at z-index 3, still takes clicks. */
.traila-hero__video-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(0, 0, 0, 0) 38%,
    rgba(0, 0, 0, 0.45) 62%,
    rgba(0, 0, 0, 0.82) 86%,
    rgba(0, 0, 0, 0.94) 100%
  );
}

/* On the video the caption reads better a touch brighter than it did on
   the page ground. */
.traila-hero__caption {
  color: #d8d3ce;
}

/* The laser-swoosh gradient on the h1 accent, as on the landing page.
   One of the three permitted gradient uses. The vendored package leaves
   <em> italic and inheriting; the landing page normalises it to the
   gradient, and this restores that. */
.traila-hero__headline em {
  font-style: normal;
  background: linear-gradient(90deg, #ed6b41 0%, #e52a12 50%, #e72c61 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Shadow insurance: the scrim handles the average frame, but the r7 loop
   passes through near-white door and window panels, and the text sits on
   whatever happens to be under it at that moment. */
.traila-hero__headline,
.traila-hero__caption {
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.65);
}

/* Below the package's own 720px breakpoint the video is width-driven and
   short (calc(100vw - 32px) at 16:9, about 200px tall on a phone), while
   the text block is roughly that tall on its own: overlaying buries the
   tram completely. So under 720px the overlay is OFF and the layout falls
   back to the package's own flow — video, band, headline, controls — which
   is what shipped and was verified before this change. */
@media (max-width: 720px) {
  .traila-hero__stack {
    display: flex;
    flex-direction: column;
  }

  .traila-hero__video-wrap,
  .traila-hero__headline,
  .traila-hero__controls,
  .traila-hero__canvas-wrap {
    grid-area: auto;
  }

  .traila-hero__video-wrap::after { display: none; } /* text is outside now */

  .traila-hero__headline {
    padding: 0;
    margin: 22px 0 4px;
    text-shadow: none;
  }

  .traila-hero__controls { padding: 0; }

  .traila-hero__caption {
    color: #9e9790;
    text-shadow: none;
  }
}
