/* =========================================================
   Vision Simulator
   Standalone styles — no Bootstrap, no framework.
   ========================================================= */

:root {
  /* Aurolab palette, carried over from the original page */
  --slate-900: #1b242e;
  --slate-800: #273746;
  --slate-600: #4d5656;
  --slate-400: #8a97a0;
  --cyan: #00aed8;
  --paper: #f1f1f1;

  --glass: rgba(12, 19, 25, 0.72);
  --glass-line: rgba(255, 255, 255, 0.14);

  --radius: 12px;
  --radius-lg: 16px;

  --panel-w: 400px;
  --step: 14px;

  /* Set --font-display to 'PP Telegraf' once the licensed files are in place. */
  --font-display: "PP Telegraf", "Lato", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body: "Lato", system-ui, -apple-system, "Segoe UI", sans-serif;
}

*,
*::before,
*::after { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--slate-800);
  color: var(--paper);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 2px;
}

/* ---------- layout shell ---------- */

.sim {
  position: relative;
  height: 100dvh;
  overflow: hidden;
  background: var(--slate-800);
}

/* ---------- video stage ---------- */

/*
 * The stage sizes its panes differently depending on how many there are.
 *
 * One pane — the pane takes the clip's own aspect ratio and centres, so the
 * picture is never distorted. This is the view someone studies, and a face
 * stretched 20% wider is the one thing a vision simulator cannot afford.
 * The cost is bars above and below on a wide window.
 *
 * Two panes — the panes go back to filling their halves of the stage, as the
 * original page had them. Ratio-locked panes here would shrink to slivers,
 * since each pane is roughly half as wide as the 16:9 source, and the two
 * clips being compared are the same shape anyway so they distort identically.
 *
 * --video-ar is published by app.js from the clip's real videoWidth and
 * videoHeight on every metadata load; 16/9 covers the moment before it lands.
 */
.stage {
  display: grid;
  grid-template-columns: 1fr;
  height: 100%;
  gap: 2px;
  background: var(--slate-900);
}

.stage[data-compare="false"] {
  place-items: center;
}

.stage[data-compare="true"] {
  grid-template-columns: 1fr 1fr;
}

.pane {
  position: relative;
  overflow: hidden;
  min-width: 0;
  width: 100%;
  height: 100%;
  background: var(--slate-800);
}

/* Ratio-locked, single-pane view. max-width keeps a narrow window from
   pushing the pane wider than the stage; below that width the bars move to
   the sides and object-fit keeps the picture honest. */
.stage[data-compare="false"] .pane {
  width: auto;
  height: 100%;
  aspect-ratio: var(--video-ar, 16 / 9);
  max-width: 100%;
}

.stage__layers {
  position: absolute;
  inset: 0;
}

/*
 * contain by default, fill in compare.
 *
 * The clips carry the Aurolab logo in the top-right corner and the lens name
 * in the top-left, burned into the frame, so cover is out at either size — it
 * crops the branding away, badly so in compare where each pane is half the
 * width of the source.
 *
 * In the single view the pane is already the clip's shape, so contain fills
 * it edge to edge with nothing to letterbox. It earns its place in the moment
 * before metadata arrives, when the pane is still assuming 16:9 and a clip of
 * some other shape would otherwise be stretched to fit the guess.
 *
 * In compare the panes are fixed halves of the stage, so fill is what keeps
 * the whole frame visible and the pane full — the trade-off the original page
 * made, and the same one it makes here.
 *
 * See the README if you would rather have cover throughout: overlay the logo
 * and lens name as HTML instead of relying on the burned-in ones and the
 * trade-off disappears.
 */
.stage__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity 380ms ease;
}

.stage[data-compare="true"] .stage__video {
  object-fit: fill;
}

.stage__video.is-visible { opacity: 1; }

/* Pane label — only meaningful when two clips are side by side. */
.pane__caption {
  position: absolute;
  left: 16px;
  bottom: calc(var(--bar-h, 70px) + 22px);
  margin: 0;
  padding: 7px 13px;
  max-width: calc(100% - 32px);
  font-size: 13px;
  letter-spacing: 0.01em;
  background: var(--glass);
  border: 1px solid var(--glass-line);
  border-radius: 100px;
  backdrop-filter: blur(8px);
}

.pane__caption:empty { display: none; }

/*
 * In compare mode the captions move inward, to either side of the divider.
 * Pane A's bottom-left corner is underneath the controls drawer whenever it
 * is open — which is exactly when someone is switching lenses and wants to
 * see which pane is which — so a left-aligned caption there is unreadable at
 * the one moment it matters.
 */
.stage[data-compare="true"] #paneA .pane__caption {
  left: auto;
  right: 16px;
}

/* Loading indicator: a thin bar, not a blocking overlay — the outgoing
   frame stays watchable while the next clip buffers. */
.stage__spinner {
  position: absolute;
  top: 0; left: 0;
  height: 2px;
  width: 100%;
  background: linear-gradient(90deg, transparent, var(--cyan), transparent);
  transform: translateX(-100%);
  opacity: 0;
  pointer-events: none;
}

.stage.is-loading .stage__spinner {
  opacity: 1;
  animation: sweep 1.1s linear infinite;
}

@keyframes sweep {
  to { transform: translateX(100%); }
}

/* ---------- control panel ---------- */

/*
 * The drawer is collapsed by default: the whole group is translated left by
 * its own width, so only the tab pokes out at the edge of the viewport. Hover,
 * keyboard focus or a tap slides it back to 0.
 *
 * Translating the wrapper rather than the panel alone means the tab travels
 * with the drawer and stays attached to its edge, so the pointer never leaves
 * the hover target mid-transition.
 */
.filter {
  position: absolute;
  top: 50%;
  left: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  transform: translateY(-50%) translateX(calc(-1 * var(--panel-w)));
  transition: transform 340ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.filter:hover,
.filter:focus-within,
.filter.is-open {
  transform: translateY(-50%) translateX(0);
}

/* A wider invisible strip along the edge, so the drawer opens as the pointer
   approaches rather than demanding a hit on a 44px tab. */
.filter::before {
  content: "";
  position: absolute;
  top: -40px;
  bottom: -40px;
  left: 100%;
  width: 26px;
}

.panel {
  width: var(--panel-w);
  max-height: 78vh;
  overflow-y: auto;
  padding: 20px 20px 22px;
  background: rgba(0, 0, 0, 0.72);
  border-top-right-radius: 15px;
  border-bottom-right-radius: 15px;
  backdrop-filter: blur(14px);
}

/* The tab: a handle on the drawer's right edge, visible when it is closed. */
.filter__tab {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 60px;
  padding: 0;
  color: var(--paper);
  /* Opaque enough to read against any frame. The video behind it is
     uncontrolled — it can be a white tablecloth or a black bar counter — so
     the tab carries its own contrast rather than relying on the backdrop. */
  background: rgba(0, 0, 0, 0.82);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-left: 0;
  border-top-right-radius: 12px;
  border-bottom-right-radius: 12px;
  box-shadow: 0 3px 16px rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(14px);
  cursor: pointer;
  transition: color 160ms ease, background 160ms ease;
}

.filter:hover .filter__tab,
.filter:focus-within .filter__tab,
.filter.is-open .filter__tab {
  color: var(--cyan);
  background: rgba(0, 0, 0, 0.88);
}

.field { margin-bottom: var(--step); }
.field[hidden] { display: none; }

.field__label {
  display: block;
  margin-bottom: 7px;
  font-size: 15px;
  font-weight: 600;
  color: var(--paper);
}

/* Select: custom chevron, transparent field. */
.select { position: relative; }

.select::after {
  content: "";
  position: absolute;
  right: 15px;
  top: 50%;
  width: 9px;
  height: 9px;
  margin-top: -7px;
  border-right: 2px solid var(--paper);
  border-bottom: 2px solid var(--paper);
  transform: rotate(45deg);
  pointer-events: none;
}

.select select {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  padding: 11px 38px 11px 14px;
  font: inherit;
  font-size: 15px;
  color: var(--paper);
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(255, 255, 255, 0.45);
  border-radius: 10px;
  cursor: pointer;
}

.select select:hover { border-color: #fff; }
.select select:disabled { opacity: 0.45; cursor: not-allowed; }

/* Both background AND colour, on option and optgroup alike. Leaving optgroup
   unstyled makes its label inherit the select's light text colour while
   falling back to the browser's native popup background — white on Windows —
   which renders the heading invisible. */
.select select option,
.select select optgroup {
  background-color: var(--slate-600);
  color: var(--paper);
}

.select select option:disabled,
.select select optgroup:disabled {
  color: #b9c2c2;
}

.select--compact select { width: 160px; }

/* Checkbox and switch */
.check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: var(--step);
}

.check[hidden] { display: none; }

.check label {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--paper);
  cursor: pointer;
}

.check__hint {
  display: block;
  font-size: 12.5px;
  font-weight: 400;
  color: var(--slate-400);
}

.check input[type="checkbox"] {
  flex: none;
  width: 17px;
  height: 17px;
  margin: 2px 0 0;
  accent-color: var(--cyan);
  cursor: pointer;
}

/* Switch variant for the compare toggle */
.check--switch {
  align-items: center;
  gap: 12px;
  padding: 4px 0 2px;
}

.check--switch input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  width: 44px;
  height: 24px;
  margin: 0;
  border-radius: 100px;
  background: var(--slate-600);
  transition: background 160ms ease;
}

.check--switch input[type="checkbox"]::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--paper);
  transition: transform 160ms ease;
}

.check--switch input[type="checkbox"]:checked { background: var(--cyan); }
.check--switch input[type="checkbox"]:checked::after { transform: translateX(20px); }

/* Feedback text */
.readout {
  margin: 6px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--slate-400);
}

.notice {
  margin: 10px 0 0;
  padding: 9px 11px;
  font-size: 13px;
  border-radius: 10px;
  background: rgba(217, 83, 79, 0.16);
  border: 1px solid rgba(217, 83, 79, 0.4);
}

.notice[hidden] { display: none; }

/* ---------- bottom bar ---------- */

.bar {
  position: absolute;
  left: 50%;
  bottom: 12px;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  width: min(95%, 1180px);
  padding: 10px 14px;
  background: var(--glass);
  border: 1px solid var(--glass-line);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(14px);
}

.bar__disclaimer {
  margin: 0;
  max-width: 64ch;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--slate-400);
}

.bar__controls {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
}

.play {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  color: var(--paper);
  background: transparent;
  border: 2px solid var(--slate-600);
  border-radius: 50%;
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease;
}

.play:hover {
  border-color: var(--cyan);
  background: rgba(0, 174, 216, 0.12);
}

/* One button, two glyphs — visibility follows the pressed state. */
.play__icon { display: none; }
.play[data-playing="false"] .play__icon--play { display: block; margin-left: 2px; }
.play[data-playing="true"]  .play__icon--pause { display: block; }

/* ---------- responsive ---------- */

/*
 * Hover does not exist on touch, and on a coarse pointer an accidental brush
 * of the screen edge should not open a drawer. Below this width, and on any
 * touch device, the drawer opens only on an explicit tap or on keyboard focus.
 */
@media (hover: none), (pointer: coarse) {
  .filter:hover {
    transform: translateY(-50%) translateX(calc(-1 * var(--panel-w)));
  }

  .filter:focus-within,
  .filter.is-open {
    transform: translateY(-50%) translateX(0);
  }

  .filter::before { display: none; }
}

@media (max-width: 991px) {
  :root { --panel-w: 320px; }

  .panel {
    max-height: 70vh;
    padding: 18px 16px 20px;
  }
}

@media (max-width: 767px) {
  :root { --panel-w: min(300px, calc(100vw - 56px)); }
}

@media (max-width: 767px) {
  /* Two clips stacked read better than two slivers side by side. The panes
     fill their rows, as they do side by side on a wider screen. */
  .stage[data-compare="true"] {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr 1fr;
  }

  /* Bottom-right, clear of the drawer on the left and of the burned-in
     labels along the top of the frame. */
  .pane__caption,
  .stage[data-compare="true"] #paneA .pane__caption {
    left: auto;
    right: 10px;
    bottom: 10px;
    top: auto;
    font-size: 12px;
  }

  /* Pane B is the lower clip, so its caption has to clear the bottom bar.
     --bar-h is measured at runtime; the bar is taller here because the
     disclaimer wraps onto more lines. */
  .stage[data-compare="true"] #paneB .pane__caption {
    bottom: calc(var(--bar-h, 110px) + 20px);
  }

  .bar {
    flex-direction: column-reverse;
    align-items: stretch;
    gap: 10px;
    width: calc(100% - 20px);
    bottom: 10px;
  }

  .bar__disclaimer { text-align: center; }

  .bar__controls { justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  .stage__video { transition: none; }
  .stage.is-loading .stage__spinner { animation: none; opacity: 0.6; transform: none; }
  .check--switch input[type="checkbox"],
  .check--switch input[type="checkbox"]::after { transition: none; }
}