/* ============================================================
   MUSIC GATE · the decision point after the logo loader
   Homepage only, once per browser session. Paired with music-gate.js.

   Three layers, painted in this order:
     1. lines    — the diamond figure, stars and the centre spike
     2. content  — icon, heading, body, buttons
     3. lights   — the hero's light streaks, blown up far past the viewport

   The lights sit ON TOP of the content deliberately: they are `overlay`
   blended, so over cream text they read as a glow across it rather than a
   sheet covering it.
   ============================================================ */

:root {
  /* The one green the whole figure is drawn in. Not --dark-green (#3f5341):
     that is the music block's icon colour and reads too dark on this panel. */
  --line-green: #5A7A5D;

  /* Panel. Matches --pt-curtain so the gate and the page-transition curtain are
     the same green — they are the same idea seen at two different moments. */
  --mg-bg: #202a22;

  /* ── Figure geometry ───────────────────────────────────────
     The diamond is a true 45° square centred ON the horizontal rule, so its
     left and right vertices land on that rule. Its half-diagonal is derived,
     not authored:

         half = (ruleY) - (top inset)

     which is what makes one set of numbers fit all three breakpoints: the
     diamond always reaches almost to the top of the screen, and its bottom
     vertex falls wherever it falls (off-screen on desktop and tablet, on-screen
     near the bottom on mobile). */
  --mg-rule: 60%;        /* horizontal rule, from the top */
  /* How far the top vertex sits below the top edge. Negative on desktop: the
     mockup has the diagonals converging just ABOVE the frame, so the top star
     is off-screen and only the two side ones are seen. */
  --mg-top-inset: -5%;

  /* Single spacing token. On desktop this is BOTH the gap from the rule down to
     the buttons and the gap from the vertical line out to each button's inner
     edge — the alignment the mockup is built on. */
  --mg-gap: 70px;

  /* ── Timeline ──────────────────────────────────────────────
     Slow on purpose. Everything grows outward from a mark that is already
     there, so the marks land first and the lines follow them. */
  --mg-mark-in: 700ms;      /* a star or the spike fading up */
  --mg-inner-grow: 1500ms;  /* rule, centre → the side stars */
  --mg-outer-grow: 1300ms;  /* rule, side stars → off-screen */
  --mg-diag-grow: 1900ms;   /* diagonals, out from their star */
  --mg-vert-grow: 1300ms;   /* vertical, down from the spike */
  --mg-content-in: 1000ms;

  --mg-ease: cubic-bezier(0.22, 1, 0.36, 1);        /* expo-out */
  --mg-ease-line: cubic-bezier(0.16, 1, 0.3, 1);    /* slower settle */
}

/* The middle treatment is keyed on SHAPE, not width. What separates it from
   desktop is a squarer frame: a tall diamond in a 16:9 window can run its side
   vertices off the edges, but the same diamond in a near-square window must
   pull them in to stay inside. A 1916×1598 screen is wider than any sane width
   breakpoint yet needs this treatment, so aspect ratio is the honest trigger —
   with a width test kept alongside it for genuinely narrow tablets. */
@media (max-width: 1200px), (max-aspect-ratio: 5/4) {
  :root { --mg-rule: 62%; --mg-top-inset: 6%; --mg-gap: 56px; }
}

@media (max-width: 700px) {
  :root { --mg-rule: 52%; --mg-top-inset: 10%; --mg-gap: 40px; }
}

/* ── Panel ────────────────────────────────────────────────
   Static markup and static CSS, so it is painted in the first frame with no
   JS involved — same reasoning as .pt-label. The figure inside is built by JS
   a beat later, which is fine: it is animating in anyway.

   z-index 9000: over the nav and status blocks (500) and the transition
   curtain (450), under the loader (10000) so the loader still slides away over
   it, and under the cursor (9999) so the custom cursor stays on top. */
.music-gate {
  position: fixed;
  inset: 0;
  z-index: 9000;
  background: var(--mg-bg);
  overflow: hidden;
  /* Contains the lights' overlay blend, so it mixes with the gate only and not
     with the hero sitting behind it. */
  isolation: isolate;
}

/* Not shown at all unless JS decides this visitor needs it. */
html:not(.mg-active) .music-gate { display: none; }

/* While the gate is up nothing behind it should be reachable or scrollable. */
html.mg-active,
html.mg-active body { overflow: hidden; }

html.mg-active .site-nav,
html.mg-active .site-nav-menu,
html.mg-active .status-blocks,
html.mg-active .scrollline { opacity: 0; pointer-events: none; }

/* A faint bloom behind everything, so the panel is not a flat field. */
.music-gate::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 90% at 50% var(--mg-rule),
      rgba(90, 122, 93, 0.30) 0%,
      rgba(90, 122, 93, 0.08) 42%,
      transparent 72%);
  pointer-events: none;
}

/* ── Layer 1 · the figure ─────────────────────────────────── */
.mg-lines {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

.mg-lines svg { display: block; width: 100%; height: 100%; }

/* Every line is drawn from its anchor mark outward, as a dashed stroke whose
   dash is the full length and whose offset starts at that same length — so it
   is a line of zero visible length that grows to full. */
.mg-line {
  stroke: var(--line-green);
  stroke-width: 1;
  fill: none;
  stroke-dasharray: var(--len);
  stroke-dashoffset: var(--len);
  transition: stroke-dashoffset var(--dur, 1500ms) var(--mg-ease-line) var(--d, 0ms);
}

.mg-lines.is-in .mg-line { stroke-dashoffset: 0; }

/* Retracting reverses it: the lines withdraw back into the marks they grew
   from, then the panel cuts away. Faster than the entry — a held breath
   released, not a second performance. */
.mg-lines.is-out .mg-line {
  stroke-dashoffset: var(--len);
  transition: stroke-dashoffset 700ms cubic-bezier(0.65, 0, 0.35, 1) var(--d-out, 0ms);
}

/* The marks: stars at the four diamond vertices, the spike at the rule's
   centre. They arrive before the lines that spring from them. */
.mg-mark {
  opacity: 0;
  transform: scale(0.4);
  transform-box: fill-box;
  transform-origin: center;
  transition: opacity var(--mg-mark-in) var(--mg-ease) var(--d, 0ms),
              transform var(--mg-mark-in) var(--mg-ease) var(--d, 0ms);
}

.mg-lines.is-in .mg-mark { opacity: 1; transform: scale(1); }

.mg-lines.is-out .mg-mark {
  opacity: 0;
  transform: scale(0.4);
  transition: opacity 500ms ease-in var(--d-out, 0ms),
              transform 500ms ease-in var(--d-out, 0ms);
}

/* ── Layer 2 · content ────────────────────────────────────
   Anchored to the rule rather than to the viewport, because the mockup's real
   constraint is the buttons' distance from that rule. The text group hangs
   above it and the buttons sit below it, so both stay correct at any height. */
.mg-content {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--mg-rule);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 6vw;
}

/* Text group: sits entirely above the rule, exactly --mg-gap clear of it — the
   same distance the buttons sit below. The two groups are then symmetrical about
   the rule, which is what the mockup measures out to. */
.mg-text {
  position: absolute;
  bottom: var(--mg-gap);
  left: 6vw;
  right: 6vw;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.mg-icon {
  color: var(--line-green);
  width: 46px;
  height: 46px;
  margin-bottom: 60px;   /* icon → text, as specified */
}

.mg-icon svg { display: block; width: 100%; height: 100%; overflow: visible; }

.mg-heading {
  font-family: 'Satoshi', -apple-system, system-ui, sans-serif;
  font-weight: 500;
  font-size: clamp(27px, 2.9vw, 40px);
  line-height: 1.22;
  letter-spacing: -0.01em;
  color: var(--cream, #f2ecdf);
  margin-bottom: 20px;   /* heading → body, as specified */
  max-width: 22ch;
}

/* The three warm words. Playfair italic against the Satoshi run. */
.mg-heading em {
  font-family: 'Playfair Display', Georgia, serif;
  font-style: italic;
  font-weight: 500;
}

/* "playlist" only — an emerald sweep left to right. */
.mg-heading .mg-playlist {
  background-image: linear-gradient(90deg, #5A7A5D 0%, #91BD91 50%, #5A7A5D 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

.mg-body {
  font-family: 'Satoshi', -apple-system, system-ui, sans-serif;
  font-weight: 400;
  font-size: clamp(15px, 1.25vw, 18px);
  line-height: 1.5;
  color: var(--cream, #f2ecdf);
}

.mg-body strong { font-weight: 700; }

/* Buttons group: below the rule by exactly --mg-gap. On desktop the pair
   straddles the vertical line, so the gap between them is twice the gap from
   the line to either inner edge — which is the same --mg-gap again. */
.mg-buttons {
  position: absolute;
  top: var(--mg-gap);
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  gap: calc(var(--mg-gap) * 2);
}

.mg-btn {
  position: relative;
  overflow: hidden;
  font-family: 'Satoshi', -apple-system, system-ui, sans-serif;
  font-weight: 500;
  font-size: 18px;
  line-height: 1;
  padding: 12px 32px;
  border-radius: 4px;
  cursor: pointer;
  white-space: nowrap;
  transition: transform 500ms var(--mg-ease),
              background 450ms ease,
              color 450ms ease,
              border-color 450ms ease,
              box-shadow 600ms ease;
}

.mg-btn-off {
  background: transparent;
  border: 1px solid var(--cream, #f2ecdf);
  color: var(--cream, #f2ecdf);
}

.mg-btn-on {
  background: var(--cream, #f2ecdf);
  border: 1px solid var(--cream, #f2ecdf);
  color: #1a1712;
}

/* Hover · the same idea as the hero's light streaks: a warm bloom sweeping
   across, not a colour swap. A blurred highlight is wiped left to right behind
   the label while the button lifts and casts a soft glow. */
.mg-btn::after {
  content: '';
  position: absolute;
  inset: -40% -10%;
  background: linear-gradient(105deg,
    transparent 20%,
    rgba(255, 240, 178, 0.55) 48%,
    transparent 76%);
  filter: blur(14px);
  opacity: 0;
  transform: translateX(-70%);
  transition: opacity 500ms ease, transform 900ms var(--mg-ease);
  pointer-events: none;
}

@media (hover: hover) and (pointer: fine) {
  .mg-btn:hover { transform: translateY(-2px); }
  .mg-btn:hover::after { opacity: 1; transform: translateX(70%); }

  .mg-btn-off:hover {
    border-color: #FFF0B2;
    box-shadow: 0 0 26px rgba(255, 240, 178, 0.22),
                inset 0 0 20px rgba(255, 240, 178, 0.10);
  }

  .mg-btn-on:hover {
    background: #FFF7E2;
    box-shadow: 0 0 34px rgba(255, 240, 178, 0.34);
  }
}

.mg-btn:focus-visible {
  outline: 2px solid #FFF0B2;
  outline-offset: 3px;
}

/* Content arrives after the figure has drawn itself. */
.mg-icon,
.mg-heading,
.mg-body,
.mg-buttons {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--mg-content-in) var(--mg-ease) var(--d, 0ms),
              transform var(--mg-content-in) var(--mg-ease) var(--d, 0ms);
}

html.mg-reveal .mg-icon,
html.mg-reveal .mg-heading,
html.mg-reveal .mg-body,
html.mg-reveal .mg-buttons { opacity: 1; transform: none; }

/* On the way out the content goes first, before the lines retract. */
html.mg-leaving .mg-icon,
html.mg-leaving .mg-heading,
html.mg-leaving .mg-body,
html.mg-leaving .mg-buttons {
  opacity: 0;
  transform: translateY(-10px);
  transition: opacity 450ms ease-in var(--d-out, 0ms),
              transform 450ms ease-in var(--d-out, 0ms);
}

/* ── Layer 3 · lights ─────────────────────────────────────
   The hero's streaks, at several times the viewport so only a slow-moving
   fragment is ever on screen. Same blur and overlay blend as the hero, so it
   is recognisably the same light. */
.mg-lights {
  position: absolute;
  left: 50%;
  top: var(--mg-rule);
  width: 320vw;
  height: 260vh;
  transform: translate(-50%, -50%);
  z-index: 3;
  pointer-events: none;
  mix-blend-mode: overlay;
  filter: blur(60px);
  opacity: 0;
  transition: opacity 2200ms ease;
}

/* Held well back. At full strength the streaks read as distinct bright patches
   rather than light in the room, which the mockup does not have. */
html.mg-reveal .mg-lights { opacity: 0.55; }
html.mg-leaving .mg-lights { opacity: 0; transition: opacity 600ms ease; }

.mg-lights-track {
  display: flex;
  height: 100%;
  width: max-content;
  will-change: transform;
  animation: mg-lights-drift 150s linear infinite;
}

.mg-lights-tile {
  display: block;
  height: 100%;
  width: auto;
  flex: 0 0 auto;
}

@keyframes mg-lights-drift {
  from { transform: translate3d(-50%, 0, 0); }
  to   { transform: translate3d(0, 0, 0); }
}

/* ── The cut ──────────────────────────────────────────────
   The panel stops existing in one frame rather than fading, so the hero is
   revealed sharply and its own stagger carries the moment. */
html.mg-gone .music-gate { display: none; }

/* ── Tablet ───────────────────────────────────────────────
   Same figure and same stacking; only the geometry variables move (above).
   Matched to the same shape test, so the two never disagree. */
@media (max-width: 1200px), (max-aspect-ratio: 5/4) {
  .mg-icon { width: 42px; height: 42px; margin-bottom: 52px; }
  .mg-lights { width: 300vw; height: 240vh; filter: blur(52px); }
}

/* ── Mobile ───────────────────────────────────────────────
   Buttons stack, and the order flips: the filled "Music on" leads. There is no
   vertical line at this size, so there is no spike either — it exists only to
   mark where that line meets the rule. */
@media (max-width: 700px) {
  .mg-icon { width: 38px; height: 38px; margin-bottom: 44px; }

  /* The authored break holds the two-line shape on wider screens. Here the
     column is too narrow for it to help, so let the text wrap on its own. */
  .mg-br { display: none; }

  .mg-heading { font-size: clamp(30px, 8.6vw, 40px); max-width: 14ch; }
  .mg-body { font-size: 14px; }

  .mg-text { bottom: calc(var(--mg-gap) + 20px); left: 8vw; right: 8vw; }

  .mg-buttons {
    flex-direction: column;
    align-items: center;
    gap: 16px;
  }

  /* Filled first, stroked second — the reverse of desktop. */
  .mg-btn-on  { order: 1; }
  .mg-btn-off { order: 2; }

  /* Matched widths, so the stack reads as one control. */
  .mg-btn { min-width: 270px; text-align: center; }

  .mg-lights { width: 420vw; height: 220vh; filter: blur(40px); }
}

/* ── No reduced-motion override, on purpose ───────────────
   There was a @media (prefers-reduced-motion: reduce) block here that showed the
   figure and the content already in place, with no growth, drift or bloom. It
   has been removed by the site owner's explicit decision, to match
   transition.css: the whole site's motion plays for every visitor, so the gate
   and the page changes cannot disagree.

   music-gate.js sets its `calm` flag to a hard false for the same reason. The
   two MUST agree — CSS holding the lines at full length while JS still waited
   to grow them would leave the figure drawn but frozen mid-timeline.

   Noted rather than hidden: this is motion that can cause nausea and vertigo for
   people with vestibular disorders. If it needs revisiting, soften the animation
   for those visitors — drop the lift and the bloom, keep the fade — rather than
   restoring an instant appearance. */
