/*
 * Audience deck view.
 *
 * The slide gets the whole viewport. Controls are revealed by tap and auto-hide,
 * because persistent chrome on a phone costs real estate the deck needs.
 *
 * All motion is transform/opacity only, and every transition collapses to an
 * instant cut under prefers-reduced-motion.
 */

/*
 * The `hidden` attribute must win over every display rule below.
 *
 * `display: grid` and `display: inline-flex` in a class rule override the
 * browser's default `[hidden] { display: none }`, so an element with `hidden`
 * set stays visible. That shipped once: every audience member saw the
 * end-of-session card and the "Back to live" button on load. This rule makes the
 * attribute authoritative, so the JS only ever has to toggle `hidden`.
 */
[hidden] {
  display: none !important;
}

.viewer {
  position: fixed;
  inset: 0;
  background: #000;
  overflow: hidden;
  touch-action: pan-y;
  /* The deck surface is always dark, even in light theme. A white slide at full
     brightness in a dark hall is the thing this product exists to avoid. */
  --stage-bg: #000;
}

/* ---------------------------------------------------------------- the stage */

.stage {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
}

.page {
  grid-area: 1 / 1;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  display: block;
  /* Contain, never cover: a cropped slide is a broken slide. */
  object-fit: contain;
  will-change: transform, opacity;
  transform: translate3d(0, 0, 0);
  backface-visibility: hidden;
}

/*
 * Slide transitions.
 *
 * Direction is meaningful: forward slides in from the right, backward from the
 * left. That is the only decorative motion in the app, and it exists because it
 * tells the audience which way the talk is moving.
 */
.page-enter-forward {
  animation: slide-in-right var(--dur-base) var(--ease) both;
}

.page-enter-back {
  animation: slide-in-left var(--dur-base) var(--ease) both;
}

.page-exit-forward {
  animation: slide-out-left var(--dur-base) var(--ease) both;
}

.page-exit-back {
  animation: slide-out-right var(--dur-base) var(--ease) both;
}

@keyframes slide-in-right {
  from {
    transform: translate3d(8%, 0, 0);
    opacity: 0;
  }
  to {
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

@keyframes slide-out-left {
  from {
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
  to {
    transform: translate3d(-8%, 0, 0);
    opacity: 0;
  }
}

@keyframes slide-in-left {
  from {
    transform: translate3d(-8%, 0, 0);
    opacity: 0;
  }
  to {
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

@keyframes slide-out-right {
  from {
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
  to {
    transform: translate3d(8%, 0, 0);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .page-enter-forward,
  .page-enter-back,
  .page-exit-forward,
  .page-exit-back {
    animation: none;
  }
  .page-exit-forward,
  .page-exit-back {
    opacity: 0;
  }
}

/* A skeleton that matches the slide's shape, so loading never shifts layout. */
.page-skeleton {
  grid-area: 1 / 1;
  width: min(100%, 100vh);
  aspect-ratio: 16 / 9;
  border-radius: 0;
  background: linear-gradient(
    100deg,
    rgba(255, 255, 255, 0.04) 30%,
    rgba(255, 255, 255, 0.08) 50%,
    rgba(255, 255, 255, 0.04) 70%
  );
  background-size: 300% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}

@keyframes shimmer {
  from {
    background-position: 150% 0;
  }
  to {
    background-position: -50% 0;
  }
}

/* ------------------------------------------------------------------ controls */

.controls {  position: absolute;
  inset: auto 0 0 0;
  z-index: var(--z-controls);
  padding: var(--space-4) var(--space-4) calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(to top, rgba(0, 0, 0, 0.86), rgba(0, 0, 0, 0));
  display: flex;
  align-items: center;
  gap: var(--space-3);
  transition: opacity var(--dur-base) var(--ease);
}

.controls[hidden] {
  display: none;
}

.ctl {
  appearance: none;
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  height: 44px;
  min-width: 44px;
  padding: 0 var(--space-4);
  border-radius: var(--r-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease), transform 90ms var(--ease);
}

.ctl:hover {
  background: rgba(255, 255, 255, 0.14);
}

.ctl:active {
  transform: scale(0.97);
}

.ctl-ghost {
  background: transparent;
  border-color: transparent;
}

.ctl:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ------------------------------------------------------------ back to live */

/*
 * The single most important control in the product. Rendered only when the
 * viewer has drifted from the speaker, sitting in thumb reach. Its appearance
 * IS the feedback that you wandered off.
 */
.rejoin {
  position: absolute;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 72px);
  transform: translateX(-50%);
  z-index: var(--z-pill);
  appearance: none;
  border: 1px solid var(--accent-dim);
  background: var(--accent);
  color: var(--accent-ink);
  font: inherit;
  font-size: 14px;
  font-weight: 650;
  height: 44px;
  padding: 0 var(--space-5);
  border-radius: var(--r-full);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5);
  animation: rise var(--dur-base) var(--ease) both;
}

.rejoin:active {
  transform: translateX(-50%) scale(0.97);
}

@keyframes rise {
  from {
    transform: translate(-50%, 12px);
    opacity: 0;
  }
  to {
    transform: translate(-50%, 0);
    opacity: 1;
  }
}

/* --------------------------------------------------------------- conn state */

/*
 * Connection state is a dot, never a banner. Banners shift the deck and make a
 * normal condition look like an error. Presenter-offline is normal.
 */
.conn {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + var(--space-3));
  right: var(--space-3);
  /* Nothing else is interactive up here, so the badge cannot be covered. */
  z-index: var(--z-pill);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: var(--r-full);
  background: rgba(0, 0, 0, 0.6);
  color: var(--text-muted);
  pointer-events: none;
}

.conn-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--r-full);
  flex: none;
}

.conn-live .conn-dot {
  background: var(--accent);
}

.conn-live {
  color: var(--accent);
}

.conn-offline .conn-dot {
  background: var(--warn);
}

.conn-offline {
  color: var(--warn);
}

.conn-dead .conn-dot {
  background: var(--text-faint);
}

/* Presenter not connected: informational, not alarming. */
.conn-away .conn-dot {
  background: var(--text-faint);
}

/* --------------------------------------------------------------- ended card */

.ended {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--scrim);
  z-index: var(--z-overlay);
  padding: var(--space-6);
  text-align: center;
}

.ended-inner {
  max-width: 320px;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  align-items: center;
}

/* --------------------------------------------------------------- page count */

.counter {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.72);
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------- pinch zoom */

.zoomable {
  touch-action: none;
  cursor: grab;
}

.zoomed .page {
  max-width: none;
  max-height: none;
}

/*
 * The deck fills the width when zoomed, then the viewer pans. Without this the
 * image stays fit-to-screen and pinch does nothing useful.
 */
.zoomed .stage {
  display: block;
  overflow: hidden;
}


/* ------------------------------------------------------------------ top bar */

/*
 * Persistent, minimal, and at the edge of the screen where it costs nothing.
 *
 * A 16:9 slide cannot fill a portrait phone without being cropped, so the space
 * above and below is unavoidable. Putting the counter and connection state here
 * means the black is doing work instead of merely existing, and the slide itself
 * stays completely unobstructed.
 */
.viewer-bar {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: var(--z-controls);
  padding: calc(env(safe-area-inset-top, 0px) + var(--space-3)) var(--space-4) var(--space-3);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  /* Fades to nothing so it never competes with the slide. */
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.72), rgba(0, 0, 0, 0));
  pointer-events: none;
}

/* Only the badge needs to be interactive, so the bar does not block gestures. */
.viewer-bar .conn {
  pointer-events: auto;
}

/* In the bar, the badge needs no positioning or background of its own. */
.viewer-bar .conn {
  position: static;
  background: transparent;
  padding: 0;
}

.viewer-bar .counter {
  color: rgba(255, 255, 255, 0.8);
}
