/* Generated by scripts/build-demos.py. */

/* ============================================================
   1. TOKENS
   ============================================================ */
:root{
  --font-ui:      -apple-system, "SF Pro Text", system-ui, "Segoe UI", Roboto, sans-serif;
  --font-display: -apple-system, "SF Pro Display", system-ui, sans-serif;

  --s-1:4px; --s-2:8px; --s-3:16px; --s-4:24px; --s-5:32px;
  --s-6:48px; --s-7:64px; --s-8:96px; --s-9:128px;
  --container:1120px;

  --radius-lg:24px; --radius-md:16px; --radius-sm:10px; --radius-pill:999px;

  --ease-out:cubic-bezier(.22,1,.36,1);
  --ease-inout:cubic-bezier(.4,0,.2,1);
  --dur-fast:150ms; --dur-mid:300ms; --dur-slow:450ms;

  --accent:#FF8A5B;
  --accent-strong:#F5713C;
  --spectrum: linear-gradient(90deg,#FF9E6D 0%, #FF5E87 50%, #7C6BFF 100%);
}

:root, :root[data-theme="dark"]{
  color-scheme: dark;
  --bg:#0B0D12;
  --bg-2:#0F131A;
  --text:#F2F4F8;
  --text-2:rgba(242,244,248,.72);
  --text-3:rgba(242,244,248,.56);
  --surface:rgba(255,255,255,.06);
  --surface-2:rgba(255,255,255,.12);
  --border:rgba(255,255,255,.14);
  --hairline:rgba(255,255,255,.24);
  --glow:rgba(255,138,91,.22);
  --shadow:none;
  --accent-text: var(--accent);

  /* App-surface tokens — the real app UI (inside device frames) follows the
     site theme. Device bodies now do too (below): Space Gray/Black in dark
     mode, Silver/natural titanium in light — matching how these devices
     actually ship, rather than a single fixed finish. */
  /* 2.4 app surfaces: pure #000 canvas, white elevations at 11%/17.6%
     (standard card ~#1C1C1C, lead card ~#2D2D2D) — sampled from the dark
     captures in raw-2026-08-17. */
  --app-bg:#000000;
  --app-text:#F5F6F8;
  --app-text-2:rgba(245,246,248,.62);
  --app-text-3:rgba(245,246,248,.56); /* alpha tuned to clear 4.5:1 on --app-bg */
  --app-elev:rgba(255,255,255,.11);
  --app-elev-2:rgba(255,255,255,.176);
  --app-track:rgba(255,255,255,.22);
  --app-sep:rgba(255,255,255,.09);
  /* Floating chrome (toolbar pill, gear): elevated gray in dark, white card
     with a soft shadow in light — per ios-home.png light/dark. */
  --app-chrome:var(--app-elev-2);
  --app-chrome-shadow:0 0 #0000; /* zero shadow, not `none` — must stay valid when composed in comma lists */
  --app-hair:rgba(255,255,255,.07);
  --app-border:rgba(255,255,255,.14);
  --app-accent-text:#8aa0ff;

  /* Device chrome — Space Gray/Black finish (iPhone/iPad/Mac body, back
     panel, iPhone rail) and Black Titanium (Apple Watch case hardware). */
  --dm-1:#75787d; --dm-2:#3c3e42; --dm-3:#17181b; --dm-4:#0d0e10;
  --dm-back-1:#63666b; --dm-back-2:#2a2c30; --dm-back-3:#0a0b0d;
  --dm-rail-1:#5b5d62; --dm-rail-2:#1c1d21; --dm-rail-accent:#47494e;
  /* Inner-edge inset shadows, where the body's own box-shadow (not the
     background gradient) defines the seam at the screen. Dark and near-
     black here reads as a natural groove on a Space Gray/Black body —
     it's the body's own shadow, not a material color, so it doesn't move
     to --dm-*. */
  --dm-edge-ring:rgba(0,0,0,.5); --dm-edge-soft:rgba(0,0,0,.6);
  --wm-1:#6e6e6e; --wm-2:#5c5c5c; --wm-3:#4a4a4a; --wm-4:#3a3a3a;
  --wm-5:#2c2c2c; --wm-6:#232323; --wm-7:#1a1a1a; --wm-8:#101010;
  --wm-5-rgb:44,44,44;
}

:root[data-theme="light"]{
  color-scheme: light;
  --bg:#F4F6FA;
  --bg-2:#EAEEF5;
  --text:#0E1220;
  --text-2:rgba(14,18,32,.70);
  --text-3:rgba(14,18,32,.60);
  --surface:rgba(255,255,255,.72);
  --surface-2:rgba(255,255,255,.92);
  --border:rgba(14,18,32,.10);
  --hairline:rgba(255,255,255,.9);
  --glow:rgba(245,113,60,.10);
  --shadow:0 8px 16px rgba(14,18,32,.08), 0 2px 4px rgba(14,18,32,.06);
  --accent-text:#B5490F; /* accent deepened to ≥4.5:1 on --bg for text-size use */

  /* 2.4 app surfaces: soft off-white canvas, black elevations at 5%/8% —
     sampled from the light captures in raw-2026-08-17. */
  --app-bg:#F4F4F6;
  --app-text:#0E1220;
  --app-text-2:rgba(14,18,32,.62);
  --app-text-3:rgba(14,18,32,.64); /* alpha tuned to clear 4.5:1 on --app-bg */
  --app-elev:rgba(0,0,0,.05);
  --app-elev-2:rgba(0,0,0,.08);
  --app-track:rgba(14,18,32,.16);
  --app-sep:rgba(14,18,32,.09);
  --app-chrome:#ffffff;
  --app-chrome-shadow:0 4px 14px rgba(0,0,0,.08);
  --app-hair:rgba(14,18,32,.08);
  --app-border:rgba(14,18,32,.12);
  --app-accent-text:#1f4fc4;

  /* Device chrome — Silver finish (iPhone/iPad/Mac) and natural titanium
     (Apple Watch), same ramp shape as the dark tokens above, just remapped
     to the lighter aluminum/titanium each ships in. */
  --dm-1:#f0f1f3; --dm-2:#cfd1d4; --dm-3:#a3a5a8; --dm-4:#85878a;
  --dm-back-1:#e7e8ea; --dm-back-2:#c4c6c9; --dm-back-3:#9a9c9f;
  --dm-rail-1:#e9eaec; --dm-rail-2:#b8babd; --dm-rail-accent:#d2d4d6;
  /* Same inset shadows as dark mode read as a black ring against a light
     silver body — the seam is real but should be a soft graphite line,
     not a black outline, so this is much lighter/lower-alpha, not just a
     straight carry-over of the dark-mode value. */
  --dm-edge-ring:rgba(30,32,36,.16); --dm-edge-soft:rgba(30,32,36,.2);
  --wm-1:#f5f2f0; --wm-2:#ebe6e1; --wm-3:#e0d9d1; --wm-4:#d6ccc2;
  --wm-5:#a38c76; --wm-6:#89735c; --wm-7:#6b5948; --wm-8:#4c4033;
  --wm-5-rgb:163,140,118;
}

@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration:.001ms !important; animation-iteration-count:1 !important;
    transition-duration:.001ms !important; scroll-behavior:auto !important;
  }
}
/* JS mirrors reduced-motion (incl. the ?static tooling flag) onto the root */
html.reduced *, html.reduced *::before, html.reduced *::after{
  animation-duration:.001ms !important; animation-iteration-count:1 !important;
  transition-duration:.001ms !important; scroll-behavior:auto !important;
}

/* ============================================================
   2. BASE
   ============================================================ */
*{ box-sizing:border-box; margin:0; padding:0; }
html{ scroll-behavior:smooth; }
html.theme-transition, html.theme-transition body, html.theme-transition .glass{
  transition: background-color var(--dur-mid) var(--ease-inout), color var(--dur-mid) var(--ease-inout), border-color var(--dur-mid) var(--ease-inout);
}
body{
  background:var(--bg);
  color:var(--text);
  font-family:var(--font-ui);
  font-size:1.0625rem;
  line-height:1.55;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img{ max-width:100%; display:block; }
a{ color:inherit; }
button{ font-family:inherit; color:inherit; }

h1,h2,h3{ font-family:var(--font-display); font-weight:700; }
h1{ font-size:clamp(2.75rem, 6vw, 4.5rem); letter-spacing:-0.03em; line-height:1.04; }
h2{ font-size:clamp(2rem, 4vw, 3rem); letter-spacing:-0.02em; line-height:1.1; }
h3{ font-size:1.375rem; font-weight:600; letter-spacing:-0.01em; line-height:1.2; }
.lead{ font-size:1.1875rem; font-weight:400; line-height:1.5; color:var(--text-2); }
p{ max-width:62ch; }
.eyebrow{ font-size:0.8125rem; font-weight:600; letter-spacing:0.08em; text-transform:uppercase; line-height:1.2; color:var(--accent-text); }
.caption{ font-size:0.875rem; line-height:1.5; color:var(--text-3); }

.container{ max-width:var(--container); margin-inline:auto; padding-inline:clamp(20px,5vw,40px); }
section{ padding-block:clamp(64px,10vw,128px); }

:focus-visible{
  outline:2px solid var(--accent-text);
  outline-offset:3px;
  border-radius:4px;
}

/* ============================================================
   3. COMPONENTS
   ============================================================ */
.glass{
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  backdrop-filter: blur(22px) saturate(1.6);
  -webkit-backdrop-filter: blur(22px) saturate(1.6);
  position: relative;
}
.glass::before{
  content:""; position:absolute; inset:0; border-radius:inherit; padding:1px;
  background: linear-gradient(180deg, var(--hairline), transparent 42%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; pointer-events:none;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){
  .glass{ background: var(--surface-2); }
}

.btn{
  display:inline-flex; align-items:center; gap:var(--s-2);
  border-radius: var(--radius-pill);
  font-weight:600; font-size:1rem;
  padding: 14px 28px; /* ~2:1 horizontal:vertical */
  border:1px solid var(--border);
  background:var(--surface-2);
  cursor:pointer;
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
  text-decoration:none;
  min-height:44px;
}
.btn:hover{ transform:translateY(-2px); }
.btn:active{ transform:scale(.98); }

.chip{
  border-radius:var(--radius-pill);
  border:1px solid var(--border);
  background:var(--surface);
  padding: var(--s-1) var(--s-3);
  font-size:0.875rem;
  font-weight:500;
  cursor:pointer;
  min-height:36px;
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.chip:hover{ border-color:var(--accent); }
.chip[aria-pressed="true"]{ border-color:var(--accent); color:var(--accent-text); }

/* ============================================================
   4. NAV
   ============================================================ */
.nav{
  position:sticky; top:0; z-index:100;
  border-bottom:1px solid transparent;
  transition: background var(--dur-mid) var(--ease-inout), border-color var(--dur-mid) var(--ease-inout), backdrop-filter var(--dur-mid) var(--ease-inout);
}
.nav.scrolled{
  background:var(--surface);
  border-bottom:1px solid var(--border);
  backdrop-filter: blur(22px) saturate(1.6);
  -webkit-backdrop-filter: blur(22px) saturate(1.6);
}
.nav-inner{
  max-width:var(--container); margin-inline:auto; padding-inline:clamp(20px,5vw,40px);
  height:64px;
  display:flex; align-items:center; justify-content:space-between; gap:var(--s-3);
}
.wordmark{ font-family:var(--font-display); font-weight:700; font-size:1.125rem; letter-spacing:-0.01em; text-decoration:none; }
.nav-links{ display:flex; align-items:center; gap:var(--s-4); }
.nav-links a{ font-size:0.9375rem; font-weight:500; text-decoration:none; color:var(--text-2); }
.nav-links a:hover{ color:var(--text); }
.nav-actions{ display:flex; align-items:center; gap:var(--s-3); }

.theme-toggle{
  width:40px; height:40px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  border:1px solid var(--border);
  background:var(--surface);
  cursor:pointer;
  font-size:1.125rem;
  position:relative;
  overflow:hidden;
}
.theme-toggle .knob{ display:inline-block; transition: transform var(--dur-mid) var(--ease-inout); }
.theme-toggle.spin .knob{ transform:rotate(180deg); }

/* ---- Header speaker widget — the app's macOS menu-bar popover, replicated
   in the site header. Built shadcn/ui-style (popover card, slider, separator
   patterns and tokens) with the Radix data-[state=open] motion (fade +
   zoom-in-95 + slide-from-top), in plain CSS since the site has no build
   step. Anchored to a ghost icon-button next to the theme toggle. ---- */
.nav-speaker{ position:relative; }
.nav-speaker-btn{
  width:40px; height:40px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  border:1px solid var(--border); background:var(--surface);
  cursor:pointer; color:var(--text);
  transition:background var(--dur-fast) var(--ease-out);
}
.nav-speaker-btn:hover, .nav-speaker-btn[aria-expanded="true"]{ background:var(--surface-2); }
.nav-speaker-btn svg{ width:17px; height:17px; }
.spk-pop{
  position:absolute; top:calc(100% + 10px); right:0; width:280px; z-index:60;
  background:var(--bg-2); border:1px solid var(--border); border-radius:14px;
  box-shadow:0 10px 15px -3px rgba(0,0,0,.35), 0 4px 6px -4px rgba(0,0,0,.35);
  padding:14px; cursor:default;
  transform-origin:top right;
  opacity:0; transform:scale(.95) translateY(-6px); pointer-events:none;
  transition:opacity 150ms cubic-bezier(.16,1,.3,1), transform 150ms cubic-bezier(.16,1,.3,1);
}
.spk-pop.open{ opacity:1; transform:scale(1) translateY(0); pointer-events:auto; }
.spk-pop::before{ content:""; position:absolute; top:-6px; right:15px; width:10px; height:10px;
  background:var(--bg-2); border-left:1px solid var(--border); border-top:1px solid var(--border);
  transform:rotate(45deg); }
:root.reduced .spk-pop{ transition:none; }
.spk-now{ display:flex; gap:10px; align-items:center; min-width:0; }
.spk-art{ width:44px; height:44px; border-radius:8px; flex:none;
  background:
    radial-gradient(90% 75% at 25% 100%, rgba(255,138,91,.5), transparent 62%),
    linear-gradient(160deg, #26304C 0%, #10141F 75%); }
.spk-tx{ min-width:0; }
.spk-t{ font-size:0.85rem; font-weight:700; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.spk-s{ font-size:0.75rem; color:var(--text-2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; margin-top:2px; }
.spk-bar{ position:relative; flex:1; height:4px; border-radius:2px; background:var(--surface-2); display:block; }
.spk-bar i{ position:absolute; left:0; top:0; bottom:0; border-radius:2px; background:var(--text); }
.spk-bar b{ position:absolute; top:50%; width:12px; height:12px; border-radius:50%; background:#fff;
  transform:translate(-50%,-50%); box-shadow:0 1px 3px rgba(0,0,0,.4); }
.spk-progress{ display:flex; align-items:center; gap:8px; font-size:0.68rem; color:var(--text-2);
  margin-top:12px; font-variant-numeric:tabular-nums; }
.spk-transport{ display:flex; align-items:center; justify-content:center; gap:26px; margin:10px 0 2px; }
.spk-transport > svg{ width:17px; height:17px; cursor:pointer; }
.spk-play{ width:34px; height:34px; border-radius:50%; background:var(--surface-2);
  display:flex; align-items:center; justify-content:center; cursor:pointer;
  transition:transform var(--dur-fast) var(--ease-out); }
.spk-play:hover{ transform:scale(1.06); }
.spk-play svg{ width:13px; height:13px; }
.spk-vol{ display:flex; align-items:center; gap:8px; margin-top:10px; }
.spk-vol > svg{ width:12px; height:12px; opacity:.7; flex:none; }
.spk-sep{ height:1px; background:var(--border); margin:12px -14px; }
.spk-label{ font-size:0.72rem; color:var(--text-2); font-weight:600; margin-bottom:6px; }
.spk-room{ padding:7px 9px; margin:0 -9px; border-radius:9px; }
.spk-room.checked{ background:var(--surface); }
.spk-room-head{ display:flex; align-items:center; gap:8px; font-size:0.8rem; font-weight:600; min-width:0; }
.spk-room-head > svg{ width:11px; height:11px; opacity:.8; flex:none; }
.spk-room-head .spk-on{ margin-left:auto; flex:none; }
.spk-room-head .spk-check{ flex:none; }
.spk-room-vol{ display:flex; align-items:center; gap:8px; margin-top:7px; }
.spk-room-vol svg{ width:9px; height:9px; opacity:.5; flex:none; }
.spk-room-vol .spk-bar b{ width:10px; height:10px; }
.spk-mini{ display:flex; align-items:center; gap:9px; font-size:0.8rem; font-weight:600; color:var(--text-2);
  border-radius:9px; padding:7px 9px; margin:0 -9px; cursor:pointer;
  transition:background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
.spk-mini:hover{ background:var(--surface); color:var(--text); }
.spk-mini svg{ width:14px; height:14px; }
@media (max-width:640px){ .spk-pop{ right:-60px; } .spk-pop::before{ right:75px; } }

/* The Apple badge is already a fully-styled lockup — it gets no button
   chrome of its own, just sizing and a hover lift. */
.nav-store-link{
  display:inline-flex;
  transition: transform var(--dur-fast) var(--ease-out);
}

.nav-store-badge{ height:40px; display:block; }
.nav-store-badge img{ height:40px; width:auto; display:block; }



@media (max-width:640px){
  .nav-links a:not(.nav-store-link){ display:none; }
}

/* ============================================================
   5. HERO
   ============================================================ */
.hero{
  position:relative;
  /* Tighter than the old clamp(64,10vw,128): the device is sized to fit the
     viewport (below) and the block padding is what let it sink too low. */
  padding-block: clamp(40px,6vw,72px) clamp(32px,5vw,56px);
  /* Clip sideways only (no horizontal scroll from the glow); the phone's
     drop shadow must be free to fall past the section's bottom edge. */
  overflow-x:clip; overflow-y:visible;
}
.hero-inner{
  display:grid; grid-template-columns:1.1fr 0.9fr; gap:var(--s-7); align-items:center;
}
@media (max-width:900px){
  .hero-inner{ grid-template-columns:1fr; }
}
.hero-copy > * + *{ margin-top:var(--s-3); }
.hero-copy .eyebrow{ margin-bottom:var(--s-2); }
.hero-cta-row{
  display:flex; align-items:center; gap:var(--s-4); flex-wrap:wrap;
  margin-top:var(--s-5) !important;
}
.hero-store-group{ display:inline-flex; align-items:center; gap:var(--s-3); flex-wrap:wrap; }
.hero-store-badge{ display:inline-flex; height:52px; }
.hero-store-badge img{ height:52px; width:auto; display:block; }
.hero-store-badge, .closing-badge{ transition: filter var(--dur-fast) var(--ease-out); }

.see-more{ font-weight:500; text-decoration:none; color:var(--text-2); border-bottom:1px solid var(--border); padding-bottom:2px; }
.see-more:hover{ color:var(--text); }
.platforms{ margin-top:var(--s-5) !important; color:var(--text-3); font-size:0.9375rem; }



.hero-anim > *{
  opacity:0; transform:translateY(14px);
}
.hero-anim.play > *{
  animation: hero-in var(--dur-slow) var(--ease-out) forwards;
}
.hero-anim.play > *:nth-child(1){ animation-delay:0ms; }
.hero-anim.play > *:nth-child(2){ animation-delay:70ms; }
.hero-anim.play > *:nth-child(3){ animation-delay:140ms; }
.hero-anim.play > *:nth-child(4){ animation-delay:210ms; }
.hero-anim.play > *:nth-child(5){ animation-delay:280ms; }
.hero-anim.play > *:nth-child(6){ animation-delay:350ms; }
.hero-anim.play > *:nth-child(7){ animation-delay:420ms; }
.hero-anim.play > *:nth-child(8){ animation-delay:490ms; }
@keyframes hero-in{
  to{ opacity:1; transform:translateY(0); }
}

.hero-media{ position:relative; display:flex; flex-direction:column; align-items:center; justify-content:center; }
.hero-hint{ position:relative; z-index:1; }
/* Fit the fold by scaling the DEVICE, not by shrinking its width: narrowing
   the device while the screen's internal px stayed fixed made every piece of
   chrome proportionally bigger (owner: "you just made the icons bigger").
   zoom scales layout and paint together, so the screen keeps the exact
   proportions that match the app; the whole phone just sits smaller. */


.hero-phone{ position:relative; width:min(92%, 360px); }
.mac-host{ width:100%; }
.music-phone{ width:min(92%, 340px); margin-inline:auto; }
.ipad-host{ width:100%; }
@media (max-height:800px){ .hero-phone{ width:min(92%, 310px); } }
.hero-glow{
  position:absolute; inset:-20% -10%;
  background: radial-gradient(closest-side, var(--glow), transparent 70%);
  filter: blur(10px);
  pointer-events:none;
  z-index:0;
}
/* App-true mockup surface: the app UI mirrors the real app's own light/dark
   appearance, which follows the site theme — these panels read as the real
   product, not a fixed dark panel regardless of site theme. */




/* Overlay scrollbars inside every device demo — thumb only (no track or
   gutter chrome), and only while the pointer is over that scroll panel,
   like macOS/iOS overlay bars. An ancestor's :hover matches while the
   pointer is anywhere inside it, so hovering a panel's content lights up
   that panel's own thumb. Firefox gets the same via scrollbar-color;
   scoped to .app-screen so the page's own scrollbar is untouched. */








/* ---- 3D device frames: the screens sit inside real device bodies with a
   resting tilt, a gentle idle float, and mouse-driven parallax. ---- */

.device{ position:relative; }




@keyframes device-float{ 0%,100%{ transform:translateY(0); } 50%{ transform:translateY(-6px); } }




/* iPhone body — titanium rail, side buttons, Dynamic Island, deep shadow. */
.iphone{
  --bz:14px; --depth:17px;
  display:inline-block; position:relative;
  transform-style:preserve-3d;
  width:min(92%, 400px);
  padding:var(--bz);
  border-radius:56px;
  background:linear-gradient(135deg,var(--dm-1) 0%,var(--dm-2) 18%,var(--dm-3) 52%,var(--dm-4) 100%);
  box-shadow:
    inset 0 1.5px 1px rgba(255,255,255,.38),
    inset 0 -1px 2px var(--dm-edge-soft),
    inset 0 0 0 2px var(--dm-edge-ring),
    0 55px 90px -30px rgba(0,0,0,.62),
    0 24px 44px -22px rgba(0,0,0,.45);
}
/* Titanium side buttons: left = action + the two volume keys; right = power. */
.iphone::before{
  content:""; position:absolute; left:-2.5px; top:104px;
  width:3px; height:24px; border-radius:3px 0 0 3px;
  background:linear-gradient(var(--dm-rail-1),var(--dm-rail-2));
  box-shadow:0 40px 0 var(--dm-rail-accent), 0 74px 0 var(--dm-rail-accent);
}
.iphone::after{
  content:""; position:absolute; right:-2.5px; top:142px;
  width:3px; height:56px; border-radius:0 3px 3px 0;
  background:linear-gradient(var(--dm-rail-1),var(--dm-rail-2));
}

/* The body's back, extruded behind and a few px larger so a titanium rim
   shows all round — on tilt this reads as real thickness, not a flat panel. */








/* iPad body — aluminium rail, thin uniform bezel, single camera, extruded. */
.ipad{
  --bz:15px; --depth:16px; --ry:-11deg; --rx:5deg;
  display:block; position:relative; transform-style:preserve-3d;
  width:100%; max-width:540px; margin-inline:auto;
  padding:var(--bz);
  border-radius:34px;
  background:linear-gradient(135deg,var(--dm-1) 0%,var(--dm-2) 20%,var(--dm-3) 55%,var(--dm-4) 100%);
  box-shadow:
    inset 0 1.5px 1px rgba(255,255,255,.34),
    inset 0 0 0 2px var(--dm-edge-ring),
    0 55px 90px -30px rgba(0,0,0,.55),
    0 24px 44px -22px rgba(0,0,0,.4);
}



/* Show the iPad full-width as a large landscape tablet, so the app UI reads
   at tablet scale rather than a zoomed slice, and it feels like a device. */
.zone.zone--showcase{ display:block; }
.zone--showcase .zone-copy{ max-width:680px; margin-inline:auto; text-align:center; }
.zone--showcase .zone-media{ display:block; margin-top:var(--s-6); }
.zone--showcase .zone-media-inner{ width:100%; max-width:920px; margin-inline:auto; }
.zone--showcase .ipad{ width:100%; max-width:100%; }
.insights-host{ border-radius:14px; overflow:hidden; box-shadow:0 30px 60px -30px rgba(0,0,0,.45), 0 0 0 1px var(--border); }
.insights-host [data-insights-demo]{ border-radius:14px; }
#insights .zone-copy .eyebrow, #insights .zone-copy .caption{ max-width:none; margin-inline:auto; }
#insights .zone-copy .eyebrow{ margin-top:0; margin-bottom:var(--s-2); }
#insights .zone-copy .caption{ margin-top:var(--s-3); color:var(--text-3); font-size:.875rem; }
/* One-time "try it" hint below any interactive device zone. Fades out
   permanently the first time the user actually interacts with the demo.
   Body copy already carries the claim — this just confirms it's clickable. */
.zone-hint{
  display:flex; align-items:center; justify-content:center; gap:6px;
  margin:var(--s-3) auto 0; padding:6px 12px;
  border-radius:var(--radius-pill);
  font-size:0.8125rem; color:var(--text-3);
  background:var(--surface); border:1px solid var(--border);
  width:fit-content; max-width:80%;
  transition:opacity .35s var(--ease-out), transform .35s var(--ease-out);
  animation:hint-nudge 2.4s var(--ease-out) infinite;
}
.zone-hint svg{ width:12px; height:12px; opacity:.7; }
.zone-hint.dismissed{ opacity:0; transform:translateY(-4px); pointer-events:none; }
@keyframes hint-nudge{
  0%,80%,100%{ transform:translateY(0); }
  87%{ transform:translateY(-3px); }
  93%{ transform:translateY(0); }
}
:root.reduced .zone-hint{ animation:none; }
   /* landscape — real 11" iPad ratio */

/* Mac — thin-bezel display + small chin, on a pedestal stand. The stand is
   part of the same rigid tilting body (a real display+stand tilts as one). */
.mac-wrap{ display:flex; flex-direction:column; align-items:center; transform-style:preserve-3d; }
.mac{
  /* A real Studio Display's bezel is uniform on all four sides — no iMac-style
     bottom chin — so top/left/right/bottom padding all share --bz. */
  --bz:11px; --depth:14px;
  display:block; position:relative; transform-style:preserve-3d;
  width:100%; max-width:880px; margin-inline:auto;
  padding:var(--bz);
  border-radius:14px;
  background:linear-gradient(160deg,var(--dm-1) 0%,var(--dm-2) 16%,var(--dm-3) 55%,var(--dm-4) 100%);
  box-shadow:
    inset 0 1.5px 1px rgba(255,255,255,.3),
    inset 0 0 0 2px var(--dm-edge-ring),
    0 60px 100px -30px rgba(0,0,0,.55),
    0 26px 48px -22px rgba(0,0,0,.4);
}



/* Studio Display stand, straight-on — per the reference: a plain slab neck
   directly beneath the display (uniform width, semicircular cable notch at
   the top), tiny dark feet at the bottom. No wide base bar — the base plate
   points backward and isn't visible from the front. The neck sits BEHIND
   the display plane (negative translateZ + tucked under via negative
   margin), so the display always draws over it — same as the real thing,
   where the arm attaches to the back panel. */



/* macOS menu bar — sits above the app window on the display, carrying the
   real app's menu-bar extra: a speaker status icon whose popover is the
   quick-control widget (now playing + transport + volume + per-room list +
   Mini Player), replicated from the actual macOS widget. */






.mac-mb-speaker{ display:flex; padding:2px 5px; margin:-2px 0; border-radius:5px; cursor:pointer;
  transition:background .15s var(--ease-out); }
.mac-mb-speaker:hover, .mac-mb-speaker.open{ background:var(--app-elev-2); }
.mac-mb-speaker svg{ width:12px; height:12px; }

/* The popover widget itself */

































/* macOS toolbar: traffic lights + title + room chip, above the shared split view */



  

/* Toolbar chrome reads RAISED off the toolbar ground (macos-music.png):
   near-white pill/field/circle buttons with a soft shadow in light —
   --app-chrome carries the themed value (white / #131313-ish). */



.mac-sidebar-ic{ opacity:.8; display:flex; flex:none; align-items:center; justify-content:center;
  width:26px; height:26px; border-radius:50%; background:var(--app-chrome); box-shadow:var(--app-chrome-shadow);
  cursor:pointer; transition:background .15s var(--ease-out), opacity .15s var(--ease-out); }
.mac-sidebar-ic:hover{ opacity:1; background:var(--app-elev-2); }
.mac-sidebar-ic svg{ width:15px; height:15px; }


/* The three right icons are free-standing glyphs — no shared capsule; a thin
   hairline separates the sliders icon from its neighbours (macos-music.png). */




/* Toolbar search — bare icon at rest (the shipped field's own toolbar
   couldn't use .searchable, so it's hand-rolled; the resting state
   deliberately stays quiet, same convention the room picker already
   settled on), a real glass pill once clicked — same backdrop-filter +
   --app-elev-2 material as .ipad-miniplayer below, not a second glass
   vocabulary invented for this one control. */







/* Mac window layout — matches macos-music / macos-rail-queue /
   macos-rail-audio: a full-height-sidebar macOS window. The sidebar column
   runs the full window height (traffic lights live inside it); the toolbar
   belongs to the detail column only (no hairline under it — content scrolls
   beneath and the toolbar only picks up a tint once it has); the inspector
   rail (Queue / Audio) is a full-height third column selected by the three
   toolbar icons. Column widths are shared custom properties so the grid,
   the toolbar's left edge and the floating playbar's two edges all animate
   from the same values — no drift. */
/* Registering these as typed custom properties (@property) is the actual
   fix for the "sidebar collapses but the playbar/toolbar/grid don't move"
   bug: an UNREGISTERED custom property is an opaque token string, so a
   `transition` declared on `left`/`right`/`grid-template-columns` (which
   only reference the var, never assign it directly) can fail to detect or
   interpolate the change when just the custom property flips — the
   dependents are left on their pre-collapse computed value. Typing the
   property as <length-percentage> gives the browser a real value to
   interpolate, so the existing transitions animate correctly with no
   change to their duration, easing, or which properties transition. */
@property --mac-side-w{ syntax:'<length-percentage>'; inherits:true; initial-value:22.22%; }
@property --mac-rail-w{ syntax:'<length-percentage>'; inherits:true; initial-value:240px; }





/* Toolbar controls are sized so the band closes at 24px — the app's 40pt
   measured off macos-music.png, 2.78% of the 1440pt window width. Every
   child has to fit inside that, so the circles, the room capsule and the
   search pill all come down together rather than one of them setting the
   band height on its own. */










/* Faint tint only once detail content has scrolled beneath the toolbar —
   at rest it reads flat, blending into the content, like the capture. */


/* Toolbar: three inspector icons far right; active one is a filled blue circle */



/* Room-picker capsule label span (JS re-targets it on room click) */

/* Traffic lights — top of the (full-height) sidebar, not the toolbar */

/* Inspector rail — plain full-height column behind a single hairline */




/* Rail sections (Queue / Audio) — swapped by the toolbar icons */



/* Group rows sit on the raised field surface (white in light, #131313-ish
   in dark — macos-rail-queue.png); only the "N songs" header strip is grey. */










/* Audio section — Playback / room labels, toggle + slider rows */








/* Native-feeling switch: thumb slides + track tints (~150ms) */




/* Slider (macos-rail-audio.png): ~5px BLACK rounded fill + solid grey
   remainder, big white shadowed thumb, and a row of tiny dot ticks sitting
   BELOW the remainder segment (drawn on the host element's own bg). */



/* Browse panel (Sonos Music) — Connected Services / Favorites / Playlists */
















/* The app's fictional demo catalog — abstract gradient art w/ soft circle,
   matched to the marketing captures. Scoped to the Mac card. */








/* Fictional service letter tiles (colors from the capture) */




/* Sidebar extras: group badge on a room tile, Zones empty caption, dots */

/* Sidebar icon tiles (macos-music.png): Radio gets a BLACK rounded-square
   tile with a white glyph, like the M/V service tiles… */

 /* dark capture: tile a step lighter than the sidebar */

/* …and each room's speaker glyph sits inside a light-grey squircle tile,
   the "2" badge overlapping the tile's top-left corner. */





/* Below 520px a 250px sidebar leaves almost nothing for the detail pane —
   used to hard-hide the sidebar here unconditionally, back when Mac only
   ever showed Now Playing and the sidebar had nothing worth reaching on a
   phone. Now that it routes to real Music/Favorites/Playlists/Queue/History
   panels (same as iPad), losing all access to it on mobile would be a real
   functional regression — so this defers to the same collapsed/recall
   mechanism iPad already uses (see the sidebar-collapsed JS below) instead
   of a permanent display:none. */
/* Sidebar drag handles on Mac (real app has them on every draggable row). */





/* Mac density — a real desktop sidebar (NSOutlineView-style) runs far
   tighter than a touch list: smaller type, thinner rows, smaller glyphs.
   Scoped to .mac-screen so iPad's touch-sized .side-row/.side-label etc.
   are untouched. */


/* Sentence case, not uppercase — macos-music.png shows "Sources"/"Rooms"/"Zones" */



/* 40px below the zones hint keeps it clear of the sticky Pause All glass
   bar when the sidebar scrolls (capture shows generous whitespace there). */





/* Mac detail column: content scrolls under the (absolute) toolbar and the
   floating playbar, so it carries their clearances as padding. */

/* Opening a rail must not reflow the browse grids (macos-rail-queue.png:
   Favorites tiles keep full size, captions untruncated — the rail simply
   covers the rightmost tile). Panels hold the rail-closed width; the
   detail column's overflow-x:hidden clips them under the rail. */







/* Track-change art variants — same abstract-art language, hues matched to
   the queue's .tile.a4/.a2 thumbs so the rotating queue stays consistent. */


/* Outgoing-art layer for the track-change dissolve */










/* Shuffle + repeat are secondary in the app — dimmed and smaller. */









/* − | + stepper, pill-filled variant for the sheet's volume row (reuses the
   Mac window's .mac-vol-step markup/behavior, just rounder and filled to
   match the app's iOS Now Playing sheet). */



/* Bottom labeled toolbar — Queue / Audio / Room, icon over label, inert
   (the real sheet's own screens aren't part of this demo). */











.pill{
  font-size:0.6875rem; font-weight:600; padding:2px 9px; border-radius:var(--radius-pill);
  background:var(--app-elev-2); border:1px solid var(--app-sep); color:var(--app-text-2);
}

/* App top bar. Confirmed against real screenshots (iphone-nowplaying.png,
   mac-nowplaying.png): Now Playing IS pushed with a back chevron + a
   trailing pill of tool icons (sliders/audio, lyrics, comments, sleep
   timer, sidebar) — no title text on that particular bar, unlike the
   Home Screen browse bar which does show one ("Music"). */



















/* Play-ripple — a single accent-tinted ring pulsing outward from the play
   button when pressed. Matches the .pd-ring idiom in the Private-and-local
   diagram so the whole page reads as one product. Quiet and Apple-native
   where the burst was noisy. Sizes to the button via inherited font-size
   so a small button gets a small ripple. */
.burst{ position:absolute; inset:0; border-radius:inherit; pointer-events:none; overflow:visible; }
.burst .ring{
  position:absolute; inset:0; border-radius:50%;
  border:1.5px solid var(--accent);
  opacity:0;
  pointer-events:none;
}
.burst.on .ring{ animation:play-ripple 620ms var(--ease-out); }
@keyframes play-ripple{
  0%   { inset:0;      opacity:.75; border-width:1.5px; }
  60%  { opacity:.35; }
  100% { inset:-16px;  opacity:0;   border-width:.5px; }
}
:root.reduced .burst .ring{ animation:none; opacity:0; }

/* ============================================================
   Shared app-UI kit — tiles, list rows, shelves, sidebar.
   Reused across every device mockup so they read as ONE app.
   ============================================================ */
.tile{ flex:none; display:flex; align-items:center; justify-content:center;
  font-weight:700; color:rgba(255,255,255,.92); overflow:hidden; letter-spacing:-.01em; }
.tile.art{ background:linear-gradient(150deg,#2a3350,#121622); }
.tile.a1{ background:radial-gradient(120% 120% at 70% 15%,#8a5a9e,#33204a); }
.tile.a2{ background:radial-gradient(120% 120% at 70% 15%,#d0663f,#5f2820); }
.tile.a3{ background:radial-gradient(120% 120% at 30% 20%,#4b7d5e,#1a2f24); }
.tile.a4{ background:radial-gradient(120% 120% at 70% 20%,#3f63a0,#172642); }
.tile.a5{ background:radial-gradient(120% 120% at 30% 15%,#a23c66,#3c1c2c); }

.tile.letter{ background:#38513f; }
/* Service brand tiles — the app's actual per-service colors, each paired
   with an ink chosen by measured WCAG contrast (not the brand's own logo
   treatment): near-black text on the bright brands, white on the dark
   ones. Sonos Radio deliberately stays a bare icon with no tile
   background — the real sidebar draws it that way too, not as a colored
   badge like the others. TIDAL's near-black gets a hairline ring so it
   doesn't vanish against the dark sidebar, same as in the app. */
.tile.applemusic{ background:#D60017; }
.tile.spotify{ background:#19E68C; color:#0f1a14; }
.tile.tidal{ background:#0A0A0B; box-shadow:inset 0 0 0 1px rgba(255,255,255,.16); }
.tile.tunein{ background:#14D8CC; color:#04211f; }

/* List row: [tile] title/subtitle [•••] with inset hairline separators */











/* Horizontal shelf of album-art cards.
   Inside a bounded panel (like the iPad detail), the shelf spills; make it
   horizontally scrollable with hidden scrollbars so it behaves like a
   real iOS shelf: swipe to reveal more cards. */
.shelf{ display:flex; gap:12px; overflow-x:auto; scrollbar-width:none;
  scroll-snap-type:x mandatory; padding-right:12px; margin-right:-12px; }
.shelf::-webkit-scrollbar{ display:none; }






/* iPad split view: sidebar + detail */


/* Collapsed state — sidebar hides, detail expands. The recall button in
   the detail toolbar is what brings it back, so there is always a way out. */
/* iPad carries a third (right-rail) track — 0 when closed, 27% when
   .rail-open (ipad-favorites/nowplaying show it open, ipad-queue closed). */





/* Mac columns come from --mac-side-w/--mac-rail-w (set above) */





/* System root toolbar — command palette, sidebar collapse, settings (3
   icons), matching iphone-system.png / ipad-system-favorites.png. Only
   .side-title-collapse is wired to actually hide the sidebar. */




/* Show-sidebar recall button — always visible in the detail toolbar; the
   only affordance to bring the sidebar back once it's hidden. */
.side-recall{ position:absolute; left:12px; top:50%; transform:translateY(-50%);
  width:30px; height:30px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  background:var(--app-elev-2); cursor:pointer; opacity:0; pointer-events:none;
  transition:opacity .2s var(--ease-out), background .15s var(--ease-out); z-index:2; }
.side-recall:hover{ background:var(--app-sep); }
.side-recall svg{ width:15px; height:15px; opacity:.85; }



.side-row{ display:flex; align-items:center; gap:10px; padding:8px 10px; border-radius:9px; font-size:0.88rem; white-space:nowrap; overflow:hidden;
  cursor:pointer; transition:background .15s var(--ease-out), color .15s var(--ease-out); }
.side-row .tile{ width:26px; height:26px; border-radius:7px; font-size:0.7rem; }
.side-row svg{ width:18px; height:18px; flex:none; opacity:.72; }
/* Not every service is connected — dim the ones that aren't, matching the
   real sidebar (Spotify shows "Set up to play" elsewhere in the app). */
.side-row.dim{ color:var(--app-text-3); }
.side-row.dim svg{ opacity:.4; }
.side-row.dim .tile{ opacity:.55; }
/* The row for whatever's currently open — confirmed against the live app,
   and it's NOT the same treatment for a nav section vs. a room. iPadOS: a
   soft gray pill either way, blue icon + text. macOS: a NAV section (Music,
   Favorites, ...) gets a solid blue fill with white text, but a ROOM whose
   Now Playing is on screen only gets a neutral gray highlight — the accent
   blue is reserved for section navigation, not "which room am I looking
   at." Only one row is ever active at a time. */
.side-row.active{ background:var(--app-elev-2); color:var(--app-accent-text); font-weight:600; border-radius:999px; }
.side-row.active svg{ opacity:1; color:var(--app-accent-text); }

/* Capture check (macos-music.png): the selected nav row on Mac is a soft
   gray pill with plain text — the accent blue is NOT used for sidebar
   selection in 2.4. */
.mac .side-row.active:not(.room){ background:var(--app-elev-2); color:var(--app-text); border-radius:8px; }
.mac .side-row.active:not(.room) svg{ color:var(--app-text); opacity:.9; }

.mac .side-row.room.active{ background:var(--app-elev-2); color:var(--app-text); font-weight:600; border-radius:8px; }
.mac .side-row.room.active svg{ opacity:.9; color:var(--app-text); }

.side-row:not(.active):hover{ background:var(--app-elev-2); }
/* Room rows are two lines: name + a live status (now-playing track, or
   idle) — real rooms always show what they're doing, not just their name. */
.side-row.room{ align-items:flex-start; padding-top:8px; padding-bottom:8px; }
.side-row.room > svg{ margin-top:2px; }







/* Every draggable row shows a hamburger grip on the right — matches iPad
   and Mac's real sidebar layout for reordering. */






/* When there ARE tools (add-to-group icon on rooms), grip sits next to them */

/* A source whose account isn't actually usable from this app (connected on
   the household, no local sign-in) — the real sidebar's inactive-service
   warning, always full opacity (unlike the other trailing tools, which
   fade in on hover — a warning shouldn't need discovery). */

.side-more{ font-size:0.8rem; color:var(--app-text-3); padding:6px 10px; border-radius:8px; cursor:pointer; transition:background .15s var(--ease-out), color .15s var(--ease-out); }
.side-more:hover{ background:var(--app-elev-2); color:var(--app-text-2); }
/* Extra rows grow into place instead of snapping — same row markup, just
   collapsed to 0 height/padding until .is-visible is toggled on. */





/* Mac-only: the real footer is a glass-tinted bar with a white/black
   (glyphTint) label and a trailing Announce button — iOS keeps the
   plain .bar/.secondary treatment above, unchanged. */



/* overflow-y:auto (not hidden) — the Mac card now reuses this same detail
   pane for its Music/Favorites/Playlists/Queue/History panels, and at
   narrower widths a panel's content can run longer than the card's fixed
   16:10 height; scrolling in place beats silently clipping the bottom. */


/* Detail toolbar — centered title with a pill of chip icons top-right,
   matching the real iPad's "Favorites [speaker | sidebar]" arrangement. */



.detail-tool{ width:28px; height:28px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  transition:background .15s var(--ease-out); }
.detail-tool:hover{ background:var(--app-sep); }
.detail-tool svg{ width:15px; height:15px; opacity:.85; }

/* Floating glass dock — matches the app's current shared MiniPlayerBar
   (same component drives iOS/iPadOS and Mac now, per the redesign): a
   rounded, glassy card inset from the screen edges rather than a flat
   full-width bar. Shared by iPad's own bottom dock and the Mac dock
   (added inside .mac-screen) — one class, one look, like the real app.
   Hidden via .is-hidden when a room's own detail page is active — the
   real dock disappears there too ("a room's own page already carries
   the full controls"), toggled in activateIpadPanel(). */










/* Shuffle/repeat bracket the transport (shuffle before skip-back, repeat
   after skip-forward) — dimmed like the phone's np-t-dim treatment,
   since they're secondary controls in the app, not primary transport. */












/* iPad — under 640px viewport, the miniplayer is too tight for both the
   volume bar and the tools cluster. Drop volume first, then tools. */


/* The iPad detail padding is snug at the design width but crushes the room
   Now Playing art on small screens; slim it and let the panels breathe. */
@media (max-width:640px){
  
  
  
  
  /* Too narrow for the right queue rail — keep its track collapsed. */
  
  
  
  
}





/* ---- Panel system: sidebar rows swap the visible panel ----
   A single stacked container; only the .on panel takes flow, the rest are
   display:none. Activation resolves from a soft, compressed material slice:
   enough spatial continuity to read as a destination change without making
   the content itself look like floating glass. Reduced motion snaps. */



/* Mac's room panels carry the richer .mac-np-detail layout (art+meta row,
   progress, transport, volume, queue) and need flex column, not the plain
   block .ipad-panel.on gives every other panel — three classes beats two,
   so this wins the cascade regardless of source order. */

@keyframes panel-in{
  from{ opacity:.38; transform:translateY(8px) scale(.985); filter:blur(8px) saturate(1.3); clip-path:inset(0 8% 72% 8% round 22px); }
  70%{ opacity:1; transform:translateY(-1px) scale(1.002); filter:blur(0) saturate(1.04); clip-path:inset(0 0 0 0 round 4px); }
  to{ opacity:1; transform:none; filter:none; clip-path:inset(0); }
}


/* Music panel: shelves, matching MusicHomeView.shelves — no in-panel tab
   strip (each service is its own sidebar row + destination, not a tab). */




/* Playlists panel reuses .lrow (see Favorites/History) — see markup at
   data-panel="playlists" — matching MusicContainerView.browseList's plain
   vertical list, not a card grid. */

/* Queue: plain "Queue — N songs" header (matches QueueSheet.swift's
   headerTitle — no duration total, no room name baked into the header) plus
   a single "•••" overflow trigger for Clear/Save, instead of two permanent
   buttons. Rows show art/title/artist + a drag grip on hover — no numbered
   position badges, matching the real queue list. */





/* Elev/accent playing-row treatment is Mac-only pending its own capture
   check — the iPad capture (ipad-queue.png) shows NO fill and NO accent:
   just a bold title + trailing speaker glyph (see iPad fidelity block). */


/* Playing row marker — the app convention (macos-rail-queue.png) is a STATIC
   filled speaker+arc glyph, never an animated EQ, so both the detail Queue
   panel and the rail's queue list reuse .mac-rail-vol. */

/* History: Today summary row + day dividers. Relative time lives inline in
   the row subtitle (see .lrow-s markup), not a separate trailing badge. */




/* Room now-playing card */





























/* ============================================================
   iPad 2.4 fidelity — capture-matched (raw-2026-08-17 ipad-*)
   Everything scoped .ipad-screen / .ipad so the Mac card (which
   shares .side-*, .lrow, .ipad-miniplayer) is untouched.
   ============================================================ */
/* Demo-catalog art — same values as the Mac card's set (scoped there),
   duplicated under .ipad-screen so both cards tell the same story. */











/* Sidebar header: just "System" — no tool cluster (capture). The floating
   round recall button in the content header is the always-visible toggle. */


/* Room rows: soft squircle behind a solid speaker glyph; grouped rooms get
   the stacked-pair glyph plus a count badge (capture: badge top-RIGHT). */



/* Add-to-group tool is tinted blue at full opacity (capture) */

/* Grips are always clearly visible (capture) — no hover reveal */


/* Queue: the playing row carries NO fill and NO accent — bold title +
   trailing speaker glyph only (ipad-queue.png). */



/* Favorites: dark-green hero tile, larger row art (capture ~64px @2x) */


/* Separators run the full row width — from the artwork's left edge to the
   list's right edge (ipad-favorites/queue captures), not inset past the art. */

/* …and the queue list closes with a hairline under its final row too. */

/* Floating Search pill, bottom-centre of the content column (all browse
   captures; absent on a room's Now Playing page). Overlaid on the grid so
   it tracks the sidebar collapse in lockstep. */









/* Toolbar pill contents swap per panel (captures: Favorites=[speaker|rail],
   Queue=[speaker|•••|rail], Now Playing=[eq|queue-note|lyrics|moon|rail]). */



/* ---- Sweep #2 fidelity (raw-2026-08-17 ipad-*) ---- */
/* Sidebar: floating inset rounded panel, not a full-bleed column. */

/* Section labels: large sentence case, no eyebrow uppercase (captures). */

/* "System" is centered in the sidebar header (tools already hidden). */

/* Grouped-room count badge sits top-RIGHT of the tile, ringed. */

/* Favorites ••• is neutral gray and always readable, not blue. */

/* Queue header: plain bold text, no rule beneath it (ipad-queue.png). */

/* "Add Source" reads as a secondary action — same mid-gray as labels. */


/* − | + steppers: large filled gray capsule, no border (captures). */



/* ---- Right queue rail (ipad-favorites/nowplaying; closed in ipad-queue) ---- */



/* Segmented pill: queue-note (active white pill) | lyrics doc | eq. */




/* "3 songs" + circled ••• header row. */


/* Rail queue rows: slightly denser than the detail lists. */



/* Zones: caption only, no actionable row (capture) — reuses .side-zones-empty */
/* ---- iPad playbar: the app's three-region full-width bar (captures) ----
   [chevrons+art+meta] | [transport over a progress line] | [queue, volume
   with knob, − | + stepper]. Scoped .ipad so the Mac dock keeps its own. */

/* Below the design viewport the iPad shrinks (max-width:920px on the device)
   while the bar's px content does not, so the capture-accurate flanks would
   crowd the three regions into each other. Keep the pre-parity padding there —
   the geometry assertion is measured at 1440x900. */

/* display:grid above outweighs the base .is-hidden rule (equal specificity,
   later source order) — restate it or the dock never hides on room pages. */

/* Active room row: name/sub go accent-blue but the speaker glyph stays
   solid black in its gray tile (capture) */


















/* Playbar thumb is a wide white PILL (captures), unlike the round knobs on
   the room Now Playing page which stay circles. */

/* Up-chevron hover: the bar lifts 2px — hints the app's real expand gesture */


/* ---- Room Now Playing panel (ipad-nowplaying.png): art-left layout ---- */























/* ---- Queue drag-to-reorder (grips are real drag handles) ---- */










/* Music home phone (browse screen) */








/* ============================================================
   6. SEARCH SIGNATURE
   ============================================================ */








.caret{
  display:inline-block; width:2px; height:1.1em; background:var(--accent);
  vertical-align:middle; margin-left:1px;
  animation-play-state:paused;
}
/* Caret blink only runs when its search card is on screen — otherwise it
   ticks endlessly in the hero background and eats attention. */

@keyframes caret-blink{ 50%{ opacity:0; } }



.results{ margin-top:var(--s-3); display:flex; flex-direction:column; gap:var(--s-4); }





@keyframes row-in{
  to{ opacity:1; transform:translateY(0); filter:blur(0); }
}
/* Neutral, monochrome badge — the real app never tints search results by
   brand color (Theme.swift is deliberately "white, not accent-blue"
   throughout); origin is conveyed by initials/text only. */
.badge{
  width:28px; height:28px; border-radius:50%; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  font-size:0.6875rem; font-weight:700; color:var(--text-2);
  background:var(--surface-2); border:1px solid var(--border);
}





/* ============================================================
   7. FEATURE ZONES
   ============================================================ */
.zone{
  display:grid; grid-template-columns:1fr 1fr; gap:var(--s-7); align-items:center;
}
.zone + .zone{ margin-top:clamp(64px,10vw,112px); }
.zone.reverse .zone-media{ order:1; }
.zone.reverse .zone-copy{ order:2; }
@media (max-width:860px){
  /* align-items:center is correct for the desktop 2-col grid (vertically
     centers copy next to media) but re-interpreted on the mobile column
     stack it becomes horizontal centering — any zone-media whose content
     is percentage-width-only (e.g. the widget cluster) then has nothing
     definite to size against and collapses to 0 width. Stretch instead. */
  .zone{ display:flex; flex-direction:column; align-items:stretch; }
  .zone.reverse .zone-media, .zone.reverse .zone-copy{ order:initial; }
}
.zone-copy h3{ font-size:clamp(1.5rem, 2.4vw, 1.875rem); }
.zone-copy p{ margin-top:var(--s-3); color:var(--text-2); }
.zone-link{
  display:inline-block; margin-top:var(--s-3); font-weight:600; color:var(--accent-text); text-decoration:none;
  border-bottom:1px solid transparent;
}
.zone-link:hover{ border-color:currentColor; }


.reveal{ opacity:0; transform:translateY(16px); }
.reveal.in{ animation: reveal-in var(--dur-slow) var(--ease-out) forwards; }
@keyframes reveal-in{ to{ opacity:1; transform:translateY(0); } }

.zone-media{ display:flex; justify-content:center; }
.zone-media-inner{ width:100%; max-width:380px; }

/* ---- Zone 02: Watch + TV (faithful to the real screens) ---- */
/* Living-room scene: wall-mounted TV as the dominant element with a Watch
   on the floor to the left and a plant on the floor to the right. Absolutely-
   positioned foreground props on a wall+floor backdrop, so the composition
   feels like a room, not three floating devices. */
.living-room{ position:relative; width:min(100%, 780px); max-width:none; margin-inline:auto; aspect-ratio:16/10;
  border-radius:20px; overflow:visible; isolation:isolate; }
/* Clips the scene (wall/floor/TV) to the rounded container; the plant
   lives outside this so it can overhang the container's right edge. */
.lr-clip{ position:absolute; inset:0; border-radius:20px; overflow:hidden; }
.lr-wall{
  position:absolute; inset:0; z-index:0;
  background:
    radial-gradient(80% 90% at 50% 40%, rgba(255,220,180,.06), transparent 70%),
    linear-gradient(180deg, #26221e 0%, #1c1815 55%, #17130f 100%);
}
.lr-floor{
  position:absolute; left:0; right:0; bottom:0; height:20%; z-index:1;
  background:linear-gradient(180deg, #100c09 0%, #060403 100%);
  border-top:1px solid rgba(255,255,255,.04);
}
.lr-tv{
  position:absolute; top:9%; left:50%; transform:translateX(-50%);
  width:68%; z-index:2;
}
.lr-tv .apple-tv{ width:100%; aspect-ratio:16/9; border-radius:6px; }
.lr-tv-halo{
  position:absolute; inset:-8% -12% -30% -12%; z-index:-1; border-radius:32px;
  background:radial-gradient(60% 60% at 50% 50%, rgba(90,140,200,.14), transparent 70%);
  pointer-events:none;
}

/* Living-room plant — real 3D via three.js. The canvas is transparent so
   the plant sits over the living-room wall/floor scene. Fixed camera in
   the JS side, no orbit controls. Clicks on a leaf trigger a small sway
   on that leaf, dispatched from a raycaster. */
.lr-plant.lr-plant-3d{
  position:absolute; right:-16%; bottom:-7%; z-index:3;
  width:210px; height:84%;   /* smaller so it doesn't compete with the TV; */
                             /* sits on the bottom-right edge, ~40% overhanging */
  pointer-events:auto;
  cursor:pointer;
}
.lr-plant-canvas{
  width:100%; height:100%; display:block;
  background:transparent;
}
/* Fallback is hidden by default. The canvas is alpha:true so any pixel
   where the 3D plant doesn't draw is transparent — a fallback shown
   underneath would bleed through as ghost imagery. It only renders when
   the JS block sets .no-gl on WebGL failure. */
.lr-plant-fallback{
  display:none;
  position:absolute; inset:0;
  align-items:flex-end; justify-content:center;
  filter: drop-shadow(0 12px 14px rgba(0,0,0,.4));
}
.lr-plant-fallback img{ width:60%; height:auto; }
.lr-plant-3d.no-gl .lr-plant-canvas{ display:none; }
.lr-plant-3d.no-gl .lr-plant-fallback{ display:flex; z-index:1; }

@media (max-width:920px){
  .living-room{ aspect-ratio:16/11; }
  .lr-tv{ width:76%; top:6%; }
  
  .lr-plant.lr-plant-3d{ width:240px; }
}
@media (max-width:560px){
  .living-room{ width:90%; aspect-ratio:4/3; border-radius:14px; }
  .lr-tv{ width:84%; top:4%; }
  
  /* Pulled further right and shrunk so its overhang clears the TV's own
     card content at this width, instead of the old fix of dropping it
     behind the TV's z-index — which just made it look like it was
     vanishing behind the screen, not a deliberate layering. Stays in
     front (inherits z-index:3 from the base rule) like every other
     breakpoint. */
  .lr-plant.lr-plant-3d{ width:100px; right:-16%; bottom:-5%; }
}
/* When a zone-hint sits inside .zone-media, we need column flow so the
   media inner uses full width and the hint sits below it (not beside). */
.zone-media:has(.zone-hint){ flex-direction:column; align-items:center; }
/* Apple Watch Ultra — natural-titanium cushion case, flat guard rail,
   orange Action button (left), Digital Crown + side button (right). */
.watch-assembly{
  position:relative; padding:60px 0; display:flex; justify-content:center; align-items:center;
  /* devices.css's Apple Watch Ultra is authored at its own fixed size
     (360x380) — scale the whole assembly down uniformly rather than
     hand-editing any of its internal px values, so every proportion
     stays exactly as shipped. */
  transform:scale(.52); transform-origin:center;
}

/* Ocean Band, slate-blue.
   The band is one continuous strap that the case sits on top of. To model
   that, each half is anchored at the assembly's outer edge and extends
   PAST the vertical center — meeting under the case with plenty of
   overlap. The case (z-index:2) covers the overlap, so the inner ends
   never need to be seen; the OUTER ends fade to transparent so the band
   appears to leave the frame. */
.watch-strap{
  position:absolute; left:50%; transform:translateX(-50%);
  width:182px; z-index:0;  /* ~57% of the devices.css case's 320px frame
                              width, matching reference-photo proportions */
  /* Layered background:
       1) horizontal ridge lines — soft, rounded repeating ribs (not
          hard-edged stripes) running across the strap's width. Pitch and
          softness are pixel-measured off a real flat product photo of the
          Ocean Band: ridge-to-ridge spacing is ~22% of the band's own
          width, and the light/dark transition is a gradual sine-like
          curve — the real rib is a rounded bump, not a sharp groove.
       2) side shading — light through the middle, dark at both edges —
          so the band reads as slightly domed/rounded
       3) base color — Ocean Band Green fluoroelastomer */
  background:
    repeating-linear-gradient(180deg,
      rgba(0,0,0,.30) 0px, rgba(0,0,0,.10) 5px,
      rgba(255,255,255,.05) 9px, rgba(255,255,255,.28) 12px,
      rgba(255,255,255,.05) 15px, rgba(0,0,0,.10) 19px,
      rgba(0,0,0,.30) 24px),
    linear-gradient(90deg,
      rgba(0,0,0,.5) 0%, rgba(0,0,0,.12) 14%,
      rgba(255,255,255,.08) 50%,
      rgba(0,0,0,.12) 86%, rgba(0,0,0,.5) 100%),
    linear-gradient(180deg, #8FAE68 0%, #71934A 55%, #566F37 100%);
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,.06),
    inset 0 -1px 0 rgba(0,0,0,.4);
}
/* Top strap: anchored to the assembly's top edge and extends past the
   vertical center — plenty of overlap so the case sits on it, not next
   to it. Outer (top) edge fades to transparent; inner (bottom) edge is
   hidden under the case. */
.watch-strap.top{
  top:0; height:calc(50% + 28px);
  border-radius:22px 22px 0 0;
  -webkit-mask-image:
    linear-gradient(180deg, transparent 0%, #000 22%, #000 100%);
  mask-image:
    linear-gradient(180deg, transparent 0%, #000 22%, #000 100%);
}
/* Bottom strap: mirror */
.watch-strap.bottom{
  bottom:0; height:calc(50% + 28px);
  border-radius:0 0 22px 22px;
  -webkit-mask-image:
    linear-gradient(0deg, transparent 0%, #000 22%, #000 100%);
  mask-image:
    linear-gradient(0deg, transparent 0%, #000 22%, #000 100%);
}
/* Case + hardware below is devices.css's Apple Watch Ultra
   (github.com/picturepan2/devices.css, MIT), copied verbatim — same
   values, same shading technique, only the classes are renamed
   (device- → aw-) because this page already has an unrelated .device
   class used by the iPhone/iPad/Mac tilt mockups, and reusing that name
   would collide with it. Every hardcoded hex from the library is now a
   --wm-* token (see :root) so the case can be natural titanium in light
   mode and Black Titanium in dark, same as devices.css's own colors but
   swappable. The orange Action button stays constant — Apple's own
   Action button is the same orange across every case finish.
   .aw-screen carries our own live Now Playing markup instead of the
   library's <img>. */
.aw-device,
.aw-device::before,
.aw-device::after,
.aw-device *,
.aw-device *::before,
.aw-device *::after{
  box-sizing:border-box;
}
.aw-device{
  position:relative; z-index:2;
  height:380px; width:360px;
}
.aw-frame{
  background:#0d0d0d;
  border-radius:92px;
  box-shadow:inset 0 0 12px 1px rgba(13,13,13,.75),inset 0 0 0 6px var(--wm-4),inset 0 0 0 12px var(--wm-4);
  height:380px;
  margin:0 20px;
  padding:38px;
  position:relative;
  width:320px;
  z-index:1;
}
.aw-frame::before{
  border:1px solid var(--wm-1);
  border-radius:80px;
  box-shadow:0 0 6px rgba(13,13,13,.2),inset 0 0 4px 1px var(--wm-1),inset 0 0 0 10px var(--wm-4);
  content:"";
  height:356px;
  left:12px;
  position:absolute;
  top:12px;
  width:296px;
}
.aw-screen{
  background-color:#000;
  position:relative;
  border:2px solid #121212;
  border-radius:62px;
  height:304px;
  width:244px;
}
.aw-header{
  background:radial-gradient(circle at center,var(--wm-4) 50%,var(--wm-2) 85%,var(--wm-5) 100%);
  border-radius:4px 4px 4px 4px/8px 4px 4px 8px;
  box-shadow:inset 0 0 16px 1px rgba(13,13,13,.5),-8px 0 4px rgba(13,13,13,.2),inset 4px 0 4px rgba(13,13,13,.2);
  height:214px;
  margin-top:-107px;
  position:absolute;
  right:4px;
  top:50%;
  width:18px;
  z-index:1;
}
.aw-header::before{
  border-radius:8px 4px 4px 8px/32px 4px 4px 32px;
  box-shadow:-10px 0 8px rgba(13,13,13,.2);
  content:"";
  height:194px;
  margin-top:-97px;
  position:absolute;
  right:8px;
  top:50%;
  width:12px;
}
.aw-btns{
  background:var(--wm-4);
  border-left:1px solid var(--wm-8);
  border-radius:8px 6px 6px 8px/20px 6px 6px 20px;
  box-shadow:inset 8px 0 8px 0 var(--wm-7),inset -2px 0 6px var(--wm-5);
  height:72px;
  position:absolute;
  right:1px;
  top:108px;
  width:24px;
  z-index:9;
}
.aw-btns::after{
  background:var(--wm-4);
  border-radius:2px 4px 4px 2px/20px 8px 8px 20px;
  box-shadow:inset -2px 0 2px 0 var(--wm-7),inset -6px 0 18px var(--wm-5);
  content:"";
  height:78px;
  position:absolute;
  right:0;
  top:-4px;
  width:6px;
}
.aw-btns::before{
  background:var(--wm-4);
  border-radius:20%;
  box-shadow:0 -30px rgba(var(--wm-5-rgb),.5),0 -27px var(--wm-4),0 -25px var(--wm-6),0 -21px rgba(var(--wm-5-rgb),.5),0 -18px var(--wm-4),0 -16px var(--wm-6),0 -12px rgba(var(--wm-5-rgb),.5),0 -9px var(--wm-4),0 -7px var(--wm-6),0 -3px rgba(var(--wm-5-rgb),.5),0 0 var(--wm-4),0 2px var(--wm-6),0 6px rgba(var(--wm-5-rgb),.5),0 9px var(--wm-4),0 11px var(--wm-6),0 15px rgba(var(--wm-5-rgb),.5),0 18px var(--wm-4),0 20px var(--wm-6),0 24px rgba(var(--wm-5-rgb),.5),0 27px var(--wm-4),0 29px var(--wm-6);
  content:"";
  height:3px;
  margin-top:-2px;
  position:absolute;
  right:2px;
  top:50%;
  width:16px;
  z-index:9;
}
.aw-stripe{
  background:var(--wm-3);
  border-radius:2px 8px 8px 2px;
  box-shadow:0 14px 0 var(--wm-4),0 28px 0 var(--wm-4);
  height:10px;
  left:19px;
  position:absolute;
  top:98px;
  width:4px;
  z-index:1;
}
.aw-power{
  background:var(--wm-4);
  border-radius:2px 4px 4px 2px/2px 8px 8px 2px;
  box-shadow:inset 0 0 2px 1px var(--wm-5);
  height:72px;
  position:absolute;
  right:1px;
  top:212px;
  width:4px;
}
.aw-home{
  background:#f18f42;
  border:1px solid #a7500c;
  border-radius:2px 4px 4px 2px/2px 8px 8px 2px;
  box-shadow:inset 0 0 1px 1px #ef812a;
  height:106px;
  left:19px;
  position:absolute;
  top:162px;
  width:4px;
  z-index:1;
}
/* Watch and TV screens are intentionally always-dark, not tied to --app-*
   theme tokens: the real watchOS Now Playing screen and tvOS focus UI are
   both OLED-black/dark-only regardless of the system's light/dark setting. */

/* Capture's clock is ~10.4% of screen width; full scale (~25px) would
   crowd our 244px screen, 1.2rem is the readable middle ground (we don't
   mirror the capture's clock-over-... button overlap, a known app defect). */


/* '...' overflow button inset in the art's top-right, as in the capture.
   The real capture's status clock overlaps this button — a defect we
   deliberately don't mirror; .watch-time stays clear at the screen edge. */




/* Capture geometry: side circles 19.2% of screen width, the centre circle
   larger at 24.2%, centres 28.4% apart. Percentages are of the 240x300
   DISPLAY area (the 244x304 .aw-screen box less its 2px bezel), so ->
   46px / 58px / 16.2px gap, row 76.0% wide against the capture's 76.1%. */







/* Any play button marked .burstable can emit a particle burst — position
   is relative so .burst absolutely sits at its own origin. */
.burstable{ position:relative; }


/* Capture-parity pill track: thick rounded gray track, light-gray fill.
   --vol is nudged by the Digital Crown wheel hint (JS); 25% default. */


/* The nav phone demo (line ~5279) borrows .watch-vol markup for its
   room-detail Volume row. Keep that surface the app's thin iOS bar in
   the site-theme tokens — the thick light-gray pill above is watch-only. */



/* No hover-to-drag animation here — the real watch volume control is a
   passive indicator; the only inputs are the Digital Crown and the two
   flanking speaker-icon nudge buttons, not a tap/drag bar. */
/* In the 2.4 Ultra capture the page dots visually overlap the volume
   track — a known clipping defect compressing two rows into one, NOT a
   design. This separate dots row is the deliberate non-defective
   rendering; do not "fix" it back to match the capture. */
/* Capture's dots band spans 157-265pt of a 422pt screen = 25.6% wide. */
.watch-page-dots{ position:relative; display:flex; justify-content:center; gap:6.5px; margin-top:2px; }
/* Gooey active-dot highlight: a pill that stretches toward the target
   dot then snaps round (two-phase, JS-driven). Sits behind the buttons. */

.watch-page-dots span,.watch-page-dots button{ appearance:none; border:0; padding:0; width:5px; height:5px; border-radius:50%; background:rgba(255,255,255,.3); cursor:pointer; }
.watch-page-dots span.on,.watch-page-dots button.on{ background:rgba(255,255,255,.85); transform:scale(1.25); }


/* Now Playing is the one page pinned to measured capture bands rather than
   centred: art top 0.97% of screen height, title band 55-60%, artist 63-67%,
   transport row centred on 81.7%. Stacking from the top with explicit
   margins is what makes those absolute positions reachable — a centred flex
   box floats the whole group with the sum of its content. */





/* Pin the capture-scale art at its true size — without this the fixed
   page height squeezes it via flex-shrink and rows start overlapping. */


/* Directional gooey slide: lateral shift + slight scaleX squash settling
   round, light blur — reads as sideways motion, not a dissolve. */
@keyframes watch-page-resolve{
  from{ opacity:.35; transform:translateX(var(--watch-shift,14px)) scaleX(1.06); filter:blur(4px); }
  to{ opacity:1; transform:none; filter:none; }
}



























/* Crown-as-volume hint: scrolling the wheel over the watch (Now Playing
   page) nudges --vol; the crown gives a tiny mechanical tick per step,
   and a one-time faint pulse ring near the crown teaches the affordance.
   These are additive classes/elements — the devices.css case block
   itself stays verbatim. */

@keyframes crown-tick{ 50%{ transform:translateY(1.5px); } }
.crown-hint{
  position:absolute; right:-16px; top:116px; width:56px; height:56px;
  border-radius:50%; border:2px solid rgba(255,255,255,.35);
  opacity:0; pointer-events:none; z-index:10;
}
.crown-hint.show{ animation:crown-hint-pulse 2s ease-out both; }
@keyframes crown-hint-pulse{
  0%{ opacity:0; transform:scale(.55); }
  18%{ opacity:.7; }
  100%{ opacity:0; transform:scale(1.15); }
}



:root.reduced .crown-hint{ display:none; }


/* flex:none — without it, a flex child with overflow-x:auto (the mobile
   scrollable-tabs rule below) gets an automatic min-width/min-height of 0,
   so under any vertical squeeze the tab strip collapses to nothing before
   its siblings give up a single pixel. Caught live at 375px: the whole
   strip vanished while Rooms' content rendered untouched. */

/* Continuous capsule container bar — all three captures show the six tabs
   sitting inside one enclosing capsule, not six naked pills. */

/* Liquid focus indicator layer (built by JS when FX is available): one white
   pill travels between tabs behind the labels; the static .active white
   background then stands down so the pill is the only white. */
/* Two classes so this beats the FX block's later .fx-goo-host{position:relative} */


/* Label colors crossfade a beat later so the dark text lands as the pill arrives */

/* Hangs BELOW the capsule bar's right end (tvos-rooms/favorites captures) —
   anchored to .tv-tabs so it tracks the bar wherever the flex column puts it. */








.tv-tab{
  /* Geometry pinned to the 2.4 tvOS captures (1920pt screen): active pill
     ~63pt tall (3.28% of screen width) and ~281pt wide (14.6%), with item
     centres ~210pt apart (10.9%). At this device's layout width those land
     on a 2px/6px pill inset and a 3px icon gap — the old 4px/11px inset ran
     the six items ~2% wide each, which pushed the whole capsule out to the
     screen edges and put the first pill 6% left of where the app has it. */
  font-size:0.7rem; font-weight:650; padding:4px 10px; white-space:nowrap;
  border-radius:var(--radius-pill);
  background:transparent; color:rgba(255,255,255,.7);
  display:inline-flex; align-items:center; gap:3px;
  position:relative; z-index:1;
}
.tv-tab svg{ width:13px; height:13px; flex:none; }
/* All six tabs are real — click swaps the visible .tv-panel below (see
   the panel system + JS near the Group Rooms flow). */
.tv-tab{ cursor:pointer; transition:background .15s var(--ease-out), color .15s var(--ease-out); }
.tv-tab:hover:not(.active){ background:rgba(255,255,255,.18); }
.tv-tab.active{ background:#fff; color:#0E1220; }
/* The focused pill is wider than its neighbours in every 2.4 tvOS capture —
   281pt against ~183pt — so it carries extra side inset rather than just a
   white fill. Widening it here (not scaling it) keeps offsetWidth honest and
   keeps the liquid indicator, which measures the active tab after the class
   swap, landing on the pill it actually draws. */
.tv-tab.active{ padding-left:15px; padding-right:15px; }

/* Every tab except Now Playing shows the mini chip hanging under the bar's
   right end — the section head starts below the chip's level, exactly as
   the captures space "Rooms"/"Favorites" below it. */

/* Search stacks head + field + three result rows — tighten so the third
   row (Artist) stays inside the fixed screen height. */
 /* .on beats plain .tv-panel gap */









/* Header pill buttons (tvos-rooms.png: Pause All / Grouping Settings /
   Edit Group). .tv-hchip is the shared pill; .tv-group-chip stays on the
   Edit Group chip only — the grouping JS selects it by that class. */






/* tvOS Rooms uses a spacious two-column grid of landscape lockups. */



/* Grouped card stays one normal grid cell (tvos-rooms.png: "Kitchen +
   Dining R…" is the same width as its neighbors, name just truncates) —
   the class remains for the JS toggle + title pop (no layout rule needed). */
/* Room cards only: large art tile (Search/Services rows keep the shared
   30px .tile — scope to .tv-card-art). */




/* The homepage's TV frame is deliberately compact. These dimensions retain
   the reference's three visible room rows instead of cropping the grid after
   the first row, while keeping the room names at a couch-readable weight. */





/* Pause All pressed: the playing waveforms stand down (room cards + mini chip). */


/* Room-card art per tvos-rooms.png: Living Room carries the same Afterglow
   asset as the NP screen and mini chip; Kitchen + Dining carries the orange
   Better Weather art (no numeral tile — the "2" badge is an iOS Rooms-list
   affordance); Bedroom the purple Slow Fade art. */










/* Room-merge animation: second row collapses (opacity + translate + max-height),
   first row briefly pulses to acknowledge it just absorbed a room. */

@keyframes tv-row-merge{
  0%{ opacity:1; transform:scale(1) translateY(0); max-height:60px; margin-top:0; }
  100%{ opacity:0; transform:scale(.94) translateY(-10px); max-height:0; margin-top:-8px; padding-top:0; padding-bottom:0; }
}

@keyframes tv-title-pop{ 0%{ opacity:.4; transform:translateY(-2px); } 100%{ opacity:1; transform:translateY(0); } }
/* Same cue, reused when the Mac's Now Playing content swaps to a
   different room — signals "this changed" instead of a silent snap. */




/* Group Rooms modal — a full-screen room picker with a checkbox per room and
   a Done button, matching the real tvOS grouping flow (GroupingTVView). */


@starting-style{
  .tv-group-modal.open{ opacity:0; transform:scale(.97); }
}


/* The shipped Party Mode button — one press sends the anchor's music to
   every room (checks every toggle here). */





/* Round, matching the shipped picker's circle toggles (the square checkbox
   was the pre-remediation design). */










/* ---- Apple TV tabs: one panel visible at a time ----
   Same show/hide + fade-in system as the iPad sidebar's .ipad-panel/
   panel-in (see activateIpadPanel in the JS), just scoped to the tv
   screen. .tv-panels is display:contents so the active .tv-panel becomes
   a direct flex child of .tv-screen and inherits its 12px tab-to-content
   gap for free — no separate spacing rule needed here. Rooms' Group Rooms
   trigger lives inside the Rooms panel itself, so it's simply not in the
   DOM's visible flow (and not clickable) whenever another tab is up. */


/* min-height:0 lets this shrink below its content's intrinsic height inside
   the fixed-aspect-ratio TV frame instead of forcing the frame to overflow;
   overflow-y:auto is the safety net if a panel still doesn't fit after the
   mobile breakpoint's own size trims (e.g. Favorites' 4-tile row on a very
   narrow phone) — scrolls in place rather than clipping silently. */



/* The Playing reference is an intentional empty state: artwork is a quiet
   placeholder and all playback controls remain visible and legible from a
   sofa. Keep the previous demo content in the DOM for the interaction code,
   but render this actual app state whenever Playing is selected. */



























/* Now Playing tab — the shared np-transport/np-play/np-vol/np-bar kit
   already used by #npCard/#macCard, sized down to remote scale and
   carrying the same demo track as the rest of the site. */
/* Left-aligned labeled pill buttons (tvos-nowplaying.png), with the
   shuffle/repeat circles pinned to the row's far right. */
/* Chips spread across ~80% of the width in tvos-nowplaying.png (gaps ≈4.3%
   of screen width) before the far-right shuffle/repeat rounds — not a tight
   7px huddle at the left. */






/* Art dominates the center per the capture (~43% of screen height there).
   The screen is a fixed 16:9 box, so the tile flexes: it absorbs whatever
   height the NP stack has spare (flex:1 + 1fr row), capped at 150px —
   never overflowing the frame at narrow widths. */
.tv-np{ display:grid; flex:1; min-height:0;
  grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);
  grid-template-rows:minmax(0,1fr) auto;
  align-items:end; gap:8px; }
.tv-np .tile{ grid-column:2; grid-row:1; justify-self:center;
  height:100%; max-height:150px; aspect-ratio:1; width:auto; border-radius:15px; }
/* The capture's NP art is the Afterglow asset (same one the mini chip and
   watch use), not the shared kit's navy .tile.art gradient. */








/* space-between — with the panel now absorbing the screen's leftover height
   (flex:1), the NP stack spreads chips-top / art-center / transport-bottom
   the way the capture does instead of top-packing. */








/* TV keeps the small round knob — the capsule knob is an iOS-sheet thing. */


/* Bottom row (tvos-nowplaying.png): transport centered, volume cluster
   right-aligned on the SAME row; every control is a filled dark circle. */









/* Favorites tab — an art grid like the iPad's shelves, with the small
   trailing play glyph the real tvOS grid shows on a focused tile. */

/* Favorites matches tvos-favorites.png: 5 columns, capture's own art per
   tile (Playlists keeps the 4-column shelf — no capture exists for it). */










/* Gray service sub-line under each tile title ("Vela Streaming" etc.) —
   part of the shipped grid anatomy in tvos-favorites.png. */


/* Search + Services tabs share a plain row list — Rooms' own .tv-row shape,
   just carrying a generic .tile (not the room-only .tv-card-art) so these
   two tabs can draw from the full art/brand-tile palette. */





/* Below the living-room scene's own 560px breakpoint, the tv screen shrinks
   faster than its 5-tab strip and row content do — tighten both so every
   tab stays reachable and no panel clips against the frame. Placed after
   the base .tv-tab/.tv-row/.tv-now-* rules above so this wins the cascade
   at equal specificity (a @media block earlier in the file would lose to
   those unconditional rules despite the query matching). */
@media (max-width:560px){
  /* Six icon+label tabs no longer fit this frame's width without either
     wrapping mid-label or shrinking past legibility — let the capsule bar
     scroll instead (hidden scrollbar; same idea as tvOS's own
     focus-scrolled top nav). */
  
  
  
  .tv-tab{ font-size:0.5625rem; padding:3px 7px; }
  .tv-tab svg{ width:9px; height:9px; }
  
  
  /* Three labeled header chips + "Rooms" can't share this width: drop the
     Grouping Settings chip first, then labels down to icon-only. */
  
  
  
  
  
  
  
  
  
  /* Search/Services can list more rows than this frame has room for —
     cap to the same two-row budget the Rooms tab already fits in
     (the section head added above the list eats one more slot of budget). */
  
  /* Service sub-lines are the first thing to give up under the added
     section heads' vertical budget. */
  
  
  
  
  
  .tv-np .tile{ max-height:104px; }
  
  
  
}

/* TV frame — the tvOS interface IS the device. A slim inset bezel (single
   pixel highlight + tiny dark rim) reads as a mounted flat-screen without
   imitating a Mac's silhouette. Ambient wall halo lives on .lr-tv-halo. */
.apple-tv{
  display:block; position:relative;
  width:100%; aspect-ratio:16/9;
  border-radius:6px;
  overflow:hidden;
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(120,140,180,.08), transparent 55%),
    linear-gradient(170deg,#141821 0%,#0a0d13 45%,#06070a 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.08),
    inset 0 0 0 1px rgba(255,255,255,.06),
    inset 0 -40px 60px -40px rgba(0,0,0,.7),
    0 30px 60px -20px rgba(0,0,0,.7),
    0 10px 20px -10px rgba(0,0,0,.5);
}


/* A true 16:9 shape at phone widths leaves the tv screen too short for
   5 tabs + a panel's content no matter how far the row/type sizes above
   get trimmed — measured live at 375px, the box only had ~158px to work
   with against a ~190px+ need. 4:3 used to be the fix here, but that's
   squashed enough to stop reading as "a TV" at all (and was flagged as
   looking broken, not intentional) — 3:2 is a much smaller compromise
   (~188px, almost the full need) that still looks like a widescreen
   panel. Selectors/specificity mirror .lr-tv .apple-tv (line ~1306) and
   the unconditional rule just above so this wins the cascade at each;
   must stay below both in source order. */


/* ---- Zone 03: Widget cluster ----
   .w-home and .w-mac are the SAME WidgetKit widget (NowPlayingWidget.swift) —
   Home Screen and Mac desktop render the identical SwiftUI view, so they
   share a widget background/typography here rather than being drawn as two
   different things. .w-lock is a placeholder: the Lock Screen family isn't
   implemented yet (SonosWidgetsBundle.swift: "one widget for now; the bundle
   exists so lock-screen ... variants can join later"). The Watch is not a
   widget in code — it's a first-class app — but it lives in this cluster
   because the story is "on your wrist and glanceable everywhere". */
.widget-cluster{ position:relative; height:320px; width:100%; max-width:520px; margin:0 auto; }
.widget-item{
  position:absolute;
  transition: transform var(--dur-mid) var(--ease-out), box-shadow var(--dur-mid) var(--ease-out);
  /* Off-stage until the cluster enters view — quiet drop-in, no overshoot */
  opacity:0; transform:translateY(12px);
}
.widget-item:nth-child(1){ --stagger:0ms; }
.widget-item:nth-child(2){ --stagger:80ms; }
.widget-item:nth-child(3){ --stagger:160ms; }
.widget-item:nth-child(4){ --stagger:240ms; }
.widget-cluster.assembled .widget-item{
  animation:widget-drop 400ms var(--ease-out) var(--stagger) forwards;
}
@keyframes widget-drop{
  0%{ opacity:0; transform:translateY(12px); }
  100%{ opacity:1; transform:translateY(0); }
}
:root.reduced .widget-item{ opacity:1; transform:none; animation:none; }
.widget-cluster:hover .widget-item.lift1{ transform:translateY(-6px); }
.widget-cluster:hover .widget-item.lift2{ transform:translateY(-3px) translateX(2px); }
.widget-cluster:hover .widget-item.lift3{ transform:translateY(-8px) translateX(-2px); }
/* The Watch does not lift or tilt with the cluster; interactions live in
   the watch UI (play button + transport controls on its screen), not on
   the physical case. */
.widget-item:not(.w-watch):hover{ transform:translateY(-10px) scale(1.03) !important; box-shadow:0 20px 40px -16px rgba(0,0,0,.5); }
.w-lock{
  top:2%; left:0%; width:84px; height:84px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  z-index:1;
}
.w-lock svg{ width:30px; height:30px; }
/* Watch tucked into the top-right of the cluster — not a "widget" family but
   part of the glanceable story. Slight rotation reads as a real object sitting
   on the composition rather than a floating rectangle. */
.w-watch{
  top:-8%; right:0%;
  z-index:4;
  transform-origin:center;
  transform:translateY(12px) rotate(-2deg);
}
.widget-cluster.assembled .w-watch{
  animation:watch-drop 480ms var(--ease-out) 240ms forwards;
}
@keyframes watch-drop{
  0%{ opacity:0; transform:translateY(12px) rotate(-2deg); }
  100%{ opacity:1; transform:translateY(0) rotate(-2deg); }
}
:root.reduced .w-watch{ transform:none !important; animation:none; }
/* Widget background: matches NowPlayingWidget's explicit containerBackground
   (Color(white:.13) → Color(white:.06)) — widgets don't inherit the app's
   theme, so this stays a fixed dark gradient regardless of site theme. */
.w-home, .w-mac{
  background:linear-gradient(180deg,#212121,#0f0f0f);
  border-radius:22px; color:#fff;
}
.wgt-art{
  width:44px; height:44px; border-radius:8px; flex:none;
  background:radial-gradient(circle at 42% 42%, rgba(255,255,255,.22) 0 44%, transparent 45%), linear-gradient(135deg,#EC8573 0%,#C98FB8 48%,#8B6EEF 100%);
}
.wgt-play{
  width:26px; height:26px; border-radius:50%; background:#fff; flex:none;
  display:flex; align-items:center; justify-content:center;
  transition:transform .15s var(--ease-out);
}
.wgt-play:hover{ transform:scale(1.1); }
.wgt-play svg{ width:13px; height:13px; }
.wgt-title{ font-weight:600; line-height:1.25;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.wgt-sub{ color:rgba(255,255,255,.6); margin-top:2px; }

/* Small widget (systemSmall) */
.w-home{
  top:12%; left:20%; width:150px; height:150px;
  padding:14px; display:flex; flex-direction:column;
  z-index:2;
}
.w-home .wgt-row{ display:flex; align-items:flex-start; justify-content:space-between; }
.w-home .wgt-title{ margin-top:auto; font-size:0.7rem; }
.w-home .wgt-sub{ font-size:0.625rem; }

/* Medium widget (systemMedium) — anchored bottom-left so the Watch owns the
   right side of the cluster without collision. */
.w-mac{
  bottom:4%; left:4%; width:264px; height:118px;
  padding:16px; display:flex; align-items:center; gap:14px;
  z-index:3;
}
/* Below ~640px the absolute layout overlaps. Scale the whole cluster down to
   keep the visual relationship intact, rather than reflowing it. Two tiers so
   the phone breakpoint still fits comfortably. */
@media (max-width:640px){
  .widget-cluster{ transform:scale(.78); transform-origin:top center; height:230px; max-width:100%; }
}
@media (max-width:420px){
  .widget-cluster{ transform:scale(.62); height:190px; }
}
.wgt-art-lg{ width:76px; height:76px; border-radius:9px; }
.wgt-mac-text{ min-width:0; flex:1; display:flex; flex-direction:column; }
.w-mac .wgt-title{ font-size:0.75rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; -webkit-line-clamp:1; }
.wgt-artist{ font-size:0.6875rem; color:rgba(255,255,255,.6); margin-top:1px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.w-mac .wgt-sub{ font-size:0.625rem; color:rgba(255,255,255,.45); margin-top:1px; }
.wgt-transport{ display:flex; align-items:center; gap:16px; margin-top:auto; }
.wgt-transport > svg{ width:14px; height:14px; color:rgba(255,255,255,.7); }
.wgt-transport .wgt-play{ width:28px; height:28px; }
.wgt-transport .wgt-play svg{ width:14px; height:14px; }

/* ---- Zone 04: Theme-mirroring phone (the app's Music screen; follows the site theme,
        because following your system is the feature) ---- */
















/* ---- Zone 05: Private diagram ----
   The "your Wi-Fi, nothing else" line demonstrated, not asserted: packets
   move back and forth between the phone and speaker over the dashed line,
   and each end briefly pulses when a packet lands. Purely local — no
   cloud icon, no spinner, no server. That's the point. */
.private-diagram{
  display:flex; align-items:center; justify-content:center; gap:var(--s-4);
  padding:var(--s-5) var(--s-4);
}
.private-diagram svg{ overflow:visible; }
.dash-line{
  stroke:var(--text-3); stroke-width:2; stroke-dasharray:4 6; fill:none;
  animation: dash-march 18s linear infinite;
}
.private-diagram .glyph-icon{ color:var(--text-2); }
.pd-node{ position:relative; display:inline-flex; align-items:center; justify-content:center;
  width:44px; height:44px; border-radius:50%; }
.pd-ring{ position:absolute; inset:-4px; border-radius:50%;
  border:1.5px solid var(--accent); opacity:0; pointer-events:none; }
.pd-node.pulse .pd-ring{ animation:pd-pulse 700ms var(--ease-out); }
@keyframes pd-pulse{
  0%{ inset:2px; opacity:.7; }
  100%{ inset:-14px; opacity:0; }
}
.pd-wire{ position:relative; display:inline-flex; align-items:center; justify-content:center; width:120px; height:14px; }
.pd-packet{
  position:absolute; top:50%; left:0; width:8px; height:8px; border-radius:50%;
  background:var(--accent); box-shadow:0 0 8px var(--glow);
  transform:translate(-50%,-50%) scale(0); opacity:0; pointer-events:none;
}
/* Three packets, each with its own path (LTR or RTL), delay, and duration
   — feels like real, asynchronous traffic, not a metronome. */
.pd-p1{ animation:pd-fly-ltr 2.4s ease-in-out infinite; }
.pd-p2{ animation:pd-fly-rtl 3.1s ease-in-out infinite; animation-delay:.8s; }
.pd-p3{ animation:pd-fly-ltr 2.7s ease-in-out infinite; animation-delay:1.6s; }
@keyframes pd-fly-ltr{
  0%{ left:4px; opacity:0; transform:translate(-50%,-50%) scale(.3); }
  15%{ opacity:1; transform:translate(-50%,-50%) scale(1); }
  85%{ opacity:1; transform:translate(-50%,-50%) scale(1); }
  100%{ left:116px; opacity:0; transform:translate(-50%,-50%) scale(.3); }
}
@keyframes pd-fly-rtl{
  0%{ left:116px; opacity:0; transform:translate(-50%,-50%) scale(.3); }
  15%{ opacity:1; transform:translate(-50%,-50%) scale(1); }
  85%{ opacity:1; transform:translate(-50%,-50%) scale(1); }
  100%{ left:4px; opacity:0; transform:translate(-50%,-50%) scale(.3); }
}
:root.reduced .pd-packet, :root.reduced .pd-ring{ animation:none !important; }

/* ============================================================
   7b. FEATURE BREAKDOWN GRID
   ============================================================ */
.grid-head{ text-align:center; max-width:640px; margin-inline:auto; margin-bottom:var(--s-6); }
.grid-head p{ margin-inline:auto; }
.feature-grid{
  list-style:none;
  display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s-3);
}
@media (max-width:900px){ .feature-grid{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:560px){ .feature-grid{ grid-template-columns:1fr; } }
.feature-card{
  padding:var(--s-4);
  border-radius:var(--radius-md);
  background:var(--surface);
  border:1px solid var(--border);
  box-shadow:var(--shadow);
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.feature-card:hover{ transform:translateY(-2px); background:var(--surface-2); }
/* Break the 12-card monotone into labelled clusters so the eye gets rest
   points and each group reads as a distinct idea, not one endless grid. */
.feature-cluster + .feature-cluster{ margin-top:var(--s-7); }
.cluster-label{
  display:flex; align-items:center; gap:var(--s-3);
  font-size:0.9375rem; font-weight:600; color:var(--text-2);
  letter-spacing:0.01em; margin-bottom:var(--s-4);
}
.cluster-label::after{
  content:""; flex:1; height:1px;
  background:linear-gradient(90deg, var(--border), transparent);
}
.feature-card svg{
  width:20px; height:20px; color:var(--accent-text);
  margin-bottom:var(--s-2); display:block;
}
.feature-card h3{ font-size:1rem; font-weight:600; letter-spacing:0; line-height:1.3; }
.feature-card p{ margin-top:4px; font-size:0.875rem; color:var(--text-2); line-height:1.45; }

/* ============================================================
   8. CLOSING CTA
   ============================================================ */
.closing{ text-align:center; }
/* .closing-word/-line are <p> tags, so the base p{max-width:62ch} narrows
   their box — text-align only centers text within a box, not the box
   itself, so without margin-inline:auto they'd sit flush-left. */
.closing-word{ font-family:var(--font-display); font-weight:700; font-size:clamp(2.5rem,7vw,4rem); letter-spacing:-0.03em; margin-inline:auto; }
.closing-line{ margin-top:var(--s-3); color:var(--text-2); font-size:1.1875rem; margin-inline:auto; }
.closing-store-group{ display:inline-flex; align-items:center; gap:var(--s-3); margin-top:var(--s-5); flex-wrap:wrap; justify-content:center; }
.closing-badge{ display:inline-flex; height:48px; }
.closing-badge img{ height:48px; width:auto; display:block; }

/* ============================================================
   9. FOOTER
   ============================================================ */
footer{
  border-top:1px solid var(--border);
  padding-block:var(--s-6);
}
.footer-top{ display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:var(--s-3); }
.footer-links{ display:flex; flex-wrap:wrap; gap:var(--s-2) var(--s-4); }
.footer-links a{ text-decoration:none; color:var(--text-2); font-size:0.9375rem; }
.footer-links a:hover{ color:var(--text); }
/* Capped measure: this ran the full 1040px container (~160 characters per
   line) where comfortable reading is 60-80. */
.footer-disclaimer{ margin-top:var(--s-4); color:var(--text-3); font-size:0.8125rem; max-width:72ch; line-height:1.5; }
/* Nominative fair use — each brand's own favicon inline before its first
   mention, purely to help identify who's being disclaimed. Matches the
   app's own splash-screen footer treatment. */
/* Override the site-wide `img{ display:block }` reset — these need to
   sit inline in running paragraph text, not force their own line. */
.footer-disclaimer .brand-icon{ display:inline-block; width:14px; height:14px; border-radius:50%; vertical-align:-2px; margin:0 1px; }
.footer-meta{ margin-top:var(--s-3); color:var(--text-3); font-size:0.8125rem; display:flex; gap:var(--s-3); flex-wrap:wrap; }
/* codeframe mark + copyright, left of the footer links */
.footer-brand{ display:flex; align-items:center; gap:10px; }
.codeframe-mark{ display:inline-flex; width:22px; height:22px; color:var(--text-2); flex:none; }
.codeframe-mark canvas{ display:block; width:22px; height:22px; }
.footer-copy{ color:var(--text-3); font-size:0.9375rem; }
.footer-copy a{ color:inherit; text-decoration:none; }
.footer-copy a:hover{ color:var(--text); text-decoration:underline; }

/* ============================================================
   10. PRIVACY VIEW
   ============================================================ */
#privacy-view{ padding-block: clamp(48px,8vw,96px); }
.privacy-view{ max-width:68ch; margin-inline:auto; }
.back-link{ display:inline-block; margin-bottom:var(--s-4); text-decoration:none; color:var(--text-2); font-weight:500; }
.back-link:hover{ color:var(--text); }
.privacy-view h1{ font-size:clamp(2rem,4vw,2.75rem); }
.privacy-view h2{ font-size:1.375rem; margin-top:var(--s-6); margin-bottom:var(--s-2); }
.privacy-view p{ margin-top:var(--s-3); color:var(--text-2); }
.privacy-view strong{ color:var(--text); }

/* ============================================================
   10b. SUPPORT VIEW
   ============================================================ */
#support-view{ padding-block: clamp(48px,8vw,96px); }
.support-view{ max-width:56ch; margin-inline:auto; }
.support-view h1{ font-size:clamp(2rem,4vw,2.75rem); }
.support-view .lead{ margin-top:var(--s-3); }
.support-form{ margin-top:var(--s-6); }
.support-guides{ margin-top:var(--s-6); }
.support-guides h2{ font-size:1.375rem; margin-bottom:var(--s-3); }
.support-guide{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md);
  margin-top:var(--s-2); overflow:hidden;
}
.support-guide summary{
  cursor:pointer; list-style:none; padding:14px 16px; font-weight:600;
  display:flex; align-items:center; justify-content:space-between; gap:var(--s-3);
}
.support-guide summary::-webkit-details-marker{ display:none; }
.support-guide summary::after{ content:'+'; color:var(--text-3); font-size:1.25rem; font-weight:400; }
.support-guide[open] summary::after{ content:'−'; }
.support-guide-body{ padding:0 16px 16px; color:var(--text-2); }
.support-guide-body p + p{ margin-top:var(--s-2); }
.support-context{ margin-top:var(--s-4); padding:10px 12px; border-radius:var(--radius-sm);
  background:var(--surface); color:var(--text-2); font-size:.875rem; }

/* Topic selector — a segmented control, not a stack of radios */
.support-topic{
  display:flex; gap:var(--s-1); padding:var(--s-1);
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md);
}
.topic-btn{
  flex:1; appearance:none; border:0; background:transparent; cursor:pointer;
  padding:10px var(--s-3); border-radius:var(--radius-sm);
  font:inherit; font-size:0.9375rem; font-weight:500; color:var(--text-2);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.topic-btn:hover{ color:var(--text); }
.topic-btn[aria-pressed="true"]{ background:var(--surface-2); color:var(--text); font-weight:600; }
.topic-btn:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
@media (max-width:520px){ .support-topic{ flex-wrap:wrap; } .topic-btn{ flex:1 1 100%; } }

/* Fields */
.field{ margin-top:var(--s-4); display:flex; flex-direction:column; gap:var(--s-2); }
.field label{ font-size:0.9375rem; font-weight:600; color:var(--text); }
.field .hint{ font-weight:400; color:var(--text-3); font-size:0.8125rem; }
.field input, .field textarea{
  width:100%; font:inherit; font-size:0.9375rem; color:var(--text);
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-sm);
  padding:12px 14px; transition: border-color var(--dur-fast) var(--ease-out);
}
.field textarea{ min-height:132px; resize:vertical; line-height:1.5; }
.field input::placeholder, .field textarea::placeholder{ color:var(--text-3); }
.field input:focus, .field textarea:focus{ outline:none; border-color:var(--accent); }
.field-group[hidden]{ display:none; }

.support-actions{ margin-top:var(--s-5); display:flex; align-items:center; gap:var(--s-4); flex-wrap:wrap; }
.support-submit{
  appearance:none; border:0; cursor:pointer; font:inherit; font-size:0.9375rem; font-weight:600;
  color:var(--bg); background:var(--text); padding:13px 22px; border-radius:var(--radius-pill);
  transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.support-submit:hover{ transform:translateY(-1px); opacity:.9; }
.support-submit:focus-visible{ outline:2px solid var(--accent); outline-offset:3px; }
.support-submit[disabled]{ cursor:default; opacity:.6; transform:none; }
.support-direct{ font-size:0.9375rem; color:var(--text-3); }
.support-direct a{ color:var(--accent-text); text-decoration:none; font-weight:500; }
.support-direct a:hover{ text-decoration:underline; }
.support-note{ margin-top:var(--s-4); font-size:0.8125rem; color:var(--text-3); line-height:1.5; }

/* Honeypot — kept in the DOM (bots fill it) but out of view and out of the
   tab order for real users. Not display:none, which some bots skip. */
.hp-field{ position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden; }

/* Submit status message */
.support-status{ margin-top:var(--s-4); font-size:0.9375rem; font-weight:500; line-height:1.5; }
.support-status.is-ok{ color:var(--accent-text); }
.support-status.is-error{ color:#e5484d; }

/* ============================================================
   11. UTILITIES
   ============================================================ */

[hidden]{ display:none !important; }

/* --- Pricing and testimonial --- */
.price-note{ display:flex; align-items:flex-start; gap:8px; margin-top:var(--s-4); color:var(--text-2); font-size:0.9375rem; }
.price-note svg{ width:17px; height:17px; margin-top:2px; color:var(--accent-text); flex:none; }
.price-note b{ color:var(--text); font-weight:600; }
.platform-requirements{ margin-top:var(--s-2); color:var(--text-3); font-size:.8125rem; }

/* Questions before purchase: a quiet reading surface, not a card grid. */
.buyer-questions{ padding-block:var(--s-9); }
.buyer-questions-inner{ max-width:900px; }
.buyer-questions-head{ max-width:62ch; }
.buyer-questions-head h2{ font-size:clamp(2rem,4vw,3rem); text-wrap:balance; }
.buyer-questions-head p{ margin-top:var(--s-3); color:var(--text-2); }
.buyer-questions-list{ margin-top:var(--s-6); display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); column-gap:var(--s-7); }
.buyer-question{ padding-block:var(--s-4); border-top:1px solid var(--border); }
.buyer-question:nth-last-child(-n+2){ border-bottom:1px solid var(--border); }
.buyer-question:last-child{ grid-column:1 / -1; }
.buyer-question dt{ color:var(--text); font-size:1.0625rem; font-weight:600; line-height:1.35; }
.buyer-question dd{ margin:var(--s-2) 0 0; color:var(--text-2); font-size:.9375rem; line-height:1.55; }
.buyer-question a{ color:var(--accent-text); text-underline-offset:3px; }
@media (max-width:700px){
  .buyer-questions{ padding-block:var(--s-8); }
  .buyer-questions-list{ grid-template-columns:1fr; gap:0; }
  .buyer-question:nth-last-child(-n+2){ border-bottom:0; }
  .buyer-question:last-child{ border-bottom:1px solid var(--border); }
}

/* Testimonial above the closing CTA */
.testimonial{ max-width:720px; margin:0 auto var(--s-8); }
.testimonial-quote{ font-family:var(--font-display); font-weight:600;
  font-size:clamp(1.5rem, 3.2vw, 2.125rem); line-height:1.32; letter-spacing:-0.02em; text-wrap:balance; }
.testimonial-by{ margin-top:var(--s-3); color:var(--text-3); font-size:0.9375rem; }
.testimonial-by b{ color:var(--text-2); font-weight:600; }
.closing-price{ margin-top:var(--s-4); margin-inline:auto; color:var(--text-2); font-size:0.9375rem; }
.closing-price b{ color:var(--text); font-weight:600; }
.closing .platform-requirements{ margin-inline:auto; }

/* ============================================================
   12. CURRENT APP CHROME — iOS 26 LIQUID GLASS
   The native app now uses four independent circular tab lenses and a
   separate two-row player lens.  They deliberately remain discrete at rest;
   proximity and deformation during selection make the material feel shared.
   ============================================================ */


/* Solid fallback where backdrop-filter is unavailable */

/* Reduce Transparency: every glass surface becomes a near-opaque slab with
   blur dropped — the same swap the app performs (glassEffect -> opaque
   appBackground). Covers the phone chrome, the iPad/Mac floating family,
   the Mac menu bar/toolbar, the TV chip/modal and the site's own nav. */
@media (prefers-reduced-transparency: reduce){
  .glass, .nav.scrolled{
    backdrop-filter:none !important; -webkit-backdrop-filter:none !important;
  }
  
   /* the specular wash implies depth that is no longer there */
   /* meniscus is pure refraction */
  
  
  .glass, .nav.scrolled{ background:var(--surface-2); }
  
  
}
/* Increase Contrast: glass solidifies AND declares its edge — real border,
   full-contrast glyphs and secondary text over what was glass. */





/* Vibrancy: labels over glass read the sampled content through them, so they
   carry slightly more weight and contrast than the same roles over solid
   surfaces — secondary text is a high-mix of --app-text, never flat gray. */


/* Room chip — the light-gray capsule on the right of the mini player's top
   row ("Living Room ⌄" with the double-speaker glyph), per ios-home.png. */






/* Bottom row per ios-home.png: plain pause glyph (no circle), speaker
   glyph, then the volume slider with the app's round white knob. */
/* The app's row 2 aligns its pause glyph with the ART COLUMN above it
   (measured: pause centre 66pt vs art centre 61.7pt), not with the card's
   content edge — so the first column mirrors the art's width. */











/* Same app measure as .phone-player-volume: the pause column mirrors the
   art column above it, so the glyph sits under the artwork exactly as the
   app draws it, and the slider starts at 34.1% of screen width. */






 /* vibrancy: dim-but-legible over glass */






/* Liquid thumb: while dragging, the knob stretches slightly along the drag
   axis and spring-settles on release. Inert under REDUCED. */










/* Gooey chain, per ios-home.png: the four glass circles nearly touch, with a
   concave meniscus join between every adjacent pair (previously #npCard-only). */
   /* tangent — app measure */









@keyframes glass-bridge{
  0%{ opacity:0; transform:scaleX(.72) scaleY(.72); filter:blur(7px); }
  30%{ opacity:.9; transform:scaleX(1.04) scaleY(.92); filter:blur(0); }
  72%{ opacity:.62; transform:scaleX(1) scaleY(.82); }
  100%{ opacity:0; transform:scaleX(.84) scaleY(.68); }
}




@keyframes glass-materialize{
  0%{ opacity:0; transform:translateX(-58px) scale(.22); filter:blur(10px); border-radius:42% 58% 62% 38%; }
  62%{ opacity:1; transform:translateX(4px) scale(1.06); filter:blur(0); border-radius:58% 42% 48% 52%; }
  100%{ opacity:1; transform:translateX(0) scale(1); filter:blur(0); border-radius:50%; }
}












@media (max-width:560px){
  /* The marketing mockup is narrower than a current physical iPhone. Keep
     the native 62/74 geometry authored above, then scale the complete strip
     as one material system so the fourth lens is not cropped by the prop. */
  
  
}

/* Give the content room to pass beneath the overlay. This intentionally does
   not reserve an opaque band: the sampled content is what makes glass glass. */




/* --app-bg now carries the app's real canvas values globally (pure #000
   dark / soft off-white light), so no per-card override is needed. */
/* (retired: #npCard mini-player art override — the base 46px/11px IS the
    device-screenshot standard, and the lens grid column is 46px) */
/* Hero play button — the app's primary control is a solid black circle with
   a white glyph (white-on-dark flips via tokens, matching the captures). */

/* Queue thumbs carry the same soft circle motif as the generated artwork */



/* miniPhone renders the hero kit on a ~322px screen — the device zooms as ONE
   unit, so every chrome and deck size below carries the same k = 322/372 (~.866)
   factor. Type leads, nothing dinky, nothing internally inconsistent. */
/* Chrome geometry: the mini phone's screen is 322px against the hero's 372px,
   so the shared kit's absolute px land ~15% oversized here. Previously the tab
   strip faked the difference with `scale(.86)` — a transform, which layout
   never sees, so the strip measured hero-sized while rendering mini-sized and
   the active tab had to be counter-scaled to 1.3 to look right. These author
   the real sizes at k = 322/372 instead, which reproduces the hero's measured
   ratios (dev/parity.js, ios-home.png) in layout space and lets the shared
   1.13 active scale apply unaltered. */

















/* Row 2 exists twice: .phone-player-volume in the static markup, rebuilt as
   .phone-mini-controls by the deck script. Both carry the geometry. */















/* Condensed fold derives from the same factor: the hero's 50px row -> 43px. */
/* Condensed (rail) state — these must restate every property the scoped
   expanded rules above set, because an ID beats the shared `.is-condensed`
   class rules that would otherwise collapse the strip. */











/* (the bottom inset lives on .phone-chrome's own `bottom`; this padding
   double-counted it and floated the tab strip off the screen edge) */

/* (retired: lead art matches the base 62px — the owner's device screenshot
    is the scale reference now) */





/* Row status glyphs sit quietly at the row edge (capture: pause disc ~13px,
   wave ~15px-equivalent) — smaller than the shared 22px cell. */




/* The phone mockups are demonstrations, not screenshots: every visible tab
   owns a real screen.  The deck sits above the older illustrative content
   and below the floating chrome, so the glass keeps sampling moving content
   while the destination changes underneath it. */

/* Scroll-edge effect: where content meets the floating chrome it dissolves
   toward the canvas — no hard divider, no opaque band. The fade tops out
   part-transparent so the glass keeps sampling motion beneath it (an opaque
   band would make the lenses read flat). Sits above every screen AND the
   pushed detail surface (z 6 > detail's 2) but below the chrome overlay. */


/* The edge effect breathes with the chrome: when the fold condenses the
   bar+strip to one 50px row, the fade shortens to match its reach. */





@keyframes phone-content-settle{
  0%{ opacity:0; filter:blur(6px); transform:scale(.976); }
  100%{ opacity:1; filter:none; transform:none; }
}











/* When history is present it shares ONE capsule with the gear (ios-browse.png);
   the lone gear keeps its own circle on home/rooms/search. */







/* The play-suggestion chip under the large title (owner's device screenshot:
   "▶ Better Pimp…") — one tap resumes/plays the named track. */



/* Browse sections use LARGE bold headers (ios-browse.png) — home's small
   'ROOMS' caps label keeps .phone-demo-label. */









/* Reorder is a dashed-outline ghost tile with a ≡ glyph (ios-browse.png). */






/* Abstract gradient art (capture parity + the standing abstract-art rule):
   a soft lighter circle inset over a diagonal two-stop gradient. */




/* Group speaker-count badge pinned to the art's top-RIGHT corner (capture
   truth, both themes). Near-black with a white numeral in BOTH themes, per
   the light and dark captures (the dark disc does not flip). The separating
   ring is a SOLID surface color — white in light (crisp against the orange
   art), dark card-surface in dark. */


/* Lead/target card is visually leading by larger art ONLY — the capture shows
   all three room cards sharing the same surface tone. */







/* Status cell: EQ wave and circled-pause stack in one 22px cell so Pause All
   can melt one into the other. */












@keyframes stage-eq{ from{ transform:scaleY(.5); } to{ transform:scaleY(1); } }

/* Pause All pressed: the waves gooey-melt (collapse + blur) into the circled
   pause; room titles dim; instant swap under REDUCED. */












/* The app draws the speaker glyph directly on the row (ios-rooms.png) — no
   tinted tile behind it; the count rides as a ringed badge on the glyph. */













/* Rail arts follow the capture: Favorites = Afterglow orange→violet, pink→
   orange, teal-green; Playlists lead purple then blue (ios-browse.png). */










/* NavigationStack push: the page slides over the tab surface from the right
   (system push feel), and the exit mirrors the entry path exactly. */

@keyframes phone-detail-in{ from{ transform:translateX(100%); } to{ transform:none; } }

@keyframes phone-detail-out{ from{ transform:none; } to{ transform:translateX(103%); } }

/* ---- Room page (GroupDetailView, compact branch): the PLACE, not the
   track. Inline nav title = room name; compact summary row; Speakers/
   Volume with collapsible per-speaker faders; the queue INLINE. ---- */












/* Solid, not muted: the app's room-page summary row uses the same strong
   filled disc as the Now Playing sheet's transport (ios-roompage.png). */


/* The app's Volume block is FLAT on the page (ios-roompage.png) — no card
   surface: a bold section header with Edit Group… opposite it, the room's
   name, then the fader with a −/+ capsule. The old micro-label + boxed
   group + bare numeral were all site inventions. */
























/* Faders/queue reveal is OPACITY-ONLY, easeInOut .22 (spring+move was
   rejected in the app as glitchy double-motion). */










/* ---- Live volume bars (sheet + room page): 1:1 pointer drag, rubber-band
   past the ends, thumb press state. --vol drives fill + knob; --rb is the
   rubber overshoot the knob carries past its terminal. ---- */










/* Mini slider rubber-band: the input nudges with the overshoot, springs back. */



/* ---- Press states everywhere — the app's PressableStyle (Components.swift
   :1500): scale .96 + opacity .75 under .smooth(0.15). Instant on pointer-
   down; :active naturally cancels when the finger drags away. ---- */







/* ---- Now Playing as a sheet (the app's own presentation) ----------------
   ios raw capture: mini-player tap slides the full player up over a dimmed
   home board, grabber at top, rounded shoulders, status bar left showing. */



/* Sheet vertical rhythm (ios-nowplaying.png): generous air above the art,
   art ~75% of the content box, then an evenly spaced stack down to the
   toolbar — no dead band above the tools. */





/* Prev/next double-triangles sit midway between the dim shuffle/repeat and
   the play circle (capture ~30px-equivalent). */

/* Dim the home board while the sheet is up — same scrim the OS draws.
   Light capture samples ~20% black over the board; dark stays heavier. */


/* The floating chrome (mini player + tabs) ducks under the sheet. */

/* The home board pushes back under the sheet (iOS presentation), scaled by
   the sheet's live progress from JS — origin high so it recedes upward. */

/* REDUCED (covers BOTH the :root.reduced/?static path and, pre-JS, the OS
   media query): instant swap with a short cross-fade — no springs, no
   slide, no push-back. */




/* The global reduced clamps force transition-duration:.001ms on everything;
   the sheet's swap-with-cross-fade is the DESIGNED reduced behavior, so it
   is explicitly exempted (higher specificity + later, same !important). */

@media (prefers-reduced-motion: reduce){
  
  
  
  
  /* Same exemption for the pre-JS/OS media path. */
  
}

/* iPad and Mac use the same floating player family, with content visible
   through the material. The Mac variant is a balanced 1fr/auto/1fr strip:
   metadata left, transport + scrubber on the window centreline, volume right. */

/* Mac playbar — persistent floating bar spanning exactly the detail column:
   both edges track the sidebar/rail columns via the shared width vars, on
   the same duration/curve as the grid columns, so they glide in lockstep. */



/* Expander: a vertical up/down chevron stack, left of the art (capture) */


  /* 2.36% — app 34pt */


















/* Rail-open: the playbar narrows but keeps its identity — captures
   (macos-rail-queue/audio) still show room/track text, slider + stepper.
   Reserve real minimums for both flanks and tighten the transport; at this
   miniature scale everything compresses a notch so nothing overflows the
   capsule onto the rail. */





 /* chevrons stay — capture shows them rail-open */





/* Round white shadowed thumb riding the fill's right edge (macos-music.png) */

/* − | + stepper capsule — FILLED light-grey with inner hairline divider,
   no outer border (macos-music.png) */






/* One material entrance language across iPad, TV, Watch and Mac. It changes
   silhouette as it arrives (not a generic fade), then becomes perfectly still. */
.ipad,.watch-assembly,.mac{ --arrival-origin:50% 70%; }
.reveal.visible .ipad,.reveal.visible .watch-assembly,.reveal.visible .mac{
  animation:surface-resolve 620ms cubic-bezier(.16,1,.3,1) both;
}
@keyframes surface-resolve{
  0%{ opacity:.35; transform:scale(.965); filter:blur(12px) saturate(.75); clip-path:inset(7% 9% round 34px); }
  68%{ opacity:1; transform:scale(1.008); filter:blur(0) saturate(1.08); clip-path:inset(0 round 22px); }
  100%{ opacity:1; transform:scale(1); filter:none; clip-path:inset(0 round 0); }
}
:root.reduced .reveal.visible .ipad,:root.reduced .reveal.visible .watch-assembly,:root.reduced .reveal.visible .mac{ animation:none; }

/* In-page jumps (hero device links, See what it does) clear the fixed nav. */
.anchor-target{ display:block; }
.zone[id], .anchor-target{ scroll-margin-top:96px; }

/* Decorative overhangs (the TV scene's plant, device shadows) must never widen
   the page: without this, phones zoom out to fit them. clip, not hidden, so
   position:sticky keeps working. */
html{ overflow-x:clip; }


  .hero-anim > *, .reveal{ opacity:1 !important; transform:none !important; }


/* Goo groups: the blurred silhouette SVG sits under the crisp children */
.fx-goo-host{ position:relative; }
.fx-goo-host > :not(.fx-goo-sil){ position:relative; z-index:1; }
.fx-goo-sil{ position:absolute; inset:0; width:100%; height:100%; overflow:visible; pointer-events:none; z-index:0; }
