/* Velocity Intelligence Assist — minimal, dark, focused, full-height. */

:root {
  --bg: #0a0e14;
  --panel: #11161d;
  --text: #e8eaed;
  --muted: #8b95a5;
  --accent: #4ea1ff;
  --accent-glow: rgba(78, 161, 255, 0.5);
  --green: #3ddc84;
  --red: #ff5757;
}

* { box-sizing: border-box; }

/* LiveKit's track.attach() inserts an <audio> element into the DOM so
   the browser actually plays the audio. Hide it globally — we never
   want a default audio player rendered on the page (Safari shows
   ~40px controls). The audio still plays; only the UI is suppressed. */
audio {
  display: none !important;
}

/* Lock the document at exactly the visible viewport height. position:
   fixed on body was clever but interacted poorly with iOS Safari's
   modal dialog teardown, leaving the body in a scrollable state once
   any post-connect DOM additions arrived. Plain height:100% on
   html+body with overflow hidden is the more bulletproof pattern. */
html {
  height: 100%;
  /* Disables touch-driven scrolling at the root — iOS rubber-band can't
     even attempt to leak past overflow:hidden when the gesture is
     declared off the table. */
  touch-action: none;
}
html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
}

/* Re-enable touch on interactive elements that touch-action:none on
   <html> would otherwise block. `manipulation` allows tap + pinch but
   no double-tap-to-zoom (the latter is what we don't want). */
button, input, [role="dialog"], dialog {
  touch-action: manipulation;
}

/* Three-zone grid: header (auto) → orb-stage (fills, can shrink) → controls (auto)
   Grid is more predictable than flex for this — explicit row sizing tells
   the browser exactly what to do. minmax(0, 1fr) on the middle row lets
   it shrink below intrinsic content size when needed (otherwise the orb
   could push controls off-screen). */
#app {
  height: 100%;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  /* Respect iOS home-bar at the bottom. The TOP safe-area is folded
     into #page-header's own padding-top instead — Safari's URL bar
     retraction shrinks env(safe-area-inset-top) post-connect, and if
     it's at the #app level the title visibly drifts upward. Doing it
     at the header level lets us pin a guaranteed minimum top clearance
     regardless of what the browser reports. */
  padding-bottom: env(safe-area-inset-bottom);
  box-sizing: border-box;
}

/* ── Header ──────────────────────────────────────────────────────────── */
#page-header {
  flex: 0 0 auto;
  text-align: center;
  padding: 20px 24px 12px;
}

h1 {
  margin: 0 0 4px 0;
  font-size: 26px;
  font-weight: 500;
  letter-spacing: -0.02em;
}

.subtitle {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
  min-height: 2.6em;     /* reserve space for quote + attribution so the
                            header doesn't jump on each rotation */
  transition: opacity 350ms ease;
}
.subtitle .quote {
  display: block;
  font-style: italic;
  color: var(--text);
}
.subtitle .quote::before { content: "\201C"; opacity: 0.6; margin-right: 1px; }
.subtitle .quote::after  { content: "\201D"; opacity: 0.6; margin-left:  1px; }
.subtitle .cite {
  display: block;
  font-size: 12px;
  color: var(--muted);
  margin-top: 2px;
  letter-spacing: 0.04em;
}
.subtitle .cite:not(:empty)::before { content: "—  "; }

/* ── Orb stage (fills remaining vertical space) ──────────────────────── */
#orb-stage {
  flex: 1 1 auto;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;            /* allow flex child to shrink below content size */
  overflow: hidden;
  /* Subtle gradient at the bottom so transcript text is legible against
     a bright orb without a hard box. */
}
#orb-stage::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 32%;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(10, 14, 20, 0) 0%,
    rgba(10, 14, 20, 0.55) 70%,
    rgba(10, 14, 20, 0.85) 100%
  );
}

#orb {
  /* Sizing: take whatever the orb-stage allows, keep square. Canvas
     internal buffer is 1200x1200; CSS resizes display. svh (smallest
     viewport height) so the value doesn't temporarily include retracting
     chrome on iOS — that was causing visible overflow on the page. The
     `max-height: 100%` of the orb-stage container is the real cap;
     this formula just provides a sensible width. */
  width: min(85svh, 92vw);
  height: min(85svh, 92vw);
  max-width: 95vw;
  max-height: 100%;
  transition: filter 0.25s ease;
  /* Drop-shadow is set inline by app.js based on agent state + audio level. */
}

#orb.interrupted {
  animation: orb-interrupt-flash 350ms ease-out;
}
@keyframes orb-interrupt-flash {
  0%   { outline: 4px solid rgba(255, 87, 87, 0.7); outline-offset: -8px; }
  100% { outline: 4px solid rgba(255, 87, 87, 0);   outline-offset: -8px; }
}

/* ── Teleprompter transcript (cylinder-scroll overlay in orb-stage) ──── */
/* The transcript is a fixed-height viewport. New text always lands at the
   bottom; older content rises and disappears as more arrives, like a
   teleprompter cylinder. The mask gradient on the top edge produces the
   gradual fade-out. No truncation, no ellipsis — long answers wrap freely
   and scroll naturally. JS keeps the bottom in view on every text update. */
#transcript {
  position: absolute;
  left: 20%;       /* 60% width — 3/5 of the orb-stage */
  right: 20%;
  bottom: 28px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  pointer-events: none;
  font-size: 22px;
  line-height: 1.45;
  height: 28vh;            /* fixed viewport — content scrolls within */
  overflow: hidden;
  /* Top-edge fade so words gracefully disappear as they rise.
     Use both standard + webkit prefix for cross-browser support. */
  -webkit-mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0)    0%,
    rgba(0, 0, 0, 0.15) 12%,
    rgba(0, 0, 0, 0.85) 35%,
    rgba(0, 0, 0, 1)    60%
  );
          mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0)    0%,
    rgba(0, 0, 0, 0.15) 12%,
    rgba(0, 0, 0, 0.85) 35%,
    rgba(0, 0, 0, 1)    60%
  );
}

#transcript .turn {
  margin: 0 0 14px 0;
  padding: 0;
  text-align: center;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.85), 0 0 8px rgba(0, 0, 0, 0.4);
  /* No line clamp — wrap freely so long answers scroll in the cylinder. */
  flex-shrink: 0;          /* don't squeeze older turns inside the viewport */
  animation: turn-slide-in 280ms ease-out;
  transition: opacity 600ms ease, transform 600ms ease;
}

/* Time-based fade: a finalized turn gets the .expiring class added by
   JS ~5 seconds after it locks. Aggressive fade so old context doesn't
   linger on the orb after the moment has passed. */
#transcript .turn.expiring {
  opacity: 0;
  transform: translateY(-20%);
}

@keyframes turn-slide-in {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}

#transcript .turn .who {
  /* Match the body text size — was 11px, looked dwarfed next to 22px text.
     Distinguish role only via weight + colour, not size. */
  display: inline;
  font-weight: 700;
  margin-right: 10px;
  font-size: inherit;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

#transcript .turn.user .who { color: var(--accent); }
#transcript .turn.via  .who { color: var(--green); }

#transcript .turn.streaming::after {
  content: "▋";
  display: inline-block;
  margin-left: 2px;
  animation: blink 1s steps(2) infinite;
  color: var(--muted);
}
@keyframes blink {
  50% { opacity: 0; }
}

/* ── Controls (fixed at bottom) ──────────────────────────────────────── */
.controls {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 16px 24px 22px;
}

.controls-row {
  display: flex;
  justify-content: center;
  gap: 12px;
}

.status {
  font-size: 12px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: 0;
}
.status.connected { color: var(--green); }
.status.error     { color: var(--red);   }

.session-timer {
  font-size: 11px;
  color: var(--muted);
  margin: 0;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}
.session-timer.warning { color: #ffb84a; }
.session-timer.expired { color: var(--red); }

button {
  background: var(--panel);
  color: var(--text);
  border: 1px solid #1f2630;
  border-radius: 8px;
  padding: 11px 22px;
  font-size: 14px;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

button:hover:not(:disabled) {
  border-color: var(--accent);
}

button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

button.primary {
  background: var(--accent);
  color: #001830;
  border-color: var(--accent);
  font-weight: 600;
}
button.primary:hover:not(:disabled) {
  background: #6ab3ff;
  border-color: #6ab3ff;
}

button.danger {
  background: transparent;
  color: var(--red);
  border-color: var(--red);
  font-weight: 600;
}
button.danger:hover:not(:disabled) {
  background: var(--red);
  color: #1a0000;
}

/* Square icon button — used by the mute toggle. Same colour scheme as
   .secondary but compact so it doesn't crowd Connect/Stop on mobile. */
button.icon-btn {
  width: 42px;
  height: 42px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
}
button.icon-btn:hover:not(:disabled) {
  color: var(--text);
}
button.icon-btn .mic-icon {
  pointer-events: none;
}
/* Slash overlay is hidden by default; revealed when the button has the
   .muted class. */
button.icon-btn .mute-slash {
  display: none;
}
button.icon-btn.muted {
  color: var(--red);
}
button.icon-btn.muted .mute-slash {
  display: inline;
}

button .kbd {
  display: inline-block;
  font-size: 10px;
  font-weight: 400;
  margin-left: 6px;
  padding: 1px 5px;
  border-radius: 3px;
  border: 1px solid currentColor;
  opacity: 0.6;
  vertical-align: 1px;
}

/* ── Password modal ───────────────────────────────────────────────── */
/* Shown when /api/token must be hit and we have no cached token. */
.password-dialog {
  border: 1px solid #1f2733;
  background: #0a0e14;
  color: #d8dde4;
  border-radius: 12px;
  padding: 28px 32px;
  max-width: 360px;
  box-shadow: 0 14px 60px rgba(0, 0, 0, 0.6);
}
.password-dialog::backdrop {
  background: rgba(5, 8, 12, 0.72);
  backdrop-filter: blur(4px);
}
.password-dialog h2 {
  margin: 0 0 6px;
  font-size: 16px;
  letter-spacing: 0.4px;
}
.password-dialog .password-hint {
  margin: 0 0 18px;
  font-size: 13px;
  color: #7d8694;
}
.password-dialog input[type="password"] {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  font-size: 15px;
  color: #d8dde4;
  background: #11161e;
  border: 1px solid #1f2733;
  border-radius: 6px;
  outline: none;
  margin-bottom: 14px;
}
.password-dialog input[type="password"]:focus {
  border-color: #4ea1ff;
}
.password-dialog button {
  width: 100%;
}
.password-dialog .password-error {
  margin: 12px 0 0;
  min-height: 18px;
  font-size: 13px;
  color: #ff7373;
}

/* ── Mobile / portrait phones ─────────────────────────────────────────── */
/* iPhone Safari's visible region is ~620–700 px on iPhone 16 with the URL
   bar visible — tight. We aggressively shrink everything except the orb,
   then size the orb in vmin so the container's max-height: 100% caps it
   naturally. No vh/dvh math, no over-reservation, no overflow. */
@media (max-width: 640px), (max-height: 740px) {
  #page-header {
    /* Pin the top clearance: device safe-area (typically ~50px for the
       Dynamic Island) PLUS 30px of breathing room. max() with a 50px
       floor ensures the Dynamic Island is cleared even on browsers that
       under-report env(safe-area-inset-top). Stays consistent pre and
       post connect — Safari's URL bar showing/hiding can shrink env()
       but max() pins the floor. */
    padding-top: calc(max(env(safe-area-inset-top), 50px) + 30px);
    padding-left: 16px;
    padding-right: 16px;
    padding-bottom: 4px;
  }
  h1 {
    font-size: 18px;
    margin: 0;
  }
  .subtitle {
    font-size: 11px;
    min-height: 2.2em;
  }
  #orb {
    /* vmin = smaller of viewport width/height. Caps at the narrow side
       of the screen with breathing room. The orb-stage's flex-shrink and
       the canvas's max-height: 100% prevent vertical overflow. */
    width: min(82vmin, 92vw);
    height: min(82vmin, 92vw);
  }
  #transcript {
    height: 14svh;
    font-size: 12px;
    bottom: 12px;
  }
  .controls {
    padding: 6px 16px 10px;
    gap: 4px;
  }
  .controls-row {
    gap: 8px;
  }
  .controls-row button {
    font-size: 14px;
    padding: 9px 12px;
  }
  /* Keep the icon button square; otherwise the generic mobile rule
     stretches it horizontally and crowds the row. */
  .controls-row button.icon-btn {
    width: 38px;
    height: 38px;
    padding: 0;
  }
  .status {
    font-size: 11px;
    margin-top: 2px;
  }
  /* Hide the session-timer on mobile — its post-connect appearance was
     pushing the layout over by ~16 px and the 60-min countdown isn't
     useful on a phone screen anyway. Status line stays. */
  .session-timer {
    display: none;
  }
}
