/* ==========================================================================
   BASSCRAFT — Premium Apple-Style Design System v4
   Cal Sans · Responsive · 4K-Ready · Seamless Scroll Fades
   ========================================================================== */

/* ── Cal Sans Fonts ── */
@font-face { font-family: 'CalSans'; src: url('assets/fonts/CalSans-Regular.ttf') format('truetype'); font-weight: 400; }
@font-face { font-family: 'CalSansUI'; src: url('assets/fonts/CalSansUI-Light.woff2') format('woff2'); font-weight: 300; }
@font-face { font-family: 'CalSansUI'; src: url('assets/fonts/CalSansUI-Regular.woff2') format('woff2'); font-weight: 400; }
@font-face { font-family: 'CalSansUI'; src: url('assets/fonts/CalSansUI-Medium.woff2') format('woff2'); font-weight: 500; }
@font-face { font-family: 'CalSansUI'; src: url('assets/fonts/CalSansUI-SemiBold.woff2') format('woff2'); font-weight: 600; }
@font-face { font-family: 'CalSansUI'; src: url('assets/fonts/CalSansUI-Bold.woff2') format('woff2'); font-weight: 700; }
@font-face { font-family: 'CalSansText'; src: url('assets/fonts/CalSansText-Light.woff2') format('woff2'); font-weight: 300; }
@font-face { font-family: 'CalSansText'; src: url('assets/fonts/CalSansText-Regular.woff2') format('woff2'); font-weight: 400; }
@font-face { font-family: 'CalSansText'; src: url('assets/fonts/CalSansText-Medium.woff2') format('woff2'); font-weight: 500; }
@font-face { font-family: 'CalSansText'; src: url('assets/fonts/CalSansText-SemiBold.woff2') format('woff2'); font-weight: 600; }
@font-face { font-family: 'CalSansText'; src: url('assets/fonts/CalSansText-Bold.woff2') format('woff2'); font-weight: 700; }

/* ── Tokens ── */
:root {
  --bg: #000000;
  --bg-light: #f5f5f7;
  --white: #ffffff;
  --dark: #1d1d1f;
  --grey: #86868b;
  --grey-light: #a1a1a6;
  --grey-dark: #6e6e73;
  /* Brushed-silver accent, lifted from the nav's BUY NOW pill and B mark so the
     pricing cards' detailing reads as the same material instead of flat black.
     Kept as tokens because it's now used in several places. */
  --silver: #aeb9c9;
  --silver-sheen: linear-gradient(110deg, #abb7c7, #bac6d6, #a5b0c2, #bac6d6, #abb7c7);
  --silver-ink: #ffffff;
  --glow: 0 0 30px rgba(180,225,255,0.35), 0 0 60px rgba(180,225,255,0.15);
  --glow-sm: 0 0 15px rgba(180,225,255,0.25);
  --nav-h: 52px;
  --r: 24px;
  --r-sm: 16px;
}

/* ── Reset ── */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* Every in-page nav link (desktop bar + mobile hamburger) uses
   scrollIntoView({block:'start'}), which lands a section's top edge flush
   with the very top of the viewport — right under the fixed nav, and with
   nothing above the heading to read. scroll-padding-top is what
   scrollIntoView actually measures "start" against, so this single rule
   fixes every one of those calls at once: sections now land with a
   breathing-room gap below the nav instead of butted up against it. */
/* scroll-padding-top is what scrollIntoView measures "start" against. The nav
   is a fixed 90px bar, so this is that height plus a small gap: sections land
   exactly at their own top edge, clear of the bar rather than tucked under it.
   (Was 20vh, which pushed every section a fifth of the way down the screen.) */
html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; scroll-padding-top: 100px; }
video { background-color: #000; }
body {
  background: #f7f4ef; color: var(--white);
  font-family: 'CalSansText','CalSansUI',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  overflow-x: hidden; line-height: 1.5;
  display: flex; flex-direction: column; align-items: center;
}
#site-wrapper {
  width: 100%; max-width: 1600px;
  background: #f7f4ef;
  position: relative;
  box-shadow: 0 0 60px rgba(0,0,0,0.12);
  overflow: hidden;
}
a { text-decoration: none; color: inherit; }
img { max-width: 100%; display: block; }
video { display: block; width: 100%; }
button { font-family: inherit; }

/* ── Containers — 4K-ready with wider bounds ── */
.container { max-width: min(1400px, 88vw); margin: 0 auto; padding: 0 5%; }

/* ── Sections ── */
.s-dark { background: var(--bg); color: var(--white); padding: clamp(80px,10vw,140px) 0; position: relative; }
.s-light { background: var(--bg-light); color: var(--dark); padding: clamp(80px,10vw,140px) 0; border-radius: 24px; overflow: hidden; margin: 0 12px; }

/* ── Typography ── */
h2 {
  font-family: 'CalSansUI',sans-serif; font-weight: 700;
  font-size: clamp(2.4rem,4.5vw,4.5rem); letter-spacing: -0.025em; line-height: 1.08;
}
h3 { font-family: 'CalSansUI',sans-serif; font-size: clamp(1.2rem,1.8vw,1.6rem); font-weight: 600; }
h4 { font-family: 'CalSansUI',sans-serif; font-size: clamp(1rem,1.4vw,1.3rem); font-weight: 600; }
p {
  font-family: 'CalSansText',sans-serif; font-weight: 400;
  line-height: 1.7; font-size: clamp(1rem,1.1vw,1.15rem); color: var(--grey-light);
}
.s-light h2 { color: var(--dark); }
.s-light p { color: var(--grey-dark); }
.glow { text-shadow: var(--glow); }
.tc { text-align: center; }

/* ── Spacing ── */
.mt-2 { margin-top: .75rem; } .mt-3 { margin-top: 1rem; } .mt-4 { margin-top: 2rem; } .mt-5 { margin-top: 3rem; }
.mb-3 { margin-bottom: 1rem; } .mb-4 { margin-bottom: 2rem; } .mb-5 { margin-bottom: 3rem; }

/* ═══ NAV ═══ */
.navbar {
  position: fixed; top: 0; left: 50%; transform: translateX(-50%);
  width: 100%; max-width: 1600px; height: 90px;
  /* Transparent (just a soft top scrim for legibility) over the hero so the video isn't
     cropped by a solid bar; goes solid+blurred once scrolled past the hero (.scrolled). */
  background: linear-gradient(to bottom, rgba(0,0,0,0.32) 0%, rgba(0,0,0,0) 100%);
  backdrop-filter: none; -webkit-backdrop-filter: none;
  transition: background .4s ease, backdrop-filter .4s ease, border-color .4s ease;
  display: flex; align-items: center; padding: 0 5%;
  z-index: 9999; border-bottom: 1px solid transparent;
}
.nav-left {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  transition: flex 0.4s ease;
}
.nav-logo {
  transition: flex 0.4s ease;
}
.nav-links {
  flex: 0 0 auto;
  transition: transform 0.4s ease, margin 0.4s ease;
}
.nav-right {
  flex: 0 0 0px;
  width: 0;
  opacity: 0;
  overflow: hidden;
  display: flex; justify-content: flex-end; align-items: center; gap: 15px;
  transition: flex 0.4s ease, opacity 0.4s ease, width 0.4s ease;
}
.navbar.show-buy-btn .nav-right {
  flex: 1 1 0;
  width: auto;
  opacity: 1;
  overflow: visible;
}
.navbar.scrolled {
  background: rgba(0,0,0,0.72);
  backdrop-filter: saturate(180%) blur(20px); -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid rgba(255,255,255,0.06);
}
.nav-logo img { height: 50px; cursor: pointer; transition: opacity 0.3s; }
.nav-logo img:hover { opacity: 0.7; }
.nav-links { display: flex; gap: 1.6rem; }
.nav-links a {
  position: relative;
  font-family: 'CalSansText',sans-serif; font-size: 12px; letter-spacing: .1em;
  text-transform: uppercase; opacity: 1; transition: opacity .3s, text-shadow .4s;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8), 0 0 8px rgba(0, 0, 0, 0.4);
}
.nav-links a:hover { text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9), 0 0 12px rgba(0, 0, 0, 0.6); }

/* Scroll-spy indicator (set via IntersectionObserver in app.js) — kept
   deliberately faint: a soft glow plus two tiny particles drifting off the
   top of the letters, not a loud "you are here" badge. */
.nav-links a.nav-active {
  opacity: 1;
  /* +20% intensity over the original pass — was reading as too faint. */
  text-shadow: 0 0 4px rgba(180,225,255,0.6), 0 0 9px rgba(180,225,255,0.26);
}
.nav-links a.nav-active::before,
.nav-links a.nav-active::after {
  content: ''; position: absolute; top: 2px;
  width: 2px; height: 2px; border-radius: 50%;
  background: rgba(180,225,255,1);
  opacity: 0; pointer-events: none;
  animation: navParticleFloat 2.6s ease-in-out infinite;
}
.nav-links a.nav-active::before { left: 25%; animation-delay: 0s; }
.nav-links a.nav-active::after { left: 70%; animation-delay: 1.3s; }
@keyframes navParticleFloat {
  0% { opacity: 0; transform: translateY(2px) scale(0.6); }
  20% { opacity: 0.6; }
  75% { opacity: 0.14; }
  100% { opacity: 0; transform: translateY(-9px) scale(1); }
}
.nav-cta {
  background: var(--white); color: var(--dark); padding: 7px 18px;
  border-radius: 20px; font-size: 11px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; transition: transform .3s, opacity .3s; border: none; cursor: pointer;
}
.nav-cta:hover { transform: scale(1.05); opacity: .9; }
/* ═══ MOBILE MENU ═══ */
.mobile-menu-btn {
  display: none; /* only shown once .scrolled, <=768px — see below */
  background-color: #aeb9c9;
  background-image: linear-gradient(110deg, #abb7c7, #bac6d6, #a5b0c2, #bac6d6, #abb7c7);
  background-size: 200% 200%;
  animation: roving-gradient 14s linear infinite;
  border: 1px solid rgba(255,255,255,0.4);
  border-radius: 50%;
  box-shadow: 0 0 15px rgba(255, 255, 255, 0.15), inset 0 0 10px rgba(255, 255, 255, 0.4);
  padding: 6px; margin: 0; cursor: pointer;
  color: var(--silver-ink); width: 36px; height: 36px;
  align-items: center; justify-content: center;
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  transition: transform 0.2s, box-shadow 0.2s, left 0.4s ease;
}
.mobile-menu-btn:active { transform: translate(-50%, -50%) scale(0.95); }
.mobile-menu-btn svg { width: 20px; height: 20px; }
/* On an SVG <line>, transform-origin:center resolves against the VIEW-BOX, not
   the line's own box — so the old translate-then-rotate order rotated each bar
   about the icon's centre while it was still sitting at its original y, swinging
   it off onto a diagonal that never met its partner. Hence the half-formed X.
   Pinning the origin to the view-box centre and translating FIRST (rightmost in
   a transform list is applied first) moves each bar onto the centre line before
   it rotates, so both land exactly on the diagonals. */
.mobile-menu-btn .bar { transition: transform 0.3s ease, opacity 0.2s ease; transform-box: view-box; transform-origin: 12px 12px; }
.mobile-menu-btn.is-active .bar-top { transform: rotate(45deg) translateY(5px); }
.mobile-menu-btn.is-active .bar-mid { opacity: 0; }
.mobile-menu-btn.is-active .bar-bot { transform: rotate(-45deg) translateY(-5px); }

.mobile-nav-menu {
  display: none; /* becomes flex only inside the media query below */
  position: fixed; top: 90px; left: 0; right: 0; z-index: 9998;
  flex-direction: column;
  background: rgba(10,10,10,0.94);
  backdrop-filter: saturate(180%) blur(20px); -webkit-backdrop-filter: saturate(180%) blur(20px);
  /* No border while closed. max-height:0 collapses the CONTENT box, but a
     border sits outside it and still paints — and since the background paints
     through the border box too, that near-invisible 0.5px rule was rendering
     as a hard 1px dark strip of this panel's near-black background right under
     the nav. Desktop never showed it because the drawer is display:none there.
     Only the navbar's own hairline should be visible when this is shut. */
  border-bottom: 0;
  max-height: 0; overflow: hidden;
  transition: max-height 0.35s ease;
}
.mobile-nav-menu.is-open { max-height: 400px; border-bottom: 0.5px solid rgba(255,255,255,0.04); }
.mobile-nav-menu a {
  color: #fff; font-family: 'CalSansText',sans-serif; font-size: 0.95rem;
  letter-spacing: 0.06em; text-transform: uppercase;
  padding: 16px 24px; border-bottom: 1px solid rgba(255,255,255,0.06);
}
.mobile-nav-menu a:active { background: rgba(255,255,255,0.06); }

@media (max-width: 768px) {
  .hero-title { font-size: clamp(3.5rem, 14vw, 4.5rem); line-height: 1.05; }
  .mobile-break { display: block; }
  .nav-links { display: none; }
  .nav-logo .logo-white { width: 160px; height: 16px; }
  .nav-logo .logo-icon { width: 35px; height: 28px; }
  /* .nav-logo stacks both marks in one grid cell (see place-items rule below),
     so the cell sizes itself to whichever is WIDER — the 160px wordmark, even
     once .scrolled fades it to opacity:0. Invisible content still occupies
     its box; that reserved-but-unseen 160px was squeezing BUY NOW right up
     against (and on real, slightly-narrower devices, past) the screen edge.
     Once scrolled only the 35px icon is actually showing, so shrink the
     container to match it and give the rest of the bar room again. */
  .navbar.scrolled .nav-logo { width: 35px; }
  .navbar { justify-content: center; }
  .navbar.scrolled { justify-content: space-between; }
  .nav-left { justify-content: center; }
  .navbar.scrolled .nav-left { justify-content: flex-start; }
  .navbar.scrolled .mobile-menu-btn { display: flex; }
  /* Centering the hamburger on the whole bar (left:50%, the base rule) looks
     right only while BUY NOW is still collapsed — logo on the left, nothing
     on the right yet. Once BUY NOW actually appears (.show-buy-btn), true
     50% sits past the true midpoint between the (narrow) icon and the
     (wider) button, so the hamburger read as glued to BUY NOW with the logo
     off on its own. Measured the gap on each side and shifted just the
     hamburger to where they land equal — logo|33px|hamburger|33px|BUY NOW
     at a 390px-wide screen, roughly balanced down to 320px too. */
  .navbar.scrolled.show-buy-btn .mobile-menu-btn { left: 48.5%; }
  /* The B mark otherwise sits flush against the bar's 6% padding edge, which
     left a much bigger gap between it and the (now nudged-right) hamburger
     than between the hamburger and BUY NOW. Small inset to even that out. */
  .navbar.scrolled .nav-logo { margin-left: 14px; }
  .mobile-nav-menu { display: flex; }
  #global-mute-btn { display: none !important; }}

/* ═══ HERO ═══ */
.hero { position: relative; height: 100vh; height: 100svh; max-height: 1200px; width: 100%; overflow: hidden; }
.hero-bg { position: absolute; inset: 0; }
.hero-bg video {
  position: absolute; top: 0; left: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  object-position: top;
  z-index: 0;
}
@media (max-width: 768px) {
  .hero-bg video {
    object-fit: cover;
    object-position: center;
    height: calc(100% + 30px);
    top: -30px;
  }}
/* Sits above the video (hiding it) until playback has genuinely started, then
   crossfades out — see the inline script beside the hero video markup. */
.hero-poster {
  position: absolute; top: 0; left: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  object-position: top;
  z-index: 1;
  transition: opacity 90ms linear;
}
@media (max-width: 768px) {
  .hero-poster {
    object-fit: cover;
    object-position: center;
  }}
.hero-poster.is-hidden { opacity: 0; pointer-events: none; }
.hero-dim { position: absolute; inset: 0; z-index: 2; background: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.15) 100%); }
.hero-center h1 {
  text-shadow: 0 2px 16px rgba(0,0,0,0.6), 0 1px 4px rgba(0,0,0,0.4);
}
/* Three equal-rail grid (1fr | auto | 1fr) rather than flex space-between.
   The button sits in the middle column, and because the two 1fr rails are
   always equal it lands on the section's true horizontal centre — not just
   centred in whatever space the copy left over. The copy is boxed into
   column 1, so the two can never run into each other at any width. */
.hero-center {
  position: absolute; inset: 0; display: grid;
  grid-template-columns: 1fr auto 1fr;
  /* Two rows: the CTA rides in row 1, the copy in row 2, and align-content:end
     packs the pair against the bottom padding. That lifts the button clear
     above the headline instead of sitting level with it, while the middle
     column keeps it on the true horizontal centre. */
  grid-template-rows: auto auto;
  align-content: end;
  z-index: 10; column-gap: 20px; row-gap: clamp(18px, 2.4vh, 34px);
  padding-bottom: clamp(60px, 8vh, 100px);
  padding-left: clamp(30px, 6vw, 100px); padding-right: clamp(30px, 6vw, 100px);
}
.hero-text-group {
  grid-row: 2; grid-column: 1;
  display: flex; flex-direction: column; align-items: flex-start; text-align: left;
  min-width: 0; /* let the column actually constrain the text instead of overflowing */
}
.hero-center .hero-cta { grid-row: 1; grid-column: 2; justify-self: center; }
.hero-center h1, .hero-center p { max-width: 100%; margin: 0; }
@media (max-width: 768px) {
  /* Phones stack instead: single column, everything centred, button under the
     copy. (The desktop rule above is a grid now, so this re-declares the grid
     rather than the old flex-direction/justify-content, which grid ignores.) */
  /* Phone order is BUY NOW → headline → description, matching the desktop
     stack: the CTA sits above the whole copy block rather than wedged between
     the two lines. The button is a sibling of .hero-text-group, not a child,
     so display:contents dissolves the wrapper — the h1 and p become direct
     flex children alongside the button, letting `order` sequence all three. */
  .hero-center {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    padding-bottom: max(32px, env(safe-area-inset-bottom, 24px)); gap: 6px;
    padding-left: 28px; padding-right: 28px;
    overflow: hidden;
  }
  .hero-text-group { display: contents; }
  .hero-center .hero-cta { order: 1; }
  .hero-center .hero-title { order: 2; }
  .hero-center .hero-tagline { order: 3; }
  .hero-center h1 { font-size: clamp(1.25rem, 3.8vw, 1.8rem) !important; line-height: 1.1; max-width: min(85vw, 400px); margin-top: 0; margin-bottom: 0; text-align: center; }
  .hero-center p {
    font-size: clamp(0.82rem, 3.4vw, 0.95rem);
    line-height: 1.3;
    max-width: 220px;
    margin-top: 0; margin-bottom: 0;
    text-align: center;
  }
  /* Sits on top now, so the breathing room moves below it, between button and
     headline (was margin-top, for when it was wedged under the headline). */
  .hero-cta { padding: 10px 26px; font-size: 0.84rem; margin-top: 0; margin-bottom: 10px; }}

/* Sized to hold "Feel every frequency." on a single line inside the grid's
   left rail. The old 5vw/4rem ran ~640px wide, which overflowed the rail once
   the centred button claimed the middle, wrapping the headline to two lines
   and pushing the whole block upward. This keeps it on one line and therefore
   sitting where it always sat. */
.hero-title {
  font-size: clamp(1.75rem, 3.1vw, 3rem);
}

/* Narrow landscape range (iPad portrait ~820px is the common one): still the
   side-by-side desktop grid, but the left rail gets tight enough to wrap the
   headline. Buy the rail a bit more room and trim the type so it stays on one
   line right down to the 768px mobile breakpoint. */
@media (min-width: 769px) and (max-width: 950px) {
  .hero-center { gap: 12px; padding-left: 4vw; padding-right: 4vw; }
  .hero-title { font-size: 2.6vw; }
  .hero-tagline { font-size: 1.05rem; }
}

/* Hero CTA (Apple AirPods Style) */
.hero-tagline {
  font-family: 'CalSansText', sans-serif; font-size: clamp(1.1rem, 1.5vw, 1.5rem);
  color: rgba(255,255,255,0.85); font-weight: 300; letter-spacing: 0.02em;
  margin-top: 4px;
  text-shadow: 0 2px 12px rgba(0,0,0,0.5), 0 1px 3px rgba(0,0,0,0.3);
}

.mobile-break { display: none; }
.hero-cta {
  display: inline-block; margin-top: 12px;
  background: rgba(255,255,255,0.95); color: var(--dark); padding: 16px 44px;
  border-radius: 40px; font-size: clamp(1rem, 1.2vw, 1.15rem); font-weight: 700;
  letter-spacing: 0.03em; text-transform: none; cursor: pointer;
  transition: transform .3s, background .3s, box-shadow .3s;
  box-shadow: 0 4px 30px rgba(0,0,0,0.15), 0 1px 3px rgba(0,0,0,0.1);
}
.hero-cta:hover {
  transform: scale(1.06); background: #fff;
  box-shadow: 0 6px 40px rgba(0,0,0,0.2), 0 2px 6px rgba(0,0,0,0.15);
}

/* ── Hero CTA: dark glass ──
   Scoped to #hero-cta on purpose. The same .k-pop-btn silver treatment is
   shared by the nav and lightbox BUY NOW buttons, which sit on light chrome
   and should stay as they are — only the one sitting over the video footage
   becomes glass. Overrides the gradient + roving animation .k-pop-btn sets. */
#hero-cta {
  background-color: rgba(18, 18, 20, 0.38);
  background-image: none;
  animation: none;
  border: 1px solid rgba(255, 255, 255, 0.30);
  color: #ffffff;
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  backdrop-filter: blur(16px) saturate(140%);
  box-shadow:
    0 6px 28px rgba(0, 0, 0, 0.34),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.45);
}
#hero-cta:hover {
  background-color: rgba(26, 26, 30, 0.56);
  border-color: rgba(255, 255, 255, 0.46);
  box-shadow:
    0 8px 34px rgba(0, 0, 0, 0.42),
    inset 0 1px 0 rgba(255, 255, 255, 0.26);
}

@keyframes roving-gradient {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

@keyframes sparkle-whirr {
  0% { background-position: 0 0; }
  100% { background-position: -200px -200px; }
}

.k-pop-text {
  color: #ffffff;
  text-shadow: 0 0 8px rgba(180, 200, 255, 0.2), 0 0 16px rgba(140, 160, 255, 0.1);
  animation: pulse-glow 4s infinite alternate;
  -webkit-background-clip: unset;
  -webkit-text-fill-color: unset;
  background-image: none;
}
@keyframes pulse-glow {
  0% { text-shadow: 0 0 8px rgba(180, 200, 255, 0.2), 0 0 16px rgba(140, 160, 255, 0.1); }
  100% { text-shadow: 0 0 12px rgba(180, 200, 255, 0.4), 0 0 24px rgba(140, 160, 255, 0.2), 0 0 32px rgba(180, 120, 255, 0.15); }
}

.k-pop-btn {
  background-color: #aeb9c9;
  background-image: linear-gradient(110deg, #abb7c7, #bac6d6, #a5b0c2, #bac6d6, #abb7c7);
  background-size: 200% 200%;
  animation: roving-gradient 14s linear infinite;
  border: 1px solid rgba(255,255,255,0.4);
  position: relative;
  overflow: hidden;
  box-shadow: 0 0 15px rgba(255, 255, 255, 0.15), inset 0 0 10px rgba(255, 255, 255, 0.4);
  color: var(--silver-ink);
  font-weight: 700;
  letter-spacing: 0.05em;
}

.k-pop-btn:hover {
  transform: scale(1.06);
  box-shadow: 0 6px 25px rgba(255, 255, 255, 0.25), inset 0 0 15px rgba(255, 255, 255, 0.6);
  background-color: #e2e8f0;
}

.k-pop-btn span {
  position: relative;
  z-index: 2;
}


/* ═══ DUO GRID (Two Cushions + Full Experience) ═══ */

/* ═══ GRID ═══ */

/* ═══ MOBILE COMPACTNESS PASS ═══
   Section paddings and grid gaps below were authored as inline styles at desktop-friendly
   sizes (100-120px). !important is required here to win over inline styles on mobile. */
@media (max-width: 768px) {
  #experience, #installation, #specs, #order { padding: 56px 0 !important; }
  .install-steps-grid { gap: 18px !important; margin-top: 28px !important; }
  .controller-placement { margin-top: 40px !important; }
  .tech-specs-container { gap: 24px !important; margin-top: 36px !important; }
}

/* ═══ ROUNDED VIDEO ═══ */
.vid-r { border-radius: var(--r); overflow: hidden; position: relative; }
/* Sunken-window effect on the "Music, activated." genre clips. Has to be an
   overlay pseudo-element rather than a plain inset box-shadow on .vid-r: an
   inset shadow paints above the element's background but BELOW its children,
   so the <video> would cover it completely. */
#experience .vid-r::after {
  content: ''; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  border-radius: inherit;
  box-shadow: inset 0 2px 10px rgba(0,0,0,0.5), inset 0 0 26px rgba(0,0,0,0.34);
}
.vid-r video { width: calc(100% + 2px); margin-left: -1px; display: block; }

/* ═══ STICKY SCROLL SECTIONS ═══ */ #ctrl-hero-vid, #stealth-bg-vid, #nobt-bg-vid, #hand-bg-vid {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 100vw; height: 100vh; max-width: 1600px; max-height: 900px;
  object-fit: cover;
}

/* Command Central / Stealth text layer — positioned ABOVE the video */

/* ═══ NO BLUETOOTH ═══ */

/* Hand sticky text layer */
#hand-bg-vid {
  border-radius: 20px;
  transform: translate(-50%, -50%) scale(0.95);
}

/* "Always in Reach" overlay card */

/* Callout cards */

/* ═══ SKIT CARD (No BT / Command Central / Stealth) ═══ */


/* ═══ COMMAND CENTRAL EXTRA ═══ */

/* ═══ SOUND TOGGLE BUTTON ═══ */

/* ═══ PLAY BUTTON ═══ */

/* ═══ WHITE VIDEO STACK (Universal I/O + Cables) ═══ */

/* ═══ GLASS CARD (Power Options) ═══ */

/* ═══ GENRE CARDS ═══ */

/* ═══ INTERACTIVE CONTROLLER DEMO (v2) ═══ */

/* ═══ SPECS ═══ */

/* ═══ SALES / ORDER ═══ */
.buy-btn {
  background: var(--bg); color: var(--white); border: none;
  padding: 16px 40px; border-radius: 30px; font-size: 1rem; font-weight: 700;
  cursor: pointer; letter-spacing: .04em; text-transform: uppercase;
  transition: opacity .3s, transform .3s;
}
.buy-btn:hover { opacity: .85; transform: scale(1.03); }

/* ═══ PRICING GRID ═══ */
.pricing-grid {
  display: grid; grid-template-columns: 1fr; gap: 2rem; max-width: 800px; margin: 0 auto;
}
.pricing-card {
  background: var(--white); border-radius: var(--r); padding: clamp(1.5rem, 3vw, 2.5rem); text-align: left;
  box-shadow: 0 10px 40px rgba(0,0,0,0.05); position: relative; border: 1px solid transparent;
}
.pricing-card-inner {
  display: flex; gap: 2rem; align-items: center;
}
.pricing-card-image {
  flex: 1; min-width: 0; position: relative;
}
.pricing-card-image img {
  width: 100%; height: auto; display: block; border-radius: 16px;
  cursor: zoom-in; /* opens the same full-screen viewer as the cart's photo */
}
.pricing-card-info {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
}
/* Was a heavy 2px black outline. Now a hairline in the same brushed silver as
   the nav's BUY NOW pill and B mark, with a soft silver halo doing the work of
   marking this card as the featured one instead of a hard black frame. */
.pricing-card.featured {
  border-color: rgba(174,185,201,0.9);
  box-shadow: 0 20px 60px rgba(0,0,0,0.08), 0 0 26px rgba(174,185,201,0.32);
  transform: scale(1.02);
}
.pricing-badge {
  position: absolute; top: -15px; left: 50%; transform: translateX(-50%);
  background-color: var(--silver);
  background-image: var(--silver-sheen);
  background-size: 200% 200%;
  animation: roving-gradient 14s linear infinite;
  border: 1px solid rgba(255,255,255,0.4);
  box-shadow: 0 2px 10px rgba(0,0,0,0.08), inset 0 0 10px rgba(255,255,255,0.4);
  color: var(--silver-ink); padding: 6px 18px; border-radius: 20px;
  font-size: 0.85rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; white-space: nowrap;
}
.pricing-card h3 { font-size: 1.8rem; margin-bottom: 0.5rem; color: var(--dark); }
.pricing-desc { color: var(--grey-dark); font-size: 1rem; margin-bottom: 1.5rem; }
.pricing-price { font-size: 2.8rem; font-weight: 900; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: var(--dark); margin-bottom: 2rem; display: flex; align-items: baseline; gap: 15px; }
/* Price-secondary tone (v3): the "was" price is a quiet reference, not competing with
   the real price — v2 had this at 4rem in alarm red, larger than the price itself. */
.pricing-was { font-size: 1.4rem; text-decoration: line-through; color: var(--grey); font-weight: 400; }
/* Secondary (Starter Kit): hairline silver outline instead of a 2px black one.
   Label stays dark — silver-on-white would be unreadable. */
.pricing-card .buy-btn { width: 100%; border: 1px solid var(--silver); background: transparent; color: var(--dark); }
.pricing-card .buy-btn:hover { background: rgba(174,185,201,0.14); }
/* Featured (Double Kit): the same brushed-silver fill as the nav's BUY NOW. */
.pricing-card .buy-btn-featured {
  background-color: var(--silver);
  background-image: var(--silver-sheen);
  background-size: 200% 200%;
  animation: roving-gradient 14s linear infinite;
  border: 1px solid rgba(255,255,255,0.4);
  box-shadow: 0 0 15px rgba(255,255,255,0.15), inset 0 0 10px rgba(255,255,255,0.4);
  color: var(--silver-ink);
}
.pricing-card .buy-btn-featured:hover { opacity: .9; }

@media (max-width: 768px) {
  .pricing-grid { display: flex; flex-direction: column; align-items: center; width: 100%; }
  .pricing-card { max-width: 450px; width: 100%; margin: 0 0 2rem 0; }
  .pricing-card.featured { transform: scale(1); margin: 0 0 2rem 0; }
  .pricing-card-image { max-width: 260px; margin: 0 auto 1.5rem; }
  .pricing-card-inner { flex-direction: column; text-align: center; gap: 1rem; }
  .pricing-card-info { text-align: center; }
  .pricing-price { justify-content: center; }

  /* Keep a whole kit card within one phone screen. The product shots are portrait,
     so height (not width) is what was overflowing — cap by max-height and let the
     width follow, then tighten the type/spacing to match. */
  .pricing-card { padding: 1.25rem; margin: 0 0 1.25rem 0; }
  .pricing-card.featured { margin: 0 0 1.25rem 0; }
  .pricing-card-image { max-width: 100%; margin: 0 auto 0.75rem; }
  .pricing-card-image img { max-height: 30vh; width: auto; margin: 0 auto; object-fit: contain; }
  .pricing-card-inner { gap: 0.6rem; }
  .pricing-card h3 { font-size: 1.35rem; margin-bottom: 0.2rem; }
  .pricing-desc { font-size: 0.9rem; margin-bottom: 0.6rem; }
  .pricing-price { font-size: 1.9rem; margin-bottom: 0.9rem; gap: 10px; }
  .pricing-was { font-size: 1.05rem; }
  .pricing-card .buy-btn { padding: 12px 24px; font-size: 0.9rem; }
  .pricing-badge { top: -11px; font-size: 0.72rem; padding: 5px 14px; }}

/* ═══ FOOTER ═══ */
.footer { background: var(--bg); border-top: 1px solid rgba(255,255,255,0.06); padding: 60px 0; }
.footer p { text-align: center; font-size: .85rem; color: var(--grey-dark); }
.footer-links { display: flex; justify-content: center; gap: 2rem; margin-top: 1rem; }
.footer-links a { color: var(--grey-dark); font-size: .85rem; transition: color .3s; }
.footer-links a:hover { color: white; }

/* ═══ INSTALLATION ═══ */
.setup-card { background: var(--grey-light); border-radius: 24px; padding: 48px; }
.install-step-vid { aspect-ratio: 1/1; background: #000; overflow: hidden; border-radius: 12px; }

/* ═══ DIAGRAM ═══ */

/* ═══ FULL-BLEED VIDEO ═══ */

/* ═══ BRANDING MOTION ═══ */

/* ═══ SECTION FADE BOTTOM ═══ */

/* ═══ PAUSED VIDEO OVERLAY ═══ */
.vid-paused video { filter: brightness(0.6) contrast(0.8) opacity(0.7); transition: filter .4s; }
.vid-paused.is-playing video { filter: brightness(1) contrast(1) opacity(1); }

/* ═══ FLOATING BUY WIDGET ═══ */

@keyframes wiggle {
  0%, 100% { transform: translateY(-50%) rotate(0deg); }
  10% { transform: translateY(-50%) rotate(-10deg); }
  20% { transform: translateY(-50%) rotate(10deg); }
  30% { transform: translateY(-50%) rotate(-10deg); }
  40% { transform: translateY(-50%) rotate(0deg); }
}

#ctrl-intro-wrap { aspect-ratio: 16/9; }

@media (max-width: 768px) {
  /* Disable Sticky Scrub Viewports */

  /* Make background videos flow naturally so cards sit cleanly right under them */ #ctrl-hero-vid, #stealth-bg-vid, #nobt-bg-vid, #hand-bg-vid {
    position: relative !important;
    top: auto !important; left: auto !important;
    transform: none !important;
    width: 100vw !important;
    height: 100vh !important;
    max-height: none !important;
    object-fit: cover !important;
    border-radius: 0 !important;
    z-index: 0 !important;
  }

  /* Force Text Overlays and Skit Cards to stack cleanly on Mobile */
  #command-central-text, #hand-text, #stealth-text{ 
    opacity: 1 !important; 
    transform: none !important; 
    position: relative !important;
    left: auto !important; top: auto !important; right: auto !important; bottom: auto !important;
    margin: 2rem auto !important;
    width: 100% !important; max-width: 90vw !important;
  }
  
  /* Make the no-bluetooth graphic explicitly overlay the center of its sticky video */
  #nobt-graphic {
    opacity: 1 !important;
    position: absolute !important;
    top: 50vh !important;
    left: 50% !important;
    transform: translate(-50%, -50%) !important;
    margin: 0 !important;
    width: 80vw !important;
  }
  #ctrl-intro-wrap { aspect-ratio: 9/16 !important; }}
/* Icy Blue Badge for Pricing */
@keyframes icyPulse {
  0% { box-shadow: 0 0 15px #00d4ff, inset 0 0 15px #00d4ff; }
  100% { box-shadow: 0 0 25px #00d4ff, inset 0 0 25px #00d4ff, 0 0 40px rgba(0,212,255,0.5); }
}

/* Icy Particles */
@keyframes floatUp {
  0% { transform: translateY(0) translateX(0) scale(1); opacity: 1; }
  100% { transform: translateY(-40px) translateX(var(--x-dir)) scale(0); opacity: 0; }
}


/* u2550u2550u2550u2550u2550 CART OVERLAY u2550u2550u2550u2550u2550 */
/* No backdrop-filter here: combined with the heavy filter:blur() on the
   product-photo backdrop inside the cart, it can make some browsers'
   compositor fail to paint the whole overlay. The solid dark fill already
   gives plenty of contrast against the page behind it. */
.cart-overlay { position: fixed; inset: 0; background: rgba(30,30,40,0.45); backdrop-filter: blur(16px) saturate(20%) brightness(170%); -webkit-backdrop-filter: blur(16px) saturate(20%) brightness(170%); z-index: 9999; display: flex; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transition: opacity 0.3s ease; padding: 24px; overflow: hidden; }
.cart-overlay.is-active { opacity: 1; pointer-events: all; }
/* Opening the cart scroll-locks the page at 0, which drops .scrolled off the
   navbar — so it falls back to its over-hero look and shows the full BASSCRAFT
   wordmark rather than the B mark. That reads nicely over the cart, but the bar
   and the cart overlay both sit at z-index 9999 and the overlay comes later in
   the DOM, so it painted on top and knocked the white wordmark down to a murky
   grey. Lift the bar above that scrim so it stays properly white. */
body.cart-open .navbar { z-index: 10001; }
body.lightbox-active .navbar { opacity: 0; pointer-events: none; }
/* The section links would otherwise sit crisply above the scrim while pointing
   at a page that's locked behind the modal. */
body.cart-open .nav-links { opacity: 0; pointer-events: none; }
@media (max-width: 768px) {
  /* Desktop only. On mobile the cart is a near-full-height bottom sheet, so a
     nav lifted above it overlapped the sheet and its BUY NOW / hamburger
     swallowed taps meant for the cart. Put the bar back under the scrim here,
     where the overlay both dims it and takes the pointer events itself. */
  body.cart-open .navbar { z-index: 9999; }}

.cart-modal { background: rgba(32,32,34,0.85); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); border: 1px solid rgba(255,255,255,0.08); border-radius: 24px; width: 100%; max-width: 912px; box-shadow: 0 40px 100px rgba(0,0,0,0.8); position: relative; transform: scale(0.95) translateY(20px); transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1); text-align: left; overflow: hidden; max-height: 92vh; overflow-y: auto; }
.cart-overlay.is-active .cart-modal { transform: scale(1) translateY(0); }
.cart-close { position: absolute; top: 16px; right: 16px; z-index: 10; width: 34px; height: 34px; border-radius: 50%; background: rgba(0,0,0,0.5); border: none; color: white; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background 0.2s; }
.cart-close svg { width: 18px; height: 18px; }
.cart-close:hover { background: rgba(0,0,0,0.7); }
.cart-secondary-btn { background: transparent; border: none; color: var(--grey-light); font-size: 0.9rem; text-decoration: underline; cursor: pointer; margin-top: 10px; transition: color 0.2s; font-family: 'Inter', sans-serif; }
.cart-secondary-btn:hover { color: white; }

.cart-item { display: flex; align-items: stretch; text-align: left; gap: 0; background: transparent; position: relative; overflow: hidden; }

/* Blurred, oversized copy of the product photo sitting behind the ENTIRE card
   (image zone + content zone both), with a scrim fading to dark toward the
   text side. This is what makes it read as one photographic surface instead
   of a light image box bolted onto a flat dark panel — the same idea as the
   hero's video + .hero-dim scrim, just applied to a still photo here. */
.cart-media-blur {
  position: absolute; inset: -60px; width: calc(100% + 120px); height: calc(100% + 120px);
  object-fit: cover; filter: blur(90px) brightness(1.35) saturate(0.85); transform: scale(1.1);
  z-index: 0; pointer-events: none;
}
.cart-media-scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to right, rgba(190,188,184,0.1) 0%, rgba(20,20,20,0.5) 42%, rgba(14,14,14,0.8) 68%);
}

/* Product photos now ship on their own near-white studio background (#fdfdfd),
   so the media zone matches that directly instead of leaning on the blurred
   wash behind it to hide a seam — any letterboxed edge (mobile's `contain`)
   reads as "sitting on the same shelf" rather than a mismatched box. */
.cart-item-media { position: relative; z-index: 2; flex-shrink: 0; width: 300px; display: flex; align-items: center; justify-content: center; background: #fdfdfd; }
/* The button fills the whole media zone (a big, forgiving tap target, and a
   stable corner to anchor the expand icon to) while the photo inside sizes
   itself off height with width left to auto-compute from aspect ratio —
   that's what keeps it from stretching to fill the zone instead of just
   sitting centered in it. */
.cart-media-btn { all: unset; position: relative; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; cursor: zoom-in; }
/* Fills the zone edge-to-edge (cropping is fine here — the box's aspect ratio
   is already close to the photo's own, so it costs very little of the image,
   and it reads far more modern than a padded photo floating in a box). */
.cart-media-fg { display: block; width: 100%; height: 100%; object-fit: cover; }
.cart-expand-icon {
  position: absolute; bottom: 10px; right: 10px; width: 38px; height: 38px; border-radius: 50%;
  background: rgba(0,0,0,0.55); border: 1px solid rgba(255,255,255,0.15); color: #fff;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 0.2s, background 0.2s;
}
.cart-expand-icon svg { width: 52%; height: 52%; }
.cart-media-btn:hover .cart-expand-icon, .cart-media-btn:focus-visible .cart-expand-icon { opacity: 1; }
.cart-media-btn:hover .cart-expand-icon { background: rgba(0,0,0,0.7); }

/* Small brand mark on the product photo itself — same grey badge language as
   the expand icon opposite it, just always-on rather than hover-revealed,
   since it's a mark rather than an affordance. Sits outside .cart-media-btn
   (a sibling, not a child) and pointer-events:none so it can't steal the
   button's tap target. */
.cart-brand-icon {
  position: absolute; top: 16px; left: 16px; z-index: 3; width: 26px; height: 26px;
  background-color: rgba(0,0,0,0.12);
  -webkit-mask: url('assets/images/logo-icon.png') no-repeat center / contain;
  mask: url('assets/images/logo-icon.png') no-repeat center / contain;
  pointer-events: none;
}
.cart-brand-icon img { display: none; }

.cart-content-wrapper { position: relative; z-index: 2; flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; align-items: flex-start; padding: 40px; }
.cart-item-details { width: 100%; }

.cart-eyebrow { font-size: 0.8rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--grey-light); margin: 0 0 10px; }
.cart-heading { font-size: 1.7rem; margin: 0 0 8px; text-shadow: 0 2px 10px rgba(0,0,0,0.8); }
.cart-subhead { color: rgba(255,255,255,0.85); margin: 0 0 22px; font-weight: 500; line-height: 1.45; }
.cart-kit-name { margin: 0; font-size: 1.35rem; }
.cart-kit-meta { color: var(--grey-light); margin: 4px 0 0; font-size: 0.92rem; }

.cart-qty-selector { display: flex; align-items: center; gap: 12px; margin-top: 16px; }
.qty-btn { background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.12); color: white; width: 30px; height: 30px; border-radius: 8px; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 1.05rem; transition: background 0.2s; }
.qty-btn:hover { background: rgba(255,255,255,0.16); }
.qty-val { font-weight: 700; min-width: 22px; text-align: center; font-size: 1.1rem; }

.cart-item-price-wrap { margin-top: 20px; }
.price-row { display: flex; align-items: center; gap: 10px; }
.cart-item-price { font-weight: 800; font-size: 1.8rem; }
.orig-price { color: var(--grey); text-decoration: line-through; font-size: 0.95rem; margin-top: 3px; }

/* Discount now reads as a real savings callout instead of a line of grey text. */
.savings-pill { background: #E63946; color: #fff; font-weight: 700; font-size: 0.75rem; letter-spacing: 0.02em; padding: 5px 11px; border-radius: 100px; white-space: nowrap; }

.cart-summary { margin-top: 22px; border-top: 1px solid rgba(255,255,255,0.08); padding-top: 16px; width: 100%; }
.cart-summary-row { display: flex; justify-content: space-between; margin-bottom: 8px; }
.cart-summary-row span:first-child { color: var(--grey-light); }
.cart-summary-row span#cart-subtotal { font-weight: 600; }
/* De-emphasized: shipping-at-checkout is a disclaimer, not a line item, so it
   shouldn't carry the same visual weight as the subtotal above it. */
.cart-summary-row--muted { margin-bottom: 0; font-size: 0.78rem; }
.cart-summary-row--muted span { color: var(--grey) !important; }

.cart-content-wrapper .buy-btn, .cart-content-wrapper .btn-ghost { width: 100%; margin-top: 22px; padding: 15px; }
.cart-content-wrapper .btn-ghost { margin-top: 12px; }

@media (max-width: 768px) {
  .cart-overlay { padding: 0; align-items: flex-end; }
  .cart-modal { max-width: 480px; border-radius: 24px 24px 0 0; max-height: 90vh; transform: translateY(30px); }
  .cart-overlay.is-active .cart-modal { transform: translateY(0); }
  
  body.cart-open .navbar { opacity: 0; pointer-events: none; }

  .cart-item { flex-direction: column; }
  /* Given real room to breathe (the product photo is the reason someone
     opens the cart) — the sheet can scroll for the rest if a shorter phone
     needs it, rather than keeping the photo small just to avoid that. */
  .cart-item-media { width: 100%; height: clamp(220px, 34vh, 320px); }
  /* Desktop's media zone is close enough to the photo's own aspect ratio that
     cover crops almost nothing. Mobile's zone is landscape-shaped against a
     strongly portrait photo, so the same cover crop zooms in to little more
     than fabric texture — back to contain here, which is the whole reason
     the "cut off on mobile" complaint got fixed in the first place. */
  .cart-media-fg { width: auto; height: 100%; max-width: 100%; object-fit: contain; }
  .cart-content-wrapper { padding: 18px 20px 20px; align-items: stretch; text-align: left; }

  /* Vertical instead of horizontal fade, since image and content stack. */
  .cart-media-scrim { background: linear-gradient(to bottom, rgba(190,188,184,0.1) 0%, rgba(20,20,20,0.5) 50%, rgba(14,14,14,0.82) 78%); }
  /* No hover on touch, so the expand hint is always visible instead, and
     sized up a bit further for an easy tap target. */
  .cart-expand-icon { opacity: 1; width: 42px; height: 42px; }
  .cart-brand-icon { width: 28px; height: 28px; }

  .cart-eyebrow { margin-bottom: 6px; }
  .cart-heading { font-size: 1.4rem; }
  .cart-subhead { font-size: 0.9rem; margin-bottom: 18px; }
  .cart-kit-meta { margin-top: 2px; }
  .cart-qty-selector { margin-top: 10px; }
  .cart-item-price-wrap { margin-top: 12px; }
  .cart-item-price { font-size: 1.5rem; }
  .cart-summary { margin-top: 12px; padding-top: 12px; }
  .cart-summary-row { margin-bottom: 6px; }

  .cart-content-wrapper .buy-btn, .cart-content-wrapper .btn-ghost { margin-top: 12px; padding: 13px; }}

.btn-ruby { 
  background-color: #E63946 !important; 
  background-image: linear-gradient(110deg, #E63946, #fa5a66, #d91c36, #E63946) !important;
  background-size: 300% 300% !important;
  animation: roving-gradient 6s linear infinite !important;
  color: white !important; 
  border: 1px solid rgba(255,255,255,0.15) !important;
  box-shadow: 0 4px 15px rgba(230, 57, 70, 0.25) !important;
  transition: transform .3s, box-shadow .3s !important;
}
.btn-ruby:hover { 
  background-color: #d62828 !important; 
  transform: scale(1.04);
  box-shadow: 0 6px 20px rgba(230, 57, 70, 0.35) !important;
}

.btn-ghost { background: transparent; border: 1px solid rgba(255,255,255,0.2); color: var(--white); padding: 15px; border-radius: 40px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s; font-family: inherit; }
.btn-ghost:hover { border-color: rgba(255,255,255,0.5); background: rgba(255,255,255,0.05); }

.cart-upsell-link { background: transparent; border: none; color: var(--grey-light); font-weight: 600; font-size: 0.9rem; cursor: pointer; transition: opacity 0.2s; font-family: inherit; letter-spacing: 0.05em; text-decoration: underline; padding: 5px; }
.cart-upsell-link:hover { opacity: 0.75; }

/* ═════ CART IMAGE LIGHTBOX ═════ */
/* Desktop: hugs the photo's own rendered footprint (height:100% + width:auto
   off this definite-height wrapper), NOT the whole wide screen — a portrait
   photo on a wide desktop viewport leaves big black bars on either side, and
   anchoring the close button to the screen edge instead of the photo's own
   corner put it nowhere near the photo. Mobile overrides this below to fill
   the screen instead, since there the photo and screen are both portrait and
   actually share roughly the same shape. */
.cart-image-lightbox {
  position: fixed; inset: 0; z-index: 10000;
  background: rgba(30,30,40,0.45);
  backdrop-filter: blur(26px) saturate(20%) brightness(170%); -webkit-backdrop-filter: blur(26px) saturate(20%) brightness(170%);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none; transition: opacity 0.25s ease;
  overflow: hidden;
}
.cart-image-lightbox.is-active { opacity: 1; pointer-events: all; }
.cart-lightbox-inner { position: relative; height: 100%; width: auto; max-width: 100%; display: flex; align-items: center; justify-content: center; }
.cart-image-lightbox img { display: block; height: 100%; width: auto; max-width: 100%; object-fit: contain; }
@media (max-width: 768px) {
  /* Here the photo and the screen are both portrait, so filling the whole
     viewport (rather than hugging just the photo) is the fuller, more
     "really full screen" presentation. */
  .cart-lightbox-inner { width: 100%; height: 100%; max-width: none; }
  .cart-image-lightbox img { width: 100%; height: 100%; }
  /* ...and because it IS full-bleed at this width, the photo's own top-right
     corner is the screen's top-right corner, i.e. exactly where BUY NOW sits.
     This is the same situation the document viewer is in, so it takes the same
     drop below the chrome bar. On desktop the photo only occupies the middle of
     the screen, so its corner is naturally clear and the default applies. */
  .cart-image-lightbox .media-close {
    top: max(84px, calc(env(safe-area-inset-top, 0px) + 76px));
    right: 16px;
  }
}

/* ═════ VIDEO LIGHTBOX ═════ */
/* For the sub-cultural genre clips + the "no bluetooth" demo: bigger and
   with sound on a frosted backdrop — not a true full-screen takeover like
   the cart's product-photo viewer, since these are short square clips that
   don't need to fill the whole screen to read clearly. */
.vid-clickable { cursor: pointer; }

.video-lightbox {
  position: fixed; inset: 0; z-index: 10000;
  background: rgba(30,30,40,0.45);
  backdrop-filter: blur(26px) saturate(20%) brightness(170%); -webkit-backdrop-filter: blur(26px) saturate(20%) brightness(170%);
  display: flex; align-items: center; justify-content: center;
  padding: 40px;
  opacity: 0; pointer-events: none; transition: opacity 0.25s ease;
  overflow: hidden;
}

.cart-overlay::before,
.cart-image-lightbox::before,
.video-lightbox::before {
  content: "";
  position: absolute; inset: -50%; z-index: -1;
  background: 
    radial-gradient(circle at 30% 30%, rgba(255, 0, 100, 0.08), transparent 40%),
    radial-gradient(circle at 70% 70%, rgba(0, 200, 255, 0.08), transparent 40%),
    radial-gradient(circle at 40% 70%, rgba(100, 0, 255, 0.08), transparent 40%);
  filter: blur(40px);
  animation: chromatic-float 20s linear infinite;
  pointer-events: none;
}

@keyframes chromatic-float {
  0% { transform: rotate(0deg) scale(1.1); }
  50% { transform: rotate(180deg) scale(1.4); }
  100% { transform: rotate(360deg) scale(1.1); }
}
.video-lightbox.is-active { opacity: 1; pointer-events: all; }
.video-lightbox-inner {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
  height: min(78vh, 720px); max-width: 100%; gap: 16px;
}
.video-lightbox-text {
  display: flex; flex-direction: column; align-items: center; gap: 8px; margin: 0;
}
.video-lightbox-title {
  color: white; font-family: 'CalSansUI', sans-serif; font-size: 2rem; font-weight: 700; text-align: center;
  display: none; margin: 0;
}
.video-lightbox-subtitle {
  color: rgba(255,255,255,0.85); font-family: 'CalSansText', sans-serif; font-size: 1.1rem; font-weight: 400; text-align: center;
  display: none; margin: 0; max-width: 600px; line-height: 1.4;
}
.video-lightbox-title:not(:empty) { display: block; }
.video-lightbox-subtitle:not(:empty) { display: block; }
/* Wraps the player so it and the X form one shrink-to-fit box. flex:0 1 auto
   + min-height:0 lets it give up height under the caption without overflowing;
   the column's align-items:center then sizes its WIDTH to the video itself, so
   the button's corner and the video's corner are the same corner. */
.video-lightbox-media {
  position: relative; flex: 0 1 auto; min-height: 0;
  display: flex; align-items: center; justify-content: center; max-width: 100%;
}
#video-lightbox-player {
  display: block; height: 100%; width: auto; max-width: 100%;
  border-radius: 16px; background: #000;
  box-shadow: 0 30px 90px rgba(0,0,0,0.55);
}

@media (max-width: 768px) {
  .video-lightbox { padding: 20px; }
  .video-lightbox-inner { flex-direction: column; align-items: center; gap: 16px; height: auto; width: 100%; }
  .video-lightbox-title { font-size: 1.5rem; }
  /* width:100% (a definite value) forced the player's own box wider than a
     portrait video's actual rendered content once max-height clamped it
     down — the leftover space inside that box showed its own black
     background as bars down the sides. width:auto lets the box size itself
     to the video's real aspect ratio instead (matching the desktop rule's
     approach), so the box and the visible video are always the same shape. */
  #video-lightbox-player { height: auto; width: auto; max-width: 100%; max-height: 65vh; }}

/* ═════ SHARED LIGHTBOX CHROME ═════ */
/* The nav's two anchors — the B mark and BUY NOW — float into every expanded
   viewer on the site (product photos, genre clips, the placement document) at
   the same coordinates they hold in the real nav, so opening a lightbox never
   feels like it dropped you out of the site. Deliberately ONE shared element
   rather than a copy per lightbox: identical geometry everywhere, one place to
   change it. Lives outside #site-wrapper — overflow:hidden there clips
   position:fixed descendants (the same trap documented on the cart modal). */
.lightbox-chrome {
  position: fixed; top: 0; left: 50%;
  transform: translateX(-50%) translateY(-14px);
  width: 100%; max-width: 1600px;
  padding: 12px 6%;
  display: flex; justify-content: space-between; align-items: center;
  z-index: 10001;
  opacity: 0; pointer-events: none;
  transition: opacity .32s ease, transform .32s cubic-bezier(.2,.8,.2,1);
}
/* Soft scrim so both marks stay legible over whatever sits behind them — a
   white-background product shot as readily as a dark video frame. Mirrors the
   navbar's own scrim-over-hero treatment. */
/* Scrim removed by user request to eliminate grey gradients. */
.lightbox-chrome::before {
  display: none;
}
.lightbox-chrome.is-active { opacity: 1; transform: translateX(-50%) translateY(0); }
/* Container stays click-through so backdrop-to-close still works around them. */
.lightbox-chrome > * { pointer-events: auto; }

.lightbox-chrome-logo { display: flex; align-items: center; }
/* Source mark is solid black with alpha. Inverted to solid white by default —
   every dark/frosted lightbox backdrop (video, document, cart, the in-place
   Tech Specs fullscreen) reads fine against that. The one exception is the
   cart's own product-photo viewer: on mobile the photo itself (shot on a
   near-white background) fills the whole screen right up under this bar, so
   a white mark there would nearly vanish — .chrome-on-light (toggled in
   app.js only while that specific viewer is open) skips the invert entirely
   and dims the mark's own natural dark tone instead, the same white-bar/
   dark-icon pairing the real navbar already uses once scrolled onto the
   light page below the hero. */
.lightbox-logo-icon { 
  width: 55px; height: 44px; display: block; opacity: 1; transition: opacity .3s, filter .3s; 
  -webkit-mask: url('assets/images/logo-icon.png') no-repeat left center / contain;
  mask: url('assets/images/logo-icon.png') no-repeat left center / contain;
  background-color: #ffffff;
  background-image: linear-gradient(110deg, #ffffff, #ebf3ff, #ffffff, #fdf6fb, #ffffff);
  background-size: 200% 200%;
  animation: roving-gradient 14s linear infinite;
}
.lightbox-chrome-logo:hover .lightbox-logo-icon { opacity: .8; }
.lightbox-chrome.chrome-on-light .lightbox-logo-icon { filter: brightness(0.75); opacity: 1; }
.lightbox-chrome.chrome-on-light .lightbox-chrome-logo:hover .lightbox-logo-icon { opacity: .8; filter: brightness(0.75); }

.lightbox-chrome-buy {
  cursor: pointer;
  padding: 10px 22px; border-radius: 999px;
  font-family: inherit; font-size: 11px;
  text-transform: uppercase; white-space: nowrap; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center;
}

@media (max-width: 768px) {
  .lightbox-chrome { padding: 10px 5%; }
  .lightbox-logo-icon { width: 42px; height: 34px; }
  .lightbox-chrome-buy { padding: 9px 18px; font-size: 10px; }}

/* ═════ DOCUMENT LIGHTBOX (pan + zoom) ═════ */
/* The placement diagrams are dense multi-panel documents — at page size you can
   see there are three options but not read any of them. This opens the full-res
   scan on a pannable stage so they can actually be explored. */
.doc-clickable { cursor: zoom-in; }

.doc-lightbox {
  position: fixed; inset: 0; z-index: 10000;
  background: #fdfdfd;
  opacity: 0; pointer-events: none; transition: opacity .28s ease;
}
.doc-lightbox.is-active { opacity: 1; pointer-events: all; }

/* The pannable surface. touch-action:none so a drag pans the document instead
   of scrolling the page still sitting behind the overlay. */
/* The fixed window. Its exact size is set in app.js from the loaded scan's
   aspect ratio so the sheet fills it edge to edge with no grey gutter, but its
   POSITION — centred in the viewer — never changes, and neither does anything
   hung off its corners. */
.doc-frame {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 44px;
  box-shadow: 0 40px 120px rgba(0,0,0,0.18);
}
.doc-stage {
  position: absolute; inset: 0; overflow: hidden;
  border-radius: inherit;              /* the window does the clipping now */
  background: #fff;
  touch-action: none; cursor: grab;
  user-select: none; -webkit-user-select: none;
}
.doc-stage.is-grabbing { cursor: grabbing; }

/* No radius here any more. The rounding used to live on this element, which is
   the one that gets transformed — so zooming in carried the rounded corners off
   with the artwork and the window stopped reading as a window. The frame above
   clips it instead, and stays put. */
.doc-paper {
  position: absolute; top: 0; left: 0;
  transform-origin: 0 0;
  background: #fff;
  isolation: isolate;
}
/* GPU-promoted only while genuinely open and interactive — this element is a
   full-resolution scan (up to ~3770x2936px) that can sit zoomed in well past
   its own footprint, and an always-on `will-change` would keep that whole
   layer composited in the background forever after first use, even while
   invisible (see app.js: display:none + a transform reset on close handle
   fully tearing the layer down once the fade-out ends). */
.doc-lightbox.is-active .doc-paper { will-change: transform; }
.doc-paper img { display: block; width: 100%; height: 100%; -webkit-user-drag: none; }
/* The source scans are pure flat #ffffff with zero grain of their own (measured:
   stddev 0.00 across the background), so the paper feel is generated here —
   fine desaturated fractal-noise fibre multiplied over the sheet. It rides
   INSIDE the transformed element on purpose: zoom in and the fibres enlarge
   with the page, the way real paper does under a loupe. */
.doc-paper::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  /* feTurbulence emits noise on the ALPHA channel too, which would make the
     sheet blotchily transparent — feFuncA pins it opaque. The RGB transfer
     then compresses the noise into a narrow band just under white (0.88–1.0),
     so this reads as fibre rather than a grey wash over the artwork. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='p' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='0.12' intercept='0.88'/%3E%3CfeFuncG type='linear' slope='0.12' intercept='0.88'/%3E%3CfeFuncB type='linear' slope='0.12' intercept='0.88'/%3E%3CfeFuncA type='linear' slope='0' intercept='1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23p)'/%3E%3C/svg%3E");
  background-size: 140px 140px;
  mix-blend-mode: multiply; opacity: 1;
  /* No radius or inset vignette any more: both belonged to the era when this
     element WAS the visible sheet edge. The frame draws that edge now, and a
     rounded, shadowed overlay riding on the zoomed artwork would put a stray
     dark arc somewhere out in the middle of the window. */
  transition: opacity .12s ease;
}
/* An isolated mix-blend-mode layer that's also rounded, clipped, and
   transformed every pointermove frame (see .doc-paper) is a known trip
   point for mobile GPU compositors — Android Chrome in particular will
   drop/tear frames on that exact combination, reading as a flicker the
   instant you start dragging. The fibre texture is decorative and the gap
   is imperceptible mid-gesture, so drop the blend for the duration of the
   drag/pinch (app.js toggles .is-grabbing on .doc-stage) and let it fade
   back in the moment the pointer lifts. */
.doc-stage.is-grabbing .doc-paper::after { opacity: 0; mix-blend-mode: normal; }

/* ═════ SHARED MEDIA CLOSE BUTTON ═════ */
/* One X for every viewer on the site — the video lightbox, the cart's product
   photo, the placement document and the Tech Specs in-place fullscreen. It used
   to be four separate buttons with four sets of coordinates, each nudged by
   hand to dodge the BUY NOW pill, which is why they appeared to move around
   from one viewer to the next.

   The rule now is positional, not numeric: the X sits in the top-right corner
   of the MEDIA, inside it. Because every viewer centres its media and the chrome
   bar lives at the very top of the screen, that corner is inherently clear of
   BUY NOW — there is no offset to keep re-tuning, and nothing to drift.

   It used to be the nav hamburger's solid brushed-silver disc, which read as a
   heavy grey coin sitting on top of the footage and pulled the eye off the
   product. It's glass now: mostly transparent, blurring and slightly warming
   whatever is behind it, with a whisper of red/cyan fringing on the rim so it
   reads as a lens rather than a plate. Present, not loud.

   The disc survives at all — rather than a bare X — because these viewers run
   over both near-black video and the white placement scan, and an unbacked
   glyph would disappear into one or the other. Legibility across that range is
   handled twice over: the rim carries a light AND a dark hairline so one of
   them always has contrast, and the glyph itself is white with a dark
   drop-shadow, so it reads as white-on-dark or as a shadowed mark on white. */
.media-close {
  position: absolute; top: 12px; right: 12px; z-index: 5;
  width: 36px; height: 36px; border-radius: 50%;
  background: rgba(255,255,255,0.11);
  backdrop-filter: blur(9px) saturate(150%);
  -webkit-backdrop-filter: blur(9px) saturate(150%);
  border: 0;
  /* Rim: light hairline for dark backgrounds, dark hairline for light ones,
     then the chromatic fringe — red pulled one way, cyan the other, the same
     dispersion the lightbox backdrops already use, scaled down to a rim. */
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,0.34),
    0 0 0 1px rgba(0,0,0,0.06),
    -1.5px 0 7px rgba(255,0,90,0.20),
     1.5px 0 7px rgba(0,190,255,0.20),
     0 3px 12px rgba(0,0,0,0.12);
  color: #fff;
  padding: 6px; margin: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .25s, box-shadow .25s, transform .2s;
}
.media-close svg {
  width: 17px; height: 17px;
  /* Carries the glyph on white paper, where a plain white X would vanish.
     Two shadows: a tight one that acts as an outline so the stroke has an
     edge against light backgrounds, and a softer one underneath for lift. */
  filter: drop-shadow(0 0 1px rgba(0,0,0,0.5)) drop-shadow(0 1px 2px rgba(0,0,0,0.45));
}
.media-close:hover {
  background: rgba(255,255,255,0.2);
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,0.5),
    0 0 0 1px rgba(0,0,0,0.08),
    -2px 0 9px rgba(255,0,90,0.28),
     2px 0 9px rgba(0,190,255,0.28),
     0 3px 14px rgba(0,0,0,0.16);
}
.media-close:active { transform: scale(0.94); }

/* Tucked inside the FRAME's top-right corner, at the same inset every other
   viewer uses, so all four read identically. (It briefly hung half over the
   edge; sitting inside matches the rest of the site.) The frame is a fixed,
   centred box, so this is a fixed position — the artwork zooming underneath
   cannot move it — and because app.js keeps the frame below the chrome strip,
   it clears the BUY NOW pill for free. */
.media-close--viewport {
  position: absolute; z-index: 6;
  top: 12px; right: 12px;
}

/* ═════ PLACEMENT HOTSPOTS ═════ */
/* One tap target per placement option, pinned to the artwork. They sit inside
   .doc-paper so they pan and zoom with the document rather than floating over
   it; app.js feeds --hs-counter the inverse of the current zoom so each marker
   holds a constant size on screen however far in you are. */
.doc-hotspots { position: absolute; inset: 0; --hs-counter: 1; }
.doc-hotspot {
  position: absolute;
  transform: translate(-50%, -50%) scale(var(--hs-counter));
  width: 54px; height: 54px; padding: 0;
  background: none; border: 0; cursor: zoom-in;
  display: flex; align-items: center; justify-content: center;
  transition: opacity .35s ease;
}
/* Same glass as the close button. These carry a bit more presence than the
   chrome does — they're the invitation to explore, not just a way out, and
   they sit on white paper where glass has less to work with — but they're
   still lens rather than coin. */
.doc-hotspot-dot {
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 50%;
  font-family: 'CalSansUI', sans-serif; font-size: 19px; font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0,0,0,0.55);
  background: rgba(150,168,196,0.30);
  backdrop-filter: blur(7px) saturate(150%);
  -webkit-backdrop-filter: blur(7px) saturate(150%);
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,0.42),
    0 0 0 1px rgba(0,0,0,0.05),
    -1.5px 0 7px rgba(255,0,90,0.20),
     1.5px 0 7px rgba(0,190,255,0.20),
     0 3px 12px rgba(0,0,0,0.14);
  transition: transform .2s, background .25s;
}
/* Soft pulse so they read as "press me" rather than as part of the drawing. */
.doc-hotspot::after {
  content: ''; position: absolute; inset: 5px; border-radius: 50%;
  border: 2px solid rgba(120,140,170,0.55);
  animation: hotspot-pulse 2.4s ease-out infinite;
  pointer-events: none;
}
@keyframes hotspot-pulse {
  0%   { transform: scale(1);   opacity: .75; }
  70%  { transform: scale(1.9); opacity: 0; }
  100% { transform: scale(1.9); opacity: 0; }
}
.doc-hotspot:hover .doc-hotspot-dot { transform: scale(1.08); }
.doc-hotspot:active .doc-hotspot-dot { transform: scale(0.94); }
/* Zoomed in, they've served their purpose and would only cover the detail. */
.doc-hotspots.is-dimmed .doc-hotspot { opacity: 0; pointer-events: none; }
/* Same precaution as the paper's fibre layer: these now carry a backdrop-filter
   AND sit inside the element being re-transformed on every pointermove, which
   is precisely the mix that made mobile compositors drop frames before. Sample
   the backdrop while the document is still; go flat for the duration of a drag,
   where nobody is looking at the glass anyway. */
.doc-stage.is-grabbing .doc-hotspot-dot {
  backdrop-filter: none; -webkit-backdrop-filter: none;
  background: rgba(150,168,196,0.55);
}

/* Only the programmatic "frame this option" jump animates. Dragging must stay
   glued to the finger, so the transition is added for the duration of the
   focus and taken straight off again (see focusRegion in app.js). */
.doc-paper.is-animating { transition: transform .5s cubic-bezier(.22,.75,.24,1); }

/* Photoshop-style hand affordance. Fades out once they've actually dragged —
   it's an instruction, not persistent furniture. */
.doc-hint {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 24px;
  color: rgba(10,10,10,0.95); text-shadow: 0.5px 0 0 rgba(255,0,0,0.4), -0.5px 0 0 rgba(0,255,255,0.4);
  padding: 30px; text-align: center;
  font-family: 'CalSansText', sans-serif; font-size: 1.15rem; font-weight: 500; letter-spacing: 0;
  z-index: 50; transition: opacity .5s ease, visibility .5s ease;
  width: min(90%, 480px); min-height: 200px;
  /* pointer-events:none is essential, not cosmetic. This sits centred over the
     document at z-index 50 and is a sibling of .doc-stage, so while it accepted
     pointers it swallowed the pointerdown that begins a pan — the drag never
     reached the stage, .is-grabbing never applied, and the document simply
     refused to move until you'd tapped the hint away first. Click-through means
     the first drag both pans and dismisses it (app.js dismisses on the stage's
     own pointerdown). */
  pointer-events: none;
}
.doc-hint::before {
  content: ""; position: absolute; inset: -20px; z-index: -1;
  background: rgba(255,255,255,0.65);
  background-image: linear-gradient(135deg, rgba(255,0,0,0.03) 0%, rgba(0,255,0,0.03) 50%, rgba(0,0,255,0.03) 100%);
  backdrop-filter: blur(5px) saturate(1.1); -webkit-backdrop-filter: blur(5px) saturate(1.1);
  mask-image: radial-gradient(ellipse at center, black 45%, transparent 80%);
  -webkit-mask-image: radial-gradient(ellipse at center, black 45%, transparent 80%);
  pointer-events: none;
}
.doc-hint-text { display: flex; flex-direction: column; gap: 12px; }
.doc-hint-text strong { font-family: 'CalSansUI', sans-serif; font-weight: 700; font-size: 1.05em; }
.doc-hint svg { width: clamp(48px, 10vw, 120px); height: clamp(48px, 10vw, 120px); flex-shrink: 0; opacity: 0.95; }
.doc-hint.is-hidden { opacity: 0; pointer-events: none; visibility: hidden; }

/* Bottom-right of the FRAME, inset just inside it. Fixed by construction: the
   frame is a centred box of known size, so these offsets resolve to the same
   place at every zoom level. Two earlier attempts got this wrong by measuring
   from something that moves — first the viewport corner (which left the cluster
   stranded out in the grey gutter beside the sheet), then the sheet's own
   corner (which travels with every pan, so the buttons crept around and slid
   off as you zoomed in). The window is the thing that holds still. */
.doc-zoom {
  position: absolute; z-index: 6;
  right: 20px; bottom: 20px;
  display: flex; gap: 10px;
}
/* Same glass as the close X and the placement markers — one control language
   across the whole site. */
.doc-zoom button {
  width: 40px; height: 40px; border-radius: 50%; padding: 0;
  background: rgba(255,255,255,0.11);
  backdrop-filter: blur(9px) saturate(150%);
  -webkit-backdrop-filter: blur(9px) saturate(150%);
  border: 0;
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,0.34),
    0 0 0 1px rgba(0,0,0,0.06),
    -1.5px 0 7px rgba(255,0,90,0.20),
     1.5px 0 7px rgba(0,190,255,0.20),
     0 3px 12px rgba(0,0,0,0.12);
  color: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .25s, box-shadow .25s, transform .2s, opacity .2s;
}
.doc-zoom button:hover {
  background: rgba(255,255,255,0.2);
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,0.5),
    0 0 0 1px rgba(0,0,0,0.08),
    -2px 0 9px rgba(255,0,90,0.28),
     2px 0 9px rgba(0,190,255,0.28),
     0 3px 14px rgba(0,0,0,0.16);
}
.doc-zoom button:active { transform: scale(0.94); }
.doc-zoom button:disabled { opacity: .4; cursor: default; }
/* Same shadow trick as the close glyph — these sit on white paper. */
.doc-zoom svg {
  width: 18px; height: 18px;
  filter: drop-shadow(0 0 1px rgba(0,0,0,0.5)) drop-shadow(0 1px 2px rgba(0,0,0,0.45));
}

@media (max-width: 768px) {
  .doc-hint { gap: 40px; padding: 40px 20px; }
  .doc-hint-text { gap: 20px; }
  .doc-hint svg { width: clamp(80px, 20vw, 140px); height: clamp(80px, 20vw, 140px); }
  .media-close--viewport { right: 16px; }
  .doc-zoom { right: 16px; }}

/*  FLASHING PLAY INDICATOR  */
@keyframes indicatorFlash { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }


@media (max-width: 768px) { .s-light { margin: 0; border-radius: 0; }}



/* Make Command Central card larger */
#ctrl-intro-card { max-width: min(1000px, 85vw) !important; }
/* ==========================================================================
   UI UX Pro Max & AirPods-Style Tech Specs
   ========================================================================== */
.tech-specs-list { flex: 1; min-width: 300px; }
.tech-item { margin-bottom: 32px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 32px; cursor: pointer; transition: all 0.3s ease; }
.tech-item h3 { font-size: 1.5rem; display: flex; align-items: center; gap: 15px; margin-bottom: 10px; color: var(--grey); transition: color 0.3s ease; }
.tech-item.active h3 { color: var(--white); }
.tech-item p { color: var(--grey-light); display: none; margin-left: 39px; font-size: 1.1rem; line-height: 1.5; }
.tech-item.active p { display: block; animation: fadeIn 0.5s ease forwards; }

/* Icon is drawn from two hairline bars rather than a "+" text glyph (which rendered
   at inconsistent weights and turned into a lopsided × when rotated). Collapsing the
   vertical bar gives a clean, precisely-centred + → − toggle. */
.tech-icon {
  width: 26px; height: 26px; flex: 0 0 26px;
  border: 1px solid rgba(0,0,0,0.18); border-radius: 50%;
  position: relative; font-size: 0; color: transparent;
  transition: border-color .3s ease, background .3s ease;
}
.tech-icon::before, .tech-icon::after {
  content: ''; position: absolute; top: 50%; left: 50%;
  background: currentColor; border-radius: 1px;
  transition: transform .3s cubic-bezier(.4,0,.2,1), background .3s ease;
}
.tech-icon::before { width: 10px; height: 1.5px; transform: translate(-50%, -50%); }
.tech-icon::after  { width: 1.5px; height: 10px; transform: translate(-50%, -50%); }
.tech-item .tech-icon { color: #86868b; }
.tech-item.active .tech-icon { color: #1d1d1f; border-color: rgba(0,0,0,0.42); }
.tech-item.active .tech-icon::after { transform: translate(-50%, -50%) scaleY(0); }
.tech-item:hover .tech-icon { border-color: rgba(0,0,0,0.35); }

.tech-specs-visual { flex: 1; min-width: 300px; display: flex; justify-content: center; }
.spec-visual-item { width: 100%; max-width: 500px; aspect-ratio: 4/3; background: #1c1c1e; border-radius: 24px; display: none; align-items: center; justify-content: center; color: var(--grey-light); font-size: 1.2rem; border: 1px solid rgba(255,255,255,0.05); }
.spec-visual-item.active { display: flex; animation: fadeInScale 0.5s ease forwards; }

/* When app.js relocates the active video into its own .tech-item on mobile (so it
   unfolds directly under the item instead of sitting in the shared panel far below
   the whole list), it needs its own sizing — the panel's max-width:500px doesn't apply. */
.tech-item .spec-visual-item { max-width: 100%; margin: 4px 0 4px 0; }

/* Caption overlaid on the video itself (bottom-anchored glass pill) — used where a
   clip has someone talking but plays muted. Dark glass keeps it legible over any
   footage, light or dark, without needing a full-width scrim across the frame. */
.media-with-caption { position: relative; }
.video-caption {
  position: absolute; left: 50%; bottom: 16px; transform: translate(-50%, 6px);
  z-index: 3; pointer-events: none;
  max-width: calc(100% - 32px); width: max-content;
  padding: 8px 16px; border-radius: 999px;
  background: rgba(12,12,14,0.62);
  backdrop-filter: blur(14px) saturate(150%); -webkit-backdrop-filter: blur(14px) saturate(150%);
  border: 1px solid rgba(255,255,255,0.14);
  box-shadow: 0 6px 24px rgba(0,0,0,0.28);
  color: #fff; font-size: 0.95rem; font-weight: 500; line-height: 1.35;
  letter-spacing: 0.005em; text-align: center; text-wrap: balance;
  opacity: 0; transition: opacity .25s ease, transform .25s ease;
}
.video-caption.is-visible { opacity: 1; transform: translate(-50%, 0); }
@media (max-width: 768px) {
  .video-caption { display: none !important; }}

/* Panels whose only footage is square (e.g. Power Options) stay square on every
   screen size, since the source never changes with breakpoint. */
.spec-visual-item.shape-square-always { aspect-ratio: 1/1; max-width: 420px; }

@media (max-width: 768px) {
  /* Panels with a true vertical export (data-v) switch to a portrait frame on mobile
     to match the active source exactly, so object-fit:cover crops nothing. */
  .spec-visual-item.shape-vertical { aspect-ratio: 9/16; max-width: 300px; }
  /* Panels whose mobile fallback is square (no vertical export) switch to a square
     frame on mobile so their square clip isn't squeezed into a landscape/portrait box. */
  .spec-visual-item.shape-square-mobile { aspect-ratio: 1/1; max-width: 380px; }
  .tech-item { margin-bottom: 24px; padding-bottom: 24px; }
  .tech-item h3 { font-size: 1.25rem; }
  
  .tech-item.active {
    display: grid;
    grid-template-columns: 1fr 160px;
    grid-template-areas: 
      "head head"
      "text visual";
    align-items: start;
    gap: 0 16px;
  }
  .tech-item.active h3 { grid-area: head; margin-bottom: 12px; }
  .tech-item.active p { grid-area: text; font-size: 0.9rem; margin-left: 20px; margin-top: 0; }
  .tech-item .spec-visual-item { grid-area: visual; margin: 0; width: 100%; max-width: 160px; border-radius: 12px; overflow: visible; cursor: zoom-in; position: relative; }
  .tech-item .spec-visual-item video { border-radius: 12px !important; overflow: hidden; }

  /* "FULL SCREEN" hint label below each tech specs video on mobile */
  .tech-item .spec-visual-item::after {
    content: "CLICK VIDEO TO EXPAND";
    position: absolute;
    top: calc(100% + 5px);
    left: 0;
    width: 100%;
    text-align: center;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.1em;
    color: #86868b;
    pointer-events: none;
    opacity: 0.8;
  }
  /* Hide the hint once the video is expanded */
  .spec-visual-item.fullscreen-overlay::after { display: none; }

  /* Fullscreen Video Overlay */
  .spec-visual-item.fullscreen-overlay {
    position: fixed !important;
    top: 0; left: 0; right: 0; bottom: 0;
    z-index: 9999;
    max-width: 100vw !important;
    width: 100vw !important;
    height: 100vh !important;
    margin: 0 !important;
    border-radius: 0 !important;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.95);
    cursor: zoom-out !important;
  }
  /* Was pinned near the top, which the shared chrome (B mark + BUY NOW) now
     also occupies — moved to the bottom instead, as a frosted pill matching
     the document viewer's own hint (.doc-hint), rather than bare text
     competing with the chrome for the same strip. */
  /* The "TAP TO CLOSE" pill that used to sit here is gone — this overlay now
     carries the same silver X as every other viewer (injected in app.js), so
     the way out is visible rather than something you have to be told about.
     Tapping anywhere still dismisses it. */
  .spec-visual-item.fullscreen-overlay video {
    max-height: 90vh;
    max-width: 100vw;
    object-fit: contain !important;
    border-radius: 0 !important;
  }}

@keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fadeInScale { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
/* ══════════════════════════════════════════════════════════════
   LIGHT COLORWAY (v3light) — every section is white/cream, including
   Tech Specs, the nav once scrolled, and the floating buy widget.
   Only the hero's video + its dark scrim stay as-is — that's real
   footage and a legibility scrim, not a page-background color choice.
   ══════════════════════════════════════════════════════════════ */
#experience { background: #f7f4ef !important; }
#experience h2 { color: #1d1d1f; }

/* Tech Specs */
#specs { background: #ffffff !important; }
#specs h2.glow { color: #1d1d1f; }
.tech-item { border-bottom-color: rgba(0,0,0,0.1); }
.tech-item h3 { color: #86868b; }
.tech-item.active h3 { color: #1d1d1f; }
.tech-item p { color: #6e6e73; }
/* (tech-icon +/− styling lives with the base .tech-icon rules above) */
.spec-visual-item { background: #f2f0eb; border-color: rgba(0,0,0,0.06); color: #6e6e73; }
/* NOTE: no .video-caption colour override here — captions sit on a dark glass pill
   over the footage, so they stay white in both colourways. */

/* Footer */
.footer { background: #f7f4ef; border-top-color: rgba(0,0,0,0.08); }
.footer-links a:hover { color: #1d1d1f; }

/* Nav: stays transparent-over-video at the top (unchanged — that's about the
   hero footage, not page theme); once scrolled the page below is light, so
   the bar goes white/blurred instead of black, and the logo/links/mute icon
   swap to dark to match. */
/* Wordmark ⇄ B-icon cross-fade. Both are stacked in the same grid cell so neither
   reflows the nav while the other fades. The icon sits at low opacity with a soft
   glass ring — present but discreet. */
.nav-logo { display: grid !important; place-items: center start; }
.nav-logo > * { grid-area: 1 / 1; transition: opacity .45s ease, transform .45s ease; }
.nav-logo .logo-white { 
  width: 250px; height: 24px; opacity: 1; 
  -webkit-mask: url('assets/images/logo-white.png') no-repeat left center / contain;
  mask: url('assets/images/logo-white.png') no-repeat left center / contain;
  background-color: #ffffff;
  background-image: linear-gradient(110deg, #ffffff, #ebf3ff, #ffffff, #fdf6fb, #ffffff);
  background-size: 200% 200%;
  animation: roving-gradient 14s linear infinite;
}
/* Just the mark itself — no pill, no border, no blur (that read as a stray grey
   button). A light tint of the dark icon on the pale bar keeps it discreet. */
.nav-logo .logo-icon { 
  width: 55px; height: 44px; opacity: 0; transform: scale(.94); 
  
  -webkit-mask: url('assets/images/logo-icon.png') no-repeat left center / contain;
  mask: url('assets/images/logo-icon.png') no-repeat left center / contain;
  
  background-color: #aeb9c9;
  background-image: linear-gradient(110deg, #abb7c7, #bac6d6, #a5b0c2, #bac6d6, #abb7c7);
  background-size: 200% 200%;
  animation: roving-gradient 14s linear infinite;
}
.navbar.scrolled .logo-white { opacity: 0; }
.navbar.scrolled .logo-icon { opacity: 1; transform: scale(1); }
.navbar.scrolled .nav-logo:hover .logo-icon { opacity: .8; }

.navbar.scrolled {
  background: rgba(255, 255, 255, 0.85); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  border-bottom: 1px solid rgba(0, 0, 0, 0.05); padding: 12px 6%;
}
.navbar.scrolled .nav-links a { color: #1d1d1f; text-shadow: none; }
.navbar.scrolled #global-mute-btn { color: #1d1d1f !important; }

.navbar .scroll-buy-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  white-space: nowrap;
  padding: 10px 22px;
}
.navbar .scroll-buy-btn:hover {
  opacity: 0.85;
}

/* Floating buy widget */

/* ═══ CONTROLLER PLACEMENT ═══ */
.controller-placement {
  margin-top: 80px;
}
.section-divider {
  border: 0;
  height: 1px;
  background: rgba(0, 0, 0, 0.08);
  margin: 0 auto 60px auto;
  max-width: 60%;
}
.controller-placement-desc {
  margin: 0 auto 40px auto;
}

.diagram-card { max-width: 700px; margin: 0 auto; background: #f5f5f7; border-radius: 16px; padding: 20px; overflow: hidden; }
.diagram-frame-inner { position: relative; width: 100%; aspect-ratio: 16/9; border-radius: 8px; overflow: hidden; }
.diagram-still, .diagram-anim { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.diagram-anim { transition: opacity 0.9s ease; }
/* The still diagram fades in on its own once the intro video ends, instead of
   just sitting there and getting "revealed" as the video on top of it fades
   out — a real crossfade rather than a one-sided one. */
.diagram-still { opacity: 0; transition: opacity 1.2s ease; }

@media (max-width: 768px) {
  .controller-placement {
    margin-top: 50px;
  }
  .controller-placement h3 {
    font-size: 1.1rem !important; /* Slightly smaller header on mobile */
  }
  .section-divider {
    margin: 0 auto 40px auto;
    max-width: 80%;
  }
  .controller-placement-desc {
    font-size: 0.95rem; /* Slightly smaller */
    line-height: 1.5;
    margin: 0 auto 20px auto; /* Reduced margin to bring it closer to the image */
    padding: 0 15px;
  }
  /* Smaller card with rounded corners, same as desktop — full-bleed edge-to-edge
     was tried and didn't read better, so this keeps the framed-card look. */
  .diagram-card { max-width: 92vw; padding: 12px; }}