/* Mobile first. Every rule here is the phone layout; wider screens are
   handled by min-width queries at the bottom. */

:root {
  --bg: #0a0d12;
  --surface: #181f29;
  --surface-2: #232c39;
  --input-bg: #1c2431;
  --line: #3d4b5e;
  --line-strong: #55687f;
  --text: #f3f6fa;
  --muted: #aab8c9;
  --accent: #5aabff;
  --good: #46c168;
  --warn: #e3a83b;
  --bad: #ff6b64;
  --r: 10px;
  --nav-h: 56px;
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
  overscroll-behavior-y: contain;
  /* Tells the browser its own chrome -- scrollbars, autofill icons, the
     built-in date/select pickers -- should use dark-mode colors instead of
     assuming a light page. */
  color-scheme: dark;
}

/* Kill the grey flash on tap; it makes a web app feel like a web page. */
a, button, .tile, .navbtn { -webkit-tap-highlight-color: transparent; }

button { font: inherit; color: inherit; }

a { color: var(--accent); text-decoration: none; }
a:hover, a:focus-visible { text-decoration: underline; }

/* ── app shell ───────────────────────────────────────────── */
#app {
  min-height: 100dvh;
  padding-top: var(--safe-t);
  padding-bottom: calc(var(--nav-h) + var(--safe-b));
}

header.bar {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px;
  padding-top: calc(10px + var(--safe-t));
  margin-top: calc(-1 * var(--safe-t));
  /* No backdrop-filter here: it would make this a containing block for the
     fixed-position nav nested inside, pinning the bottom bar to the header
     instead of the viewport. */
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
header.bar h1 { margin: 0; font-size: 17px; font-weight: 650; letter-spacing: -.2px; }

/* A ghost button for navigation inside the header bar -- transparent until
   touched, so it doesn't compete with the title next to it. */
.backlink {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  margin: -6px -4px -6px -6px; padding: 6px 8px;
  border-radius: 8px; color: var(--muted); font-size: 13px; font-weight: 600;
}
.backlink:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
/* An author display rule beats the browser's own [hidden] rule, so without
   this the "Sites" link showed on a recorder reached directly on its LAN,
   where there is no site list to go back to. */
.backlink[hidden] { display: none; }
.backlink svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2.2; flex: none; }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); flex: none; }
.dot.ok { background: var(--good); }
.dot.bad { background: var(--bad); }
.grow { flex: 1; }

.screen { display: none; padding: 12px; }
.screen.active { display: block; }

/* ── bottom navigation (thumb reach) ─────────────────────── */
nav.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: flex;
  height: calc(var(--nav-h) + var(--safe-b));
  padding-bottom: var(--safe-b);
  background: rgba(20, 25, 32, .96);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--line);
}
.navbtn {
  flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px;
  background: none; border: 0; cursor: pointer;
  color: var(--muted); font-size: 11px; padding: 0;
}
.navbtn svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 1.8; }
.navbtn.active { color: var(--accent); }

/* ── camera grid ─────────────────────────────────────────── */
.grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}
.grid.one-col { grid-template-columns: 1fr; }

.tile {
  position: relative;
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: var(--r);
  overflow: hidden;
  border: 1px solid var(--line);
  cursor: pointer;
}
.tile img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  opacity: 0; transition: opacity .25s;
}
.tile img.loaded { opacity: 1; }
.tile .cap {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 14px 8px 6px;
  background: linear-gradient(transparent, rgba(0,0,0,.8));
  font-size: 12px; font-weight: 600;
  display: flex; align-items: center; gap: 6px;
  pointer-events: none;
}
.tile .cap .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tile .badge-rec {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--bad); flex: none;
  animation: pulse 2s infinite;
}
@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: .35 } }

/* ── recording stop/start switch ─────────────────────────────── */
.rec-switch {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; margin-bottom: 10px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
}
.rec-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; background: var(--muted); }
.rec-dot.on { background: var(--bad); animation: pulse 2s infinite; }
.tile .msg {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  padding: 10px; text-align: center;
  font-size: 11px; color: var(--muted);
}
.tile .msg.err { color: var(--bad); }
.tile-dot {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  box-shadow: 0 1px 4px rgba(0,0,0,.7);
}
.tile .stale {
  position: absolute; top: 6px; right: 6px;
  background: rgba(0,0,0,.6); border-radius: 20px;
  padding: 2px 7px; font-size: 10px; color: var(--warn);
}

.empty { text-align: center; padding: 48px 20px; color: var(--muted); }
.empty p { margin: 6px 0; }

/* ── viewer (fullscreen live) ────────────────────────────── */
#viewer {
  position: fixed; inset: 0; z-index: 60;
  background: #000;
  display: flex; flex-direction: column;
  touch-action: pan-y;
}
#viewer[hidden] { display: none; }
/* min-height:0 is load-bearing: without it a flex item refuses to shrink
   below its content's natural size, so the video pushes the bottom bar off
   the screen -- which is exactly what "it goes too fullscreen" looked like. */
#viewer .vwrap {
  flex: 1 1 auto; min-height: 0; position: relative;
  display: grid; place-items: center;
  overflow: hidden; background: #000;
}
/* The element is sized to the picture, rather than being stretched to the
   whole wrap and asking object-fit to letterbox inside it.

   That distinction matters with a 4K source. object-fit only changes how the
   frame is painted *within* the element's box; the video still gets its own
   compositing surface, and when a browser cannot scale a 3840x2160 surface
   into a much smaller box it falls back to drawing it 1:1 anchored at the
   top-left -- which looks exactly like being zoomed into a corner of the
   camera. Letting max-width/max-height shrink a replaced element keeps its
   aspect ratio and makes the box equal the picture, so there is nothing left
   to letterbox and the compositor is only ever asked to scale, which it
   always gets right. place-items:center on the wrap centres what is left. */
#viewer video {
  display: block;
  width: auto; height: auto;
  max-width: 100%; max-height: 100%;
  object-fit: contain; background: #000;
}

/* Windowed: a large panel instead of true edge-to-edge, so it doesn't run
   under notches/safe areas or feel like a jarring zoom transition. */
#viewer.windowed {
  inset: 28px; border-radius: 14px; overflow: hidden;
  box-shadow: 0 24px 64px rgba(0,0,0,.6);
  border: 1px solid var(--line);
}
@media (max-width: 700px) {
  #viewer.windowed { inset: 10px; }
}

/* Solid bars, always on screen. They never fade and never overlap the
   picture, so there is nothing to bring back and nothing to lose behind the
   video layer. */
#viewer .vtop {
  flex: none; z-index: 2;
  display: flex; align-items: center; gap: 10px;
  padding: calc(9px + var(--safe-t)) 12px 9px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
#viewer .vbot {
  flex: none; z-index: 2;
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px calc(9px + var(--safe-b));
  background: var(--surface);
  border-top: 1px solid var(--line);
}
#viewer .vtitle { min-width: 0; }
#viewer .vtitle > div { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.iconbtn {
  width: 40px; height: 40px; flex: none;
  display: grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 50%;
  cursor: pointer;
}
.iconbtn:hover { border-color: var(--line-strong); }
.iconbtn svg { width: 20px; height: 20px; stroke: var(--text); fill: none; stroke-width: 2; }
#viewer .vname { font-weight: 650; font-size: 15px; }
#viewer .vsub { font-size: 11px; color: var(--muted); }
#viewer .vstate {
  position: absolute; inset: 0; display: grid; place-items: center;
  text-align: center; padding: 24px; font-size: 13px; color: #b9c4d1;
  pointer-events: none;
}
#viewer .vstate.err { color: var(--bad); }

.pill {
  background: var(--surface-2); border: 1px solid var(--line-strong);
  border-radius: 20px; padding: 7px 13px; font-size: 12px;
  cursor: pointer; white-space: nowrap;
}
.pill:hover { border-color: var(--accent); }
.pill.on { background: var(--accent); border-color: var(--accent); color: #05121f; font-weight: 650; }

.dots { display: flex; gap: 5px; justify-content: center; }
.dots i { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.3); }
.dots i.on { background: #fff; }

/* ── timeline ────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 14px;
  margin-bottom: 12px;
}
.card h2 { margin: 0 0 10px; font-size: 15px; }
a.card { transition: background-color .15s ease, border-color .15s ease; }
a.card:hover { background: var(--surface-2); border-color: var(--line-strong); }
.card-link { display: block; color: inherit; text-decoration: none; }

/* ── site picker ─────────────────────────────────────────── */
.site-grid { display: grid; grid-template-columns: 1fr; gap: 10px; }
.site-grid .card { margin-bottom: 0; }
.site-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.site-head strong { font-size: 15px; font-weight: 650; }
.site-name { display: flex; align-items: center; gap: 6px; min-width: 0; }
.rename-btn {
  flex: none; background: none; border: 0; cursor: pointer;
  color: var(--muted); font-size: 13px; padding: 2px 4px; border-radius: 6px;
  line-height: 1;
}
.rename-btn:hover { background: var(--surface-2); color: var(--text); }
.site-sub { margin-top: 3px; }

.metrics { margin-top: 14px; display: flex; flex-direction: column; gap: 9px; }
.metric-top { display: flex; justify-content: space-between; font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.metric-bar { height: 5px; border-radius: 3px; background: var(--surface-2); overflow: hidden; }
.metric-bar i { display: block; height: 100%; border-radius: 3px; background: var(--good); }
.metric-bar i.warn { background: var(--warn); }
.metric-bar i.bad { background: var(--bad); }

.disks { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: 8px; }
.disk-top { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; margin-bottom: 4px; }
.disk-top > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── camera list (Setup tab) ─────────────────────────────────── */
.cam-row { border-bottom: 1px solid var(--line); }
.cam-row:last-child { border-bottom: 0; }
.cam-head {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 0; cursor: pointer; user-select: none;
}
.cam-head:hover strong { color: var(--accent); }
.cam-body label { margin-bottom: 12px; }
.cam-chevron { flex: none; font-size: 10px; color: var(--muted); width: 12px; text-align: center; }
.cam-body { padding: 2px 0 14px 22px; }
.cam-thumb {
  display: block; width: 100%; max-width: 320px; aspect-ratio: 16/9;
  object-fit: cover; background: #000; border-radius: 8px;
  border: 1px solid var(--line); margin-bottom: 10px;
}
.cam-thumb-err { display: flex; align-items: center; justify-content: center; }

/* ── Setup sub-tabs ──────────────────────────────────────────── */
#setup-tabs { margin-bottom: 12px; }

/* ── System: history charts + log ───────────────────────────── */
.hist-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px; margin-top: 10px; }
.hist-chart {
  width: 100%; height: 130px; margin-top: 4px;
  background: var(--surface-2); border-radius: 8px; display: block;
}
.hist-line { fill: none; stroke: var(--accent); stroke-width: 2; vector-effect: non-scaling-stroke; }
.hist-area { fill: var(--accent); opacity: .15; stroke: none; }
.hist-empty { fill: var(--muted); font-size: 11px; }
.hist-grid-line { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.hist-axis { fill: var(--muted); font-size: 10px; }
.hist-dot { fill: var(--accent); stroke: var(--surface-2); stroke-width: 2; }
.hist-current { fill: var(--text); font-size: 12px; font-weight: 650; }
@media (max-width: 480px) {
  .hist-grid { grid-template-columns: 1fr; }
}

.log-view {
  margin-top: 10px; max-height: 320px; overflow: auto;
  background: var(--input-bg); border: 1px solid var(--line);
  border-radius: 8px; padding: 10px; font-size: 11px; line-height: 1.5;
  white-space: pre-wrap; word-break: break-all;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

@media (min-width: 560px) {
  .site-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); align-items: start; }
}

.row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.chips { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 4px; -webkit-overflow-scrolling: touch; }
.chips::-webkit-scrollbar { display: none; }
.chip {
  flex: none; padding: 8px 13px; border-radius: 20px;
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: 13px; cursor: pointer; white-space: nowrap;
}
.chip.on { background: var(--accent); border-color: var(--accent); color: #05121f; font-weight: 650; }
/* A camera with nothing recorded still gets a chip -- hiding it would be
   confusing -- but it should not look like the ones worth clicking. */
.chip.empty { color: var(--muted); border-style: dashed; }
.chip.empty.on { color: #05121f; border-style: solid; }
.chip-note { opacity: .7; font-size: 11px; margin-left: 5px; }

#tl-track {
  position: relative; height: 54px; margin: 12px 0 6px;
  background: var(--surface-2); border-radius: 6px;
  overflow: hidden; cursor: pointer; touch-action: none;
}
/* Recorded footage must read instantly as "this is what exists". It sits
   above the hour marks and is deliberately far brighter than them -- at a
   glance you should see coverage and gaps, not a grid. */
#tl-track .span {
  position: absolute; top: 6px; bottom: 6px;
  background: linear-gradient(180deg, #56b0ff, #2f7fc7);
  border-radius: 2px; z-index: 2;
}
#tl-track .hour { position: absolute; top: 0; bottom: 0; width: 1px; background: rgba(255,255,255,.05); z-index: 1; }
#tl-track .hour.major { background: rgba(255,255,255,.12); }
#tl-track .cursor {
  position: absolute; top: 0; bottom: 0; width: 2px;
  background: var(--warn); z-index: 3; pointer-events: none;
  box-shadow: 0 0 6px rgba(210,153,34,.8);
}
#tl-track .none {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 12px; color: var(--muted);
}
.tl-labels { display: flex; justify-content: space-between; font-size: 11px; color: var(--muted); }
.tl-legend { font-size: 12px; color: var(--muted); margin-top: 8px; }

/* Same reasoning as the fullscreen viewer: bound it and let the element take
   the picture's own shape rather than scaling a 4K surface inside a fixed
   box. 70vh stops a 16:9 clip from running off a laptop screen. */
#tl-video {
  width: auto; height: auto;
  max-width: 100%; max-height: 70vh;
  border-radius: var(--r); background: #000; display: block; margin-top: 10px;
}

/* ── generic bits ────────────────────────────────────────── */
.muted { color: var(--muted); }
.small { font-size: 12px; }
/* Forms are capped at a readable width. Without this every text field
   stretched to the full card -- 1300px wide on a desktop -- which is how a
   "Username" box ended up looking like a banner. */
label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 10px; max-width: 520px; }
input[type=text], input[type=email], input[type=password], input:not([type]), select {
  width: 100%; margin-top: 5px; padding: 11px 12px;
  background: var(--input-bg); color: var(--text);
  border: 1px solid var(--line-strong); border-radius: 8px;
  font: inherit;
}
/* A text field sitting in a row next to a button shares the line instead of
   taking all of it and shoving the button underneath. */
.row > input[type=text], .row > input:not([type]) {
  flex: 1 1 160px; width: auto; min-width: 0; margin-top: 0;
}
input.check { width: auto; margin: 0; accent-color: var(--accent); }
.check-row { display: flex; align-items: center; gap: 8px; color: var(--text); cursor: pointer; }
.card.sub { margin-bottom: 8px; padding: 12px; background: var(--surface-2); }
input:focus, select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(90, 171, 255, .25); }
/* Chrome/Safari paint an autofilled field with their own background --
   normally a pale yellow -- ignoring the CSS above entirely. The only way
   to override it is this inset box-shadow trick, painted the size of the
   field so it reads as the background; the long transition-delay stops
   Chrome's own color from flashing in first. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--text);
  -webkit-box-shadow: 0 0 0 1000px var(--input-bg) inset;
  box-shadow: 0 0 0 1000px var(--input-bg) inset;
  caret-color: var(--text);
  transition: background-color 9999s ease-in-out 0s;
}
.btn {
  padding: 11px 16px; border-radius: 8px; min-height: 44px;
  background: var(--surface-2); border: 1px solid var(--line-strong);
  cursor: pointer;
}
.btn.primary { background: var(--accent); border-color: var(--accent); color: #05121f; font-weight: 650; }
.btn.sm { padding: 7px 12px; min-height: 0; font-size: 13px; }
.btn.danger { color: var(--bad); }
.btn:disabled { opacity: .5; }

.stat { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 7px 0; border-bottom: 1px solid var(--line); font-size: 13px; }
.stat:last-child { border-bottom: 0; }
.stat .k { color: var(--muted); flex: none; }
.stat > span:last-child { text-align: right; min-width: 0; }
.stat > span:first-child { min-width: 0; }

.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--nav-h) + var(--safe-b) + 14px); z-index: 70;
  background: var(--surface-2); border: 1px solid var(--line);
  padding: 11px 16px; border-radius: 24px; font-size: 13px;
  max-width: 90vw; box-shadow: 0 8px 28px rgba(0,0,0,.5);
}
.toast.bad { border-color: var(--bad); color: var(--bad); }
.toast.good { border-color: var(--good); }

.notes {
  margin-top: 8px; padding: 9px 11px; font-size: 12px;
  border-left: 2px solid var(--warn); background: rgba(227, 168, 59, .12);
  border-radius: 0 6px 6px 0;
}
.notes-danger { border-left-color: var(--bad); background: rgba(255, 107, 100, .12); }

/* ── sign-in ─────────────────────────────────────────────── */
.auth-wrap {
  min-height: calc(100dvh - 49px - var(--safe-t));
  display: flex; align-items: center; justify-content: center;
  padding: 24px 16px;
}
.auth-card { width: 100%; max-width: 380px; margin: 0; }
.brand-row {
  display: flex; align-items: center; gap: 10px; margin-bottom: 18px;
}
.brand-row img { width: 34px; height: 34px; border-radius: 8px; flex: none; }
.brand-row h2 { margin: 0; font-size: 19px; }
.auth-card .actions { margin-top: 4px; }
.auth-card .btn { width: 100%; }

/* ── landscape phone: video takes the whole screen ───────── */
@media (max-width: 900px) and (orientation: landscape) {
  /* Short screen: give the picture every pixel the bars can spare. */
  #viewer .vtop { padding-top: calc(3px + var(--safe-t)); padding-bottom: 3px; }
  #viewer .vbot { padding-top: 3px; padding-bottom: calc(3px + var(--safe-b)); }
  #viewer .vsub { display: none; }
  .iconbtn { width: 34px; height: 34px; }
  .grid { grid-template-columns: repeat(3, 1fr); }
}

/* ── tablet ──────────────────────────────────────────────── */
@media (min-width: 700px) {
  .grid { grid-template-columns: repeat(3, 1fr); gap: 10px; }
  .screen { padding: 16px; max-width: 1400px; margin: 0 auto; }
  /* The camera grid earns the full width; forms and stat lists do not.
     Setup and Timeline sit in a settings-page column so nothing is
     stretched across a widescreen monitor. */
  #s-setup, #s-time { max-width: 860px; }
}

/* ── desktop: top nav instead of bottom, denser grid ─────── */
@media (min-width: 1000px) {
  :root { --nav-h: 0px; }
  #app { padding-bottom: 20px; }
  nav.tabbar {
    position: static; height: auto; padding: 0; border-top: 0;
    background: none; backdrop-filter: none;
    gap: 4px; width: auto;
  }
  header.bar { gap: 18px; }
  .navbtn {
    flex: 0 0 auto; flex-direction: row; gap: 7px;
    padding: 7px 13px; border-radius: 8px; font-size: 14px;
  }
  .navbtn svg { width: 17px; height: 17px; }
  .navbtn.active { background: var(--surface-2); color: var(--text); box-shadow: inset 0 -2px 0 var(--accent); }
  .grid { grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); }
  .toast { bottom: 24px; }
}

/* ── machines waiting for approval (hub site picker) ────────── */
.pending-card { border-color: var(--warn); margin-bottom: 10px; }
.pending-card select, .pending-card input[type=text] {
  margin-top: 0; flex: 1 1 140px; width: auto; min-width: 0;
}
.notes-head {
  margin-bottom: 10px; padding: 10px 14px;
  border-left: 3px solid var(--warn); background: rgba(227, 168, 59, .12);
}

/* ── staff controls on a site card ──────────────────────────
   These live beside the card's link, not inside it: a <select> nested in an
   <a> is invalid HTML and the anchor swallows the click, which made the
   customer dropdown impossible to open. */
.site-card { padding: 0; }
.site-card-body { padding: 14px; }
.site-manage {
  margin: 0; padding: 10px 14px;
  border-top: 1px solid var(--line);
  align-items: center; gap: 8px;
}
.site-manage select {
  margin-top: 0; flex: 1 1 120px; width: auto; min-width: 0;
  font-size: .85rem; padding: 4px 6px;
}
.site-manage .s-msg { color: var(--muted); min-height: 1em; }

/* ── folder picker ──────────────────────────────────────────
   A short scrolling list rather than a tree: one level at a time is easier
   to hit on a phone, and a recorder's folders are rarely deep. */
.browser {
  margin-top: 10px; border: 1px solid var(--line);
  border-radius: var(--r); background: var(--surface-2); padding: 10px;
}
.browser-head { align-items: center; gap: 8px; margin-bottom: 8px; }
.browser-head .mono { word-break: break-all; }
.browser-list {
  max-height: 260px; overflow-y: auto; margin-bottom: 10px;
  border: 1px solid var(--line); border-radius: var(--r);
}
.browser-row {
  display: block; width: 100%; text-align: left;
  background: none; border: 0; border-bottom: 1px solid var(--line);
  color: var(--text); padding: 9px 10px; cursor: pointer; font-size: .9rem;
  word-break: break-all;
}
.browser-row:last-child { border-bottom: 0; }
.browser-row:hover, .browser-row:focus-visible { background: var(--surface); }
.browser-list p { padding: 10px; margin: 0; }

/* ── true full screen ───────────────────────────────────────
   In full screen the viewer is the whole display, so the black bars around a
   16:9 picture on a 16:10 laptop are the letterboxing, not wasted layout.
   The bars stay solid black rather than the page background, which is what
   makes a camera feel like a camera rather than a web page. */
#viewer:fullscreen,
#viewer:-webkit-full-screen {
  inset: 0; border: 0; border-radius: 0; box-shadow: none;
  width: 100vw; height: 100vh; background: #000;
}
#viewer:fullscreen .vwrap,
#viewer:-webkit-full-screen .vwrap { background: #000; }
/* The bars fade out of the way in full screen and come back on hover, so a
   4K picture is not permanently cropped by a toolbar. */
#viewer:fullscreen .bar { transition: opacity .2s; opacity: .15; }
#viewer:fullscreen .bar:hover { opacity: 1; }
