/* ---- Full-viewport panel — hero and stacked-panel template ---- */

.panel {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  color: var(--color-paper);
}

/* Shorter hero for category/listing pages (e.g. projects) — full-height
   100svh stays the default for the homepage's scroll-sequence panels. */
.panel--short {
  min-height: 80svh;
}

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

.panel__bg img,
.panel__bg video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
}

/* iframe embeds (e.g. a YouTube background) can't use object-fit — this is
   the standard oversize-and-center substitute so the embedded player fills
   .panel__bg edge-to-edge the same way img/video above do. */
.panel__bg iframe {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100vw;
  height: 56.25vw; /* 16:9 */
  min-width: 177.78vh; /* 16:9 */
  min-height: 100%;
  transform: translate(-50%, -50%);
  border: 0;
  pointer-events: none;
}

/* Gradient overlay — anchors heading legibility at the bottom and keeps
   the nav area readable at the top without obscuring the image */
.panel__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.42) 0%,
    rgba(0, 0, 0, 0.04) 45%,
    rgba(0, 0, 0, 0.16) 100%
  );
}

.panel__content {
  position: relative;
  z-index: 1;
  width: 100%;
  padding-top: var(--header-height);
  padding-bottom: 6vh;
  text-align: center;
}

@media (min-width: 768px) {
  .panel__content {
    padding-bottom: 7vh;
  }
}

@media (min-width: 1024px) {
  .panel__content {
    padding-bottom: 8vh;
  }
}

.panel__heading + .panel__cta {
  display: inline-block;
  margin-top: var(--space-8);
}

/* ---- Filter links (category/listing page heroes, e.g. projects) ----
   Plain underlined toggle buttons, not boxed buttons — reuses .btn--text's
   underline treatment, laid out as a centered row instead of a single CTA.
   These are real <button>s with aria-pressed (not <a> + aria-current) since
   they toggle a filter state in place rather than navigate, so the
   permanent-underline-when-active rule lives here rather than reusing
   button.css's generic [aria-current="true"] one. */
.panel__heading + .panel__filters {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-6);
  margin-top: var(--space-8);
}

/* Active/inactive states — dim to the same rgba(255,255,255,0.55) treatment
   .panel__eyebrow already uses for de-emphasized text over this photography.
   No font-weight change on the active link — this row is justify-content:
   center, so a weight-driven width change would re-center (and visibly
   jitter) the whole row on every click. */
.panel__filter-link {
  color: rgba(255, 255, 255, 0.55);
  transition: color var(--duration-base) var(--ease-standard);
}

.panel__filter-link:hover,
.panel__filter-link:focus-visible {
  color: rgba(255, 255, 255, 0.85);
}

.panel__filter-link[aria-pressed="true"] {
  color: var(--color-paper);
}

.panel__filter-link[aria-pressed="true"]::after {
  transform: scaleX(1);
  transform-origin: left;
}

/* ---- Scroll cue (panel 1 only) ----
   Replaces the CTA slot with a passive "keep scrolling" affordance instead
   of a link — decorative only, so it's aria-hidden rather than announced.

   Two independent pieces, deliberately NOT nested together:
   - .panel__scroll-cue-label lives inside [data-panel-text] like the rest
     of the hero copy, so it drifts/fades with the heading (see
     TEXT_ANIMATION in panel-text.js).
   - .panel__scroll-cue-line is a sibling of .panel__content, positioned
     directly against .panel (not .panel__content, which has padding-bottom
     eating into the space, and not the animated wrapper, which would drag
     the line's position around with the GSAP scroll-drift transform). Its
     bottom edge is pinned to the true bottom of the viewport at all times,
     independent of how far the text above has scrolled/faded away. */
.panel__heading + .panel__scroll-cue-label {
  display: block;
  margin-top: var(--space-6);
  color: rgba(255, 255, 255, 0.75);
}

.panel__scroll-cue-line {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  z-index: 1;
  width: 1px;
  /* Matches .panel__content's padding-bottom at each breakpoint exactly
     (both anchored to the same panel bottom edge) so the line's top always
     lands right at the text's bottom edge — a fixed px height here would
     drift out of sync with that viewport-relative padding and leave a gap
     or overlap depending on viewport height. */
  height: 6vh;
  background: rgba(255, 255, 255, 0.25);
  overflow: hidden;
}

@media (min-width: 768px) {
  .panel__scroll-cue-line {
    height: 7vh;
  }
}

@media (min-width: 1024px) {
  .panel__scroll-cue-line {
    height: 8vh;
  }
}

/* A brighter segment travels the length of the line on a loop, drawing the
   eye downward — the animation itself, not just the static line, is the
   "look, scroll" cue. */
.panel__scroll-cue-line::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.95);
  transform: translateY(-100%);
  animation: panel-scroll-cue-drip 1.8s ease-in-out infinite;
}

@keyframes panel-scroll-cue-drip {
  0%   { transform: translateY(-100%); }
  60%  { transform: translateY(100%); }
  100% { transform: translateY(100%); }
}

@media (prefers-reduced-motion: reduce) {
  .panel__scroll-cue-line::after {
    animation: none;
    transform: translateY(0);
  }
}

/* ---- Eyebrow slot ----
   Fixed height (not min-height) + overflow: hidden so every panel's slot —
   populated or empty — occupies the exact same box: one line at the
   .eyebrow type style's own font-size/line-height. This keeps headings
   aligned across panels regardless of eyebrow content, and keeps Panel 1's
   heading/CTA at their existing resting position, since .panel__content
   is bottom-anchored (align-items: flex-end on .panel) — anything added
   above the heading shifts the whole content block up by the same amount
   it grows, so the heading's absolute position is unaffected. */
.panel__eyebrow {
  display: block;
  height: calc(var(--font-size-2) * var(--line-height-normal));
  overflow: hidden;
  margin-bottom: var(--space-2);
  /* .eyebrow's default color is a dark ink tone for light backgrounds;
     panels sit on photography, so override to match the heading's white
     (same treatment as .sg-cover .eyebrow in style-guide.css). */
  color: rgba(255, 255, 255, 0.55);
}

/* ---- Scroll-driven panel sequence (applied by JS via .panel-sequence--ready) ----
   Default state (no .panel-sequence--ready): panels flow vertically, all content
   visible — correct for no-JS and prefers-reduced-motion. ---- */

.panel-sequence--ready .panel-sequence__track {
  height: 100svh;
  overflow: hidden;
  /* GSAP applies position: fixed during pin; overflow: hidden clips stacked panels */
}

.panel-sequence--ready .panel {
  position: absolute;
  inset: 0;
  min-height: unset;
  /* Bottom-anchored (align-items: flex-end, inherited from the base .panel
     rule) — unchanged from the original static-hero position. What makes
     this "stick in place, then fade" instead of scrolling to the top is
     panel-text.js's scrollDriftDistance defaulting to 0 (no scroll-linked
     drift at all) — the entry/resting position itself was never meant to
     move to the centre. */
  /* z-index per panel is set by JS as i + 1 — panel 0 at the bottom, each
     later panel one step higher, so an incoming panel fades in on top of
     the outgoing one, which stays fully opaque below it. */
  /* Every panel's box occupies the same full-bleed rect regardless of which
     is actually visible — a later (higher z-index) panel's bg/content stays
     hit-testable even at opacity 0, silently swallowing clicks meant for the
     active panel's CTA underneath it. Click-through by default; re-enabled
     on [data-panel-text] below, whose own visibility state already correctly
     tracks which panel is active. */
  pointer-events: none;
}

/* Initial text visibility: every text block starts hidden — including panel
   1's, whose one-time entrance animation brings it in — so nothing flashes
   at rest before the entrance begins. Unconditional CSS (not gated on the
   JS-added .panel-sequence--ready class) so this applies from first paint —
   .panel-sequence--ready is only added once scroll-panels.js's module
   script runs, which (being a deferred module) is not guaranteed to run
   before the browser's first paint, so gating on it left a race where the
   heading could flash visible then snap hidden right before its entrance
   tween. Guarded to prefers-reduced-motion: no-preference — scroll-panels.js
   bails out entirely under reduced motion without ever calling gsap.set to
   reveal these, so hiding them unconditionally would leave reduced-motion
   users' hero text stuck invisible forever. The <noscript> override in each
   page's <head> is the belt-and-suspenders fallback for JS disabled
   entirely — same technique as [data-project-fade]/[data-journal-fade]
   (see card.css / journal-grid.css). GSAP inline styles take over from here
   (panel-text.js owns entrance/exit/active/inactive states). */
@media (prefers-reduced-motion: no-preference) {
  [data-panel-text] {
    visibility: hidden;
    opacity: 0;
    pointer-events: auto;
    will-change: transform, filter;
  }
}

/* Outer scroll-drift wrapper — scrubbed y only (set by panel-text.js from
   scroll progress). Plain block with no layout styles of its own so it
   cannot affect spacing, alignment, or resting positions. */
.panel-sequence--ready [data-panel-text-motion] {
  will-change: transform;
}

/* ---- Mid-page "Enquire Now" CTA variant — same pinned panel-sequence
   system as any hero, reused wherever a page wants a full-viewport enquiry
   break partway down (see architectural-design/index.html, the original;
   also reused on melody-house/index.html). Solid sand, no photography. ---- */
.panel-sequence--cta {
  margin-block: var(--space-20);
}

@media (min-width: 1024px) {
  .panel-sequence--cta {
    margin-block: var(--space-32);
  }
}

.panel--cta {
  color: var(--color-text);
}

/* The sand colour lives on .panel__bg, not .panel itself — .panel__bg is
   the element scroll-panels.js actually crossfades between panels (via
   [data-panel-bg] opacity). Putting it on .panel instead left every panel's
   own box permanently opaque regardless of that crossfade, so the
   top-stacked (higher z-index) panel silently covered the one beneath it
   at all times — that's why panel 1's text wasn't showing. */
.panel--cta .panel__bg {
  background-color: var(--color-sand);
}

/* This variant's gradient legibility overlay exists for text over
   photography — there's no image here, so it would just muddy the solid
   sand. */
.panel--cta .panel__bg::after {
  content: none;
}

.panel--cta .panel__heading {
  max-width: 63rem;
  margin-inline: auto;
}

/* Per-panel image focal points — homepage hero sequence only (also reused,
   panel--home-1 only, by the Journal/Projects listing heroes, which share
   its background video). Named panel--home-N (not the bare panel--N these
   used to be) so a future page's own numbered panel sequence can never
   silently inherit these homepage-specific crops — see the .panel--melody-*
   equivalents in project-detail.css, which already had to dodge this exact
   collision once. */
.panel--home-1 .panel__bg img,
.panel--home-1 .panel__bg video { object-position: center 30%; } /* existing hero crop */
.panel--home-2 .panel__bg img,
.panel--home-2 .panel__bg video { object-position: center 50%; } /* adjust after reviewing */
.panel--home-3 .panel__bg img,
.panel--home-3 .panel__bg video { object-position: center 45%; } /* adjust after reviewing */
.panel--home-4 .panel__bg img,
.panel--home-4 .panel__bg video { object-position: center 50%; } /* adjust after reviewing */
.panel--home-5 .panel__bg img,
.panel--home-5 .panel__bg video { object-position: center 30%; } /* matches panel--home-1 crop — same source video */
