/* ═══════════════════════════════════════════════════════════════
   pages.css — page-specific layouts (home, media, live, etc.)
   ═══════════════════════════════════════════════════════════════ */

@layer pages {

  /* ─────────────────────────────────────────────────────────
     HOME — type-led hero w/ reactive logo
     ─────────────────────────────────────────────────────── */
  .home-hero {
    position: relative;
    padding: var(--s-4) 0 var(--s-5);
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
  }

  .home-eyebrow-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--s-3);
    flex-wrap: wrap;
    gap: var(--s-3);
  }
  .home-eyebrow-row .stamp {
    font-family: var(--font-mono);
    font-size: 10.5px;
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--text-muted);
  }

  .home-tagline {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: clamp(40px, 5.4vw, 76px);
    line-height: 0.92;
    letter-spacing: -0.04em;
    color: var(--text-primary);
    margin: 0 0 var(--s-3);
    text-wrap: balance;
  }
  .home-tagline .accent {
    color: var(--accent);
    text-shadow: 0 0 32px var(--accent-glow);
    font-style: italic;
    font-weight: 400;
  }
  .home-tagline .strike {
    color: var(--text-muted);
    text-decoration: line-through;
    text-decoration-thickness: 2px;
    font-weight: 400;
    text-shadow: none;
  }

  .home-intro {
    max-width: 620px;
    font-size: 15px;
    line-height: 1.5;
    color: var(--text-secondary);
    margin: 0 0 var(--s-4);
  }
  .home-intro strong {
    color: var(--text-primary);
    font-weight: 500;
  }

  .home-cta-row {
    display: flex;
    gap: var(--s-3);
    flex-wrap: wrap;
    margin-bottom: var(--s-5);
  }

  /* signal panel: telemetry card grid */
  .signal-grid {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    gap: var(--s-3);
    margin-top: var(--s-4);
  }
  .signal {
    grid-column: span 4;
    padding: var(--s-4);
    min-height: 140px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    transition: border-color var(--t-fast), transform var(--t-fast);
    /* Promote cards to their own compositor layer up front. Without
       this, hover would force the browser to allocate a new layer + do
       a fresh paint of the card's contents on EVERY mouseenter (the
       lag spike you'd feel on hover). With it, the hover is just a
       cheap transform + border swap on an already-composited layer.
       contain: layout paint keeps re-paints scoped to the card. */
    transform: translateZ(0);
    contain: layout paint;
  }
  .signal.wide   { grid-column: span 8; }
  .signal.tall   { grid-row: span 2; }
  .signal:hover  { border-color: var(--accent); transform: translate3d(0, -2px, 0); }

  .signal-eyebrow {
    font-family: var(--font-mono);
    font-size: 10.5px;
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--text-muted);
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  .signal-eyebrow .badge {
    color: var(--accent);
    background: var(--accent-soft);
    padding: 2px 8px;
    border-radius: var(--r-pill);
    border: 0.5px solid color-mix(in oklch, var(--accent) 40%, transparent);
  }
  .signal h3 {
    font-size: 19px;
    line-height: 1.15;
    margin: var(--s-3) 0 var(--s-2);
    letter-spacing: -0.02em;
    font-weight: 500;
    color: var(--text-primary);
  }
  .signal p {
    margin: 0;
    font-size: 12.5px;
    color: var(--text-secondary);
    line-height: 1.45;
  }
  .signal-foot {
    margin-top: var(--s-3);
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-family: var(--font-mono);
    font-size: 10.5px;
    letter-spacing: var(--tracking-mono);
    color: var(--text-muted);
  }
  .signal-foot .arrow {
    width: 24px; height: 24px;
    border-radius: 50%;
    border: 0.5px solid var(--line-edge);
    display: flex; align-items: center; justify-content: center;
    /* Explicit property list — `all` makes the browser watch every
       computed style for changes, which is more expensive than just
       the three properties this transition actually animates. */
    transition: border-color var(--t-fast),
                background    var(--t-fast),
                color         var(--t-fast);
  }
  .signal:hover .signal-foot .arrow {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent);
  }

  /* live signal — special treatment */
  .signal.live-signal {
    background:
      radial-gradient(circle at 80% 0%, var(--accent-soft), transparent 60%),
      var(--bg-pane);
    border-color: color-mix(in oklch, var(--accent) 30%, var(--line-edge));
  }
  .signal.live-signal .signal-eyebrow .badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
  }

  .home-readout {
    margin-top: var(--s-8);
    padding-top: var(--s-5);
    border-top: 0.5px solid var(--line-hairline);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-6) var(--s-7);
    font-family: var(--font-mono);
    font-size: 10.5px;
    letter-spacing: var(--tracking-mono);
    text-transform: uppercase;
    color: var(--text-muted);
  }
  .home-readout dt { color: var(--text-muted); margin-bottom: 4px; opacity: 0.7; }
  .home-readout dd {
    margin: 0;
    color: var(--text-primary);
    font-size: 13px;
    text-transform: none;
    letter-spacing: normal;
    font-family: var(--font-body);
  }

  /* ─────────────────────────────────────────────────────────
     LIVE / NOW PLAYING PAGE
     ─────────────────────────────────────────────────────── */
  .live-page-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--s-5);
    margin-bottom: var(--s-3);
    flex-wrap: wrap;
  }
  /* Right side of the live page header — holds either the live viewer
     count (when broadcasting) or the compact schedule (when offline). */
  .live-page-aside {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--s-2);
  }
  .live-head-schedule {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
  }
  .live-head-schedule-label {
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--text-muted);
  }
  .live-head-schedule-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    justify-content: flex-end;
  }
  .live-head-schedule-row {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-mono);
  }
  .live-head-schedule-row .day {
    font-size: 9.5px;
    letter-spacing: var(--tracking-wide);
    color: var(--accent);
    text-transform: uppercase;
    padding: 3px 7px;
    border: 0.5px solid var(--accent);
    border-radius: var(--r-1, 4px);
    line-height: 1;
  }
  .live-head-schedule-row .time {
    font-size: 11px;
    color: var(--text-secondary);
    letter-spacing: var(--tracking-mono);
  }
  .live-title {
    font-size: clamp(40px, 5.5vw, 72px);
    line-height: 0.95;
    margin: var(--s-2) 0 0;
    letter-spacing: -0.03em;
  }
  .live-title .accent { color: var(--accent); text-shadow: 0 0 24px var(--accent-glow); }

  /* Flex layout (was grid) so both columns stretch to the same height
     and the stream frame can grow to match the side column's content
     instead of leaving negative space below itself. */
  .live-hero {
    display: flex;
    align-items: stretch;
    gap: var(--s-4);
    margin-bottom: var(--s-4);
  }
  .live-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
    min-width: 0;
  }
  .live-side {
    width: 320px;
    flex-shrink: 0;
  }
  /* On narrower viewports the side column collapses under the player. */
  @media (max-width: 1100px) {
    .live-hero { grid-template-columns: 1fr; }
  }
  .stream-frame {
    /* Drop the fixed 16:9 lock — flex:1 inside .live-main makes the
       frame stretch to match the side column's content height. The min
       keeps a sensible baseline; YouTube's player letterboxes inside if
       the frame ends up taller than 16:9. */
    flex: 1;
    min-height: 360px;
    border-radius: var(--r-4);
    background:
      radial-gradient(circle at 50% 50%, var(--accent-soft), transparent 70%),
      linear-gradient(135deg, rgba(255,255,255,0.04), transparent),
      var(--bg-base);
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    overflow: hidden;
  }
  .stream-frame .placeholder {
    text-align: center;
    font-family: var(--font-mono);
    font-size: 12px;
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--text-muted);
  }
  .stream-frame .play-orb {
    width: 72px; height: 72px;
    border-radius: 50%;
    background: var(--bg-pane-strong);
    border: 0.5px solid var(--accent);
    display: flex; align-items: center; justify-content: center;
    margin: 0 auto var(--s-3);
    box-shadow: 0 0 40px var(--accent-glow);
    color: var(--accent);
  }

  .live-side {
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
  }
  /* Lighter side panels — no glass background, just a hairline rule on top
     to separate the sections. The page reads less heavy than before, more
     like the media page. */
  .live-card {
    padding: var(--s-3) 0 var(--s-3);
    border-top: 0.5px solid var(--line-hairline, var(--line-edge));
  }
  .live-card:first-child { border-top: none; padding-top: 0; }

  /* Recent-broadcasts: YouTube-playlist-style scrollable column. Each row
     is a thumb + title + relative-time, click loads it in the main player. */
  .live-card-broadcasts h4 { margin-bottom: var(--s-2); }
  .broadcast-scroll {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 360px;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 4px;
  }
  /* Slim themed scrollbar */
  .broadcast-scroll::-webkit-scrollbar { width: 6px; }
  .broadcast-scroll::-webkit-scrollbar-thumb {
    background: var(--line-edge);
    border-radius: 3px;
  }
  .broadcast-scroll::-webkit-scrollbar-thumb:hover { background: var(--accent); }
  .broadcast-row {
    display: grid;
    grid-template-columns: 84px 1fr;
    gap: var(--s-2);
    align-items: center;
    padding: 6px;
    background: transparent;
    border: 0.5px solid transparent;
    border-radius: var(--r-2);
    cursor: pointer;
    text-align: left;
    color: inherit;
    font: inherit;
    transition: background var(--t-fast), border-color var(--t-fast);
  }
  .broadcast-row:hover {
    background: var(--bg-pane-strong, rgba(255,255,255,0.04));
    border-color: var(--line-hairline, var(--line-edge));
  }
  .broadcast-row.active {
    border-color: var(--accent);
    background: var(--bg-pane-strong, rgba(255,255,255,0.04));
  }
  .broadcast-thumb {
    width: 84px;
    height: 47px;
    border-radius: var(--r-1);
    background-color: var(--bg-base);
    background-size: cover;
    background-position: center;
    flex-shrink: 0;
  }
  .broadcast-meta { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .broadcast-title {
    font-size: 12.5px;
    font-weight: 500;
    line-height: 1.3;
    color: var(--text-primary);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .broadcast-time {
    font-size: 10.5px;
    color: var(--text-muted);
    font-family: var(--font-mono);
    letter-spacing: var(--tracking-mono);
  }

  /* Live page head — actions cluster (live-meta + tip button) on the right */
  .live-page-actions {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    flex-wrap: wrap;
  }
  .live-tip-btn {
    text-decoration: none;
    white-space: nowrap;
  }
  .live-side h4 {
    font-size: 14px;
    font-family: var(--font-mono);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    color: var(--text-muted);
    margin: 0 0 var(--s-3);
    font-weight: 500;
  }

  /* Offline state in the now-playing panel — muted art swatch + plain text.
     No fake track names, no animated VU bars. */
  .now-playing-art-offline {
    background: linear-gradient(135deg, var(--bg-pane-strong, rgba(0,0,0,0.35)), rgba(0,0,0,0.15));
    opacity: 0.7;
  }
  .now-playing-offline .now-playing-text .track {
    color: var(--text-muted);
    font-family: var(--font-mono);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    font-size: 12px;
  }

  /* Live featured row — mirrors .media-featured-row so the SocialRail sits
     to the right of the stream window. */
  .live-featured-row {
    display: flex;
    align-items: stretch;
    gap: var(--s-5);
    margin-bottom: var(--s-4);
  }
  .live-featured-row .live-hero {
    flex: 1;
    min-width: 0;
    margin-bottom: 0;
  }
  @media (max-width: 720px) {
    .live-featured-row { flex-direction: column; }
  }

  .now-playing {
    display: flex;
    align-items: center;
    gap: var(--s-3);
  }
  .now-playing-art {
    width: 56px; height: 56px;
    border-radius: var(--r-2);
    background: linear-gradient(135deg, var(--accent), var(--accent-2));
    flex-shrink: 0;
    position: relative;
    overflow: hidden;
  }
  .now-playing-art::after {
    content: "";
    position: absolute; inset: 0;
    background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.4), transparent 60%);
  }
  .now-playing-text { min-width: 0; flex: 1; }
  .now-playing-text .track {
    font-size: 14px; font-weight: 500;
    color: var(--text-primary);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .now-playing-text .by {
    font-size: 11.5px;
    color: var(--text-muted);
    font-family: var(--font-mono);
    letter-spacing: var(--tracking-mono);
  }

  .vu-bars {
    display: flex;
    align-items: flex-end;
    gap: 3px;
    height: 24px;
    margin-top: var(--s-3);
  }
  .vu-bars span {
    flex: 1;
    background: linear-gradient(180deg, var(--accent), color-mix(in oklch, var(--accent) 35%, transparent));
    border-radius: 1px;
    animation: vu 1.2s ease-in-out infinite;
    transform-origin: bottom;
  }
  .vu-bars span:nth-child(1) { animation-delay: 0.0s; }
  .vu-bars span:nth-child(2) { animation-delay: 0.1s; }
  .vu-bars span:nth-child(3) { animation-delay: 0.25s; }
  .vu-bars span:nth-child(4) { animation-delay: 0.05s; }
  .vu-bars span:nth-child(5) { animation-delay: 0.18s; }
  .vu-bars span:nth-child(6) { animation-delay: 0.32s; }
  .vu-bars span:nth-child(7) { animation-delay: 0.12s; }
  .vu-bars span:nth-child(8) { animation-delay: 0.22s; }
  @keyframes vu {
    0%, 100% { transform: scaleY(0.25); }
    50%      { transform: scaleY(1.0); }
  }
  [data-motion="still"] .vu-bars span { animation: none; transform: scaleY(0.6); }

  /* Schedule rows: day chip on the left, title + time stacked on the right.
     Two-row grid with the day spanning both rows so long titles never crowd
     the time underneath. Reads cleanly even in the narrow side column. */
  .schedule-list { display: flex; flex-direction: column; gap: var(--s-2); }
  .schedule-row {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    column-gap: var(--s-3);
    row-gap: 2px;
    align-items: start;
    padding: var(--s-2) 0;
    border-bottom: 0.5px solid var(--line-hairline);
  }
  .schedule-row:last-child { border-bottom: 0; }
  .schedule-row .day {
    grid-row: 1 / span 2;
    align-self: center;
    font-family: var(--font-mono);
    font-size: 9.5px;
    letter-spacing: var(--tracking-wide);
    color: var(--accent);
    text-transform: uppercase;
    padding: 4px 8px;
    border: 0.5px solid var(--accent);
    border-radius: var(--r-1, 4px);
    line-height: 1;
    min-width: 44px;
    text-align: center;
  }
  .schedule-row .title {
    grid-column: 2;
    grid-row: 1;
    color: var(--text-primary);
    font-size: 13px;
    line-height: 1.3;
  }
  .schedule-row .time {
    grid-column: 2;
    grid-row: 2;
    font-family: var(--font-mono);
    font-size: 10.5px;
    color: var(--text-muted);
    letter-spacing: var(--tracking-mono);
  }

  /* ─────────────────────────────────────────────────────────
     MEDIA PAGE
     ─────────────────────────────────────────────────────── */
  .featured-card {
    padding: var(--s-5);
    margin-bottom: var(--s-4);
    background:
      radial-gradient(circle at 12% 0%, var(--accent-soft), transparent 50%),
      var(--bg-pane);
  }
  .featured-card .release-title {
    font-size: clamp(32px, 4vw, 48px);
    line-height: 0.95;
    margin: var(--s-2) 0 var(--s-2);
    letter-spacing: -0.03em;
  }

  .platform-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin-top: var(--s-4);
  }

  .release-list {
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    margin-top: var(--s-5);
  }
  .release-row {
    display: grid;
    grid-template-columns: 80px 1fr 120px auto;
    gap: var(--s-4);
    padding: var(--s-4);
    border-radius: var(--r-3);
    border: 0.5px solid transparent;
    transition: border-color var(--t-fast), background var(--t-fast);
    align-items: center;
  }
  .release-row:hover {
    border-color: var(--line-edge);
    background: rgba(255,255,255,0.02);
  }
  .release-cover {
    aspect-ratio: 1;
    border-radius: var(--r-2);
    background: linear-gradient(135deg, var(--accent), var(--accent-2));
    position: relative;
    overflow: hidden;
  }
  .release-cover::after {
    content: "";
    position: absolute; inset: 0;
    background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.4), transparent 65%);
  }
  .release-row .title {
    font-size: 17px;
    color: var(--text-primary);
    font-weight: 500;
  }
  .release-row .descriptor {
    font-size: 12.5px;
    color: var(--text-muted);
    margin-top: 2px;
  }
  .release-row .date {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--text-muted);
    letter-spacing: var(--tracking-mono);
  }

  /* page header */
  .page-head {
    margin-bottom: var(--s-4);
  }
  .page-head h1 {
    font-size: clamp(40px, 5vw, 64px);
    line-height: 0.95;
    margin: var(--s-2) 0 var(--s-3);
    letter-spacing: -0.03em;
  }
  .page-head .lede {
    max-width: 640px;
    font-size: 15px;
    color: var(--text-secondary);
    line-height: 1.5;
  }

  /* glossary list */
  .glossary-list {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--s-3);
  }
  .glossary-row {
    display: grid;
    grid-template-columns: 40px 1fr;
    gap: var(--s-3);
    padding: var(--s-4) var(--s-5);
    align-items: start;
    transition: border-color var(--t-fast);
  }
  .glossary-row:hover { border-color: var(--accent); }
  .glossary-num {
    font-family: var(--font-mono);
    font-size: 10.5px;
    letter-spacing: var(--tracking-wide);
    color: var(--accent);
    padding-top: 4px;
  }
  .glossary-term {
    font-size: 17px; font-weight: 500;
    margin: 0 0 4px;
    color: var(--text-primary);
    letter-spacing: -0.01em;
  }
  .glossary-def {
    font-size: 13.5px;
    color: var(--text-secondary);
    line-height: 1.55;
    margin: 0;
  }
  .uses-list {
    margin: var(--s-3) 0 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .uses-list li {
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--text-secondary);
    padding: 6px 0;
    border-bottom: 0.5px solid var(--line-hairline);
    display: flex; align-items: center; gap: 10px;
  }
  .uses-list li::before {
    content: "";
    width: 4px; height: 4px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 6px var(--accent-glow);
    flex-shrink: 0;
  }
  .uses-list li:last-child { border-bottom: 0; }
  @media (max-width: 880px) {
    .glossary-list { grid-template-columns: 1fr; }
  }

  .foot {
    margin: var(--s-5) 0 var(--s-3);
    padding-top: var(--s-3);
    border-top: 0.5px solid var(--line-hairline);
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--s-4);
    font-family: var(--font-mono);
    font-size: 10.5px;
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--text-muted);
  }
  /* The .rail-actions copy of theme picker + lightswitch is mobile-
     only; on desktop the HUD's copy is the active one. */
  .rail-actions { display: none; }

  .foot-links { display: flex; gap: var(--s-4); flex-wrap: wrap; }
  /* Footer links — visually distinct from the surrounding muted
     copyright text (which is plain) so the user reads them as
     clickable. Slight glow + lighter colour + dotted underline.
     Applies on both desktop and mobile. */
  .foot-links a {
    color: var(--text-secondary);
    text-decoration: none;
    border-bottom: 0.5px dotted currentColor;
    padding-bottom: 1px;
    text-shadow: 0 0 6px rgba(255, 255, 255, 0.12);
    transition: color var(--t-fast), text-shadow var(--t-fast),
                border-color var(--t-fast);
  }
  .foot-links a:hover,
  .foot-links a:focus-visible {
    color: var(--accent);
    text-shadow: 0 0 10px var(--accent-glow);
    border-bottom-color: var(--accent);
  }

  /* ─────────────────────────────────────────────────────────
     RESPONSIVE
     ─────────────────────────────────────────────────────── */
  @media (max-width: 880px) {
    .app { grid-template-columns: 1fr; }
    .rail {
      position: static;
      flex-direction: row;
      align-items: center;
      width: auto;
      margin: var(--s-3);
      padding: var(--s-3);
      border-radius: var(--r-3);
      gap: var(--s-3);
    }
    .rail-logo { width: 36px; height: 36px; margin: 0; }
    .wordmark { padding: 0; font-size: 26px; }
    .nav { flex-direction: row; flex: 1; margin: 0; gap: 4px; overflow-x: auto; }
    .nav-item { padding: 8px 12px; }
    .live-pip { display: none; }
    .main { padding: 0 var(--s-4) var(--s-7); grid-column: 1; }
    .hud { margin: var(--s-3); }
    .signal-grid { grid-template-columns: 1fr; }
    .signal, .signal.wide { grid-column: span 1; }
    .live-hero { grid-template-columns: 1fr; }
    .release-row { grid-template-columns: 64px 1fr; }
    .release-row .date, .release-row .arrow { display: none; }
  }

  /* ─────────────────────────────────────────────────────────
     HOME (logo-led variant) — portal beside compact content+cards
     The portal is the centerpiece; the right column stacks
     headline → intro → CTAs → signal cards so empty space
     beside the portal's lower half gets used.
     ─────────────────────────────────────────────────────── */
  .home-hero-row {
    display: flex;
    align-items: flex-start;
    gap: var(--s-7);
    flex-wrap: wrap;
    margin: var(--s-4) 0 0;
  }

  .home-hero-side {
    flex: 1;
    min-width: 320px;
    display: flex;
    flex-direction: column;
    gap: var(--s-4);
  }

  /* Inside the narrower hero side column the 12-col signal grid is
     too tight, so drop to a 6-col layout: default cards span half,
     "wide" / live cards span full width. */
  .home-hero-side .signal-grid {
    margin-top: var(--s-2);
    grid-template-columns: repeat(6, 1fr);
  }
  .home-hero-side .signal             { grid-column: span 3; min-height: 120px; padding: var(--s-3); }
  .home-hero-side .signal.wide        { grid-column: span 6; }
  .home-hero-side .signal.live-signal { grid-column: span 6; }
  .home-hero-side .signal h3          { font-size: 18px; }
  .home-hero-side .signal p           { font-size: 12.5px; }
}

/* ──────────────────────────────────────────────────────────────────
 * PHONE (≤600px) — desktop-style layout, just compressed
 * ──────────────────────────────────────────────────────────────────
 * The 880px tablet breakpoint above collapses the vertical rail to
 * a horizontal nav at the top, but the user wants the desktop layout
 * preserved on phones: vertical sidebar on the left, portal + cards
 * on the right (stacked underneath each other when narrow), ticker
 * pinned at the bottom of <main> as usual.
 *
 * This block UNDOES the rail-row collapse and re-pins the rail as a
 * narrow vertical capsule. Wordmark hides; logo + nav items stay.
 * Hero stacks portal-above-cards. Inline <span style="font-size:Npx">
 * bombs in the headline copy get clamped via !important. */
@media (max-width: 600px) {
  /* ── grid: minmax(0, 1fr) is THE fix for the "everything is too
     wide" problem. The plain 1fr track has implicit min-width: auto,
     which means it sizes to the intrinsic width of its widest
     descendant — long unbreakable text, fixed-width buttons, long
     URLs, etc. all force the track wider than the viewport, and the
     whole page horizontal-scrolls. minmax(0, 1fr) tells the grid
     "this column can shrink to zero," so the track honors the
     viewport regardless of content. min-width: 0 on every layout
     container does the same for flex children. ─────────────────── */
  .app {
    grid-template-columns: 88px minmax(0, 1fr);
    gap: 0;
    min-width: 0;
  }
  .main { min-width: 0; }
  .home-hero, .home-hero-row, .live-featured-row,
  .media-featured-row, .signal-grid, .live-hero,
  .content-web, .content-web > *, .featured-card,
  .apps-stack, .glossary-list, .gear-stack, .gear-cols,
  .live-side, .live-main, .live-card, .live-page-head,
  .page-head, .signal {
    max-width: 100%;
    min-width: 0;
  }
  /* Defensive: catch any descendant with a long unbreakable string
     (URL, code, unsplit word) and let it break instead of pushing
     the viewport sideways. */
  .main, .main * {
    overflow-wrap: anywhere;
    word-break: break-word;
  }
  /* The content explorer canvas is sized in JS via ResizeObserver
     from its container's clientWidth. Force the container to never
     exceed its grid cell so the canvas's CSS width fits. */
  .content-web canvas {
    max-width: 100%;
    height: auto;
  }
  /* Images + iframes default to intrinsic-width, which can spill
     past a narrow column. Cap them. */
  .main img, .main iframe, .main video, .main svg {
    max-width: 100%;
    height: auto;
  }

  /* ── rail: revert to fixed vertical, narrow. Use top + height
     instead of top + bottom so the rail's height stays stable when
     mobile browsers' address bar shows/hides (which changes the
     visual viewport height — top:X + bottom:Y stretches/shrinks the
     element, which the user reported as the bottom "glitching up").
     100lvh is the largest viewport height (chrome-hidden); fallback
     to 100vh on browsers without dvh/lvh support. ────────────── */
  .rail {
    position: fixed;
    left: var(--s-2);
    top: var(--s-2);
    bottom: auto;
    height: calc(100vh - var(--s-2) * 2);
    height: calc(100lvh - var(--s-2) * 2);
    width: 76px;
    flex-direction: column;
    align-items: center;
    margin: 0;
    padding: var(--s-2);
    border-radius: var(--r-3);
    gap: var(--s-1);
  }
  .rail-logo {
    width: 36px; height: 36px;
    margin: 0 0 var(--s-2);
  }
  .wordmark { display: none; }
  .nav {
    flex-direction: column;
    /* flex: 0 0 auto — let nav take only its content height instead
       of stretching to fill remaining rail space. Without this the
       rail-actions theme controls get pushed to the bottom of the
       sidebar by nav's flex: 1, no matter what margin-top we set
       on rail-actions. */
    flex: 0 0 auto;
    width: 100%;
    margin: 0;
    gap: 4px;
    overflow-x: visible;
    overflow-y: auto;
  }
  .nav-item {
    flex-direction: column;
    gap: 2px;
    padding: 6px 2px;
    font-size: 9.5px;
    letter-spacing: 0.04em;
    justify-content: center;
    text-align: center;
    line-height: 1.15;
  }
  /* Force the label span to wrap inside the narrow rail. min-width: 0
     overrides the flex-item intrinsic size that otherwise locks the
     span to its longest unbreakable word ("GLOSSARY" at 9.5px mono is
     wider than 60px). */
  .nav-item > span {
    min-width: 0;
    max-width: 100%;
    word-break: break-word;
    overflow-wrap: anywhere;
    white-space: normal;
    text-align: center;
  }
  .live-pip { display: none; }

  /* ── main: reset grid-column from the 880px row-layout rule ── */
  .main {
    padding: 0 var(--s-3) var(--s-7);
    grid-column: 2;
  }

  /* ── HUD bar: one line, smaller font + tighter tracking. The
     default .hud has height: 52px which is too tall for the small
     phone HUD content; auto-height + tight padding keeps the bar
     slim. flex-wrap: nowrap forces a single line; overflow: hidden
     truncates if anything still doesn't fit. */
  .hud {
    margin: var(--s-2) 0;
    padding: 4px var(--s-3);
    flex-wrap: nowrap !important;
    gap: var(--s-2);
    height: auto !important;
    overflow: hidden;
  }
  .hud-meta {
    flex-wrap: nowrap !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Don't apply the small font to .hud-actions — its children are
     theme dots and the lightswitch, both icon-shaped, not text. */
  .hud-meta * {
    font-size: 9px !important;
    letter-spacing: 0.02em !important;
  }
  /* Hide the HUD's copy of theme picker + lightswitch on phone —
     the duplicate inside .rail-actions (rendered by Sidebar) is
     the visible one. Position: fixed wasn't sticking reliably
     across address-bar show/hide; baking the controls into the
     rail's normal flow as a real DOM child is the stable fix. */
  .hud-actions { display: none !important; }
  .rail-actions {
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    gap: var(--s-2) !important;
    /* Removed margin-top: auto — that was pushing the controls to
       the bottom of the rail. The user wants them sitting right
       under the About nav item, in normal flow. */
    margin-top: var(--s-3) !important;
    padding-top: var(--s-3) !important;
    border-top: 0.5px solid var(--line-hairline) !important;
    width: 100% !important;
  }
  /* 2x2 grid (mobile only) — flex-wrap leaves 3 on top + 1 below
     when fitting; grid forces a clean 2-column / 2-row square. */
  .rail-actions .rail-theme-row {
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
    gap: 4px !important;
    justify-items: center !important;
    align-items: center !important;
  }
  .hud-actions .dot-row {
    flex-wrap: wrap;
    gap: 4px;
    justify-content: center;
  }
  /* Don't push the rail's bottom UP through the actions box — the
     rail's calculated height needs to leave room for the actions
     pinned at bottom: var(--s-3). At 76px wide actions × ~50px tall
     + 12px breathing = ~62px reserved. */
  .rail {
    bottom: auto;
    height: calc(100vh - 36px - 80px) !important;
    height: calc(100lvh - 36px - 80px) !important;
  }

  /* ── home hero: header (eyebrow / title / intro / CTAs) above the
     portal, cards below it. Done with display: contents on the side
     wrapper to flatten its children into siblings of the portal-wrap,
     then `order` sets the visual sequence. ─────────────────────── */
  .home-hero-side { display: contents; }
  .home-tagline    { order: 1; }
  .home-intro      { order: 2; }
  .home-cta-row    { order: 3; }
  .hero-portal-wrap { order: 4; }
  .home-hero-side .signal-grid { order: 5; }
  .home-hero-row {
    flex-direction: column;
    gap: var(--s-3);
  }
  .home-hero-side {
    min-width: 0;
    width: 100%;
  }
  /* Portal wrap takes the full main column on phone so the controls
     have room to lay out 2-up below the stage. The stage itself is
     capped narrower so it doesn't dominate vertical space. */
  .hero-portal-wrap {
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 !important;
    align-self: stretch;
  }
  .fs-stage-wrap {
    /* Center under the control grid below. The previous 200px cap
       sat flush-left while the controls filled 100%, reading
       off-center. Cap at 92vw so the canvas still leaves a small
       breathing margin on phone but visually balances the wider
       control surface beneath it. */
    max-width: min(360px, 92vw);
    margin: 0 auto;
  }
  /* The portal halos (.fs-portal-glow* with inset: -60px) and the
     SVG outer linework (.fs-exterior with inset: -22px and
     width: calc(100% + 44px)) bleed past the stage edges by design
     on desktop, but on a phone column they push the page wider than
     the viewport — that was the remaining horizontal-wiggle on
     home. Drop them on mobile; the canvas alone reads fine. */
  .fs-portal-glow,
  .fs-portal-glow-2,
  .fs-exterior {
    display: none !important;
  }
  /* folded-space.css has @media (max-width:720px) { .fs-stage {
     aspect-ratio: 4/5; min-height: 480px } } — at 4:5 aspect with
     480px min-height the canvas is forced to 384px wide, blowing past
     the wrap. Force a small 1:1 stage. */
  .fs-stage {
    min-height: 160px !important;
    aspect-ratio: 1 / 1 !important;
    max-width: 100% !important;
  }

  /* ── headline: clamp inline-span font-size bombs ─────────────── */
  .home-tagline span[style*="font-size"] {
    font-size: clamp(20px, 6.8vw, 44px) !important;
    line-height: 1.05 !important;
  }
  .home-tagline {
    font-size: clamp(20px, 6.8vw, 44px);
    line-height: 1.05;
    overflow-wrap: break-word;
    word-break: break-word;
  }

  /* ── eyebrow / intro / CTAs ──────────────────────────────────── */
  .home-eyebrow-row { flex-wrap: wrap; gap: 4px var(--s-2); }
  .home-eyebrow-row .eyebrow,
  .home-eyebrow-row .stamp { font-size: 10px; }
  .home-intro { font-size: 12.5px; line-height: 1.5; }
  .home-cta-row { flex-wrap: wrap; gap: var(--s-2); }

  /* ── signal cards: single column, more compact ──────────────── */
  .home-hero-side .signal-grid {
    grid-template-columns: 1fr;
    gap: var(--s-3);
  }
  .home-hero-side .signal,
  .home-hero-side .signal.wide,
  .home-hero-side .signal.live-signal {
    grid-column: span 1;
    min-height: 76px;
    padding: var(--s-3);
  }
  .home-hero-side .signal h3 { font-size: 14px; }
  .home-hero-side .signal p  {
    font-size: 11.5px;
    line-height: 1.4;
    -webkit-line-clamp: 2;
    overflow: hidden;
    display: -webkit-box;
    -webkit-box-orient: vertical;
  }

  /* ── page heads: smaller, fluid title ───────────────────────── */
  .page-head h1 {
    font-size: clamp(28px, 8vw, 56px);
    line-height: 1.1;
  }
  .page-head .eyebrow { font-size: 10px; }
  .page-head .lede    { font-size: 13px; line-height: 1.5; }

  /* ── live page: stack video player above now-playing + schedule ─
     .live-hero is a flex row by default with .live-side at a fixed
     320px (wider than the phone column → it eats all the space and
     hides the player). Force the column to stack with player first. */
  .live-hero {
    flex-direction: column;
    gap: var(--s-3);
  }
  .live-main {
    width: 100%;
    min-width: 0;
    order: 1;
  }
  .live-side {
    width: 100%;
    min-width: 0;
    order: 2;
  }
  .stream-frame {
    min-height: 200px;
    aspect-ratio: 16 / 9;
  }

  /* ── folded-space control surface: 2 columns on phone. 3-col was
     too tight (each cell ~60px couldn't fit label + value + track,
     they overlapped). 2-col gives ~130px per cell which actually
     reads. */
  .fs-controls-grid {
    grid-template-columns: repeat(2, 1fr) !important;
    gap: var(--s-2) var(--s-3) !important;
  }
  /* Make slider components stack so label + value + track each get
     full cell width (instead of fighting for inline space). */
  .fs-slider-row {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 2px !important;
  }
  .fs-slider-label,
  .fs-slider-val {
    font-size: 9.5px !important;
    line-height: 1.1 !important;
  }
  /* Make the slider tracks capture touch — without this, dragging
     a fader pans the page horizontally instead of moving the value. */
  .fs-slider-track,
  .fs-slider,
  .fs-slider-cursor,
  .fs-slider-cursor-bar {
    touch-action: none;
  }

  /* ── content explorer borders: keep corner brackets visible
     (user wants chrome back), hide the ::before/::after 14×14
     corner accent squares (bottom-right one cuts through the
     canvas's lower edge), AND hide the .cw-frame SVG rectangle
     outline because preserveAspectRatio="none" with a 100×100
     viewBox + a non-square container makes the "border square"
     end up shorter than the canvas → bottom intersects the
     explorer area. The corner brackets give visual chrome
     without the rectangle alignment issues. */
  .content-web::before,
  .content-web::after { display: none !important; }
  .cw-frame { display: none !important; }
  .cw-bracket {
    width: 24px !important;
    height: 24px !important;
  }

  /* Content explorer header: stack the title above the stats line
     so the head doesn't sprawl. Stats become one tight mono line. */
  .content-web-head {
    flex-direction: column !important;
    align-items: flex-start !important;
    gap: 4px !important;
  }
  .content-web-head h2,
  .content-web-head h3 {
    font-size: 17px !important;
  }
  .content-web-stamp {
    font-size: 9.5px !important;
    line-height: 1.2 !important;
    letter-spacing: 0.04em !important;
    flex: none !important;
  }
  .cw-filter-btn {
    align-self: flex-end;
    font-size: 10px !important;
    padding: 4px 8px !important;
  }

  /* Legend list: tighter chips, two-column-ish grid via flex-wrap so
     the playlist colors fit cleanly under the canvas without each
     entry hogging a full row. */
  .content-web-legend {
    gap: 6px var(--s-2) !important;
    margin-top: var(--s-2) !important;
    padding-top: var(--s-2) !important;
  }
  .cw-legend-item {
    font-size: 10.5px !important;
    padding: 3px 6px !important;
    gap: 4px !important;
    line-height: 1.15 !important;
  }
  .cw-legend-dot {
    width: 8px !important;
    height: 8px !important;
    border-width: 1.2px !important;
  }

  /* ── focused-video iframe: take ~92% of the canvas, centered.
     Default React positionFocus() inline-styles set width/height
     in pixels assuming the desktop "iframe + side-info popup"
     layout, which on the phone column collapses to a smaller
     stacked iframe. We hide the info popup on mobile and force
     the iframe to a comfortable centered 92% box. !important
     beats positionFocus's inline styles. */
  .cw-focus-node {
    position: absolute !important;
    top: 4% !important;
    left: 4% !important;
    right: auto !important;
    bottom: auto !important;
    width: 92% !important;
    height: 92% !important;
    max-width: none !important;
    max-height: none !important;
  }
  /* The consent-denied placeholder inside the focus-node has its own
     min-height: 240px which can fight the focus-node's percentage
     sizing — kill the floor so the placeholder fills its container
     instead of pushing it taller than the canvas. */
  .cw-focus-node .yt-consent-gate {
    min-height: 0 !important;
    height: 100% !important;
  }

  /* ── focus-info popup: don't let it cover the whole viewport.
     The React component positions it absolutely via inline styles
     from positionFocus(). !important on these properties beats
     inline. Hide on phone to keep the iframe + close button as
     the only focused-state UI; user can read details on YouTube. */
  .cw-focus-info {
    display: none !important;
  }

  /* ── about page: keep the contact email on one line, AND make sure
     the contact card doesn't have a basis-width that overflows the
     column (caused horizontal scroll on About). The .main *
     overflow-wrap: anywhere rule earlier breaks the email mid-string;
     scope an exception for mailto links inside the contact card. */
  .about-contact-card a[href^="mailto:"] {
    /* The JSX renders this <a> with inline style={{ fontSize: 17,
       wordBreak: 'break-all' }} — !important is required to beat
       the inline declaration. Without it the link stays at 17px,
       and "contact@sassquatchmusic.com" overflows the column with
       the .com TLD clipped off. */
    word-break: keep-all !important;
    overflow-wrap: normal !important;
    white-space: nowrap !important;
    font-size: clamp(10px, 3.2vw, 13px) !important;
  }
  .about-contact-card {
    flex-basis: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
  }
  .about-contact-row {
    flex-direction: column !important;
    align-items: stretch !important;
    min-width: 0 !important;
    max-width: 100% !important;
  }

  /* ── live page schedule: stack vertically, left-aligned, tight.
     Default is flex-row + wrap + justify: flex-end (right-aligned).
     On phone we want a compact stack hugging the left edge. */
  .live-head-schedule {
    align-items: flex-start !important;
    margin-top: var(--s-2);
  }
  .live-head-schedule-list {
    flex-direction: column !important;
    align-items: flex-start !important;
    flex-wrap: nowrap !important;
    justify-content: flex-start !important;
    gap: 2px !important;
  }
  .live-head-schedule-row {
    flex-shrink: 0;
    gap: 4px !important;
  }
  .live-head-schedule-row .day {
    font-size: 9px !important;
    padding: 2px 5px !important;
    line-height: 1 !important;
  }
  .live-head-schedule-row .time {
    font-size: 10px !important;
  }
  .live-head-schedule-label {
    font-size: 9px !important;
  }

  /* ── eyebrow row: left-aligned (no space-between pushing the
     stamp to the right where it gaps awkwardly). Stamp + eyebrow
     stack tight on the left. */
  .home-eyebrow-row {
    margin-top: var(--s-3);
    margin-bottom: var(--s-3);
    justify-content: flex-start !important;
    gap: var(--s-2) var(--s-3) !important;
  }
  .home-eyebrow-row .stamp,
  .home-eyebrow-row .eyebrow {
    font-size: 9px !important;
    letter-spacing: 0.04em !important;
  }

  /* ── hard-lock scroll: kill the iOS/Android overscroll bounce that
     creates negative space (rubber band) at top and bottom on
     phones. Page scroll stays normal; only the over-scroll past
     content is suppressed. */
  html, body {
    overscroll-behavior-y: none;
  }

  /* ── ticker pinned to the very top of the viewport. The default
     position is at the bottom of <main> as a slim status strip;
     on phone we promote it to a viewport-fixed banner above
     everything (sidebar, HUD, content). The rail's top + .main's
     padding-top each get bumped to clear the ticker height (30px
     + a 6px breathing margin = 36px reserved). z-index 100 sits
     above sidebar (50) and HUD (40). */
  .ticker {
    position: fixed !important;
    top: 0;
    left: 0;
    right: 0;
    z-index: 100;
    margin: 0 !important;
    border-radius: 0;
  }
  .rail {
    top: 36px !important;
    height: calc(100vh - 36px - var(--s-2)) !important;
    height: calc(100lvh - 36px - var(--s-2)) !important;
  }
  .main {
    padding-top: 36px !important;
  }
}

/* ──────────────────────────────────────────────────────────────────
 * APPS PAGE — accordion stack
 * Each app is a full-width clickable header. Click expands the body
 * (media grid + markdown + CTA). Only one open at a time (handled
 * in JS); closed cards just show the header. Width-preset media
 * items lay out in a flex row, wrapping to a new line when they
 * exhaust the available space.
 * ──────────────────────────────────────────────────────────────── */
.apps-stack {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  max-width: 880px;
}

.app-card {
  padding: 0;
  overflow: hidden;
  /* Pre-promote so the chevron / expand transition stays smooth and
     hovering doesn't trigger a fresh layer allocation + paint. */
  transform: translateZ(0);
  contain: layout paint;
  transition: border-color var(--t-fast);
}
.app-card.open { border-color: var(--accent); }

.app-head {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto auto;
  gap: 4px var(--s-3);
  align-items: center;
  padding: var(--s-4) var(--s-5);
  background: transparent;
  border: 0;
  text-align: left;
  cursor: pointer;
  color: inherit;
  font: inherit;
  transition: background var(--t-fast);
}
.app-head:hover { background: rgba(255,255,255,0.02); }
.app-head:focus-visible { outline: 1px solid var(--accent); outline-offset: -1px; }

.app-head-meta {
  grid-column: 1 / 2;
  grid-row: 1;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
}
.app-eyebrow {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* About page — contact card on the left, social-link rail on the right.
   Drops to a stacked column on narrow screens. The card is allowed to
   grow but capped so very wide About pages don't stretch it absurdly. */
.about-contact-row {
  display: flex;
  align-items: stretch;
  gap: var(--s-5);
  margin-top: var(--s-6);
  flex-wrap: wrap;
}
.about-contact-card {
  flex: 1 1 320px;
  min-width: 0;
  max-width: 540px;
}
.about-contact-row .social-rail {
  align-self: flex-start;
  padding-top: 0;
}
@media (max-width: 720px) {
  .about-contact-row { flex-direction: column; }
  .about-contact-row .social-rail {
    flex-direction: row;
    flex-wrap: wrap;
  }
}

/* Footnote on live-status cards explaining the 5-min cache window and
   pushing a Subscribe link as the canonical realtime alert channel.
   Reads as a small italic disclaimer; never grabs visual weight from
   the live indicator above it. */
.live-cache-disclaimer {
  margin-top: var(--s-3);
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--text-muted);
  font-style: italic;
  letter-spacing: 0.02em;
}
.live-cache-disclaimer a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px dashed currentColor;
  font-style: normal;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding-bottom: 1px;
  transition: color var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out);
}
.live-cache-disclaimer a:hover {
  color: var(--text-primary);
  border-bottom-color: var(--accent);
}
.live-cache-disclaimer-asterisk {
  color: var(--accent);
  margin-right: 2px;
  font-style: normal;
}
/* Brand portion of a gear/app eyebrow — rendered as neon-outlined text in
   the theme accent colour, so the manufacturer reads as a sign rather than
   an afterthought. Transparent fill + colored stroke + double glow shadow
   gives the classic neon outline. */
.gear-brand {
  color: transparent;
  -webkit-text-stroke: 1px var(--accent);
  text-shadow:
    0 0 4px var(--accent-glow),
    0 0 9px var(--accent-glow);
  letter-spacing: 0.18em;
  font-weight: 600;
  padding: 0 2px;
}
[data-mode="light"] .gear-brand {
  text-shadow:
    0 0 3px var(--accent-glow),
    0 0 6px var(--accent-glow);
}
.app-status {
  font-size: 10px;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.app-title {
  grid-column: 1 / 2;
  grid-row: 2;
  margin: 0;
  font-size: 24px;
  line-height: 1.2;
}

.app-chevron {
  grid-column: 2;
  grid-row: 1 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  transition: transform var(--t-fast), color var(--t-fast);
}
.app-card.open .app-chevron {
  transform: rotate(180deg);
  color: var(--accent);
}

.app-body {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  padding: 0 var(--s-5) var(--s-5);
  border-top: 0.5px solid var(--line-edge);
  margin-top: 0;
  padding-top: var(--s-4);
}

/* Media grid — flex row that wraps. Width classes set flex-basis so
   matching widths line up cleanly side-by-side. */
.app-media-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}
.app-media-item {
  border-radius: var(--r-2);
  overflow: hidden;
  background: rgba(0,0,0,0.3);
  border: 0.5px solid var(--line-edge);
}
.app-media-item img,
.app-media-item video {
  display: block;
  width: 100%;
  height: auto;
  max-height: 480px;
  object-fit: contain;
}
.app-media-full  { flex: 1 1 100%; }
.app-media-half  { flex: 1 1 calc(50%  - var(--s-3) / 2); min-width: 240px; }
.app-media-third { flex: 1 1 calc(33.333% - var(--s-3) * 2 / 3); min-width: 180px; }

.app-text {
  margin: 0;
  color: var(--text-secondary);
  line-height: 1.65;
}

.app-cta-row {
  display: flex;
  justify-content: flex-start;
  margin-top: var(--s-2);
}
.app-cta { text-decoration: none; }
.app-cta-soon {
  pointer-events: none;
  opacity: 0.65;
  background: var(--bg-pane);
  border: 0.5px dashed var(--line-edge);
  color: var(--text-muted);
  font-family: var(--font-mono);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  font-size: 11px;
}

@media (max-width: 720px) {
  .app-head { padding: var(--s-3) var(--s-4); }
  .app-title { font-size: 20px; }
  .app-body { padding: var(--s-3) var(--s-4) var(--s-4); padding-top: var(--s-3); }
  .app-media-half  { flex: 1 1 100%; }
  .app-media-third { flex: 1 1 100%; }
}

/* ──────────────────────────────────────────────────────────────────
 * GEAR & GLOSSARY PAGE
 * Section headers above the gear accordion and the terms list. The
 * gear cards reuse the apps-page accordion (.app-card / .app-head /
 * .app-body) so we only need to style the bits that are unique to
 * gear: the two-column bullet grid + the closing-line typography.
 * ──────────────────────────────────────────────────────────────── */
.gg-section-h {
  font-size: 11px;
  font-family: var(--font-mono);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-muted);
  margin: var(--s-6) 0 var(--s-3);
  padding-bottom: var(--s-2);
  border-bottom: 0.5px solid var(--line-edge);
  max-width: 880px;
}
.gear-section .gg-section-h:first-child,
.gear-section + .glossary-section .gg-section-h { margin-top: 0; }
.gear-section { margin-bottom: var(--s-7); }

/* Two-column bullet grid inside an open gear card. Lists on each side
   line up so headers and first bullets align horizontally. Stacks on
   narrow screens. */
.gear-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-5);
}
.gear-col-h {
  font-size: 10.5px;
  font-family: var(--font-mono);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--s-2);
}
.gear-col-list {
  margin: 0;
  padding-left: var(--s-4);
  display: flex;
  flex-direction: column;
  gap: 4px;
  color: var(--text-secondary);
  line-height: 1.5;
  font-size: 14px;
}
.gear-col-list li::marker { color: var(--text-muted); }

.gear-closing {
  margin-top: 0;
  font-style: italic;
  color: var(--text-secondary);
}

@media (max-width: 720px) {
  .gear-cols { grid-template-columns: 1fr; gap: var(--s-3); }
}
