/* solstice-hero.css — the scroll-scrubbed portrait hero.
 *
 * Every rule is scoped to .solstice-hero. `.page-hero` is shared with the
 * peripheral pages (pricing, about, contact, trust), which legitimately want
 * the quiet header treatment — landing.css:541-545 says so explicitly, and
 * widening a selector here would restyle four pages nobody asked about.
 *
 * Tokens only, no raw hex. Note that `npm run lint:css` only scans files
 * literally named styles.css, so this file is not covered by it — the rule
 * still holds, it just has to hold by hand.
 */

.solstice-hero {
  /* Taller than the standard homepage hero (clamp(420px, 58vh, 640px)) so a
   * portrait frame reads properly beside the copy rather than being squeezed
   * into a letterbox — but still short enough that the label strip below shows
   * a sliver above the fold. A hero that fills the viewport exactly reads as a
   * dead end; a hero that is slightly overrun tells you to keep going. */
  min-height: clamp(520px, 68vh, 660px);
}

/* Two columns instead of one centred stack. The copy keeps the page's own
 * left edge — the 1180px measure's 24px gutter, the same line "List a Piece
 * Once" starts on below — rather than being pushed to the viewport edge.
 * Alignment with the rest of the page is what makes it read as deliberate. */
.solstice-hero .section-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-6);
  align-items: center;
  /* width:100% is load-bearing. A grid container is sized by its tracks, so
   * with an `auto` media column the box shrank to 1122px inside its 1180px
   * max-width — and `margin: 0 auto` then centred that narrower box, putting
   * the copy's left edge at x=63 while every section below it started at x=34.
   * Measured live; the computed max-width read a correct 1180px throughout,
   * which is exactly the class of bug only a screenshot catches. */
  width: 100%;
}

.solstice-hero__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/* The measure cap that landing.css applies to the homepage hero's children
 * lives on the copy column's children here, so the grid column — not the text
 * — decides where the media starts. */
.solstice-hero__copy > * {
  max-width: 46rem;
}

/* ── Media ──────────────────────────────────────────────────────────── */
.solstice-hero__media {
  position: relative;
  z-index: 1;
  /* Bounded by the hero's own height as well as its width. A 9:16 frame at
   * 420px wide is 746px tall — measured live, that overran the fold and pushed
   * the copy down the page. Capping the height and letting the width follow
   * keeps the frame inside the hero at every viewport. The 0.72 factor is the
   * 3:4 frame plus the timecode line beneath it. */
  width: min(var(--solstice-media-w, 420px), 38vw, calc((68vh - var(--space-8)) * 0.72));
}

.solstice-hero__video {
  display: block;
  width: 100%;
  height: auto;
  /* 3:4 rather than the source's 9:16. The clip is a head-and-shoulders
   * portrait — the full 9:16 frame is mostly background above the hat and
   * below the shoulder, and object-fit: cover crops to the face, which is the
   * part that carries the motion. */
  aspect-ratio: 3 / 4;
  object-fit: cover;
  /* 22% -> 35%. The 3:4 frame on a 9:16 source crops 25% of every clip
   * (measured: it shows source rows 71..1030 of 1280 at 22%), and 22% was
   * tuned for ONE clip — the original head-and-shoulders portrait. The heroes
   * uploaded since are full-figure compositions, where a top-weighted window
   * cut the subject's head at the top and their feet at the bottom.
   *
   * 35% chosen by rendering all three configured clips at 22/35/50 and
   * comparing: at 22% the guitar figure loses its head, at 50% the solstice
   * portrait loses its hat, and 35% is the only value where no clip loses its
   * subject. It is a compromise across compositions, not an optimum for any
   * one of them — the real fix for a full-figure clip is a 9:16 frame, which
   * costs either width or hero height. */
  object-position: center 35%;
  /* Media frames are 8px and crisp — the three-tier radius vocabulary. */
  border-radius: var(--radius-media);
  border: 1px solid var(--yeja-border);
  /* The artwork floats over the masonry backdrop, so it earns a lift. This is
   * the "floating" tier, the lightest of the five sanctioned shadows. */
  box-shadow: 0 1px 4px color-mix(in oklch, var(--yeja-gray-1000) 12%, transparent);
  background: var(--yeja-canvas-paper);
}

/* ── Play control (touch only) ──────────────────────────────────────── */
/* Touch has no wheel, so it gets neither the scroll-to-play nor the scrub —
 * without this the hero is a still poster with no way in. Centred over the
 * frame rather than tucked in a corner: it is the primary action on that
 * surface, not a secondary toggle like the sound button.
 *
 * The runtime only ever unhides this on a coarse pointer, so there is no
 * desktop media query to fight — it simply never appears there. */
.solstice-hero__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 56px, above the 44px floor: it sits over artwork rather than chrome, and
   * it is the one control a thumb goes looking for. */
  width: 56px;
  height: 56px;
  border-radius: var(--radius-circle, 50%);
  border: none;
  /* INK, not paper. The first version was 88% canvas with a 1px border, and
   * over a pale painting it vanished — the border is a --yeja-border grey that
   * disappears against light artwork, and a near-white disc on a near-white
   * frame has nothing to separate it. Screenshot caught it; the computed
   * values were all exactly as authored.
   *
   * The ink/paper inversion is the system's own answer to "a control that must
   * read on anything": guide §1 uses it for every solid control precisely
   * because it does not depend on what is underneath. A scrim over imagery is
   * still a color-mix over a ramp token, never rgba(). */
  background: color-mix(in oklch, var(--yeja-gray-1000) 72%, transparent);
  color: var(--yeja-gray-0);
  /* Lifts the disc off busy artwork so its edge is legible even where the
   * frame behind it happens to be dark too. */
  box-shadow: 0 1px 4px color-mix(in oklch, var(--yeja-gray-1000) 24%, transparent);
  cursor: pointer;
  transition: opacity var(--motion-default);
}
.solstice-hero__play:hover { opacity: 0.85; }
.solstice-hero__play:focus-visible {
  outline: 2px solid var(--yeja-focus);
  outline-offset: 2px;
}
.solstice-hero__play[hidden] { display: none; }

/* One button, two glyphs — same class-toggle contract as the sound control.
 * The play triangle is optically centred, not geometrically: a triangle's
 * visual mass sits left of its bounding box, so a mathematically centred one
 * reads as off-centre. 2px is the usual correction. */
.solstice-hero__play-icon,
.solstice-hero__replay-icon { display: none; line-height: 0; }
.solstice-hero__play:not(.is-replay) .solstice-hero__play-icon {
  display: block;
  transform: translateX(2px);
}
.solstice-hero__play.is-replay .solstice-hero__replay-icon { display: block; }

/* ── Sound toggle ───────────────────────────────────────────────────── */
/* A real button, not an overlay div: it must be reachable and operable by
 * keyboard, because it is the only way to stop audio the visitor did not ask
 * for (WCAG 1.4.2). Circular, per the icon-button convention. */
.solstice-hero__sound {
  position: absolute;
  inset-inline-end: var(--space-2);
  bottom: var(--space-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44px: the pointer-target floor, and it keeps the glyph off the frame edge. */
  width: 44px;
  height: 44px;
  border-radius: var(--radius-circle, 50%);
  border: 1px solid var(--yeja-border);
  background: var(--yeja-canvas);
  color: var(--yeja-text-primary);
  cursor: pointer;
  transition: opacity var(--motion-default);
}

.solstice-hero__sound:hover { opacity: 0.85; }
.solstice-hero__sound:focus-visible {
  outline: 2px solid var(--yeja-focus);
  outline-offset: 2px;
}
/* [hidden] needs restating because the display above would otherwise win. */
.solstice-hero__sound[hidden] { display: none; }

/* Which glyph shows is a class toggle, not a markup swap: both ship, one
 * displays. Swapping innerHTML on every toggle would re-parse SVG for no
 * reason and lose the element identity a transition could hang off. */
.solstice-hero__sound-on,
.solstice-hero__sound-off { display: none; line-height: 0; }
.solstice-hero__sound.is-muted .solstice-hero__sound-off { display: block; }
.solstice-hero__sound:not(.is-muted) .solstice-hero__sound-on { display: block; }

/* The inline glyphs replace Material Symbols ligatures, which never resolve
 * here — renderPageShell() links Inter and JetBrains Mono only. 20px is the
 * platform's chrome icon size. */
.solstice-hero__glyph {
  display: block;
  width: 20px;
  height: 20px;
  flex: none;
}

/* ── Timecode and hint ──────────────────────────────────────────────── */
/* Mono, muted, unembellished: anything measurable is mono, and this is a
 * position in a clip. 12.5px with 0.04em tracking is the metadata row spec.
 *
 * It sits UNDER the video rather than in the copy column: it is a readout of
 * the media, so it belongs to the media. In the copy column it read as part of
 * the marketing stack, sandwiched between the waitlist note and the scroll
 * hint, where a running counter competes with the CTA for attention. */
.solstice-hero__timecode {
  margin-top: var(--space-2);
  font-family: var(--font-family-mono);
  font-size: 12.5px;
  letter-spacing: 0.04em;
  color: var(--yeja-text-muted);
  /* Digits must not reflow the line as they change. */
  font-variant-numeric: tabular-nums;
  text-align: end;
}
.solstice-hero__timecode[hidden] { display: none; }

.solstice-hero__hint {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-top: var(--space-1);
  font-size: 0.875rem;
  color: var(--yeja-text-muted);
}
.solstice-hero__hint[hidden] { display: none; }

/* ── Mobile ─────────────────────────────────────────────────────────── */
/* One column, media below the copy. There is no wheel to scrub with on touch,
 * so the runtime leaves the poster frame — the layout just has to stop
 * pretending there are two columns. */
@media (max-width: 767px) {
  .solstice-hero .section-inner {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
  }
  .solstice-hero__media {
    /* The desktop height cap is meaningless once the frame is below the copy
     * rather than beside it. */
    width: 100%;
    max-width: 420px;
  }
  .solstice-hero__video {
    /* Was 5:4 to save fold space, which crops 55% of a 9:16 source — it held a
     * face and nothing else. That was tolerable for the original
     * head-and-shoulders clip and wrong for everything uploaded since: at 5:4
     * the guitar figure is beheaded at every focal point, and the Klimt
     * portrait loses her face entirely below 45%.
     *
     * 3:4 matches desktop and shows 75% of the frame. Rendered all three
     * configured clips at 5:4, 1:1 and 3:4 to choose: 3:4 is the only ratio
     * where no subject loses its head. It costs ~180px of height on a phone,
     * which the stacked layout can afford — the copy leads there anyway. */
    aspect-ratio: 3 / 4;
    object-position: center 35%;
  }
  /* A counter that cannot move is noise. There is no wheel on touch, so the
   * clip never scrubs and the timecode would read 00:00:00 forever. */
  .solstice-hero__timecode { display: none; }
}

/* ── Reduced motion ─────────────────────────────────────────────────── */
/* This is the fallback for when the SCRIPT fails to load, not a duplicate of
 * the JS hard stop — cat-gaze.css:85 makes the same distinction for the same
 * reason. Without the script the video has no autoplay attribute and so holds
 * its poster anyway; this guarantees the affordances do not sit there inviting
 * an interaction that will never be answered. */
@media (prefers-reduced-motion: reduce) {
  .solstice-hero__hint { display: none; }
}
