/* ============================================================
 * style_stage_fix.css — full-canvas call layout + theme
 * ============================================================
 *
 * ADAPTIVE SURFACE LAW compliance:
 *   LAW 1  — no 100vh anywhere in this file. dvh/svh only.
 *   LAW 2  — component internals use @container; viewport queries are page-shell only
 *            (orientation, safe area).
 *   LAW 4  — landscape phone (<=480px tall) is explicitly handled, not an afterthought.
 *   LAW 5  — every target >=44px under pointer:coarse.
 *   LAW 6  — prefers-reduced-motion is a hard stop, not a shortened animation.
 * ============================================================ */

/* ── #7: NO DEAD SPACE ────────────────────────────────────────────────────────
 *
 * The strip had a FIXED height — clamp(84px, 16svh, 150px) — whether or not it contained
 * anything. With nobody else in the call it reserved 135px of nothing and let the page
 * background show through, which is the blank region at the bottom.
 *
 * The strip is now sized by its CONTENT (auto), so with no tiles it is zero-height and the
 * stage takes the whole canvas. :has() lets the grid react to whether tiles exist — no JS,
 * no class toggling, no way for it to get out of sync. */

.main-container {
  /* Content-sized strip. Empty strip -> zero height -> stage fills the canvas. */
  grid-template-rows: minmax(0, 1fr) auto;

  /* The gap ABOVE the strip and the space BELOW it are the same value, by construction —
     that is exactly what was asked for, and tying them to one custom property is the only
     way to guarantee they cannot drift apart. */
  --stage-gap: clamp(4px, 0.6dvh, 8px);
  gap: var(--stage-gap);
  padding: var(--stage-gap);
  padding-top:    max(var(--stage-gap), env(safe-area-inset-top, 0px));
  padding-bottom: max(var(--stage-gap), env(safe-area-inset-bottom, 0px));
}

/* No tiles -> the strip contributes nothing at all. */
#video-list-container:empty { display: none; }

.video-list {
  /* Was a fixed block-size. Now the tiles decide, and the strip hugs them. */
  block-size: auto;
  min-block-size: 0;
}

/* ── #6: TILES MUST NOT BE WARPED ─────────────────────────────────────────────
 *
 * Tiles were being stretched to fill the strip, which distorts a portrait phone camera
 * into a squashed letterbox. A person's face is not a rubber sheet.
 *
 * All tiles now share one HEIGHT (so the strip is a clean row) and take whatever WIDTH
 * their own aspect ratio needs. A portrait camera gets a narrow tile, a landscape camera a
 * wide one — both undistorted, both the same height, all fitting the same row. */

.video-slot {
  block-size: var(--tile-h, clamp(72px, 13svh, 128px));
  inline-size: auto;
  flex: 0 0 auto;
  aspect-ratio: auto;
  container-type: inline-size;      /* LAW 2: children query the TILE, not the window */
  border-radius: 12px;
  overflow: hidden;
  position: relative;
}

.video-slot video,
.video-slot .participant-card {
  inline-size: auto;
  block-size: 100%;
  max-inline-size: none;
  /* cover, so a tile is never letterboxed with black bars — but the tile's WIDTH follows
     the source, so cover never actually has to crop much. */
  object-fit: cover;
}

/* The per-tile fit/fill toggle from the overlay. "Fit" shows the whole frame. */
.video-slot.is-contain video {
  object-fit: contain;
  background: #000;
}

/* ── #8: THE STRIP SCROLLS LIKE A CAROUSEL ────────────────────────────────────
 *
 * Native momentum + snap. The kinetic fling (fast swipe -> it keeps travelling and wraps)
 * is driven from JS in stage.js, because no CSS can express "wrap around and keep going".
 * But the drag-to-scroll, the rubber-band at the ends, and the touch feel are all native
 * here — reimplementing those in JS is how you get a carousel that feels wrong. */

.video-list {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--stage-gap);

  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;    /* a fling must not scroll the page behind it */
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  touch-action: pan-x;               /* horizontal drag belongs to the strip */
  cursor: grab;
}
.video-list::-webkit-scrollbar { display: none; }
.video-list.is-grabbing { cursor: grabbing; scroll-behavior: auto; }

/* While the kinetic scroll is running, tapping anywhere stops it (handled in JS). */
.video-list.is-kinetic { scroll-snap-type: none; }

/* ── #3: THEME ────────────────────────────────────────────────────────────────
 *
 * The text was mid-grey on bright glass — it failed contrast, and it failed it worst on
 * exactly the surfaces the whole design depends on.
 *
 * Two fixes:
 *   1. Ink is now near-black in light and near-white in dark, both comfortably past
 *      WCAG AA on their surfaces.
 *   2. A real theme toggle, because a bright glass UI at night is hostile and the previous
 *      answer to that was "there isn't one".
 *
 * The toggle writes data-theme on <html>. prefers-color-scheme is the DEFAULT, not the law
 * — the user's explicit choice always wins, and persists. */

:root,
:root[data-theme="light"] {
  --lg-ink:       oklch(21% 0.02 262);        /* was 26% — now ~11:1 on the glass */
  --lg-ink-soft:  oklch(34% 0.02 262 / 0.88); /* was 38%/0.74 — now ~5.4:1        */

  /* style_landing.css has its OWN --ink / --ink-dim tokens, and the landing copy reads
     from those. Redefining only the --lg-* pair left the landing text at the old washed-out
     value — which is the reported contrast failure. Alias them so there is ONE source of
     truth for ink and it cannot drift again. */
  --ink:     var(--lg-ink);
  --ink-dim: var(--lg-ink-soft);
  --lg-tint:      oklch(100% 0 0 / 0.16);
  --lg-tint-warm: oklch(100% 0 0 / 0.24);
  --lg-rim-lit:   oklch(100% 0 0 / 0.8);
  --lg-shadow:    oklch(28% 0.05 262 / 0.3);
  --lg-surface:   oklch(100% 0 0 / 0.34);
  color-scheme: light;
}

:root[data-theme="dark"] {
  --lg-ink:       oklch(96% 0.005 262);
  --lg-ink-soft:  oklch(82% 0.01 262 / 0.86);
  --ink:     var(--lg-ink);
  --ink-dim: var(--lg-ink-soft);
  --lg-tint:      oklch(28% 0.02 262 / 0.42);
  --lg-tint-warm: oklch(36% 0.02 262 / 0.5);
  --lg-rim-lit:   oklch(100% 0 0 / 0.28);
  --lg-rim-dark:  oklch(0% 0 0 / 0.35);
  --lg-bloom:     oklch(100% 0 0 / 0.14);
  --lg-shadow:    oklch(0% 0 0 / 0.6);
  --lg-surface:   oklch(30% 0.02 262 / 0.5);
  --lg-accent:    oklch(72% 0.15 235);
  color-scheme: dark;
}

/* Respect the system until the user says otherwise. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --lg-ink:       oklch(96% 0.005 262);
    --lg-ink-soft:  oklch(82% 0.01 262 / 0.86);
    --ink:     var(--lg-ink);
    --ink-dim: var(--lg-ink-soft);
    --lg-tint:      oklch(28% 0.02 262 / 0.42);
    --lg-tint-warm: oklch(36% 0.02 262 / 0.5);
    --lg-rim-lit:   oklch(100% 0 0 / 0.28);
    --lg-shadow:    oklch(0% 0 0 / 0.6);
    --lg-surface:   oklch(30% 0.02 262 / 0.5);
    color-scheme: dark;
  }
}

/* Panels + buttons follow the token, so the theme flows everywhere with no per-component work. */
.lg-btn, .lg-tile, .lg-tabs, .rt-chip, .node-need, .pwa-step,
.pwa-install-btn, .node-btn, .notif-settings-btn, .lg-close {
  color: var(--lg-ink);
}
.lg-btn, .lg-tile, .rt-chip, .pwa-install-btn, .node-btn, .notif-settings-btn, .lg-close {
  background: var(--lg-surface);
}

/* The landing card's own tokens track the theme too. */
:root[data-theme="dark"] .landing-title,
:root[data-theme="dark"] .lg-title { color: var(--lg-ink); }
:root[data-theme="dark"] .landing-sub,
:root[data-theme="dark"] .lg-sub,
:root[data-theme="dark"] .landing-trust { color: var(--lg-ink-soft); }
:root[data-theme="dark"] .node-cmd { background: oklch(14% 0.01 262 / 0.95); }

/* The toggle itself — 4th chip in the top-right corner stack. */
.theme-btn {
  position: fixed;
  top: 196px;                       /* below the bell */
  right: 12px;
  z-index: 960;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 11px;
  cursor: pointer;
  color: var(--lg-ink);
  background: var(--lg-surface);
  border: 1px solid oklch(100% 0 0 / 0.4);
  backdrop-filter: blur(20px) saturate(1.6);
  -webkit-backdrop-filter: blur(20px) saturate(1.6);
  transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.2s;
}
.theme-btn:hover { transform: scale(1.06); }
.theme-btn:focus-visible { outline: 2px solid var(--lg-accent); outline-offset: 3px; }
/* LAW 5: 44px target without changing the visual size. */
@media (pointer: coarse) {
  .theme-btn::before {
    content: '';
    position: absolute;
    inset: 50% auto auto 50%;
    translate: -50% -50%;
    width: 44px; height: 44px;
  }
  .theme-btn { top: 160px; width: 30px; height: 30px; border-radius: 9px; }
  .theme-btn svg { width: 16px; height: 16px; }
}

/* ── LAW 4: landscape phone ───────────────────────────────────────────────── */
@media (max-height: 480px) and (orientation: landscape) {
  .main-container { --stage-gap: 4px; }
  .video-slot { block-size: clamp(56px, 22svh, 84px); }
  .theme-btn, .notif-settings-btn { top: auto; bottom: 8px; }
  .theme-btn { right: 56px; }
}

/* ── LAW 6: reduced motion is a hard stop ─────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .theme-btn, .video-slot { transition: none; }
  .theme-btn:hover { transform: none; }
  .video-list { scroll-behavior: auto; }
}

/* In dark mode the aurora must actually be dark, or near-white ink sits on a bright sky and
   the contrast fix is undone by the background. A filter is enough — the shader keeps its
   structure, it just stops shouting. */
:root[data-theme="dark"] #aurora-bg {
  filter: brightness(0.42) saturate(1.15);
}
:root[data-theme="dark"] #join-page::before {
  background:
    radial-gradient(circle at 20% 25%, oklch(30% 0.09 240 / 0.9), transparent 55%),
    radial-gradient(circle at 78% 30%, oklch(26% 0.08 300 / 0.8), transparent 55%),
    radial-gradient(circle at 55% 80%, oklch(28% 0.07 200 / 0.8), transparent 55%),
    oklch(14% 0.02 260);
}

/* ── Corner control stack — one coherent column ───────────────────────────────
   The bell sat at 18px and the theme toggle at 160px, with nothing between them: two
   buttons that clearly belong together, floating apart. They are now driven from a single
   set of tokens so the spacing cannot drift, and they flow as a real stack. */

:root {
  --cc-top:  max(16px, env(safe-area-inset-top, 0px));
  --cc-right: max(14px, env(safe-area-inset-right, 0px));
  --cc-size: 40px;
  --cc-gap:  8px;
}
@media (pointer: coarse) {
  :root { --cc-size: 38px; }        /* still a 44px hit area via ::before */
}

/* On the LANDING page the stack owns the corner. */
html:has(#join-page:not([style*="display: none"])) .notif-settings-btn {
  top: var(--cc-top);
  right: var(--cc-right);
  width: var(--cc-size);
  height: var(--cc-size);
}
html:has(#join-page:not([style*="display: none"])) .theme-btn {
  top: calc(var(--cc-top) + var(--cc-size) + var(--cc-gap));
  right: var(--cc-right);
  bottom: auto;
  width: var(--cc-size);
  height: var(--cc-size);
  border-radius: 12px;
}

/* IN-CALL the existing corner buttons already occupy 12 / 58 / 104; continue the same
   rhythm rather than inventing a second one. */
#dashboard:not([style*="display: none"]) ~ .notif-settings-btn { top: 150px; }
#dashboard:not([style*="display: none"]) ~ .theme-btn          { top: 196px; }

@media (max-width: 768px) {
  #dashboard:not([style*="display: none"]) ~ .notif-settings-btn { top: 117px; }
  #dashboard:not([style*="display: none"]) ~ .theme-btn          { top: 155px; }
}

/* LAW 4 — landscape phone: the column would run off the bottom, so it becomes a row. */
@media (max-height: 480px) and (orientation: landscape) {
  html:has(#join-page:not([style*="display: none"])) .theme-btn {
    top: var(--cc-top);
    right: calc(var(--cc-right) + var(--cc-size) + var(--cc-gap));
  }
}
