/* Album Site — DESKTOP styles (fluid responsive architecture).
   Imported by index.html only. Mobile-portrait equivalents live in mobile.css.

   === Architecture ===
   No viewport-width buckets. No per-browser branches. Every dimension that
   varies across viewports derives from one of two driving variables:

     --splashScale    drives the three splash elements (album art, widget,
                      social row) AND the gaps between them, so their RELATIVE
                      sizes and positions are locked across every display.
                      Curve uses min(100vmin, 100dvh) so the splash always
                      fits inside the actual visible area — including the
                      Mac-Chrome vs Mac-Safari browser-chrome-height delta
                      that broke the bucket architecture.

     --contentScale-derived clamps drive typography, nav, section spacing.
                      Each is a clamp(min, vw-based, max) so text never gets
                      illegibly small or comically large, and scales smoothly
                      in between.

   To retune: change one number in :root. Element sizes downstream rebalance
   proportionally. No bucket cascade to keep in sync. */

:root {
  /* === Splash: single driving scale =====================================
     This is a UNITLESS NUMBER set by JS at load + resize (inline script in
     index.html head). Reason: pure-CSS computation requires
     calc(<length>/<length>) for unit cancellation (CSS Values 4) which
     Firefox 151 fails to parse inside var(), causing every element that
     consumes it to fall back to intrinsic size. JS sidesteps that.
     Reference points (typical chrome):
       MacBook Air Chrome (~800 dvh) → 1.00
       MacBook Air Safari (~756 dvh) → 0.95
       Legion 5i          (~1010 dvh) → 1.26
       UM790 4K           (~2080 dvh) → 2.60 (ceiling 2.8)
     1.0 fallback below covers the unlikely case where JS doesn't run. */
  --splashScale: 1.0;

  /* Splash element NATURAL sizes (in px). Each splash element multiplies
     its natural size by --splashScale. Tune these to change relative
     proportions between the three elements; tune the splashScale formula
     above to change overall splash size across viewports. */
  /* Album natural sized to absorb the vertical budget freed by removing
     the pre-save patch. 472 sits in the per-config sweet spot: all 6
     canonical viewports settle within [target-2, target+12] of
     splashElementGapNatural × splashScale. Tune if a new viewport class
     drifts (the binding constraints are midrange-chrome at the small
     end and um790-firefox at the large end). */
  --splashAlbumCoverNatural:       472px;
  --splashWidgetWrapperNaturalW:   320px;
  /* Natural height of the THREE-element stack:
       ~50px EMAIL SIGN UP button (18px font + 14×2 padding ≈ 50)
       +  6px gap
       + ~98px FOLLOW (Sonikit, ~70 × 1.4 zoom)
       +  6px gap
       + ~166px pre-save (Amazon removed)
       = ~326 natural. Bumped from prior 270 to accommodate the new
     EMAIL SIGN UP button at the top of the stack. Splash fits 100vh at
     mid-resolution viewports; tighten if needed. */
  --splashWidgetWrapperNaturalH:   330px;
  /* Gap between stacked Sonikit widgets at NATURAL scale; gets scaled
     visually by the wrapper transform. ~Sonikit's internal button gap. */
  --splashWidgetStackGapNatural:    6px;
  /* FOLLOW button visually larger than pre-save buttons. Implemented via
     `zoom` (not `transform: scale`) so its LAYOUT BOX grows with the
     visual — keeps the column gap consistent across browsers. */
  --splashFollowExtraScale:         1.4;
  --splashSocialIconNatural:        48px;
  --splashSocialIconFontNatural:    28px;
  --splashSocialGapNatural:         28px;
  --splashElementGapNatural:        18px;

  /* === Layout structure ================================================= */
  --siteEdgePadding:   clamp(24px, 3vw, 80px);

  /* === Typography (fluid via clamp) =====================================
     Title: min(width-clamp, pageContentWidth/19). The /19 bound ensures
     the longest title ("THE CROWN OUT OF WHICH DREAMS ARISE" — 35 chars)
     fits on one line at any viewport, since at Roboto Condensed font-size
     × 19 ≈ width of a 35-char string.
     Body: min(width-driven, height-driven) so on short viewports the
     font shrinks enough that the ALBUM text column (fixed character count)
     fits in the section's height budget. */
  --titleSize:              min(clamp(32px, calc(2.5vw + 10px), 80px), calc(var(--pageContentWidth) / 19));
  --bodySize:               clamp(18px, min(calc(0.84vw + 12px), calc(1.56vh + 6px)), 38px);
  --captionSize:            clamp(12px, calc(0.5vw + 8px),  24px);
  --musicVideoSubtitleSize: clamp(15px, calc(0.6vw + 10px), 28px);

  /* === Nav bar ========================================================== */
  --navBarHeight:   clamp(56px, calc(3.5vh + 30px), 110px);
  --navAvatarSize:  clamp(36px, calc(1.6vw + 14px), 72px);
  --navFontSize:    clamp(15px, calc(0.7vw + 10px), 30px);
  --navItemGap:     clamp(40px, 7vw, 200px);

  /* === Cascading page-content frame =====================================
     pageContentWidth is the MUSIC VIDEO player width. It is the binding
     constraint for the whole site below the splash: every section title
     sits at its left edge, every body text indents inward by --bodyIndent
     of it, the album image right-margins mirror that indent, the Yehoshua
     avatar left aligns to the body indent, etc.

     pageContentWidth is height-driven so the MUSIC VIDEO section fits in
     one viewport on nav-click. The height-budget reserves room for the
     title block, subtitle, caption, PRE-SAVE row, and section vertical
     padding. Anything left over becomes player height; player width =
     height × 16/9.

     The min() of width-bound + height-bound means whichever constraint
     is tighter wins — on tall viewports the width bound caps growth; on
     short viewports the height bound dominates and the player narrows. */
  --musicVideoNonPlayerBudget: clamp(280px, 35vh, 700px);
  --pageContentWidth:
    min(
      calc(100vw - 2 * var(--siteEdgePadding)),
      calc((100dvh - var(--navBarHeight) - var(--musicVideoNonPlayerBudget)) * 16 / 9)
    );
  --pageMarginX: calc((100vw - var(--pageContentWidth)) / 2);
  --bodyIndent:  calc(var(--pageContentWidth) * 0.07);

  /* === Section spacing ================================================== */
  --sectionVerticalPadding: clamp(24px, 3vh, 60px);
  --sectionContentGap:      clamp(20px, 2.5vw, 60px);
}

/* === Splash poster background ===========================================
   Fills the entire viewport, no bars on any side. */
#posterSplashImage {
  position: absolute;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100dvh;
  object-fit: cover;
  object-position: center center;
}

/* === Nav bar ============================================================ */

#navBar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--navBarHeight);
  background: var(--backgroundBlack);
  z-index: 100;
  opacity: 0;
  transition: opacity 1000ms ease;
}
#navBar.navBarVisible {
  opacity: 1;
}

#navBarInner {
  width: var(--pageContentWidth);
  max-width: calc(100vw - 2 * var(--siteEdgePadding));
  height: 100%;
  margin: 0 auto;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--navItemGap);
}

#navAvatarLink {
  display: block;
  width: var(--navAvatarSize);
  height: var(--navAvatarSize);
  border-radius: 50%;
  overflow: hidden;
  text-decoration: none;
  flex-shrink: 0;
}
#navAvatarImage {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.navButton {
  color: var(--goldYellow);
  /* Brand font (Barcode128 Readable) — matches the workshop button look
     and ties the nav into the WoP brand identity. */
  font-family: var(--fontFamilyWoPBrand);
  font-weight: 400;
  font-size: var(--navFontSize);
  letter-spacing: 0.03em;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: color 200ms ease;
  /* Positioning context for the hover-only wireframe border in ::before. */
  position: relative;
  /* Equal-width modular feel: all three nav items use the same min-width,
     centered text. The widest text — "MUSIC VIDEO" — measures ≈ 6.5 ×
     navFontSize across viewports, so 7 × navFontSize gives a small
     breathing buffer while keeping all three at the same width. With the
     flex gap already equal between siblings, the result is a uniform
     module-and-gap rhythm across the row. */
  min-width: calc(var(--navFontSize) * 7);
  text-align: center;
}
/* Wireframe button border — invisible by default, fades in on hover.
   Same hairline-plus-corner-L treatment as the workshop section buttons
   (4 dim edges, 8 bright 1px corner arms, 4 bright 2×2 corner
   reinforcements). The pseudo-element uses negative inset so it extends
   OUTSIDE the text bounding box (i.e., the nav layout is unaffected by
   the wireframe — buttons stay at their text-only positions). */
.navButton::before {
  content: "";
  position: absolute;
  /* Vertical inset = 8px (text padding); horizontal = 33px on each side
     for a ~20% wider wireframe than the underlying text-bounding box.
     Text stays centered (it's inside the underlying button box; the
     wireframe is purely a decoration painted in negative-inset space). */
  inset: -8px -33px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 150ms ease;
  background-color: transparent;
  background-image:
    /* 4 corner reinforcement patches (2×2 bright). */
    linear-gradient(#A6DBFE, #A6DBFE),
    linear-gradient(#A6DBFE, #A6DBFE),
    linear-gradient(#A6DBFE, #A6DBFE),
    linear-gradient(#A6DBFE, #A6DBFE),
    /* 4 dim edges between corners. */
    linear-gradient(rgba(166,219,254,0.35), rgba(166,219,254,0.35)),
    linear-gradient(rgba(166,219,254,0.35), rgba(166,219,254,0.35)),
    linear-gradient(rgba(166,219,254,0.35), rgba(166,219,254,0.35)),
    linear-gradient(rgba(166,219,254,0.35), rgba(166,219,254,0.35)),
    /* 8 bright corner arms — TL h, TL v, TR h, TR v, BL h, BL v, BR h, BR v. */
    linear-gradient(#A6DBFE, #A6DBFE),
    linear-gradient(#A6DBFE, #A6DBFE),
    linear-gradient(#A6DBFE, #A6DBFE),
    linear-gradient(#A6DBFE, #A6DBFE),
    linear-gradient(#A6DBFE, #A6DBFE),
    linear-gradient(#A6DBFE, #A6DBFE),
    linear-gradient(#A6DBFE, #A6DBFE),
    linear-gradient(#A6DBFE, #A6DBFE);
  background-size:
    2px 2px, 2px 2px, 2px 2px, 2px 2px,
    calc(100% - 14px) 1px,
    1px calc(100% - 14px),
    calc(100% - 14px) 1px,
    1px calc(100% - 14px),
    7px 1px, 1px 7px,
    7px 1px, 1px 7px,
    7px 1px, 1px 7px,
    7px 1px, 1px 7px;
  background-position:
    left 0 top 0, right 0 top 0, left 0 bottom 0, right 0 bottom 0,
    50% 0%, 100% 50%, 50% 100%, 0% 50%,
    left top, left top,
    right top, right top,
    left bottom, left bottom,
    right bottom, right bottom;
  background-repeat: no-repeat;
}
.navButton:hover {
  color: #40AFFA;
}
.navButton:hover::before,
.navButton:focus-visible::before {
  opacity: 1;
}

/* === Splash content =====================================================
   Column-stacked: album cover, Sonikit widget, social row. Centered both
   axes inside the first viewport. min-height ensures the box matches the
   visible area (dvh — accounts for browser chrome on Mac etc.); content
   sized via --splashScale to always fit inside (with breathing room). */

#splashContent {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  min-height: 100dvh;
  padding-top: var(--navBarHeight);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* space-evenly distributes the leftover vertical space as 5 EQUAL gaps
     (before / between each pair of / after the 4 flex children). Matches
     the mobile splash rule:
       nav→album = album→email = email→follow = follow→social = social→bottom
     The album cover (sized via --splashAlbumCoverNatural × scale) is
     tuned to absorb the freed budget so each gap settles near the prior
     splashElementGapNatural × scale. */
  justify-content: space-evenly;
  z-index: 2;
  opacity: 0;
  transition: opacity 1000ms ease;
  pointer-events: none;
}
#splashContent.splashContentVisible {
  opacity: 1;
  pointer-events: auto;
}

#splashAlbumCoverButton {
  display: block;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  text-decoration: none;
  /* Positioning context for the border overlay drawn in ::after. Hover
     transform sits here so the overlay scales with the image. */
  position: relative;
  transition: transform 250ms ease;
}
#splashAlbumCoverButton:hover {
  transform: scale(1.025);
}
#splashAlbumCoverButton img {
  display: block;
  width: calc(var(--splashAlbumCoverNatural) * var(--splashScale));
  height: auto;
  /* Square corners (no fillet) — the wireframe border in ::after sits
     flush to the image edge. */
}
/* Plain 3px border at full opacity, with 3px corner fillet matching the
   splash EMAIL SIGN UP button. Positioned so the border occupies 2px
   INSIDE the image edge and 1px OUTSIDE: box-sizing:border-box +
   inset:-1px puts the pseudo's outer border edge 1px past the image,
   and the 3px-thick border drawn inward from there ends 2px inside the
   image. (Gradient-based dim/bright corner reinforcement was tried
   here but the user found it visually off for the splash context.) */
#splashAlbumCoverButton::after {
  content: "";
  position: absolute;
  inset: -1px;
  pointer-events: none;
  box-sizing: border-box;
  border: 3px solid #A6DBFE;
  border-radius: 3px;
}

/* EMAIL SIGN UP button — direct flex child of #splashContent. Two
   overrides on tokens.css defaults:
   1. Zero the 10px top/bottom margins. They were there to align with
      the now-removed pre-save patch's internal padding inside the old
      3-button widget stack. With the button as a flex child, those
      margins would offset 2 of the 5 space-evenly gaps.
   2. Scale font/padding/border with --splashScale (intrinsic CSS, not
      zoom — zoom drifts across real Chromium builds and would break gap
      equality on real devices, same lesson as the mobile rebuild). */
#splashContent > .splashMailingListButton {
  margin: 0;
  font-size:    calc(21px * var(--splashScale));
  padding:      calc(14px * var(--splashScale)) calc(24px * var(--splashScale));
  /* border-width and border-radius left at the tokens.css default (3px /
     3px). Scaling them broke on Chromium small-scale viewports (calc(3 ×
     0.94) renders as 2px due to subpixel rounding, tripping the existing
     "border-width must be ≥ 2.5px" assertion), and on large-scale
     viewports made the radius read as pill-shaped (≥5px) per the
     existing "border-radius must be ≤4px" assertion. Fixed 3px stays
     proportional enough at all scales. */
}

/* Sonikit FOLLOW patch — direct flex child of #splashContent. `zoom`
   affects layout box, so the patch's getBoundingClientRect() IS the
   visible content extent; the parent flex's space-evenly then sees the
   true rendered size and distributes the 5 gaps correctly. zoom =
   followExtraScale × splashScale (consolidating the per-viewport
   scaling previously done by transform: scale on a wrapper). */
.sonikitFollowPatch {
  zoom: calc(var(--splashFollowExtraScale) * var(--splashScale));
  pointer-events: auto;
}
/* Firefox interprets `zoom` differently from Chromium/WebKit (added in
   Firefox 126; the implementation is compatible but produces a visibly
   smaller result on this widget — likely because Sonikit's FOLLOW iframe
   has a different intrinsic width in Firefox). Inline JS in <head> adds
   .isFirefox to <html> when navigator.userAgent contains "Firefox"; we
   use that class to bump zoom in Firefox only. */
.isFirefox .sonikitFollowPatch {
  zoom: calc(1.8 * var(--splashScale));
}

#splashSocialRow {
  display: flex;
  align-items: center;
  gap: calc(var(--splashSocialGapNatural) * var(--splashScale));
}

.socialIcon {
  display: flex;
  align-items: center;
  justify-content: center;
  width:     calc(var(--splashSocialIconNatural)     * var(--splashScale));
  height:    calc(var(--splashSocialIconNatural)     * var(--splashScale));
  font-size: calc(var(--splashSocialIconFontNatural) * var(--splashScale));
  color: var(--goldYellow);
  text-decoration: none;
  transition: color 200ms ease, transform 200ms ease;
}
.socialIcon:hover {
  color: #ffffff;
  transform: scale(1.1);
}
.socialIcon.disabled,
.socialIcon[aria-disabled="true"] {
  opacity: 0.35;
  pointer-events: none;
}

/* === Main container + scrollable sections ===============================
   #mainContainer is the visual content frame, capped at --contentMaxWidth
   on big displays. Section content fills mainContainer; per-section
   indents come from --sectionBodyPadding inside each section. */

#mainContainer {
  position: relative;
  width: var(--pageContentWidth);
  margin: 100dvh auto 0;
  background: var(--backgroundBlack);
  z-index: 10;
}

#mainContainer section {
  scroll-margin-top: var(--navBarHeight);
  padding: var(--sectionVerticalPadding) 0;
  box-sizing: border-box;
  /* Each section fills at least one viewport (below the nav). Prevents the
     next section's title from peeking up at the bottom of this section on
     nav-click landing. */
  min-height: calc(100dvh - var(--navBarHeight));
}

.sectionContainer {
  width: 100%;
  margin: 0 auto;
  text-align: left;
}

.sectionTitle {
  color: var(--goldYellow);
  font-family: 'Roboto Condensed', sans-serif;
  font-weight: 500;
  font-size: var(--titleSize);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0;
  line-height: 1;
}
/* MY OWN DREAMS — weight reduced ~20% (500 → 400) so the title reads
   lighter; other section titles keep the 500 weight. Font-size also
   trimmed by 2pt off the base --titleSize clamp (fluid behavior
   preserved, just shifted down). */
#musicVideoSection .sectionTitle {
  font-weight: 400;
  font-size: calc(var(--titleSize) - 2pt);
}

/* === MUSIC VIDEO section ================================================ */

/* "Context frame" braces — tall narrow square brackets `[ ]` flanking the
   MUSIC VIDEO content. Drawn entirely from layered linear-gradients in
   ::before (left brace) and ::after (right brace). Position and height
   come from CSS variables set at runtime by syncMusicVideoBraces() in
   albumSiteBootstrap.js (the brace bounds depend on positions of the
   title, video, and translated buttons — relationships pure CSS can't
   express). All segments are 1px thick; the visual treatment mirrors the
   music-video button border (dim hue along edges, bright at corners). */
#musicVideoSection {
  position: relative;
  --braceBright: #A6DBFE;
  --braceDim:    rgba(166, 219, 254, 0.35);
  --braceArm:    7px;    /* corner L-arm length (matches button corners) */
  --braceWidth:  15px;   /* horizontal bar length */
}
#musicVideoSection::before,
#musicVideoSection::after {
  content: "";
  position: absolute;
  top:    var(--braceTop,    0);
  height: var(--braceHeight, 0);
  width:  var(--braceWidth);
  pointer-events: none;
  background-repeat: no-repeat;
}
/* LEFT brace [ — vertical line on the LEFT edge of the brace box. Top and
   bottom horizontal bars extend RIGHT (into the brace box). The free end
   of each bar (right end) gets a 1px bright terminal accent. */
#musicVideoSection::before {
  left: var(--braceLeft, -15px);
  background-image:
    /* DIM segments: top bar, bottom bar, vertical line (in that order) */
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    /* BRIGHT corner arms: TL horiz, TL vert, BL horiz, BL vert */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    /* BRIGHT terminals: TR pixel, BR pixel (the free ends of the bars) */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright));
  background-size:
    calc(100% - var(--braceArm) - 1px) 1px,    /* top dim bar  */
    calc(100% - var(--braceArm) - 1px) 1px,    /* bot dim bar  */
    1px calc(100% - 2 * var(--braceArm)),       /* vertical dim */
    var(--braceArm) 1px,                        /* TL h arm     */
    1px var(--braceArm),                        /* TL v arm     */
    var(--braceArm) 1px,                        /* BL h arm     */
    1px var(--braceArm),                        /* BL v arm     */
    1px 1px,                                     /* TR terminal  */
    1px 1px;                                     /* BR terminal  */
  background-position:
    var(--braceArm) 0,        /* top dim bar  — left edge at arm-end       */
    var(--braceArm) 100%,     /* bot dim bar  — left edge at arm-end       */
    0 50%,                    /* vertical dim — at left edge, centered     */
    0 0,                      /* TL h arm                                   */
    0 0,                      /* TL v arm                                   */
    0 100%,                   /* BL h arm                                   */
    0 100%,                   /* BL v arm                                   */
    100% 0,                   /* TR terminal — far end of top bar           */
    100% 100%;                /* BR terminal — far end of bot bar           */
}
/* RIGHT brace ] — top bar EXTENDED leftward across the video to form the
   top of the context frame. The pseudo-element's width therefore expands
   to --braceTopRightWidth (computed in JS to terminate symmetrically with
   the left brace's top bar, mirrored across the title text). The vertical
   line stays at the box's right edge; the BOTTOM bar stays SHORT (length
   --braceWidth, same as the left brace's bottom bar) and is anchored to
   the right side of the now-wider box. */
#musicVideoSection::after {
  right: var(--braceRight, -15px);
  width: var(--braceTopRightWidth, var(--braceWidth));
  background-image:
    /* DIM segments: top bar (full width), vertical, bottom bar (short) */
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    /* BRIGHT corners: TR h, TR v, BR h, BR v */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    /* BRIGHT terminals: TL (free end of long top bar), BL (free end of short bot bar) */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright));
  background-size:
    calc(100% - var(--braceArm) - 1px) 1px,                     /* dim top bar — long  */
    1px calc(100% - 2 * var(--braceArm)),                       /* dim vertical        */
    calc(var(--braceWidth) - var(--braceArm) - 1px) 1px,        /* dim bot bar — short */
    var(--braceArm) 1px,                                        /* TR h arm            */
    1px var(--braceArm),                                        /* TR v arm            */
    var(--braceArm) 1px,                                        /* BR h arm            */
    1px var(--braceArm),                                        /* BR v arm            */
    1px 1px,                                                     /* TL terminal         */
    1px 1px;                                                     /* BL terminal         */
  background-position:
    1px 0,                                                       /* dim top:   left at 1px      */
    100% 50%,                                                    /* dim vert:  right edge       */
    /* 4-value `right X bottom Y` syntax: in background-position, a 100% term
       inside `calc()` resolves against (container − image_size) rather than
       container_size, which sent the previous `calc(100% - braceWidth + 1px)`
       expressions ~braceWidth px too far left. Anchoring from the right edge
       with explicit lengths avoids the percentage entirely. */
    right var(--braceArm) bottom 0,                              /* dim bot:   right edge at arm from box right */
    100% 0,                                                      /* TR h                         */
    100% 0,                                                      /* TR v                         */
    100% 100%,                                                   /* BR h                         */
    100% 100%,                                                   /* BR v                         */
    0 0,                                                         /* TL terminal: top-left corner */
    right calc(var(--braceWidth) - 1px) bottom 0;                /* BL terminal: free end of short bot bar */
}

.musicVideoSubtitle {
  /* Color: cyan-tinted off-white (~70% from #A6DBFE toward white) so the
     subtitle harmonizes with the cyan border elements but reads as body
     copy rather than another border accent. Tokenize on promotion. */
  color: #E3F2FF;
  font-family: var(--fontFamilyBody);
  /* Weight reduced from the body default 400 → 300 (~-25%, the closest
     discrete weight loaded for Roboto Condensed) so the subtitle reads
     lighter under MY OWN DREAMS. */
  font-weight: 300;
  font-size: var(--musicVideoSubtitleSize);
  margin: calc(0.6em) 0 calc(1em);
}

#inlineMusicVideo {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  display: block;
}

/* Fade the native playback controls in only when the user hovers the
   wrapper, so the video reads cleanly while the user isn't engaging.
   WebKit/Blink only — Firefox doesn't expose ::-webkit-media-controls-panel,
   so Firefox falls back to its default always-visible bar (acceptable). */
#inlineMusicVideo::-webkit-media-controls-panel {
  opacity: 0;
  transition: opacity 250ms ease;
}
.inlineVideoWrapper:hover #inlineMusicVideo::-webkit-media-controls-panel,
#inlineMusicVideo:focus::-webkit-media-controls-panel {
  opacity: 1;
}

.videoSubCaption {
  color: var(--bodyTextTertiary);
  font-family: var(--fontFamilyBody);
  font-weight: 400;
  font-size: var(--captionSize);
  text-align: left;
  margin: 0.8em 0 0;
}
.videoSubCaption a {
  color: var(--bodyTextTertiary);
  text-decoration: underline;
}
.videoSubCaption a:hover {
  text-decoration: none;
}

.preSaveRow {
  display: flex;
  /* space-around: each button gets its own half of the row, so the left
     button's center lands ~W/4 and the right ~3W/4 — i.e. roughly centered
     in the gap between the WoP logo (below, centered) and the video player
     edge. With one flex child (the album-pre-save section), this resolves
     to the same position as `center`. */
  justify-content: space-around;
  align-items: center;
  /* Tight to the video (was clamp(20px, 2.5vh, 48px)) so the buttons sit
     close above and the breathing room shifts BELOW the row instead, opening
     the gap between the buttons and the WoP logo. */
  margin-top: clamp(8px, 1vh, 18px);
  margin-bottom: clamp(28px, 3.5vh, 56px);
  /* Acts as a floor for narrow viewports where space-around has no surplus
     to distribute — keeps the buttons from touching. */
  gap: clamp(20px, 2vw, 48px);
}

/* WoP logo row — own row beneath .preSaveRow, centered.
   Logo height intentionally NOT linked to .preSaveButton height — the logo
   stays at its current visual size even as the buttons scale down. */
.worksOfPeaceLogoRow {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: clamp(8px, 1vh, 16px);
  margin-bottom: clamp(20px, 2.5vh, 48px);
  --logoBtnHeight: calc(2 * (clamp(14px, 0.8vw + 10px, 28px) * 1.2
                            + 2 * clamp(6px, 0.6vh, 12px)
                            + 2px));
}

/* Music-video PRE-SAVE buttons (WORKSHOP) — these two buttons are being
   styled with a brand look distinct from the standard .preSaveButton: the
   WoP brand font, a +15% size bump, a hairline rectangle border in a
   whiter cyan, and L-bracket corner reinforcements. The lone PRE-SAVE
   button in #albumPreSaveSection intentionally keeps the original look
   while the workshop is in progress. Promote (move into the base
   .preSaveButton rule in tokens.css) once the visual is settled.

   Color: --musicVideoButtonBorder is a desaturated/lighter variant of
   --preSaveBorder (#40AFFA). Tuning here keeps the original token intact
   for the other PRE-SAVE button(s). */
#musicVideoSection      .preSaveButton,
#albumPreSaveSection    .preSaveButton {
  --musicVideoButtonBorderBright: #A6DBFE;
  --musicVideoButtonBorderDim:    rgba(166, 219, 254, 0.35);
  --musicVideoCornerArm:    7px;   /* bright corner-arm length (spec: 6–9px) */

  /* Frame and text both scaled ×1.15 over the previous workshop step
     (padding clamps and font-size clamp each multiplied by 1.15). The
     buttons read large enough to sit visually balanced when their outer
     edges are flush with the video-player edges. */
  font-family: var(--fontFamilyWoPBrand);
  font-size: clamp(10px, calc(0.55vw + 6.9px), 20px);
  /* Barcode128 Readable's x-height differs from Roboto Condensed; widen the
     letter-spacing slightly so the all-caps label reads cleanly. */
  letter-spacing: 0.06em;
  padding: clamp(10px, 0.75vh, 15px) clamp(23px, 2.9vw, 55px);

  /* All-gradient border. Twelve 1px-thick line segments are drawn directly
     in background-image:
       — 4 DIM edge segments span between corners (length = full side − 2×arm)
       — 8 BRIGHT corner arms (2 per corner: one horizontal, one vertical)
     Every segment lies on the same 1px line at the box edge, so the dim
     baseline reads through and the bright corner arms reinforce just the
     L-shaped corner accents without changing the line's thickness anywhere.
     `box-shadow: inset` would paint above background-image and cover the
     bright corner overlays — so we draw EVERY segment as a gradient layer. */
  border: none;
  background-color: var(--backgroundBlack);
  background-image:
    /* 4 corner reinforcement patches (2×2 bright). Listed FIRST so they
       paint on TOP of the 1px corner arms. Buttons render at fractional
       widths (e.g. 235.3px at 1440vw), which means the corner pixel of
       the 1px arms lands at sub-pixel position and renders at ~50% alpha
       — perceived as a 1px fillet. A 2×2 patch positioned with `<edge> 0
       <edge> 0` straddles two columns, so even with anti-aliasing the
       inner column is always 100% bright and the corner reads sharp. */
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright)),
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright)),
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright)),
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright)),
    /* 4 dim edges (run between the corner arms) */
    linear-gradient(var(--musicVideoButtonBorderDim),    var(--musicVideoButtonBorderDim)),
    linear-gradient(var(--musicVideoButtonBorderDim),    var(--musicVideoButtonBorderDim)),
    linear-gradient(var(--musicVideoButtonBorderDim),    var(--musicVideoButtonBorderDim)),
    linear-gradient(var(--musicVideoButtonBorderDim),    var(--musicVideoButtonBorderDim)),
    /* 8 bright corner arms — TL h, TL v, TR h, TR v, BL h, BL v, BR h, BR v */
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright)),
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright)),
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright)),
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright)),
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright)),
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright)),
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright)),
    linear-gradient(var(--musicVideoButtonBorderBright), var(--musicVideoButtonBorderBright));
  background-size:
    2px 2px, 2px 2px, 2px 2px, 2px 2px,                                    /* 4 corner reinforcements */
    calc(100% - 2 * var(--musicVideoCornerArm)) 1px,                       /* top    edge */
    1px calc(100% - 2 * var(--musicVideoCornerArm)),                       /* right  edge */
    calc(100% - 2 * var(--musicVideoCornerArm)) 1px,                       /* bottom edge */
    1px calc(100% - 2 * var(--musicVideoCornerArm)),                       /* left   edge */
    var(--musicVideoCornerArm) 1px, 1px var(--musicVideoCornerArm),        /* TL h, TL v */
    var(--musicVideoCornerArm) 1px, 1px var(--musicVideoCornerArm),        /* TR h, TR v */
    var(--musicVideoCornerArm) 1px, 1px var(--musicVideoCornerArm),        /* BL h, BL v */
    var(--musicVideoCornerArm) 1px, 1px var(--musicVideoCornerArm);        /* BR h, BR v */
  background-position:
    left 0 top 0,                                                          /* TL reinforcement */
    right 0 top 0,                                                         /* TR reinforcement */
    left 0 bottom 0,                                                       /* BL reinforcement */
    right 0 bottom 0,                                                      /* BR reinforcement */
    50%   0%,     /* top    edge centered */
    100%  50%,    /* right  edge centered */
    50%   100%,   /* bottom edge centered */
    0%    50%,    /* left   edge centered */
    left top,     left top,
    right top,    right top,
    left bottom,  left bottom,
    right bottom, right bottom;
  background-repeat: no-repeat;
}
/* Hover: keep the background black (no fill change), flip the text
   gold→cyan, AND bump the dim edge opacity from 0.35 to 0.675 (halfway
   to full opacity). The bg-image gradients reference --musicVideoButton
   BorderDim, so re-defining it here lights the edges up automatically. */
#musicVideoSection   .preSaveButton:hover,
#albumPreSaveSection .preSaveButton:hover {
  color: var(--preSaveBorder);
  --musicVideoButtonBorderDim: rgba(166, 219, 254, 0.675);
}

/* Push each music-video button further into its own half (an extra 10% of
   its own width outward) and drop both rows by 30% of their own height to
   open the gap from the video and close it to the WoP-logo row below.
   Percent translate references the element's own dimensions; scoped to
   #musicVideoSection so the lone button in #albumPreSaveSection — which
   matches both :first-child and :last-child — is unaffected.
   --musicVideoButtonDropPct is the vertical drop; tuned (see loop notes)
   so the button TOP aligns with the WoP-logo TOP. */
/* Override the global .preSaveRow `justify-content: space-around` so the
   two music-video buttons sit flush with the video-player edges: the row
   spans the section container (= video-player width), so space-between
   plants the L button's LEFT edge and the R button's RIGHT edge at the
   row's left and right edges. The --musicVideoButtonShiftPct horizontal
   translate is therefore zeroed; the prior outward shift is no longer
   needed once the buttons are pinned to the edges. */
#musicVideoSection .preSaveRow {
  --musicVideoButtonDropPct: 110%;
  --musicVideoButtonShiftPct: 0%;
  justify-content: space-between;
}
#musicVideoSection .preSaveRow > .preSaveButton:first-child {
  transform: translate(calc(-1 * var(--musicVideoButtonShiftPct)),
                       var(--musicVideoButtonDropPct));
}
#musicVideoSection .preSaveRow > .preSaveButton:last-child {
  transform: translate(var(--musicVideoButtonShiftPct),
                       var(--musicVideoButtonDropPct));
}

/* === ALBUM section ======================================================
   Two-column row: text (left) at --bodyIndent from page-content left;
   image (right) ends with the same margin from page-content right —
   symmetric. align-items:stretch makes the image box exactly as tall as
   the text box. object-fit:cover never distorts (user constraint); the
   image becomes a portrait crop on narrow viewports, centered on the
   woman's face + flashlight, which the source composition supports.

   ALBUM section content is VERTICALLY CENTERED in its viewport-tall
   slot — per user spec, this puts equal buffer above the title and
   below the image, making the scroll rhythm between MUSIC VIDEO→ALBUM
   match ALBUM→YEHOSHUA. On nav-click the title therefore lands roughly
   centered (not flush below nav) — the documented exception to the
   default landing rule. */
/* Higher-specificity selector beats `#mainContainer section { padding: ... }`
   for the asymmetric padding. Bottom padding gets +1 title-height extra so
   the centered content shifts UP by half a title-height (justify-content
   centers in available space; extra bottom padding eats half above-center,
   half below-center, net effect is content moves up by half the delta). */
section#albumSection {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-bottom: calc(var(--sectionVerticalPadding) + var(--titleSize));
  /* Establish positioning context for the brace pseudo-elements below.
     Same palette + arm length as the music-video braces — when tuning,
     change the value in one place and both sections rebalance together. */
  position: relative;
  --braceBright:               #A6DBFE;
  --braceDim:                  rgba(166, 219, 254, 0.35);
  --braceArm:                  7px;
  /* Standoff between the album image's edges and its L-corner accents. */
  --albumImageCornerOffset:    10px;
}
/* Title conformed to the style guide (matches MY OWN DREAMS): weight
   dropped from base 500 → 400 and font-size trimmed by 2pt off the base
   --titleSize clamp. Other section titles (YEHOSHUA, etc.) keep their
   500 weight until conformance reaches them. */
#albumSection .sectionTitle {
  font-weight: 400;
  font-size: calc(var(--titleSize) - 2pt);
}

/* ALBUM-page "context frame" braces.
   The two braces wrap the section content (title → last child) and join
   at the section midpoint along the bottom, forming a continuous bottom
   border. CSS variables are set at runtime by syncAlbumBraces() in
   albumSiteBootstrap.js (margins are inherited from the music-video
   braces so the two sections share consistent outer X positions).

   Structure per brace (LEFT [ on ::before, RIGHT ] on ::after):
     — Vertical line at the outer edge (left edge of left brace, right
       edge of right brace), full braceHeight.
     — Short top bar at the outer side (braceWidth long) for the LEFT
       brace; extended top bar for the RIGHT brace (terminates with
       symmetric padding around the title text).
     — Long bottom bar spanning the full pseudo-element width — the two
       braces' bottom bars meet at the section midpoint with no terminal
       and no overlap.
     — 2×2 bright corner reinforcements only at the OUTER corners (TL+BL
       on left, TR+BR on right); the INNER bottom-bar ends abut. */
#albumSection::before,
#albumSection::after {
  content: "";
  position: absolute;
  top:    var(--braceTop,    0);
  height: var(--braceHeight, 0);
  pointer-events: none;
  background-repeat: no-repeat;
}

/* LEFT brace [ ----------------------------------------------------------
   Box spans from outer-left vertical to section midpoint. */
#albumSection::before {
  left:  var(--braceLeft,         -15px);
  width: var(--braceLeftBoxWidth,  15px);
  background-image:
    /* 2 corner reinforcement patches: TL + BL (outer corners only) */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    /* DIM segments: top bar (short), vertical, bottom bar (full width) */
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    /* BRIGHT corner arms at outer corners: TL h, TL v, BL h, BL v */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    /* BRIGHT terminal at TR (free end of short top bar) */
    linear-gradient(var(--braceBright), var(--braceBright));
  background-size:
    2px 2px, 2px 2px,                                              /* TL + BL reinforcements */
    calc(var(--braceWidth) - var(--braceArm) - 1px) 1px,            /* dim top bar (short)    */
    1px calc(100% - 2 * var(--braceArm)),                           /* dim vertical           */
    calc(100% - var(--braceArm)) 1px,                               /* dim bottom bar (long)  */
    var(--braceArm) 1px, 1px var(--braceArm),                       /* TL h, TL v             */
    var(--braceArm) 1px, 1px var(--braceArm),                       /* BL h, BL v             */
    1px 1px;                                                         /* TR terminal            */
  background-position:
    left 0 top 0,                                                   /* TL reinforcement       */
    left 0 bottom 0,                                                /* BL reinforcement       */
    var(--braceArm) 0,                                              /* dim top bar            */
    0 50%,                                                          /* dim vertical           */
    var(--braceArm) 100%,                                           /* dim bottom bar         */
    left top, left top,                                             /* TL h, TL v             */
    left bottom, left bottom,                                       /* BL h, BL v             */
    calc(var(--braceWidth) - 1px) 0;                                /* TR terminal at top free end */
}

/* RIGHT brace ] --------------------------------------------------------
   Box spans from section midpoint to outer-right vertical. */
#albumSection::after {
  right: var(--braceRight,           -15px);
  width: var(--braceRightBoxWidth,    15px);
  background-image:
    /* 2 corner reinforcement patches: TR + BR (outer corners only) */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    /* DIM segments: top bar (extended), vertical, bottom bar (full width) */
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    /* BRIGHT corner arms at outer corners: TR h, TR v, BR h, BR v */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    /* BRIGHT terminal at TL (free end of extended top bar) */
    linear-gradient(var(--braceBright), var(--braceBright));
  background-size:
    2px 2px, 2px 2px,                                              /* TR + BR reinforcements */
    calc(var(--braceTopRightWidth) - var(--braceArm) - 1px) 1px,    /* dim top bar (extended) */
    1px calc(100% - 2 * var(--braceArm)),                           /* dim vertical           */
    calc(100% - var(--braceArm)) 1px,                               /* dim bottom bar (long)  */
    var(--braceArm) 1px, 1px var(--braceArm),                       /* TR h, TR v             */
    var(--braceArm) 1px, 1px var(--braceArm),                       /* BR h, BR v             */
    1px 1px;                                                         /* TL terminal            */
  background-position:
    right 0 top 0,                                                  /* TR reinforcement       */
    right 0 bottom 0,                                               /* BR reinforcement       */
    /* dim top bar — image right edge at arm from container right;
       4-value `right X top Y` avoids the calc-with-100% pitfall that
       bit the music-video bottom-bar positioning earlier. */
    right var(--braceArm) top 0,
    100% 50%,                                                       /* dim vertical           */
    /* dim bottom bar — image left edge at 0 (so the inner-left end
       sits at the joined midpoint); right edge falls arm short of the
       BR corner arm. */
    0 100%,
    right top, right top,                                           /* TR h, TR v             */
    right bottom, right bottom,                                     /* BR h, BR v             */
    /* TL terminal — at the free (left) end of the extended top bar,
       which is at (box right − braceTopRightWidth + 1)px from the right
       (the +1 leaves a 1px terminal pixel at column 0 of that span). */
    right calc(var(--braceTopRightWidth) - 1px) top 0;
}

.albumDescription {
  margin-top: clamp(20px, 2.5vh, 48px);
  /* Body text shifted left as a block: the tab-in from the title's left
     edge (was --bodyIndent) is halved here, while padding-right stays at
     --bodyIndent so the image's right edge remains anchored to the page's
     right content edge. The flex gap is grown by the same amount the
     padding-left shrank, which keeps the text-column WIDTH constant —
     so the text's right edge also moves left by exactly the same amount
     as its left edge, with the image stationary. */
  padding-left:  calc(var(--bodyIndent) / 2);
  padding-right: var(--bodyIndent);
  display: flex;
  align-items: stretch;
  gap: calc(var(--sectionContentGap) + var(--bodyIndent) / 2);
}

.albumTextBox {
  /* 60/40 text/image split (3/2). Gives text more room so it doesn't wrap
     to too many lines on short viewports — keeps body font readable. The
     image still crops nicely (the source composition holds up under taller
     portrait crops). */
  flex: 3 1 0;
  font-family: var(--fontFamilyBody);
  font-weight: 400;
  font-size: var(--bodySize);
  line-height: 1.5;
  /* Conformed to the music-video style-guide color space — same #E3F2FF
     cyan-tinted off-white. Weight intentionally preserved (per user). */
  color: #E3F2FF;
}
.albumTextBox p {
  margin: 0 0 0.75em;
}
.albumTextBox p:last-child {
  margin-bottom: 0;
}

.albumImageBox {
  flex: 2 1 0;
  position: relative;
  min-width: 0;
  /* border-radius + overflow:hidden removed — the ::before pseudo-element
     below needs to render OUTSIDE this box's edges (inset:-3px) to position
     the L-corner accents 3px clear of the image. */
}
/* 1px equilateral L-corner accents 3px outside the image, in the same
   dim cyan as the brace SIDES (--braceDim — explicitly NOT the brace
   corners' bright color, per user). Drawn entirely from 8 layered
   linear-gradients on a single ::before; pointer-events:none so they
   don't interfere with anything. Arm length matches --braceArm so the
   accents read at the same scale as the brace corners around them. */
.albumImageBox::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--albumImageCornerOffset, 3px));
  pointer-events: none;
  background-image:
    /* TL: horiz + vert arms */
    linear-gradient(var(--braceDim), var(--braceDim)),
    linear-gradient(var(--braceDim), var(--braceDim)),
    /* TR: horiz + vert arms */
    linear-gradient(var(--braceDim), var(--braceDim)),
    linear-gradient(var(--braceDim), var(--braceDim)),
    /* BL: horiz + vert arms */
    linear-gradient(var(--braceDim), var(--braceDim)),
    linear-gradient(var(--braceDim), var(--braceDim)),
    /* BR: horiz + vert arms */
    linear-gradient(var(--braceDim), var(--braceDim)),
    linear-gradient(var(--braceDim), var(--braceDim));
  background-size:
    var(--braceArm) 1px, 1px var(--braceArm),
    var(--braceArm) 1px, 1px var(--braceArm),
    var(--braceArm) 1px, 1px var(--braceArm),
    var(--braceArm) 1px, 1px var(--braceArm);
  background-position:
    left top,     left top,
    right top,    right top,
    left bottom,  left bottom,
    right bottom, right bottom;
  background-repeat: no-repeat;
}
.albumStillBottomRight {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

/* === ALBUM single player ================================================
   The track is a STANDALONE SINGLE, not part of the album — so the player
   was moved OUT of the .sectionContainer (in index.html) and now sits
   BELOW the ALBUM "context-frame" braces, between the frame bottom and the
   barcode divider.

   Horizontal extents: the player's outer frame aligns to the ALBUM frame's
   LEFT and RIGHT brace verticals (wider than the body content column).
   --braceLeft / --braceRight are section-local offsets published by
   syncAlbumBraces() on #albumSection (both NEGATIVE — the braces sit
   OUTSIDE the section content box). The player inherits them and overflows
   the content width on both sides to meet the braces. 100% here = section
   content width; subtracting the two negatives widens by their magnitudes.
   Fallbacks keep the player at content width before JS runs.

   Vertical: brace bottom = description bottom + titleGap (titleGap is the
   brace's overshoot below the wrapped content). margin-top therefore =
   titleGap + --albumPlayerGap, so the visible frame-bottom→player-top gap
   == --albumPlayerGap. The matching player-bottom→barcode-divider gap is
   set by syncAlbumPlayerPlacement() via #albumSection padding-bottom. */
.albumPlayer {
  margin-left:  var(--braceLeft, 0px);
  margin-right: 0;
  width: calc(100% - var(--braceLeft, 0px) - var(--braceRight, 0px));
  box-sizing: border-box;
  --albumPlayerGap: clamp(44px, calc(3vh + 30px), 66px);
  margin-top:    20px;   /* standardized 20px stack gap: frame→player1 */
  margin-bottom: 0;
  padding:      var(--albumPlayerOuterPad, 20px);
  /* 20px outer pad supports the "20px visual padding around decorative
     barcodes" protocol — header SVG's right/top sit at exactly 20px from
     the frame edges. Was 18 prior to barcode addition. */
  --albumPlayerOuterPad:  20px;
  --albumPlayerRowHeight: 80px;       /* equal height for artwork, title, icon */
  --albumPlayerRowGap:    15px;       /* user-specified modulus */
}
/* All singles-stack gaps standardized to 20px (per user): frame→player1 (base
   rule above) and between consecutive players (here). The player3→Spotify CTA
   gap is set on .albumSpotifyCta. */
.albumPlayer + .albumPlayer {
  margin-top: 20px;
}
/* Player header label — same brand font + clamp as the track title button
   so the header text reads as scale-coherent with the row below. Color is
   the cornerFrame's dim edge color so the header sits visually between the
   gold track label and the dim frame line. Left-aligned to the artwork's
   left edge — both are children of the same padded .albumPlayer container,
   so the artwork's leftmost X = padding-left = header's text-start X.
   margin-bottom adds the user-requested extra breathing room above the row. */
/* Header row: label + decorative barcode side-by-side. align-items:center so
   they share a vertical centerline (label is shorter than the SVG strip).
   gap:20px is half of the "20px visual padding" protocol between barcode
   and label; the other half comes from each element's own visible edges
   (label ends at its glyph right edge, SVG starts at its first bar). */
.albumPlayerHeader {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-bottom: 20px;       /* 20px to track row, per protocol */
}
.albumPlayerHeaderLabel {
  font-family: var(--fontFamilyWoPBrand);
  /* ~2pt smaller than the track-title clamp (14-22 → 12-20) per user. */
  font-size: clamp(12px, calc(0.55vw + 6px), 20px);
  letter-spacing: 0.08em;
  color: #A6DBFE;                    /* full brightness per user */
  flex: 0 0 auto;            /* natural width — label doesn't grow */
  white-space: nowrap;
}
/* SVG strip filling the remaining header width. Height set in CSS as the
   visible cap; syncAlbumPlayerDecorativeBarcodes() sets the viewBox so the
   bars scale to fill width AND either letterbox (when natural ink is
   shorter than this cap) OR symmetric-crop top/bottom (when taller). */
.albumPlayerHeaderBarcode {
  flex: 1 1 auto;
  min-width: 0;              /* allow flex shrink below intrinsic */
  height: 28px;
  display: block;
  overflow: hidden;          /* viewBox crop relies on this */
  opacity: 0.05;             /* matches the in-button barcode (5%) */
}
.albumPlayerHeaderBarcodeText {
  font-family: 'Libre Barcode 128', monospace;
  font-size: 100px;          /* natural size; scaled via viewBox */
  fill: #A6DBFE;             /* matches page-divider barcodes */
  dominant-baseline: hanging;
}
/* One track row — repeats per added track. To add a track: copy this
   block in the HTML (artwork + title button + play icon), swap src,
   title, data-track-id. CSS automatically applies. Tracks stack
   vertically with row-gap so additional rows breathe consistently. */
.albumPlayerTrack {
  display: flex;
  align-items: center;
  gap: var(--albumPlayerRowGap);
  height: var(--albumPlayerRowHeight);
}
.albumPlayerTrack + .albumPlayerTrack {
  margin-top: var(--albumPlayerRowGap);
}
.albumPlayerArtwork {
  width:  var(--albumPlayerRowHeight);
  height: var(--albumPlayerRowHeight);
  object-fit: cover;
  display: block;
  flex: 0 0 auto;
}
/* Title button — cornerFrame styled, fills the middle space between
   artwork and play icon. Click-equivalent to the play icon (JS opens
   the same modal). Hover mirrors the workshop button's text-shift.
   position:relative establishes the stacking context for the decorative
   PROBABILITY-IS-THE-ANSWER barcode that sits behind the label text. */
.albumPlayerTitleButton {
  position: relative;
  flex: 1 1 auto;
  height: var(--albumPlayerRowHeight);   /* frame size locked; font grows inside */
  cursor: pointer;
  padding: 0 clamp(12px, 1.5vw, 28px);
  display: flex;             /* center the label span vertically + horizontally */
  align-items: center;
  justify-content: center;
  overflow: hidden;          /* clip the absolutely-positioned barcode */
  transition: color 200ms ease;
}
.albumPlayerTitleButton:hover {
  --cornerFrameDim: rgba(166, 219, 254, 0.675);
}
.albumPlayerTitleButton:hover .albumPlayerTitleText {
  color: var(--preSaveBorder);
}
/* Decorative barcode — absolutely positioned behind the label text with
   20px inset from each edge per user protocol. Color/opacity match the
   section-divider barcodes (#A6DBFE × 0.15). Sized + cropped by
   syncAlbumPlayerDecorativeBarcodes() same way as the header barcode. */
.albumPlayerTitleBarcode {
  /* 10px inset on ALL sides (consistent). The JS helper sets the SVG's
     viewBox tight to the ink AND preserveAspectRatio:none so the bars
     stretch vertically to fill the box — without that stretch the natural
     barcode aspect would letterbox and the visible ink would sit further
     from the top/bottom edges than from the left/right (user-flagged).
     Explicit width/height — see SVG intrinsic-sizing trap note above. */
  position: absolute;
  top: 10px;
  left: 10px;
  width:  calc(100% - 20px);
  height: calc(100% - 20px);
  z-index: 0;
  display: block;
  overflow: hidden;
  pointer-events: none;
  opacity: 0.05;
}
.albumPlayerTitleBarcodeText {
  font-family: 'Libre Barcode 128', monospace;
  font-size: 100px;
  fill: #A6DBFE;
  dominant-baseline: hanging;
}
/* The label text — paints ON TOP of the barcode. */
.albumPlayerTitleText {
  position: relative;
  z-index: 1;
  font-family: var(--fontFamilyWoPBrand);
  /* +4px over the original clamp per user (14-22 → 18-26). Button height
     stays at --albumPlayerRowHeight (80px) regardless. */
  font-size: clamp(18px, calc(0.55vw + 12px), 26px);
  letter-spacing: 0.08em;
  color: var(--goldYellow);
  /* Stays on one line at wide desktop widths; wraps to a centered second
     line only when the title would otherwise overflow the button (the
     desktop site runs as narrow as 769px — below ~1010px the single's full
     title no longer fits on one line). -webkit-line-clamp caps it at two
     lines and is honored by Chrome, Safari AND Firefox. Two lines at this
     font/line-height clear the locked --albumPlayerRowHeight (80px). */
  white-space: normal;
  text-align: center;
  line-height: 1.15;
  overflow: hidden;
  max-width: 100%;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  transition: color 200ms ease;
}
/* Square play icon — same cornerFrame, contains the SVG triangle. */
.albumPlayerPlayButton {
  width:  var(--albumPlayerRowHeight);
  height: var(--albumPlayerRowHeight);
  flex: 0 0 auto;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: --cornerFrameDim 200ms ease;   /* nudges hover */
}
.albumPlayerPlayButton:hover {
  --cornerFrameDim: rgba(166, 219, 254, 0.675);
}
/* ---- FULL ALBUM track list (the 9 album tracks, below the single) -----
   Container + per-track number chip. Rows carry .albumPlayerTrack so they
   reuse the row / artwork / title-button / play-button rules above; the row
   CSS vars live on .albumPlayer (don't cascade here) so they're re-declared.
   Rows are injected by renderAlbumTrackList() from window.ALBUM_TRACKS. */
.albumTrackList {
  margin-left:  var(--braceLeft, 0px);
  margin-right: 0;
  width: calc(100% - var(--braceLeft, 0px) - var(--braceRight, 0px));
  box-sizing: border-box;
  margin-top: clamp(44px, calc(3vh + 30px), 66px);  /* gap below the single */
  padding: var(--albumPlayerOuterPad, 20px);
  --albumPlayerOuterPad:  20px;
  --albumPlayerRowHeight: 80px;
  --albumPlayerRowGap:    15px;
}
/* Per-track number as a dot-matrix glyph block (canvas) — ON_COLOR_CYAN digits,
   sized to the row height by renderAlbumNumberMatrix(). Intrinsic width; the
   row's flex gap + the canvas's internal L/R padding center the digits between
   the artwork and the title button. */
.albumTrackNumMatrix {
  flex: 0 0 auto;
  display: block;
  align-self: center;
}
/* wireframePlayIcon* shared rules moved to tokens.css so videoPlayer.html
   (which only loads tokens.css) picks them up alongside the inline player
   and album-sampler use. See tokens.css. */

/* === LISTEN TO THE FULL ALBUM ON SPOTIFY CTA ============================
   Replaces the former on-site FULL ALBUM list (album songs aren't hosted
   here). The button reuses .albumPlayerTitleButton (cornerFrame + gold brand
   text) minus the barcode; it's centred and flanked by two 1px cyan hairlines
   (cornerFrame palette) with 1px bright finials, set 20px off the button edges
   and filling out to the brace verticals. JS (syncAlbumSpotifyHairlines) hides
   a hairline once it would render <20px. Top + bottom gaps mirror the former
   single→album gap (clamp matches .albumTrackList margin-top above). */
.albumSpotifyCta {
  margin-left:  var(--braceLeft, 0px);
  width: calc(100% - var(--braceLeft, 0px) - var(--braceRight, 0px));
  box-sizing: border-box;
  /* The album context-frame braces sit --albumTitleGap px BELOW the description
     content box (syncAlbumBraces: braceBottom = lastChild.bottom + titleGap), so
     to leave a true 20px between the VISIBLE brace and the button we add that gap
     here. --albumTitleGap is published on #albumSection by syncAlbumBraces. */
  margin-top:    calc(var(--albumTitleGap, 39px) + 20px);
  /* CTA → player1 is provided by player1's own 20px margin-top; #albumSection is
     a flex column so sibling margins do NOT collapse — keep this 0 or it doubles. */
  margin-bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  --albumPlayerRowHeight: 80px;   /* the title button reads this for its height */
}
.albumSpotifyButton {
  flex: 0 1 auto;          /* fit content + centre (don't stretch like a row) */
  width: auto;
  min-width: 0;
  text-decoration: none;   /* it's an <a> */
}
.albumSpotifyHairline {
  flex: 1 1 0;
  min-width: 0;
  height: 1px;
  margin: 0 20px;          /* 20px standoff from each button edge */
  background: var(--cornerFrameDim, rgba(166, 219, 254, 0.35));
  position: relative;
}
.albumSpotifyHairline::before,
.albumSpotifyHairline::after {
  content: "";
  position: absolute;
  top: 0;
  height: 1px;
  width: var(--cornerArm, 7px);   /* bright finials — cornerFrame arm length */
  background: var(--cornerFrameBright, #A6DBFE);
}
.albumSpotifyHairline::before { left: 0; }
.albumSpotifyHairline::after  { right: 0; }
/* Hidden by JS when the line would be <20px (e.g. wrapped button on narrow
   screens); visibility (not display) keeps the flex gap so the button stays
   centred. */
.albumSpotifyHairlineHidden { visibility: hidden; }

/* === ALBUM player pop-out modal =========================================
   Fixed overlay sitting above #mainContainer (z=10) and #navBar (z=100).
   Two dismissable surfaces: backdrop + close button — both data-modal-
   dismiss="true", picked up by the click handler in albumSiteBootstrap.js.
   Frame interior is intentionally empty for this build (per user — the
   visualizer + audio scrubber arrive in a follow-up pass). */
.albumPlayerModal {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;                  /* always flex — kept for transition */
  align-items: center;
  justify-content: center;
  opacity: 1;
  pointer-events: auto;
  transition: opacity 300ms ease;
}
/* Hidden state: backdrop fades out, frame slides UP off-screen. The
   modal element stays in the layout (no display:none) so CSS transitions
   can run on the toggle. pointer-events:none keeps the invisible-but-
   present modal from intercepting clicks. */
.albumPlayerModal.albumPlayerModalHidden {
  opacity: 0;
  pointer-events: none;
}
.albumPlayerModalBackdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.75);
}
.albumPlayerModalFrame {
  /* Width + height set in JS by syncAlbumPlayerModalLayout — snapped to
     50px multiples so the decorative grid + label scheme stays integer-
     aligned. Fallback dimensions for first paint before JS runs (also
     50px multiples, square-ish). Frame edges use the default cornerFrame
     dim color (rgba 0.35) so they sit visibly less opaque than the
     bright corner reinforcements — same convention as every other
     cornerFrame button on the site. */
  position: relative;
  width:  600px;
  height: 700px;
  box-sizing: border-box;
  /* Drop-down animation — slides in from above the viewport when the
     hidden class is removed. Slight overshoot at the landing via the
     cubic-bezier curve. */
  transform: translateY(0);
  transition: transform 400ms cubic-bezier(0.18, 0.7, 0.25, 1.05);
}
/* In this modal context only, shrink the cornerFrame's bright corner
   arms to 5px — same length as the half-arm of a crosshair (5px from
   intersection center to tip). When a cornerFrame's corner sits on a
   grid intersection, the bright L-bracket exactly overlays two of the
   crosshair's four arms — the reinforcement reads as "the crosshair
   lighting up where the frame's corner sits on it".
   Descendant selector (not just inheritance) because .cornerFrame
   itself sets --cornerArm:7px on every element it's applied to, which
   would override an inherited value. This rule's higher specificity
   (.albumPlayerModal .cornerFrame = 0,2,0 vs .cornerFrame = 0,1,0)
   wins, applying 5px to the modal frame, both cell frames, and the
   close button.

   Also shift every RIGHT and BOTTOM background-position by 1px outward:
   the base cornerFrame paints arms 1px INSIDE each edge, so adjacent
   boxes' shared corners are 1px apart (user-flagged "slightly offset"
   at the X-button/visualizer junction). With this shift, right + bottom
   arms paint AT the grid-line pixel column/row, matching the top + left
   arms (which were already at the grid line because `top 0`/`left 0`
   paints at row/col 0 = the first interior pixel = the grid line). */
.albumPlayerModal .cornerFrame {
  --cornerArm: 5px;
  /* Right + bottom arm alignment with the grid: the cornerFrame paints
     its right/bottom arms at the INSIDE pixel of the box (containerW-1),
     so without compensation a box covering cols 50-650 paints its right
     arm at col 649 — 1 pixel SHORT of the grid line at col 650.
     Backgrounds get clipped at the box's painting area, so we can't
     just shift positions outward (the user observed disappearing right/
     bottom arms when that was tried). Instead, JS extends each
     cornerFrame's BOX by 1px on right and bottom — the original
     cornerFrame positions then paint at the grid lines. The 1px
     overshoot lives in the layout box but contains no content, so the
     overlap between adjacent cornerFrames at shared corners is purely
     decorative (both paint the same color at the shared pixel). */
}
.albumPlayerModal.albumPlayerModalHidden .albumPlayerModalFrame {
  transform: translateY(-110vh);
}
/* Decorative grid SVG — fills the modal interior. Crosshairs + labels
   populated by syncAlbumPlayerModalLayout(). */
.albumPlayerModalGrid {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  /* Below close button + future contents; above cornerFrame background. */
  z-index: 0;
}
.albumPlayerModalGridLine {
  stroke: rgb(150, 200, 230);
  stroke-width: 1;
  opacity: 0.10;
  shape-rendering: crispEdges;
  vector-effect: non-scaling-stroke;
}
/* Dot-field chrome canvas — covers the whole modal frame interior; sits with
   the grid (above the cornerFrame background, below content). Size set by JS. */
.albumPlayerDotField {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  display: block;
  z-index: 0;
}
/* Crosshairs that fall inside a .albumPlayerModalCellFrame footprint —
   dimmed further so the cell frame reads as the primary container and
   the underlying grid hints recede. Class applied by JS during grid
   generation. */
.albumPlayerModalGridLineFramed {
  opacity: 0.04;
}
/* Visualizer field (B2-M10) interior crosshairs — 20% denser than the other
   framed zones (0.04 -> 0.048). Defined after .Framed so the equal-specificity
   rule wins. Class applied by syncAlbumPlayerModalLayout. */
.albumPlayerModalGridLineViz {
  opacity: 0.048;
}
/* Radar-scope underlay inside the visualizer field (B2-M10): concentric rings
   + radial spokes drawn by syncAlbumPlayerModalLayout. Same dim grey as the
   crosshairs; fill:none keeps the circles open; clipped to the field rect.
   opacity is the chart's calibration knob. */
.albumPlayerModalRadarLine {
  fill: none;
  stroke: rgb(150, 200, 230);
  stroke-width: 1;
  opacity: 0.10;
  vector-effect: non-scaling-stroke;
  shape-rendering: geometricPrecision;
}
/* Content cell frames inside the modal — cornerFrame around a range of
   grid cells, marking where future contents (visualizer, scrubber, etc.)
   will live. Position + size set by JS from the data-cells attribute.
   Transparent background so the dimmed crosshairs inside still show. */
.albumPlayerModalCellFrame {
  position: absolute;
  background-color: transparent;
  pointer-events: none;
  box-sizing: border-box;
}
/* Particle visualizer canvas — fills B2-M10 exactly (positioned by
   syncAlbumPlayerModalLayout, which uses the same data-cells parser as
   the cell frames). Sits inside the visualizer cell frame's interior.
   No cornerFrame: the surrounding B2-M10 cell frame already paints the
   border. Black background here would defeat the cell-frame edges, so
   the canvas itself is transparent and the parent modal's dark
   background reads through (the visualizer module clears each frame to
   transparent and draws particles over it). */
.albumPlayerVisualizer {
  position: absolute;
  background-color: transparent;
  pointer-events: none;
  display: block;
}
/* Track-title matrix canvas — fills B11-M12 (positioned by
   syncAlbumPlayerModalLayout). Transparent; albumTitleMatrix.js draws the
   title as diagonal hatch marks. No cornerFrame: the B11-M12 cell frame
   already paints the border. */
.albumPlayerTitleMatrix {
  position: absolute;
  background-color: transparent;
  pointer-events: none;
  display: block;
}
/* Progress bar — 550×20 wireframe rect centered inside B11-M11.
   cornerFrame border with bright corner reinforcements suppressed
   (only the dim edges show). Inner .albumPlayerModalProgressFill
   grows left→right as engine.currentTime advances. 90% opacity cyan
   fill per spec. Click/drag inside the bar seeks via engine.seek(). */
.albumPlayerModalProgressBar {
  position: absolute;
  background-color: transparent;
  box-sizing: border-box;
  cursor: pointer;
  overflow: hidden;
}
/* Specificity matters here: the modal-context override
   `.albumPlayerModal .cornerFrame { --cornerArm: 5px; … }` has
   specificity 0,2,0 — a same-element single-class rule (0,1,0) can't
   beat it. This descendant selector is 0,3,0 and decisively wins,
   making the bright reinforcements invisible AND extending the dim
   edges full-length corner-to-corner. */
.albumPlayerModal .albumPlayerModalProgressBar.cornerFrame {
  --cornerFrameBright: transparent;
  --cornerArm: 0px;
}
/* Legacy solid fill — superseded by the dot-matrix progress fill painted on
   the dot-field canvas (albumDotField.js). Hidden but kept in the DOM. */
.albumPlayerModalProgressFill {
  display: none;
}
/* Side joiners — 1px-wide vertical dim cyan lines at the left + right
   edges of the empty band at rows 11-12 (immediately under the
   visualizer cornerFrame). Stitch the visualizer cornerFrame (B2-M10)
   to the controls cornerFrame (B12-M14) so the two inner cornerFrames
   read as a single continuous frame with the empty band visually
   enclosed. Span rows 11-12 (y=500-600), 100px tall. */
.albumPlayerModalSideJoiner {
  position: absolute;
  /* Positions are in GRID STEPS (--albumModalStep, default 50px, shrunk by
     syncAlbumPlayerModalLayout on narrow viewports). row 10 top = 10 steps;
     span rows 11+12 = 2 steps. */
  top: calc(var(--albumModalStep, 50px) * 10);   /* row 11 top */
  width: 1px;
  height: calc(var(--albumModalStep, 50px) * 2); /* span rows 11 + 12 */
  background-color: rgba(166, 219, 254, 0.35);
  pointer-events: none;
}
.albumPlayerModalSideJoinerLeft  { left: calc(var(--albumModalStep, 50px) * 1);  }
.albumPlayerModalSideJoinerRight { left: calc(var(--albumModalStep, 50px) * 13); }
/* Cell BUTTON: clickable variant of the cell frame. Same cornerFrame
   look, positioned by data-cells (JS), and accepts pointer events.
   Used for B12 pause, C12 mute, F12-I12 MAILING LIST, J12-M12 ALBUM
   FOLLOW PAGE. Width/height set by JS like cell frames (with the +1
   right/bottom extension so corner reinforcements paint at the grid). */
.albumPlayerModalCellButton {
  position: absolute;
  background-color: transparent;
  box-sizing: border-box;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Hover boosts the dim cornerFrame edges (same trick as the album
     player buttons) — slight visual lift on hover. */
}
.albumPlayerModalCellButton:hover {
  --cornerFrameDim: rgba(166, 219, 254, 0.6);
}
/* Pause/play/mute glyph SVGs — fill the button square (60% so there's
   visual margin between the glyph and the cornerFrame edges). The
   .albumPlayerPaused class on the button toggles which glyph shows
   (pause when playing, play when paused). */
.albumPlayerPauseGlyph,
.albumPlayerPlayGlyph,
.albumPlayerMuteGlyph {
  width: 60%;
  height: 60%;
  display: block;
  pointer-events: none;
}
.albumPlayerPlayGlyph { display: none; }
.albumPlayerPaused .albumPlayerPauseGlyph { display: none; }
.albumPlayerPaused .albumPlayerPlayGlyph  { display: block; }
/* Pause/play bars + triangle — solid dim cyan fill, NO corner
   reinforcement (per user — these glyphs are simple, only the button
   frame itself gets the L-bracket treatment). */
.albumPlayerGlyphBar {
  fill: rgb(150, 200, 230);
  shape-rendering: crispEdges;
}
.albumPlayerGlyphStroke {
  fill: none;
  stroke: rgb(150, 200, 230);
  stroke-width: 1.5;
  stroke-linejoin: round;
  shape-rendering: geometricPrecision;
  vector-effect: non-scaling-stroke;
}
/* Mute-state "bend sinister" — 1px diagonal line from top-right to
   bottom-left across the WHOLE button (not just the speaker SVG).
   Sibling SVG that fills the button via inset:0; preserveAspectRatio
   none on the SVG element so the line stretches with button size. */
.albumPlayerMuteSinister {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: none;
  pointer-events: none;
}
.albumPlayerMuted .albumPlayerMuteSinister { display: block; }
.albumPlayerMuteSinisterLine {
  stroke: rgb(150, 200, 230);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  shape-rendering: crispEdges;
}

/* Volume slider — cornerFrame container, 10 vertical pip elements that
   fill left→right as audio.volume rises. JS sets data-filled on the
   container; CSS uses an attribute selector to brighten the pips up to
   that count. */
.albumPlayerModalCellSlider {
  position: absolute;
  background-color: transparent;
  box-sizing: border-box;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 8px;
  gap: 4px;
}
/* Legacy DOM pips — superseded by the dot-matrix volume strip painted on the
   dot-field canvas (albumDotField.js). Hidden but kept in the DOM. */
.albumPlayerVolumePip {
  display: none;
}
.albumPlayerModalCellSlider[data-filled="1"]  .albumPlayerVolumePip:nth-child(-n+1),
.albumPlayerModalCellSlider[data-filled="2"]  .albumPlayerVolumePip:nth-child(-n+2),
.albumPlayerModalCellSlider[data-filled="3"]  .albumPlayerVolumePip:nth-child(-n+3),
.albumPlayerModalCellSlider[data-filled="4"]  .albumPlayerVolumePip:nth-child(-n+4),
.albumPlayerModalCellSlider[data-filled="5"]  .albumPlayerVolumePip:nth-child(-n+5),
.albumPlayerModalCellSlider[data-filled="6"]  .albumPlayerVolumePip:nth-child(-n+6),
.albumPlayerModalCellSlider[data-filled="7"]  .albumPlayerVolumePip:nth-child(-n+7),
.albumPlayerModalCellSlider[data-filled="8"]  .albumPlayerVolumePip:nth-child(-n+8),
.albumPlayerModalCellSlider[data-filled="9"]  .albumPlayerVolumePip:nth-child(-n+9),
.albumPlayerModalCellSlider[data-filled="10"] .albumPlayerVolumePip:nth-child(-n+10) {
  background-color: #A6DBFE;
}

/* Text-bearing cell buttons (MAILING LIST, ALBUM FOLLOW PAGE). */
.albumPlayerModalCellButtonText {
  font-family: var(--fontFamilyWoPBrand);
}
.albumPlayerCellLabel {
  font-size: clamp(11px, 0.85vw, 16px);
  letter-spacing: 0.08em;
  /* Exception to the usual gold-yellow button text — these labels match
     the player border color at 90% opacity per spec (more integrated
     with the cornerFrame vocabulary than a contrasting gold). */
  color: rgba(166, 219, 254, 0.9);
  pointer-events: none;
  white-space: nowrap;
  transition: color 200ms ease;
}
.albumPlayerModalCellButton:hover .albumPlayerCellLabel {
  color: #A6DBFE;
}

/* === VISUALIZER on/off toggle (H1-K1, half height) ====================== */
/* ON uses the default cyan .albumPlayerCellLabel colour; OFF switches the
   label to a muted rust. Hover keeps the rust (no cyan brighten) when OFF. */
.albumPlayerVizToggle.albumPlayerVizOff .albumPlayerVizToggleLabel,
.albumPlayerVizToggle.albumPlayerVizOff:hover .albumPlayerVizToggleLabel {
  color: var(--vizOffColor);
}
/* When OFF, recolour the cornerFrame edges + corner reinforcements to the
   equivalent rust (the cornerFrame gradients read these vars). */
.albumPlayerVizToggle.albumPlayerVizOff {
  --cornerFrameBright: var(--vizOffColor);
  --cornerFrameDim:    rgba(181, 101, 74, 0.35);
}

/* Generic "hide this layer" — used to drop the WebGL viz canvas when the
   visualizer is toggled off, leaving only the radar-scope chart lines. */
.albumPlayerLayerHidden { display: none !important; }

/* === Visualizer-field overlay (B2-M10): click-to-pause + SYSTEM IDLE ===== */
.albumPlayerVizOverlay {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  cursor: pointer;
  z-index: 3;            /* above the viz canvas so it catches clicks */
}
.albumPlayerSystemIdle {
  font-family: var(--fontFamilyWoPBrand);
  font-size: clamp(11px, 0.85vw, 16px);
  letter-spacing: 0.08em;
  color: var(--systemIdleColor);
  pointer-events: none;  /* clicks fall through to the overlay */
  user-select: none;
  /* Track titles can be long — allow wrapping, centred, within the field. */
  white-space: normal;
  max-width: 80%;
  text-align: center;
  line-height: 1.5;
  opacity: 0;            /* hidden unless systemIdleVisible */
}
/* "[PAUSED]" sits on its own line beneath the title. */
.albumPlayerIdlePaused {
  display: block;
}
.albumPlayerVizOverlay.systemIdleVisible .albumPlayerSystemIdle {
  animation: albumSystemIdlePulse 2.8s ease-in-out infinite;
}
@keyframes albumSystemIdlePulse {
  0%, 100% { opacity: 0.12; }
  50%      { opacity: 0.70; }
}
/* Viz-off track-title context: slower pulse, only dipping to ~50% opacity. */
.albumPlayerVizOverlay.systemIdleVisible.albumPlayerIdleTitle .albumPlayerSystemIdle {
  animation: albumIdleTitlePulse 5s ease-in-out infinite;
}
@keyframes albumIdleTitlePulse {
  0%, 100% { opacity: 0.50; }
  50%      { opacity: 1.00; }
}

/* === Liner-notes pull-out (DESKTOP: projects right) ===================== */
/* Panel width tracks the resolved modal step so it scales with the grid. */
/* Gap between player and panel = 1.5× the chevron width (a quarter-chevron of
   clear space on each side of the centred chevron). Declared on the frame so the
   panel margin and the chevron tab share the same values. */
.albumPlayerModalFrame {
  --linerChevronW: 14px;
  --linerGap: calc( var( --linerChevronW ) * 1.5 );
}
/* Liner notes are disabled (LINER_NOTES_TEST=false) — updateLinerNotes() sets the
   `hidden` attribute, but the .albumPlayerLinerTab class's `display:flex` (below)
   outranks the UA `[hidden]` rule, so without this the chevron would still show.
   Force [hidden] to actually hide the tab + panel (shared pop-out → all tracks). */
.albumPlayerLinerTab[hidden],
.albumPlayerLinerPanel[hidden] { display: none !important; }
.albumPlayerLinerPanel {
  --linerPanelW: calc( var( --albumModalStep, 50px ) * 6 );
  position: absolute;
  left: 100%;                 /* hinge on the frame's right edge */
  top: 0;
  height: 100%;
  width: var( --linerPanelW );
  margin-left: var( --linerGap );  /* gap holds the floating chevron */
  opacity: 0;
  transform: translateX( -12px );
  pointer-events: none;
  transition: opacity 280ms ease, transform 280ms ease;
  overflow: hidden;
  /* background-color (NOT the `background` shorthand) so the .cornerFrame
     edge/corner gradients on background-image survive — gives the panel its
     standard cyan edges + corner reinforcements. */
  background-color: var( --backgroundBlack );
}
.albumPlayerLinerPanel.albumPlayerLinerOpen {
  opacity: 1;
  transform: translateX( 0 );
  pointer-events: auto;
}
.albumPlayerLinerDots {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
}
.albumPlayerLinerScroll {
  position: relative;
  z-index: 1;
  height: 100%;
  box-sizing: border-box;
  padding: 20px;
  overflow-y: auto;
  /* Scrollbar fades in only when the cursor is over the panel's right edge
     (albumPlayerLinerScrollActive toggled by JS). */
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
  transition: scrollbar-color 300ms ease;
}
.albumPlayerLinerScroll.albumPlayerLinerScrollActive {
  scrollbar-color: rgba( 166, 219, 254, 0.55 ) transparent;
}
.albumPlayerLinerScroll::-webkit-scrollbar { width: 8px; }
.albumPlayerLinerScroll::-webkit-scrollbar-track { background: transparent; }
.albumPlayerLinerScroll::-webkit-scrollbar-thumb {
  background: transparent;
  transition: background-color 300ms ease;
}
.albumPlayerLinerScroll.albumPlayerLinerScrollActive::-webkit-scrollbar-thumb {
  background: rgba( 166, 219, 254, 0.55 );
}
.albumPlayerLinerText {
  font-family: var( --fontFamilyBody );
  /* Body weight (~400) reduced ~30% per spec → 300. */
  font-weight: 300;
  font-size: clamp( 13px, 0.9vw, 16px );
  line-height: 1.6;
  /* Same colour as the MAILING LIST button label. */
  color: rgba( 166, 219, 254, 0.9 );
}
.albumPlayerLinerText p { margin: 0 0 1em; }

/* Chevron tab at the right-edge centre. */
.albumPlayerLinerTab {
  position: absolute;
  left: 100%;                 /* hug the player's right edge */
  top: 50%;
  transform: translateY( -50% );
  width: var( --linerGap );   /* spans the gap; chevron centres within it */
  height: 64px;
  margin: 0;
  padding: 0;
  border: 0;                  /* kill the default <button> bevel/field */
  box-shadow: none;
  -webkit-appearance: none;
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;    /* shows through to the page behind */
  cursor: pointer;
  z-index: 4;
}
.albumPlayerLinerChevron {
  width: var( --linerChevronW );
  height: var( --linerChevronW );
}
.albumPlayerLinerChevronLine {
  fill: none;
  stroke: #A6DBFE;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
/* Open: flip the chevron to point back toward the player (collapse hint). */
.albumPlayerLinerTab.albumPlayerLinerOpen .albumPlayerLinerChevron {
  transform: rotate( 180deg );
}
.albumPlayerModalGridLabel {
  fill: rgb(150, 200, 230);
  opacity: 0.15;
  font-family: var(--fontFamilyWoPBrand);
  font-size: 8px;
  letter-spacing: 0.05em;
}
/* Close "button module" — occupies the top-right 50×50 grid cell. The
   .cornerFrame class normally paints 12 layers (4 patches + 4 dim edges +
   8 bright corner arms); we override --cornerFrameDim to transparent so
   only the bright corner reinforcements show, with no continuous edges.
   --cornerArm shrunk from the cornerFrame default (7px) so the L-bracket
   arms read at the smaller 50×50 button scale. */
.albumPlayerModalClose,
.albumPlayerModalMinimize {
  position: absolute;
  top: 0;
  /* Box extended by 1px on right + bottom so cornerFrame corner arms
     paint AT the grid lines. The modal frame itself is already
     extended to w+1 in JS, so `right: 0` here positions the button's
     right edge at the modal's painted right (= grid col 700 + 1px
     overshoot). Button left lands at col 650 — exactly the shared
     grid intersection with the visualizer's TR corner. */
  /* One grid cell (+1px paint extension), scaled by the modal step. */
  width:  calc(var(--albumModalStep, 50px) + 1px);
  height: calc(var(--albumModalStep, 50px) + 1px);
  padding: 0;
  cursor: pointer;
  z-index: 1;
  --cornerFrameDim: transparent;
  /* Transparent fill instead of the cornerFrame utility's default opaque black
     (--backgroundBlack), so the dot-field's OFF_COLOR_BACKGROUND modules in
     cells M1/N1 show through BEHIND the minimize + close buttons. The bright
     corner-arm brackets paint via background-image, so they still show. */
  background-color: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
}
.albumPlayerModalClose    { right:  0;  }                              /* cell N1 (top-right) */
.albumPlayerModalMinimize { right: calc(var(--albumModalStep, 50px)); } /* one cell left of close */
.albumPlayerModalCloseIcon,
.albumPlayerModalMinimizeIcon {
  width: 100%;
  height: 100%;
  display: block;
  /* Clicks pass through to the parent button — without this the SVG's
     <line>/<rect> children become the click target and the parent
     button's data-modal-dismiss attribute is never read. */
  pointer-events: none;
}
/* Minimize "−" — single 1px horizontal stroke, same color as the X. */
.albumPlayerModalMinimizeLine {
  stroke: rgb(150, 200, 230);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  shape-rendering: crispEdges;
}
.albumPlayerModalMinimize:hover .albumPlayerModalMinimizeLine {
  stroke: #A6DBFE;
}
/* X arms — dim, same color as the modal frame edges + grid (no opacity
   because the modal sits on a solid black background and we want the
   color stable). Width 1px, non-scaling so it stays 1px even though the
   SVG viewBox (0-50) scales 1:1 with the 50px button (no scaling in
   practice, but defensive). */
.albumPlayerModalCloseSides {
  stroke: rgb(150, 200, 230);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  shape-rendering: crispEdges;
}
/* 1px bright dots — reinforce the 4 tips + the center cross-joint. */
.albumPlayerModalCloseDot {
  fill: #A6DBFE;
  shape-rendering: crispEdges;
}
.albumPlayerModalClose:hover .albumPlayerModalCloseSides {
  stroke: #A6DBFE;
}

/* === YEHOSHUA section ===================================================
   Avatar left aligns to ALBUM body text left (= --bodyIndent). Bio is
   vertically centered against the avatar (align-items:center). Bio's
   right margin mirrors ALBUM body right (= --bodyIndent). A second
   PRE-SAVE button sits below the social row so the section has enough
   vertical content for the title-lands-at-top anchor-scroll to work. */

/* Title conformed to style guide (matches MY OWN DREAMS, THE CROWN…):
   weight 500→400, size -2pt off the base --titleSize clamp. */
#yehoshuaSection .sectionTitle {
  font-weight: 400;
  font-size: calc(var(--titleSize) - 2pt);
}

/* YEHOSHUA-page "context frame" braces.
   Same outer-X margins as the other sections (read --braceWidth from
   #musicVideoSection via JS). Vertical span: title midpoint → email-
   widget vertical center. Bottom bars are LONG (extending inward toward
   the email widget) and terminate titleGap short of the widget's sides.
   Top bars follow the same symmetric-title-padding rule as before
   (short left + extended right). The two braces don't join — the email
   widget sits in the gap. */
section#yehoshuaSection {
  position: relative;
  --braceBright: #A6DBFE;
  --braceDim:    rgba(166, 219, 254, 0.35);
  --braceArm:    7px;
}
#yehoshuaSection::before,
#yehoshuaSection::after {
  content: "";
  position: absolute;
  top:    var(--braceTop,    0);
  height: var(--braceHeight, 0);
  pointer-events: none;
  background-repeat: no-repeat;
}
/* LEFT brace [ ----------------------------------------------------------
   Box width = long bottom-bar width. Top bar is SHORT (braceWidth) and
   sits in the leftmost area of the box; bottom bar is LONG (fills box).
   Vertical line at the box's left edge. */
#yehoshuaSection::before {
  left:  var(--braceLeft,         -15px);
  width: var(--braceBotLeftWidth,  15px);
  background-image:
    /* 2 corner reinforcements: TL + BL (outer corners only) */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    /* DIM segments: top bar (short), vertical, bottom bar (long) */
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    /* BRIGHT corner arms at outer corners: TL h, TL v, BL h, BL v */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    /* 2 terminals: TR (top bar's free end), BR (bottom bar's free end) */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright));
  background-size:
    2px 2px, 2px 2px,                                                /* TL + BL reinforcements */
    calc(var(--braceWidth) - var(--braceArm) - 1px) 1px,              /* dim top bar (short) */
    1px calc(100% - 2 * var(--braceArm)),                             /* dim vertical */
    calc(100% - var(--braceArm) - 1px) 1px,                           /* dim bottom bar (long, 1px shy of right edge for the BR terminal) */
    var(--braceArm) 1px, 1px var(--braceArm),                         /* TL h, TL v */
    var(--braceArm) 1px, 1px var(--braceArm),                         /* BL h, BL v */
    1px 1px,                                                          /* TR terminal */
    1px 1px;                                                          /* BR terminal */
  background-position:
    left 0 top 0,                                                     /* TL reinforcement */
    left 0 bottom 0,                                                  /* BL reinforcement */
    var(--braceArm) 0,                                                /* dim top bar */
    0 50%,                                                            /* dim vertical */
    var(--braceArm) 100%,                                             /* dim bottom bar */
    left top, left top,                                               /* TL h, TL v */
    left bottom, left bottom,                                         /* BL h, BL v */
    calc(var(--braceWidth) - 1px) 0,                                  /* TR terminal — top bar free end */
    right 0 bottom 0;                                                 /* BR terminal — bottom bar free end (right edge of box) */
}

/* RIGHT brace ] --------------------------------------------------------
   Box width = long top-bar width. Top bar is LONG (extends across to
   terminate symmetric to the title); bottom bar is SHORT (anchored to
   the right side of the box). Vertical line at the box's right edge. */
#yehoshuaSection::after {
  right: var(--braceRight,           -15px);
  width: var(--braceTopRightWidth,    15px);
  background-image:
    /* 2 corner reinforcements: TR + BR */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    /* DIM segments: top bar (long), vertical, bottom bar (short) */
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    linear-gradient(var(--braceDim),    var(--braceDim)),
    /* BRIGHT corner arms: TR h, TR v, BR h, BR v */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright)),
    /* 2 terminals: TL (top bar free end), BL (bottom bar free end) */
    linear-gradient(var(--braceBright), var(--braceBright)),
    linear-gradient(var(--braceBright), var(--braceBright));
  background-size:
    2px 2px, 2px 2px,                                                /* TR + BR reinforcements */
    calc(100% - var(--braceArm) - 1px) 1px,                           /* dim top bar (long) */
    1px calc(100% - 2 * var(--braceArm)),                             /* dim vertical */
    calc(var(--braceBotRightWidth) - var(--braceArm) - 1px) 1px,      /* dim bottom bar (short) */
    var(--braceArm) 1px, 1px var(--braceArm),                         /* TR h, TR v */
    var(--braceArm) 1px, 1px var(--braceArm),                         /* BR h, BR v */
    1px 1px,                                                          /* TL terminal */
    1px 1px;                                                          /* BL terminal */
  background-position:
    right 0 top 0,                                                    /* TR reinforcement */
    right 0 bottom 0,                                                 /* BR reinforcement */
    1px 0,                                                            /* dim top — starts at 1px after the TL terminal */
    100% 50%,                                                         /* dim vertical at right edge */
    right var(--braceArm) bottom 0,                                   /* dim bot — right end at arm from container right */
    right top, right top,                                             /* TR h, TR v */
    right bottom, right bottom,                                       /* BR h, BR v */
    0 0,                                                              /* TL terminal — top-left corner of box */
    right calc(var(--braceBotRightWidth) - 1px) bottom 0;             /* BL terminal — at free end of short bottom bar */
}

.yehoshuaContent {
  display: flex;
  align-items: center;
  gap: var(--sectionContentGap);
  margin-top: clamp(20px, 2.5vh, 48px);
  padding-left:  var(--bodyIndent);
  padding-right: var(--bodyIndent);
}

/* Avatar size tracks --bodySize so it stays visually proportional to the
   bio text next to it (vertical centering is handled by align-items:center
   on .yehoshuaContent regardless of the two heights). */
.yehoshuaAvatar {
  width:  clamp(130px, calc(8 * var(--bodySize)), 260px);
  height: clamp(130px, calc(8 * var(--bodySize)), 260px);
  border-radius: 50%;
  overflow: hidden;
  flex-shrink: 0;
}
.yehoshuaAvatar img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Reticle frame around the avatar: 4 cardinal tick marks + 2 quadrant
   arcs (9→12 and 3→6), all in the same dim cyan as the section braces.
   Padding (10 standoff + 7 tick length = 17px each side) creates the
   space; the ticks live in background-image, and each arc is a 1px
   border-radius:50% pseudo-element masked to one quadrant via a
   conic-gradient. The avatar nests inside, centered. */
.yehoshuaAvatarFrame {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 17px;
  box-sizing: content-box;     /* padding adds to outer size so the avatar inside keeps its full clamp dim */
  flex-shrink: 0;
  --reticleStandoff: 10px;     /* gap between avatar edge and tick / arc */
  --reticleArm:      7px;      /* tick length (matches --braceArm) */
  background-image:
    /* 4 bright 1×1 TERMINAL pixels at the outer end of each tick (listed
       first so they paint on top of the dim tick beneath them). */
    linear-gradient(var(--braceBright), var(--braceBright)),  /* 12 terminal */
    linear-gradient(var(--braceBright), var(--braceBright)),  /* 6  terminal */
    linear-gradient(var(--braceBright), var(--braceBright)),  /* 3  terminal */
    linear-gradient(var(--braceBright), var(--braceBright)),  /* 9  terminal */
    /* 4 dim tick bodies. */
    linear-gradient(var(--braceDim), var(--braceDim)),  /* 12 tick (top) */
    linear-gradient(var(--braceDim), var(--braceDim)),  /* 6  tick (bottom) */
    linear-gradient(var(--braceDim), var(--braceDim)),  /* 3  tick (right) */
    linear-gradient(var(--braceDim), var(--braceDim));  /* 9  tick (left) */
  background-size:
    1px 1px, 1px 1px, 1px 1px, 1px 1px,                  /* terminals */
    1px var(--reticleArm),
    1px var(--reticleArm),
    var(--reticleArm) 1px,
    var(--reticleArm) 1px;
  background-position:
    50% 0, 50% 100%, 100% 50%, 0 50%,                    /* terminals at outer ends */
    50% 0,
    50% 100%,
    100% 50%,
    0    50%;
  background-repeat: no-repeat;
}
.yehoshuaAvatarFrame::before,
.yehoshuaAvatarFrame::after {
  content: "";
  position: absolute;
  /* inset = padding − standoff = 17 − 10 = 7 → the border-radius circle
     drawn here sits exactly --reticleStandoff outside the avatar's edge. */
  inset: calc(17px - var(--reticleStandoff));
  border: 1px solid var(--braceDim);
  border-radius: 50%;
  pointer-events: none;
}
/* TL arc (9 → 12, clockwise): conic-gradient from 9 o'clock (270°) clockwise
   for 90°. Black region reveals the arc; transparent hides the rest of the
   full-ring border. */
.yehoshuaAvatarFrame::before {
  -webkit-mask: conic-gradient(from 270deg, #000 90deg, transparent 90deg);
          mask: conic-gradient(from 270deg, #000 90deg, transparent 90deg);
}
/* BR arc (3 → 6, clockwise): from 3 o'clock (90°). */
.yehoshuaAvatarFrame::after {
  -webkit-mask: conic-gradient(from 90deg,  #000 90deg, transparent 90deg);
          mask: conic-gradient(from 90deg,  #000 90deg, transparent 90deg);
}

.yehoshuaBio {
  flex: 1;
  font-family: var(--fontFamilyBody);
  font-weight: 400;
  font-size: var(--bodySize);
  line-height: 1.5;
  /* Conformed to style-guide color space. Weight preserved per user. */
  color: #E3F2FF;
}
.yehoshuaBio p {
  margin: 0 0 1.4em;
}
.yehoshuaBio p:last-child {
  margin-bottom: 0;
}

#connectSocialRow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sectionContentGap);
  /* Top margin reduced by ~30px (≈ half the social row's rendered height)
     from the prior clamp(60,7vh,140) — together with the gap-below-social
     that alignBottomLayout pins to gap-above-social, this moves the
     social row UP by ~30 (in flow) and the email form UP by ~60 in the
     viewport on EMAIL SIGN UP landing. Goal: bring the form visually
     higher in the YEHOSHUA scroll context. */
  margin: calc(clamp(60px, 7vh, 140px) - 30px) 0 0;
  /* Anchor target for the splash EMAIL SIGN UP button. scroll-margin-top
     places the social row just below the nav bar on landing so the form
     (which lives directly below this row inside yehoshuaSection) is in
     context. */
  scroll-margin-top: var(--navBarHeight);
}

/* Spacing above the email form (G1) — calibrated to match the gap
   below the form down to the pre-save button (G2) so the form sits
   visually centered between its neighbors on EMAIL SIGN UP landing. */
.yehoshuaEmailFormWrapper {
  margin-top: 20px;
}

/* YEHOSHUA section spans the available viewport below the nav bar so the
   nav-anchor scroll can actually place the title at the top of the
   viewport. Inside the section, the title + avatar/bio + social row sit
   at their natural positions near the top; the second PRE-SAVE button
   gets margin-top:auto in the column flex, which pushes it down to the
   bottom of the section — filling "the new blank space" per the spec. */
/* YEHOSHUA is no longer the last scrollable section — MAILING LIST and
   ALBUM PRE-SAVE follow it — so the prior min-height override that subtracted
   footer headroom (to pull the footer up into YEHOSHUA's viewport) has been
   removed. Section now uses the generic #mainContainer section min-height.
   The pre-save button that lived at the end of YEHOSHUA was extracted into
   #albumPreSaveSection. */

/* Section's internal padding-top zeroed so the whole YEHOSHUA section
   contents (title, bio, social, form) shift UP by ~24-27px (the original
   sectionVerticalPadding). The brace bottom (= email-form midpoint)
   correspondingly shifts up on YEHOSHUA nav-anchor landing, pulling it
   into the viewport on shorter displays. EMAIL SIGN UP landing is
   unaffected — its scroll target (#connectSocialRow) shifts up by the
   same amount, so the anchor-scroll viewport position is preserved. */
#yehoshuaSection {
  padding-top: 0;
}

/* === ALBUM PRE-SAVE section ============================================= */
/* Just the pre-save button. Shorter section than the standard 100dvh — the
   button doesn't need a full viewport to itself. */
section#albumPreSaveSection {
  min-height: 0;
  /* padding-top contributes to G2 (form→preSave gap), paired with
     yehoshuaSection.padding-bottom; sum equals .yehoshuaEmailFormWrapper
     margin-top (G1) so both gaps around the form are equal. */
  padding: 8px 0 0 0;
}
/* .preSaveRow's global rule sets margin-top/bottom: clamp(20,2.5vh,48)
   for spacing on the music-video page. Inside albumPreSaveSection we
   want zero margin so the button sits tight to section padding — keeps
   G2 (form→preSave) equal to G1 (social→form). */
#albumPreSaveSection .preSaveRow {
  margin-top: 0;
  margin-bottom: 0;
}
/* yehoshuaSection has the form as its last child. Override its
   min-height (the inherited calc(100dvh - navBarHeight) was creating
   huge dead space below the form on tall viewports), and override
   padding-bottom to contribute exactly the right amount to G2. */
section#yehoshuaSection {
  min-height: 0;
  padding-bottom: 12px;
}

/* Footer trailing margin is set dynamically by
   alignFooterToEmailLandingViewport() in albumSiteBootstrap.js. Pure
   CSS calc couldn't precisely satisfy this because nav height, footer
   padding, and social row height all use clamp(vh) — the required
   margin doesn't simplify. JS reads actual rendered positions and sets
   the right value. */
#albumPreSaveSection .sectionContainer {
  display: flex;
  justify-content: center;
  align-items: center;
}

/* === Footer credits ===================================================== */

#siteFooter {
  padding: clamp(28px, 3vh, 56px) clamp(16px, 2vw, 40px);
  text-align: center;
  font-family: var(--fontFamilyBody);
  font-weight: 400;
  font-size: var(--captionSize);
  color: var(--bodyTextTertiary);
  background: var(--backgroundBlack);
  position: relative;
  z-index: 10;
  /* Sizes the WoP logo button inside (tokens.css reads this variable). */
  --logoBtnHeight: clamp(48px, calc(1.8vw + 33.6px), 76.8px);
}
