:root {
  --bg: #0b1020;
  --card: #151b30;
  --ink: #e7ecf5;
  --muted: #9aa6c0;
  --accent: #2d8cff;
  --ok: #38c172;
  --line: #25304d;

  /* Text links. Same blue as --accent and the same green as --ok, but named
     separately so a restyle of the buttons/badges that use those two doesn't
     silently repaint every link on every page. */
  --link: #2d8cff;
  --link-hover: #38c172;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--ink);
  line-height: 1.5;
}

/* apphome.ejs only (see server/views/apphome.ejs's <body class="apphome-body">
   and public/js/apphome-camera-toggle.js). Transparent by DEFAULT — the same
   "camera-safe by default" principle already applied to apphome.ejs's
   content (see #apphome-camera in apphome.ejs) extended to the page
   background: a solid background would paint over the live camera feed
   wherever the camera tile isn't covered by a card, in every surface,
   before client-side JS has even run. Restored to the normal opaque
   var(--bg) only once apphome-camera-toggle.js confirms we're NOT in the
   Camera surface. */
.apphome-body {
  background: transparent;
}

.apphome-body.not-camera {
  background: var(--bg);
}

.app {
  max-width: 720px;
  margin: 0 auto;
  padding: 2.5rem 1.25rem;
}

.header h1 {
  margin: 0 0 0.25rem;
  font-size: 1.5rem;
}

.header .sub {
  margin: 0 0 1.5rem;
  color: var(--muted);
}

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 1.25rem 1.5rem;
  margin-bottom: 1rem;
}

.card h2 {
  margin: 0 0 0.75rem;
  font-size: 1.05rem;
  transition: color 0.2s ease, text-shadow 0.2s ease;
}

.card h2.highlight {
  color: var(--accent);
  text-shadow: 0 0 0.5rem rgba(45, 140, 255, 0.6);
}

.card h3 {
  margin: 1rem 0 0.4rem;
  font-size: 0.9rem;
  color: var(--muted);
}

.kv {
  display: grid;
  grid-template-columns: 9rem 1fr;
  gap: 0.35rem 1rem;
  font-size: 0.95rem;
}

.kv dt { color: var(--muted); }
.kv dd { margin: 0; word-break: break-all; }

.badge {
  display: inline-block;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
}

.badge.ok { background: rgba(56, 193, 114, 0.15); color: var(--ok); }
.badge.muted { background: rgba(154, 166, 192, 0.15); color: var(--muted); }

.btn {
  display: inline-block;
  background: var(--accent);
  color: #fff;
  text-decoration: none;
  padding: 0.6rem 1.1rem;
  border-radius: 8px;
  font-weight: 600;
}

/* ── Links ────────────────────────────────────────────────────────────────
   Global, applies to every <a> on every page (home.css is the one stylesheet
   loaded by all views). Underlined blue at rest, green with the underline
   removed on hover.

   :visited is given the SAME treatment as :link deliberately — the browser
   default of recolouring visited links is not wanted here, and the pair has to
   be stated explicitly because `a:visited` (0,1,1) outranks a bare `a` (0,0,1).

   :focus-visible rides along with :hover so keyboard users get the same
   feedback; it is focus-VISIBLE rather than :focus so a mouse click doesn't
   leave the link stuck green afterwards. */
a,
a:link,
a:visited {
  color: var(--link);
  text-decoration: underline;
}

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

/* .btn is an <a> painted as a button (blue fill, white text) — the rule above
   would underline it and turn its label green on hover, so it opts out. Listed
   with the same states so it wins on specificity rather than source order. */
a.btn,
a.btn:link,
a.btn:visited,
a.btn:hover,
a.btn:focus-visible {
  color: #fff;
  text-decoration: none;
}

/* Kept as an alias: .link predates the global rule above and is still on the
   nav/footer anchors, which now inherit their colour from it anyway. */
.link { color: var(--link); }

/* Button + secondary link row (e.g. the logout confirm form). */
.actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1rem;
}

/* In-client sign-in progress line (apphome.js). Empty until it has something
   to say, so it takes no space in the recognised/idle states. */
.status {
  margin: 0.75rem 0 0;
  min-height: 1.2em;
  color: var(--muted);
  font-size: 0.9rem;
}

.foot {
  margin-top: 1.5rem;
  color: var(--muted);
  font-size: 0.85rem;
}

.site-foot {
  margin-top: 1.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  font-size: 0.85rem;
}

.site-foot .dot {
  margin: 0 0.5rem;
  color: var(--muted);
}

.site-menu {
  margin-bottom: 1.5rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--line);
  font-size: 0.85rem;
}

.site-menu .dot {
  margin: 0 0.5rem;
  color: var(--muted);
}

.kv-list {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.95rem;
}

.kv-list li {
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--line);
}

.kv-list li:last-child {
  border-bottom: none;
}

// now follows polls.css content, which is included in home.css for this app, so that the app can be used in the Zoom client without needing to load a second CSS file.  This is done by including the pollsCssUrl in the head of the import and export pages.  The polls.css file is also included in the home page for use when running outside of the Zoom client.

/* Poll list page — layered on top of home.css, which owns the palette. */

:root {
  --warn: #f0b429;
  --quiz: #b98cff;
}

/* Poll cards carry more content than the base pages, so widen the column. */
.app.wide { max-width: 880px; }

.muted { color: var(--muted); }
.small { font-size: 0.85rem; }

.card.warn { border-color: rgba(240, 180, 41, 0.5); }

.stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin: 0 0 1rem;
  padding: 0;
  list-style: none;
  color: var(--muted);
  font-size: 0.95rem;
}

.stats strong { color: var(--ink); font-size: 1.1rem; }

.window-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.75rem;
}

.window-form label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  color: var(--muted);
  font-size: 0.85rem;
}

.window-form input {
  width: 5rem;
  padding: 0.5rem 0.6rem;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
}

/* Put the rescan form beside the scan summary when there is room, and let it
   wrap underneath on narrow viewports (Zoom sidebar, small phones) instead of
   relying on a fixed breakpoint. */
#scan {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
}

/* The flex-BASIS is what makes the wrap happen: flexbox breaks a line when the
   items' hypothetical sizes overflow, so `1 1 auto` + min-width:0 would let the
   summary squeeze to a sliver and never wrap. A basis says "below this, put the
   form on its own row" — fluid at every width, no media query. */
.scan-summary {
  flex: 1 1 15rem;
  min-width: 0;
}

.scan-summary h2 { margin-bottom: 0.25rem; }
.scan-summary .stats { margin: 0; }

#rescan {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
}

/* home.css styles .btn for links; these are the bits a <button>/<label> needs
   to match them. */
.btn { border: 0; cursor: pointer; font: inherit; font-weight: 600; }

.btn:disabled { opacity: 0.6; cursor: default; }

.poll-title { margin-bottom: 0.5rem; }

.list-hint { margin: 0 0 0.75rem; }

/* Poll cards are clickable — but only once polls.js has confirmed the webview
   can actually open a <dialog>. Until then they stay plain, so nothing offers
   a click that would do nothing. */
.js-cards .card.poll {
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

.js-cards .card.poll:hover,
.js-cards .card.poll:focus-visible {
  background: #1b2340;
  border-color: var(--accent);
}

.js-cards .card.poll:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.js-cards .card.poll:active { background: #202a4c; }

/* ──────────────────────────────────────────────────────────────────────
   Poll modal
   ────────────────────────────────────────────────────────────────────── */

.poll-modal {
  width: min(40rem, calc(100vw - 2rem));
  max-height: calc(100vh - 3rem);
  padding: 1.5rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--card);
  color: var(--ink);
  overflow-y: auto;
}

.poll-modal::backdrop { background: rgba(5, 8, 18, 0.7); }

/* Sticky, not absolute: the dialog is the scroll container, so an absolutely
   positioned close button scrolls out of reach on a long quiz. The negative
   margins pull the row out over the dialog's own padding. */
.poll-modal-dismiss {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  justify-content: flex-end;
  margin: -1.5rem -1.5rem 0;
  padding: 0.4rem 0.8rem 0.2rem;
  background: var(--card);
}

.poll-modal-close {
  padding: 0 0.35rem;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 1.5rem;
  line-height: 1.2;
  cursor: pointer;
}

.poll-modal-close:hover { color: var(--ink); }

.modal-h {
  margin: 1.5rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  font-size: 0.95rem;
}

/* ──────────────────────────────────────────────────────────────────────
   Poll content — badges, questions, answers, and the meetings a poll came
   from. Shared by the card and the modal.
   ────────────────────────────────────────────────────────────────────── */

.badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 0.5rem;
}

.badge.adv { background: rgba(45, 140, 255, 0.15); color: var(--accent); }
.badge.quiz { background: rgba(185, 140, 255, 0.15); color: var(--quiz); }
.badge.warn-badge { background: rgba(240, 180, 41, 0.15); color: var(--warn); }

/* Collapsed detail blocks keep a long poll list scannable. */
.detail { margin-top: 0.75rem; }

.detail > summary {
  cursor: pointer;
  color: var(--muted);
  font-size: 0.9rem;
}

.detail > summary:hover { color: var(--ink); }

.questions {
  margin: 0.75rem 0 0;
  padding-left: 1.25rem;
}

.questions > li { margin-bottom: 0.9rem; }

.q-name { font-weight: 600; }

.answers {
  margin: 0.35rem 0 0;
  padding-left: 1.1rem;
  color: var(--muted);
  font-size: 0.9rem;
}

/* Quiz answer keys — worth seeing at a glance, since they're the part most
   likely to be lost when polls are copied by hand. */
.answers .right { color: var(--ok); }
.answers .right::after { content: ' (correct)'; font-size: 0.8em; }

.sources, .meetings {
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.9rem;
}

.sources > li, .meetings > li {
  padding: 0.3rem 0;
  border-bottom: 1px solid var(--line);
}

.sources > li:last-child, .meetings > li:last-child { border-bottom: 0; }

.when {
  display: inline-block;
  min-width: 3.6rem;
  margin-right: 0.5rem;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}

.when.future { color: var(--accent); }

/* Export action, bottom-right of each poll card. The status sits to its left
   and stays empty until there is something to say, so the row does not jump.
   In-client the download happens in another browser entirely, which is
   invisible from inside Zoom — that message is the only feedback there is. */
.card-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.75rem;
  margin-top: 1rem;
}

.card-actions .export-status {
  margin: 0;
  flex: 1 1 auto;
  min-width: 0;
  text-align: right;
}

/* Slightly tighter than the page's primary buttons: one per card adds up. */
.small-btn {
  padding: 0.4rem 0.9rem;
  font-size: 0.9rem;
}

/* Put each poll's export action beside the title/badges when there is room,
   and wrap underneath on narrow viewports instead of using a fixed breakpoint. */
.card.poll {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
}

/* Same reasoning as .scan-summary: the basis is the wrap trigger. Below roughly
   16rem of title/badges the Export action drops to its own row. */
.poll-info {
  flex: 1 1 16rem;
  min-width: 0;
}

.poll-info .poll-title { margin-bottom: 0.25rem; }
.poll-info .badges { margin-bottom: 0; }

/* grow:1 so that when this wraps to its own row it fills the width and the
   button stays hard right, exactly where it sits when the row is side-by-side —
   the button doesn't jump to the left edge the moment the card gets narrow.
   Its own justify-content: flex-end keeps it right in both cases. */
.card.poll .card-actions {
  flex: 1 0 auto;
  margin-top: 0;
}

/* ──────────────────────────────────────────────────────────────────────
   Import page
   ────────────────────────────────────────────────────────────────────── */

/* `hidden` must actually hide. The UA stylesheet's [hidden] { display: none }
   loses to ANY author rule that sets display — a class with `display: flex` is
   enough — so an element toggled with .hidden = true can stay on screen. Pin it
   once rather than remembering it per component. */
[hidden] { display: none !important; }

/* The real file input is hidden and driven by its <label class="btn">, which is
   the only way to make a file picker look like the app's other buttons. Hidden
   this way rather than display:none so it stays keyboard-reachable. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.file-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.file-row .status { margin: 0; }

/* The input is keyboard-focusable but invisible, so its focus ring has to be
   drawn on the label that follows it. */
.visually-hidden:focus-visible + .file-row .btn,
.visually-hidden:focus-visible + .btn { outline: 2px solid var(--accent); outline-offset: 2px; }

.meeting-label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  color: var(--muted);
  font-size: 0.85rem;
}

.meeting-label input {
  width: 11rem;
  padding: 0.5rem 0.6rem;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  letter-spacing: 0.04em;
}

/* What the chosen file turned out to contain. */
.poll-list {
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}

.poll-list > li {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--line);
}

.poll-list > li:last-child { border-bottom: 0; }

.pick-topic { font-weight: 600; }

.card.ok-card { border-color: rgba(56, 193, 114, 0.5); }

/* A caveat the user needs to read, not general small print — enough weight to
   be seen without being an error. */
.note {
  margin: 0.75rem 0 0;
  padding: 0.6rem 0.8rem;
  border-left: 3px solid var(--warn);
  border-radius: 0 6px 6px 0;
  background: rgba(240, 180, 41, 0.08);
  color: var(--ink);
}

/* Upcoming-meeting pick-list. Each row is a real <button>: it is a control, and
   keyboard users get that for free. */
.meeting-picks {
  margin: 0.5rem 0 0;
  padding: 0;
  list-style: none;
}

.pick {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  width: 100%;
  padding: 0.55rem 0.7rem;
  border: 1px solid transparent;
  border-radius: 8px;
  background: none;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.pick:hover, .pick:focus-visible {
  border-color: var(--accent);
  background: #1b2340;
  outline: none;
}

/* ──────────────────────────────────────────────────────────────────────
   Import stepper
   ────────────────────────────────────────────────────────────────────── */
/* ── Import stepper ──
   One stage on screen at a time; this strip is the only thing that shows the
   whole shape of the process, so it stays visible throughout. */
.progress {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.25rem;
  margin: 0 0 1rem;
  padding: 0;
  list-style: none;
  counter-reset: step;
  font-size: 0.9rem;
  color: var(--muted);
}

.progress li {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  counter-increment: step;
}

.progress li::before {
  content: counter(step);
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
}

.progress li.current { color: var(--ink); font-weight: 600; }

.progress li.current::before {
  border-color: var(--accent);
  background: rgba(45, 140, 255, 0.15);
  color: var(--accent);
}

/* Completed stages get a tick instead of their number — at a glance it's the
   difference between "step 2" and "step 2, done". */
.progress li.done::before {
  content: '✓';
  border-color: var(--ok);
  color: var(--ok);
}

/* Each stage's heading takes focus when it appears (it's tabindex="-1"), so
   the ring would otherwise show on a heading nobody clicked. */
.step h2:focus { outline: none; }

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

.step-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
}

/* Back sits left of the primary action but shouldn't compete with it. */
.step-actions .btn.secondary { order: -1; }

.btn.secondary {
  background: none;
  border: 1px solid var(--line);
  color: var(--ink);
  font-weight: 500;
}

.btn.secondary:hover { border-color: var(--accent); }

/* The chosen meeting, so stage 1 shows its answer rather than only holding it
   in the number box. Cleared when the box stops matching. */
.pick.selected {
  border-color: var(--accent);
  background: rgba(45, 140, 255, 0.15);
}

.pick.selected .pick-topic::after {
  content: ' ✓';
  color: var(--ok);
}

/* One editable name per poll, pre-filled from the file. */
.title-label {
  display: block;
  margin-bottom: 0.25rem;
  color: var(--muted);
  font-size: 0.85rem;
}

.title-input {
  display: block;
  width: 100%;
  max-width: 28rem;
  margin-bottom: 0.35rem;
  padding: 0.5rem 0.6rem;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
}

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

/* Account settings read from Zoom before anything is sent. */
.preflight { margin: 0.75rem 0 0; }

