/* Shared styling for the live poll. All colour pairs below were measured for
   WCAG contrast: ink on white 16.5:1, white on brand 12.3:1, green 7.1:1,
   red 7.6:1, muted grey on white 7.1:1. Nothing is signalled by colour alone. */

:root {
  --ink: #16202b;
  --muted: #4a5a68;
  --brand: #0f3757;
  --brand-soft: #e3ecf3;
  --open: #12653d;
  --closed: #9b2c17;
  --line: #c8d6e2;
  --page: #ffffff;
  --panel: #f2f5f8;
  --radius: 10px;
  --font: Lato, "Segoe UI", system-ui, -apple-system, Arial, sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.35;
}

h1, h2, h3 { line-height: 1.15; margin: 0 0 0.5em; }

a { color: var(--brand); }

button {
  font-family: var(--font);
  font-size: 1rem;
  padding: 0.6em 1.1em;
  border: 2px solid var(--brand);
  border-radius: var(--radius);
  background: var(--brand);
  color: #ffffff;
  cursor: pointer;
}

button.secondary { background: #ffffff; color: var(--brand); }
button.danger { background: #ffffff; color: var(--closed); border-color: var(--closed); }
button:disabled { opacity: 0.45; cursor: not-allowed; }
button:focus-visible, a:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible {
  outline: 3px solid var(--open);
  outline-offset: 2px;
}

input[type="text"], input[type="number"], textarea, select {
  font-family: var(--font);
  font-size: 1rem;
  width: 100%;
  padding: 0.6em 0.7em;
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: #ffffff;
  color: var(--ink);
}

.wrap { max-width: 720px; margin: 0 auto; padding: 1rem; }

.status {
  display: inline-block;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 0.25em 0.7em;
  border-radius: 999px;
  border: 2px solid currentColor;
}
.status.open { color: var(--open); }
.status.closed { color: var(--closed); }

.notice {
  padding: 0.8em 1em;
  border-left: 6px solid var(--brand);
  background: var(--panel);
  border-radius: 0 var(--radius) var(--radius) 0;
}

/* A problem the instructor has to notice mid-class, so it is warm rather than
   quiet. Measured against its own background: 7.4:1. */
.notice.warn {
  border-left-color: #9a4a12;
  background: #fdf1e7;
  color: #6b3208;
  font-weight: 600;
}

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

/* ---------- phone / join view ---------- */
.join-header {
  background: var(--brand);
  color: #ffffff;
  padding: 1rem;
}
.join-header p { margin: 0.2rem 0 0; font-size: 0.95rem; }

.question-text { font-size: 1.45rem; font-weight: 700; margin: 1rem 0; }

.choice-list { list-style: none; margin: 0; padding: 0; }
.choice-list li { margin: 0 0 0.6rem; }

.choice {
  display: block;
  width: 100%;
  text-align: left;
  font-size: 1.1rem;
  padding: 1rem;
  background: #ffffff;
  color: var(--ink);
  border: 2px solid var(--line);
}
.choice[aria-pressed="true"] {
  border-color: var(--brand);
  background: var(--brand-soft);
  font-weight: 700;
}
.choice[aria-pressed="true"]::before { content: "\2713\00a0"; color: var(--brand); }

.scale-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.scale-row .choice {
  flex: 1 1 100%;
  display: flex;
  align-items: center;
  gap: 0.8rem;
  text-align: left;
  padding: 0.75rem 1rem;
}
.scale-number {
  flex: 0 0 2rem;
  text-align: center;
  font-size: 1.3rem;
  font-weight: 700;
}
.scale-label {
  font-size: 1rem;
  line-height: 1.25;
}

.big-submit { width: 100%; font-size: 1.15rem; padding: 0.9em; margin-top: 1rem; }

.thanks {
  margin-top: 1rem;
  padding: 1rem;
  border: 2px solid var(--open);
  border-radius: var(--radius);
  color: var(--open);
  font-weight: 700;
}

/* ---------- presenter view ---------- */
/* Four layouts, chosen live from the control panel. Every colour pair below was
   measured against the darkest and the brightest part of the scrimmed photo:
   white 13.1:1, amber 9.6:1, muted 7.6:1 at worst. On the light layouts,
   ink 16.5:1, accent 7.3:1, muted 7.1:1. */

body.present { background: #ffffff; }
body.present:has(.present-wrap[data-layout="full"]) { background: #0e0b08; }

.present-wrap {
  --s-ink: #16202b;
  --s-muted: #4a5a68;
  --s-accent: #7a4d0c;
  --s-alt: #16202b;
  --s-alt2: #4a5a68;
  --s-track: #efe9e1;
  --s-track-edge: #ddd2c3;
  --s-card-bg: #ffffff;
  --s-card-edge: #16202b;

  display: grid;
  /* Exactly one screen, never more. This was min-height, which let a long set
     of answers push the stage past the bottom of the projector where nobody can
     scroll it back. Everything inside now has a real ceiling to fit into. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  color: var(--s-ink);

  /* Set by the fitting pass in present.html: 1 is full size, smaller shrinks
     the result area until it fits the screen. */
  --fit: 1;
}

.stage-art {
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  position: relative;
}

.stage-art::after { content: ""; position: absolute; inset: 0; }

.stage {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  padding: 1.6vw 2.2vw;
  position: relative;
}

/* The product mark on the projected screen, with an optional session title
   after it. The mark is the constant; the title is content and is set quieter
   so it never competes with the Aula name. */
.course {
  margin: 0 0 .6vw;
  font-weight: 700;
  font-size: clamp(.8rem, 1.5vw, 1.5rem);
  color: var(--s-accent);
  display: flex;
  align-items: baseline;
  gap: .6em;
  flex-wrap: wrap;
}
.course .mark { letter-spacing: .01em; }
.course .session {
  font-weight: 400;
  color: var(--s-ink);
  opacity: .85;
}
.course .session::before { content: "·"; margin-right: .6em; opacity: .6; }

.rail { display: none; }

/* ---- layout: full background ---- */
.present-wrap[data-layout="full"] {
  --s-ink: #ffffff;
  --s-muted: #ccc4b8;
  --s-accent: #ffd79a;
  --s-alt: #ffffff;
  --s-alt2: #f0c48a;
  --s-track: rgba(255, 255, 255, .16);
  --s-track-edge: rgba(255, 255, 255, .30);
  grid-template: "main" 1fr / 1fr;
}
.present-wrap[data-layout="full"] .stage-art { grid-area: main; }
.present-wrap[data-layout="full"] .stage-art::after {
  background: linear-gradient(180deg, rgba(12, 9, 6, .74) 0%, rgba(12, 9, 6, .84) 100%);
}
.present-wrap[data-layout="full"] .stage { grid-area: main; }

/* ---- layout: banner strip ---- */
.present-wrap[data-layout="banner"] { grid-template: "art" 24% "main" 1fr / 1fr; }
.present-wrap[data-layout="banner"] .stage-art { grid-area: art; background-position: center 42%; }
.present-wrap[data-layout="banner"] .stage-art::after {
  background: linear-gradient(90deg, rgba(20, 15, 10, .80) 0%, rgba(20, 15, 10, .40) 60%, rgba(20, 15, 10, .10) 100%);
}
.present-wrap[data-layout="banner"] .stage { grid-area: main; }
/* The course name sits on the band rather than above the question. */
.present-wrap[data-layout="banner"] .stage-art .band-label,
.present-wrap[data-layout="banner"] .stage > .course { color: var(--s-accent); }

/* ---- layout: side rail ---- */
.present-wrap[data-layout="rail"] { grid-template: "art main" 1fr / minmax(0, 32%) 1fr; }
.present-wrap[data-layout="rail"] .stage-art { grid-area: art; background-position: 60% center; }
.present-wrap[data-layout="rail"] .stage-art::after {
  background: linear-gradient(180deg, rgba(18, 13, 9, .74) 0%, rgba(18, 13, 9, .46) 45%, rgba(18, 13, 9, .88) 100%);
}
.present-wrap[data-layout="rail"] .rail {
  grid-area: art;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 1rem;
  padding: 2vw 1.6vw;
  position: relative;
  min-width: 0;
}
.present-wrap[data-layout="rail"] .rail .course {
  color: #ffffff;
  font-size: clamp(.9rem, 1.7vw, 1.8rem);
  line-height: 1.15;
}
.present-wrap[data-layout="rail"] .stage { grid-area: main; }
.present-wrap[data-layout="rail"] .stage > .course { display: none; }
/* The rail already carries the join code, so the header one steps aside. */
.present-wrap[data-layout="rail"] .stage .join-card { display: none; }

/* ---- layout: plain ---- */
.present-wrap[data-layout="plain"] { grid-template: "main" 1fr / 1fr; }
.present-wrap[data-layout="plain"] .stage-art { display: none; }
.present-wrap[data-layout="plain"] .stage { grid-area: main; }

/* ---- shared furniture ---- */
.present-head {
  display: flex;
  gap: 1.5rem;
  align-items: flex-start;
  justify-content: space-between;
  padding-bottom: .8rem;
}

.head-text { min-width: 0; }

.present-question {
  font-size: clamp(1.5rem, 3.2vw, 3.1rem);
  font-weight: 700;
  line-height: 1.1;
  margin: 0;
  color: var(--s-ink);
}

.present-meta {
  font-size: clamp(.85rem, 1.25vw, 1.25rem);
  color: var(--s-muted);
  margin: .4rem 0 0;
}

.join-card {
  flex: 0 0 auto;
  text-align: center;
  border-radius: 8px;
  padding: .6rem;
  background: var(--s-card-bg);
  border: 3px solid var(--s-card-edge);
}
.join-card img { display: block; width: clamp(84px, 10vw, 172px); height: auto; }
/* The address a student at the back has to type. It used to be capped at the
   width of the QR image and broken at any character, which split the host name
   mid-word. It now stays on one line and the card widens to suit it. */
.join-card .url {
  font-size: clamp(.62rem, .95vw, 1rem);
  font-weight: 700;
  color: #16202b;
  white-space: nowrap;
  margin: .3rem auto 0;
}
.rail .join-card { border-color: transparent; max-width: 100%; align-self: center; }
.rail .join-card img { width: min(100%, 15vw); }
.rail .join-card .url { max-width: none; }

.present-body { flex: 1 1 auto; min-height: 0; padding-top: 1rem; overflow: hidden; }

/* A last resort for a long rating scale or a long list of choices: two columns
   rather than shrinking the type past the point of being readable from the back
   of a room. Categories are never dropped, because a missing bar would misread
   as nobody having chosen it. */
.present-body.split { column-count: 2; column-gap: 2.4vw; }
.present-body.split .bar-row { break-inside: avoid; }
.present-body.split .bar-heading { column-span: all; }

/* Every result size is multiplied by --fit, so one variable shrinks the whole
   results area together and nothing is ever cut off the bottom of the screen. */
.bar-row { margin-bottom: calc(clamp(.5rem, 1.4vh, 1.4rem) * var(--fit)); }
.bar-label {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  font-size: calc(clamp(.95rem, 1.8vw, 1.8rem) * var(--fit));
  font-weight: 700;
  margin-bottom: calc(.25rem * var(--fit));
  color: var(--s-ink);
}
.bar-track {
  background: var(--s-track);
  border: 2px solid var(--s-track-edge);
  border-radius: 6px;
  height: calc(clamp(1.3rem, 3vh, 2.8rem) * var(--fit));
}
.bar-fill { background: var(--s-accent); height: 100%; border-radius: 4px 0 0 4px; transition: width 350ms ease; }

.bar-heading {
  font-size: clamp(1rem, 1.9vw, 1.9rem);
  font-weight: 700;
  color: var(--s-ink);
  margin: 0 0 .6rem;
}

.cloud {
  display: flex;
  flex-wrap: wrap;
  gap: .4em .9em;
  align-items: baseline;
  justify-content: center;
  align-content: center;
  height: 100%;
  /* Word sizes are set in em against this, so --fit scales the whole cloud. */
  font-size: calc(clamp(1rem, 1.5vw, 1.6rem) * var(--fit));
}
.cloud span { font-weight: 700; color: var(--s-accent); line-height: 1.1; }
.cloud span.alt { color: var(--s-alt); }
.cloud span.alt2 { color: var(--s-alt2); }

.feed {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(calc(240px * var(--fit)), 1fr));
  gap: calc(.8rem * var(--fit));
  align-content: start;
  height: 100%;
  overflow: hidden;
}
.feed .card {
  border: 2px solid var(--s-track-edge);
  border-left: 8px solid var(--s-accent);
  border-radius: 8px;
  padding: calc(.7rem * var(--fit)) calc(.9rem * var(--fit));
  font-size: calc(clamp(.95rem, 1.4vw, 1.4rem) * var(--fit));
  background: var(--s-card-bg);
  color: #16202b;
}

/* Shown when even at the smallest readable size some answers will not fit. The
   count is the promise that nothing vanished silently. */
.more-chip {
  grid-column: 1 / -1;
  justify-self: center;
  align-self: center;
  font-weight: 700;
  font-size: calc(clamp(.9rem, 1.3vw, 1.3rem) * var(--fit));
  color: var(--s-card-bg);
  background: rgba(22, 32, 43, .78);
  border: 2px solid var(--s-card-bg);
  border-radius: 999px;
  padding: .35em 1em;
}
.cloud .more-chip { align-self: baseline; }

.hint {
  color: var(--s-muted);
  font-size: clamp(1rem, 1.6vw, 1.6rem);
  text-align: center;
  margin-top: 10vh;
}

.hidden { display: none !important; }

/* ---------- admin view ---------- */
.admin-grid { display: grid; grid-template-columns: minmax(280px, 1fr) minmax(280px, 1.2fr); gap: 1.5rem; align-items: start; }
@media (max-width: 800px) { .admin-grid { grid-template-columns: 1fr; } }
/* Wide enough for the phone preview to sit beside the questions. */
@media (min-width: 1200px) {
  .admin-grid { grid-template-columns: minmax(230px, 0.85fr) minmax(270px, 1fr) minmax(22rem, 1.15fr); }
}

.wrap.admin { max-width: 1500px; }

/* ---- what the room can see ----------------------------------------------
   Driving a class means looking up from the room and needing the answer to
   one question immediately: what is on the wall right now. This bar carries
   it in words as well as colour, and stays put while the page scrolls. */
.live-bar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem 1.5rem;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
  padding: 0.85rem 1rem;
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
}
.live-bar[data-state="live"] { border-color: var(--brand); }
.live-bar[data-state="cleared"] { border-color: var(--muted); }

.live-bar-what { display: flex; gap: 0.75rem; align-items: center; min-width: 15rem; flex: 1 1 22rem; }
.live-bar-text { min-width: 0; }
.live-title {
  margin: 0;
  font-weight: 700;
  font-size: 1.05rem;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
.live-sub { margin: 0.15rem 0 0; color: var(--muted); font-size: 0.9rem; }

/* Never colour alone: the chip always spells out the state. */
.live-chip {
  flex-shrink: 0;
  align-self: flex-start;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 0.35em 0.7em;
  border-radius: 999px;
  border: 2px solid var(--muted);
  color: var(--muted);
  background: #ffffff;
  white-space: nowrap;
}
.live-chip.is-live { border-color: var(--open); color: #ffffff; background: var(--open); }
.live-chip.is-cleared { border-color: var(--ink); color: var(--ink); }

.live-bar-actions { display: flex; flex-direction: column; gap: 0.45rem; align-items: flex-end; }
.live-bar-actions .row { justify-content: flex-end; }
.live-hint { align-self: center; font-size: 0.8rem; }

/* ---- the question list ---- */
/* Cards live inside a folder, so the selected-card rule has to reach them
   there as well as in a plain list. */
.q-list li.is-selected,
.folder-questions > li.is-selected { border-color: var(--brand); background: #ffffff; }
.q-badges { display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: center; margin-bottom: 0.3rem; }
.q-badge {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.2em 0.55em;
  border-radius: 999px;
  border: 1.5px solid var(--line);
  color: var(--muted);
  background: #ffffff;
  white-space: nowrap;
}
.q-badge.is-live { border-color: var(--open); background: var(--open); color: #ffffff; }
.q-badge.is-done { border-color: var(--muted); }

/* Everything that is not pressed every few minutes lives behind this, so the
   two that are stay easy to hit without looking. */
.more { position: relative; display: inline-block; }
.more > summary {
  list-style: none;
  cursor: pointer;
  border: 2px solid var(--brand);
  border-radius: var(--radius);
  background: #ffffff;
  color: var(--brand);
  font-weight: 700;
  padding: 0.4em 0.8em;
  font-size: 0.9rem;
  line-height: 1.2;
}
.more > summary::-webkit-details-marker { display: none; }
.more[open] > summary { background: var(--brand); color: #ffffff; }
.more-menu {
  position: absolute;
  right: 0;
  /* Above the sticky live bar, which the menu can reach on the topmost card. */
  z-index: 40;
  min-width: 13rem;
  margin-top: 0.35rem;
  padding: 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: #ffffff;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
}
.more-menu button { width: 100%; text-align: left; font-size: 0.9rem; padding: 0.45em 0.7em; }

/* ---- preview -------------------------------------------------------------
   The projector frame is the real /present page rendered at a real projector
   size and scaled down, so line wrapping and text size are what the room will
   actually get rather than an approximation of it. */
.preview-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.preview-tabs { margin: 0.6rem 0; }
.preview-stage {
  position: relative;
  width: 100%;
  overflow: hidden;
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: #ffffff;
}
.preview-stage[data-view="projector"] { aspect-ratio: 16 / 9; }
.preview-stage[data-view="phone"] { aspect-ratio: 9 / 16; max-width: 15rem; margin: 0 auto; border-width: 8px; border-color: var(--ink); border-radius: 22px; }
.preview-stage iframe { position: absolute; top: 0; left: 0; border: 0; transform-origin: top left; }
.preview-actions { margin-top: 0.7rem; }

/* Below the three-column width the preview stops competing for space and
   comes over the page instead, so the question list keeps its full width. */
@media (max-width: 1199px) {
  .preview-panel {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 30;
    max-height: 85vh;
    overflow-y: auto;
    border-radius: var(--radius) var(--radius) 0 0;
    box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.2);
  }
  .preview-panel.is-idle { display: none; }
  .preview-stage[data-view="projector"] { max-width: 34rem; margin: 0 auto; }
}

/* A phone-shaped window onto the real student page. */
.phone-frame {
  width: 100%;
  max-width: 18rem;
  margin: 0 auto;
  aspect-ratio: 9 / 17;
  border: 10px solid var(--ink);
  border-radius: 26px;
  overflow: hidden;
  background: #ffffff;
}
.phone-frame iframe { display: block; width: 100%; height: 100%; border: 0; }
.panel { border: 2px solid var(--line); border-radius: var(--radius); padding: 1rem; background: var(--panel); }
.panel h2 { font-size: 1.15rem; }
.field { margin-bottom: 0.8rem; }
.field label { display: block; font-weight: 700; margin-bottom: 0.25rem; }
.field .help { font-weight: 400; color: var(--muted); font-size: 0.9rem; }

/* The class list was once grouped into collapsible folder sections. It is a
   flat chronological list now - what was asked, in the order it was asked - so
   .folder-list and .question-folder are gone. The card styles below stayed
   exactly as they were; only their container changed. */
.folder-questions { list-style: none; margin: 0; padding: 0; }
.folder-questions > li {
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 0.7rem 0.8rem;
  margin-bottom: 0.6rem;
  background: #ffffff;
}
.folder-questions > li:last-child { margin-bottom: 0; }
.folder-questions > li.active { border-color: var(--brand); border-left-width: 8px; }
.q-title { font-weight: 700; }
.q-meta { color: var(--muted); font-size: 0.9rem; margin: 0.15rem 0 0.5rem; }
.row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.row button { padding: 0.4em 0.8em; font-size: 0.9rem; }

/* Tall enough to hunt through a class's answers without a fight. It scrolls,
   and the panel keeps your place in it as new answers arrive. */
.moderate {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  max-height: min(46vh, 30rem);
  overflow: auto;
  overscroll-behavior: contain;
}
.moderate li { display: flex; gap: 0.5rem; align-items: flex-start; justify-content: space-between; border-bottom: 1px solid var(--line); padding: 0.4rem 0; }
.moderate li.is-hidden { opacity: 0.55; text-decoration: line-through; }

/* The layout currently on the projected screen. Marked by fill and weight, not
   colour alone, so it reads without relying on the blue. */
.row button[aria-pressed="true"] {
  background: var(--brand);
  color: #ffffff;
  border-color: var(--brand);
  font-weight: 700;
}

/* ---------- class activities ---------- */
/* Front-of-room only. Tile colours were measured against white text and clear
   AA at 5.0:1 in the worst case; card text is ink on cream at 16.2:1. */

body.play { background: #1d2b24; color: #ffffff; }

.play-setup { padding: 2rem 1rem 4rem; background: #ffffff; color: var(--ink); min-height: 100vh; }
.play-setup .wrap { max-width: 720px; }
.play-setup h1 { margin-bottom: 0.3rem; }
.play-setup > .wrap > .help { display: block; color: var(--muted); margin-bottom: 1.5rem; }
.play-setup .field { margin-bottom: 1.2rem; }
.play-setup label { display: block; font-weight: 700; margin-bottom: 0.3rem; }

.own-list { margin-top: 2rem; border-top: 2px solid var(--line); padding-top: 1rem; }
.own-list summary { font-weight: 700; cursor: pointer; padding: 0.3rem 0; }
.own-list .help { margin: 0.6rem 0 1rem; }

/* ---- the table ---- */
.play-stage {
  display: flex;
  flex-direction: column;
  height: 100vh;
  position: relative;
  background-size: cover;
  background-position: center;
  /* Dark tables by default. A light table overrides these four. */
  --stage-ink: #ffffff;
  --stage-veil: rgba(0, 0, 0, 0.30);
  --stage-btn: rgba(255, 255, 255, 0.12);
  --stage-btn-edge: rgba(255, 255, 255, 0.55);
  --stage-scrim: transparent;
  /* "colour" lets the tiles carry the colour; "cream" hands it to the photo. */
  --tile-mode: colour;
}

.play-stage::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--stage-scrim);
  pointer-events: none;
}

/* Everything sits above the wash. */
.stage-head, .stage-area, .stage-controls { position: relative; z-index: 1; }

.play-stage[data-theme="felt"] {
  background:
    radial-gradient(ellipse at 50% 35%, rgba(255, 255, 255, 0.10), transparent 62%),
    repeating-linear-gradient(45deg, rgba(0, 0, 0, 0.05) 0 2px, transparent 2px 4px),
    #1d6b4a;
}
.play-stage[data-theme="sagrada"] {
  background-image: url("/textures/sagrada.jpg");
  /* Its brightest window reaches 2.8:1 for white text on its own; under
     this wash the worst patch clears 5.5:1. */
  --stage-scrim: rgba(8, 6, 4, 0.42);
}

.play-stage[data-theme="cork"] {
  background-image: url("/textures/cork.jpg");
  --stage-ink: #16202b;
  --stage-veil: rgba(255, 255, 255, 0.55);
  --stage-btn: rgba(255, 255, 255, 0.78);
  --stage-btn-edge: rgba(22, 32, 43, 0.45);
}
.play-stage[data-theme="chalk"] {
  background:
    radial-gradient(ellipse at 30% 20%, rgba(255, 255, 255, 0.07), transparent 55%),
    #22262b;
}
.play-stage[data-theme="wood"] {
  background:
    repeating-linear-gradient(91deg, rgba(0, 0, 0, 0.13) 0 3px, transparent 3px 22px),
    linear-gradient(180deg, #7d4a24, #5d3517);
}
.play-stage[data-theme="night"] {
  background: radial-gradient(ellipse at 50% 30%, #23334a, #0f1722 70%);
}

.stage-head {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.7rem 1.2rem;
  background: var(--stage-veil);
  color: var(--stage-ink);
  backdrop-filter: blur(2px);
}
.stage-title { font-weight: 700; font-size: 1.05rem; }
.stage-count { color: var(--stage-ink); opacity: 0.85; font-size: 0.95rem; margin-left: auto; }
button.small { padding: 0.35em 0.9em; font-size: 0.9rem; }

.stage-area {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2vh 2vw;
}

.stage-controls {
  display: flex;
  gap: 0.7rem;
  justify-content: center;
  flex-wrap: wrap;
  padding: 1rem 1.2rem 1.6rem;
}
.stage-controls button { min-width: 7rem; border-radius: 999px; }
.play-stage button.secondary {
  background: var(--stage-btn);
  color: var(--stage-ink);
  border-color: var(--stage-btn-edge);
}

.stage-hint { font-family: var(--card-font, Lato, sans-serif); color: var(--stage-ink); opacity: 0.92; font-size: clamp(1rem, 1.8vw, 1.7rem); text-align: center; }

/* ---- flash cards ---- */
.flip {
  width: min(88vw, 58rem);
  height: min(62vh, 27rem);
  position: relative;
  transform-style: preserve-3d;
  transition: transform 520ms cubic-bezier(.3, .8, .3, 1);
}
.flip.flipped { transform: rotateY(180deg); }

.flip .face {
  font-family: var(--card-font, Lato, sans-serif);
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 3vh 3vw;
  border-radius: 24px;
  font-size: clamp(1.5rem, 4.6vw, 4.2rem);
  font-weight: 900;
  line-height: 1.12;
  box-shadow: 0 26px 50px -26px rgba(0, 0, 0, 0.85), inset 0 -8px 0 rgba(0, 0, 0, 0.16);
}
.flip .face.front { color: #ffffff; }
.flip .face.back {
  transform: rotateY(180deg);
  background: #fffdf5;
  color: var(--ink);
}

/* ---- speaking cards ---- */
.deal-table { display: flex; align-items: center; justify-content: center; gap: clamp(1rem, 4vw, 4rem); width: 100%; }

.deck-pile {
  width: clamp(7rem, 15vw, 14rem);
  aspect-ratio: 3 / 4;
  border-radius: 14px;
  position: relative;
  flex: 0 0 auto;
  background:
    repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.16) 0 6px, transparent 6px 12px),
    linear-gradient(160deg, #d9a441, #b8892f);
  border: 5px solid #fdf6e3;
  box-shadow:
    0 5px 0 #a97d2a, 0 10px 0 #986f24, 0 15px 0 #8a6320,
    0 26px 34px -20px rgba(0, 0, 0, 0.9);
  display: flex;
  align-items: center;
  justify-content: center;
}
.deck-pile.empty { opacity: 0.35; }
.pile-count {
  font-size: clamp(1.4rem, 3vw, 2.6rem);
  font-weight: 900;
  color: #5d3517;
  background: rgba(255, 253, 245, 0.85);
  border-radius: 999px;
  min-width: 2.2em;
  padding: 0.1em 0.3em;
  text-align: center;
}

.dealt {
  font-family: var(--card-font, Lato, sans-serif);
  flex: 1 1 auto;
  max-width: 46rem;
  min-height: min(52vh, 24rem);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 3vh 3vw;
  border-radius: 20px;
  border-top: 12px solid #1668c9;
  background: #fffdf5;
  color: var(--ink);
  font-size: clamp(1.3rem, 3.2vw, 2.9rem);
  font-weight: 700;
  line-height: 1.2;
  box-shadow: 0 26px 50px -26px rgba(0, 0, 0, 0.85);
  animation: deal-in 420ms cubic-bezier(.2, .9, .3, 1);
}
.dealt.empty { color: var(--muted); font-weight: 400; border-top-color: rgba(0, 0, 0, 0.15); animation: none; }

@keyframes deal-in {
  from { transform: translateX(-22%) rotate(-7deg) scale(.9); opacity: 0; }
  to { transform: none; opacity: 1; }
}

/* ---- spin the wheel ---- */
.wheel-area {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 4vw, 4rem);
  width: 100%;
  flex-wrap: wrap;
  justify-content: center;
}
.wheel-stage {
  position: relative;
  flex: 0 0 auto;
  /* Bigger than it was: the wheel is the thing the room is watching, and the
     right-hand card no longer needs room for a wall of wedge text. */
  width: min(72vh, 44rem);
  aspect-ratio: 1;
}

.wheel-viewport {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 50%;
  box-shadow: 0 20px 34px -18px rgba(0, 0, 0, 0.75);
}

.wheel-holder {
  width: 100%;
  height: 100%;
  /* Closing in on the wedge under the pointer, which sits at the top. */
  /* The pointer sits at three o’clock, so that is where we close in. */
  transform-origin: 84% 50%;
  transition: transform 720ms cubic-bezier(.2, .8, .2, 1);
  touch-action: none;
  cursor: grab;
}

.wheel-holder.zoomed { transform: scale(2.7); }
.wheel-holder:active { cursor: grabbing; }
.wheel {
  width: 100%;
  height: 100%;
  display: block;
  filter: drop-shadow(0 18px 26px rgba(0, 0, 0, 0.55));
}
.wheel-pointer {
  position: absolute;
  top: 50%;
  right: -20px;
  transform: translateY(-50%);
  width: 0;
  height: 0;
  border-top: 17px solid transparent;
  border-bottom: 17px solid transparent;
  border-right: 30px solid #ffd166;
  filter: drop-shadow(0 3px 3px rgba(0, 0, 0, 0.6));
}
.wheel-result { flex: 1 1 20rem; max-width: 40rem; }
.landed-card {
  background: #fffdf5;
  color: var(--ink);
  border-radius: 20px;
  padding: 1.4rem 1.6rem;
  box-shadow: 0 22px 40px -24px rgba(0, 0, 0, 0.85);
  animation: deal-in 420ms cubic-bezier(.2, .9, .3, 1);
}
.landed { font-family: var(--card-font, Lato, sans-serif); font-size: clamp(1.3rem, 2.8vw, 2.6rem); font-weight: 900; line-height: 1.2; margin: 0; }
.landed-back { font-size: clamp(1rem, 1.9vw, 1.7rem); color: #7a4d0c; margin: 0.6rem 0 0; font-weight: 700; }

/* ---- open the box ---- */
.box-area { display: flex; flex-direction: column; align-items: center; gap: 2.5vh; width: 100%; }
.box-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: 1rem;
  width: min(80vw, 46rem);
}
button.box {
  position: relative;
  aspect-ratio: 4 / 3;
  border: 0;
  border-radius: 12px;
  padding: 0;
  overflow: hidden;
  background: var(--box);
  color: #ffffff;
  box-shadow: 0 6px 0 rgba(0, 0, 0, 0.28), 0 14px 20px -12px rgba(0, 0, 0, 0.8);
  transition: transform 140ms ease, box-shadow 140ms ease;
}
button.box .lid {
  position: absolute;
  inset: 0 0 auto 0;
  height: 34%;
  background: rgba(255, 255, 255, 0.22);
  transition: transform 260ms cubic-bezier(.3, .9, .3, 1);
}
button.box .num {
  position: relative;
  font-size: clamp(1.2rem, 2.4vw, 2.1rem);
  font-weight: 900;
}
button.box:hover:not(:disabled) { transform: translateY(-3px); }
button.box:active:not(:disabled) { transform: translateY(2px); box-shadow: 0 2px 0 rgba(0, 0, 0, 0.28); }
/* A spent box has to read as spent on a light table and a dark one alike, so
   it takes a solid slate fill rather than a translucent one. */
button.box.used {
  background: #4a5158;
  color: #d7dade;
  box-shadow: none;
  opacity: 0.85;
}
button.box.used .lid { transform: translateY(-100%); }

.box-open {
  width: min(88vw, 58rem);
  border-radius: 20px;
  background: #fffdf5;
  color: var(--ink);
  padding: 3vh 2.5vw;
  text-align: center;
  box-shadow: 0 22px 40px -24px rgba(0, 0, 0, 0.85);
  animation: box-pop 320ms cubic-bezier(.2, .9, .3, 1);
}
.box-open p { font-family: var(--card-font, Lato, sans-serif); margin: 0; font-size: clamp(1.2rem, 2.7vw, 2.3rem); font-weight: 900; line-height: 1.2; }

@keyframes box-pop {
  from { transform: scale(.9); opacity: 0; }
  to { transform: none; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .flip, .dealt, .landed-card, .box-open, button.box, button.box .lid { transition: none; animation: none; }
}


/* On a photograph, the colour belongs to the picture. The cards go to cream
   card stock with dark type, lifted off the table with a real edge and a
   pooled shadow so they read as objects rather than rectangles. */
.play-stage[data-tile="cream"] .flip .face.front {
  background: linear-gradient(180deg, #fffdf6, #f3ecdc) !important;
  color: #16202b;
  border: 1px solid rgba(0, 0, 0, 0.10);
  box-shadow:
    0 2px 0 rgba(255, 255, 255, 0.9) inset,
    0 -10px 0 rgba(0, 0, 0, 0.06) inset,
    0 3px 0 #d9cfb8,
    0 8px 0 #cdc2a9,
    0 34px 46px -22px rgba(0, 0, 0, 0.85);
}
.play-stage[data-tile="cream"] .dealt {
  border-top-width: 14px;
  box-shadow:
    0 3px 0 #d9cfb8,
    0 8px 0 #cdc2a9,
    0 34px 46px -22px rgba(0, 0, 0, 0.85);
}
.play-stage[data-tile="cream"] button.box {
  background: linear-gradient(180deg, #fffdf6, #efe7d4);
  color: #16202b;
  border-bottom: 5px solid var(--box);
  box-shadow: 0 5px 0 #cdc2a9, 0 14px 20px -12px rgba(0, 0, 0, 0.85);
}
.play-stage[data-tile="cream"] button.box .lid { background: rgba(0, 0, 0, 0.06); }
.play-stage[data-tile="cream"] .landed-card,
.play-stage[data-tile="cream"] .box-open {
  box-shadow: 0 3px 0 #d9cfb8, 0 8px 0 #cdc2a9, 0 30px 44px -22px rgba(0, 0, 0, 0.85);
}

/* The pointer knocks against each peg as it goes by. */
.wheel-pointer.flick { animation: peg 110ms ease-out; }
@keyframes peg {
  0% { transform: translateY(-50%) rotate(0deg); }
  35% { transform: translateY(-50%) rotate(-13deg); }
  100% { transform: translateY(-50%) rotate(0deg); }
}
  100% { transform: translateX(-50%) rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .wheel-pointer.flick { animation: none; }
}


/* The result card asks to be clicked when it is hiding an answer. */
.landed-card.askable { cursor: pointer; }
.landed-cue {
  margin: 0.8rem 0 0;
  font-size: clamp(0.85rem, 1.1vw, 1.05rem);
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: #6b5a3a;
}

/* ---- the question library -------------------------------------------------
   The instructor's own material, kept in this browser. It sits below the
   teaching console because during class it is not what you are looking at;
   between classes it is the only thing you are looking at. */
/* The library is not something you open any more. It is the main question list
   and sits directly under the live bar, so it has a heading rather than a
   summary, and there is no disclosure triangle to leave it shut over the thing
   you teach from. */
.library-head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.library-title { font-size: 1.15rem; font-weight: 700; margin: 0; }

/* "Pictures from this computer" is still a details, and stays shut until it is
   wanted. Its summary has to carry the whole status on one line, because that
   line is all you see of it while it is closed. */
.library-summary {
  cursor: pointer;
  padding: 0.15rem 0;
}
.library-summary > .library-title { margin-right: 0.6rem; }

.library-grid {
  display: grid;
  grid-template-columns: minmax(12rem, 18rem) minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
  margin-top: 1rem;
}
@media (max-width: 800px) { .library-grid { grid-template-columns: 1fr; } }

.library-tree {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: #ffffff;
  padding: 0.5rem;
  max-height: 26rem;
  overflow-y: auto;
}
.library-folder {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  text-align: left;
  border: 2px solid transparent;
  background: transparent;
  color: var(--ink);
  font-weight: 600;
  font-size: 0.95rem;
  padding: 0.4em 0.6em;
  border-radius: var(--radius);
}
.library-folder:hover { background: var(--brand-soft); }
.library-folder.is-selected { border-color: var(--brand); background: var(--brand-soft); }

/* The root bucket is pinned below the folders and is not one of them, so it is
   set off by a rule. It still shows its name, its dot and its count exactly
   like the rows above, so nothing here is carried by position alone. */
.library-folder.is-root {
  margin-top: 0.45rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--line);
}

.folder-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.folder-count { color: var(--muted); font-weight: 400; font-size: 0.85rem; }

/* Colour is an orientation cue and never the only one: the folder's name is
   always beside it, and the dot carries a title for anyone who cannot see it. */
.folder-dot {
  flex-shrink: 0;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.25);
}
.folder-dot[data-color="slate"]  { background: #64748b; }
.folder-dot[data-color="red"]    { background: #b3391c; }
.folder-dot[data-color="amber"]  { background: #a76a08; }
.folder-dot[data-color="green"]  { background: #2f7a37; }
.folder-dot[data-color="teal"]   { background: #0f7b74; }
.folder-dot[data-color="blue"]   { background: #1f5fa8; }
.folder-dot[data-color="violet"] { background: #6b46b8; }
.folder-dot[data-color="pink"]   { background: #a83a6f; }

.library-path { font-weight: 700; margin: 0 0 0.6rem; overflow-wrap: anywhere; }
.library-items > .row { margin-bottom: 0.9rem; }
.library-questions { list-style: none; margin: 0; padding: 0; }
.library-questions > li {
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 0.7rem 0.8rem;
  margin-bottom: 0.6rem;
  background: #ffffff;
}
.library-questions > li:last-child { margin-bottom: 0; }

/* ---- a photograph on the projected screen -------------------------------
   For a question that is about the picture, the picture is the point: it takes
   the room it needs and the question and results share what is left. Both are
   still sized by the fitting pass, so nothing is pushed off the bottom.

   A landscape photograph sits above the text. A portrait one sits beside it,
   because stacking something tall above a question leaves neither readable
   from the back of a room. */
.stage-photo {
  margin: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4vw;
}
.stage-photo img {
  display: block;
  min-height: 0;
  object-fit: contain;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.25);
}
.stage-photo figcaption {
  font-size: clamp(0.7rem, 1.05vw, 1rem);
  opacity: 0.75;
  line-height: 1.3;
}

/* Landscape: under the question, taking about half the height. The figure is
   sized by the picture rather than stretched across the stage, so the credit
   sits under the photograph instead of out at the far edge, and the picture
   itself is as large as the space allows rather than letterboxed inside a
   full-width box. */
.present-wrap[data-photo="wide"] .stage-photo {
  flex: 0 1 auto;
  margin: 0 auto 0.8vw;
  align-items: center;
  max-width: 100%;
}
.present-wrap[data-photo="wide"] .stage-photo img {
  width: auto;
  max-width: 100%;
  max-height: 42vh;
}

/* Portrait: beside the question and its results, so the text keeps its width. */
@media (min-aspect-ratio: 4/3) {
  .present-wrap[data-photo="tall"] .stage {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
    grid-template-rows: auto 1fr;
    grid-template-areas: "photo head" "photo body";
    column-gap: 2vw;
    align-items: start;
  }
  .present-wrap[data-photo="tall"] .stage > .course { grid-area: head; }
  .present-wrap[data-photo="tall"] .stage-photo { grid-area: photo; align-self: stretch; justify-content: center; }
  .present-wrap[data-photo="tall"] .stage-photo img { width: auto; max-width: 100%; max-height: 74vh; margin: 0 auto; }
  .present-wrap[data-photo="tall"] .present-head { grid-area: head; }
  .present-wrap[data-photo="tall"] .present-body { grid-area: body; }
}
/* A narrow or tall screen has no room beside anything: stack it. */
@media (max-aspect-ratio: 4/3) {
  .present-wrap[data-photo="tall"] .stage-photo img { width: 100%; max-height: 42vh; }
  .present-wrap[data-photo="tall"] .stage-photo { margin-bottom: 0.8vw; }
}

/* ---- a photograph on a phone ---- */
.join-photo { margin: 0 0 0.9rem; }
.join-photo img {
  display: block;
  width: 100%;
  max-height: 45vh;
  object-fit: contain;
  border-radius: 8px;
  background: #eef3f8;
}
.join-photo figcaption {
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 0.3rem;
  line-height: 1.35;
}

/* ---- a thumbnail in the question library ---- */
.q-thumb {
  float: right;
  width: 5.5rem;
  height: 4rem;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--line);
  margin: 0 0 0.4rem 0.7rem;
  background: #eef3f8;
}
.q-image-note { color: var(--muted); font-size: 0.85rem; }

/* ---- the picture controls on the question form ---- */
.image-summary { cursor: pointer; font-weight: 700; list-style: none; }
.image-summary::-webkit-details-marker { display: none; }
.image-summary::before { content: "▸ "; color: var(--muted); font-weight: 400; }
#imageField[open] > .image-summary::before { content: "▾ "; }
.image-summary .help { font-weight: 400; }
.image-box { padding-top: 0.6rem; }
.image-preview {
  display: block;
  width: 100%;
  max-height: 9rem;
  object-fit: contain;
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: #ffffff;
  margin-bottom: 0.6rem;
}

/* The instructor's own note on a picture. It appears in the control panel and
   nowhere else: the server strips it from the projector's and the phones'
   views, so this styling has no counterpart on /present or /join. The dashed
   edge is deliberate - it should not look like something the room can read. */
.live-note {
  margin: 0.35rem 0 0;
  padding: 0.4rem 0.6rem;
  border-left: 3px solid var(--accent, #d08b2c);
  background: rgba(208, 139, 44, 0.10);
  border-radius: 0 6px 6px 0;
  font-size: 0.9rem;
  line-height: 1.4;
  white-space: pre-wrap;
}

.instructor-note-field textarea {
  border-style: dashed;
}

.q-note {
  margin: 0.35rem 0 0;
  font-size: 0.88rem;
}

.q-note > summary {
  cursor: pointer;
  color: var(--muted, #6b6257);
}

.q-note > p {
  margin: 0.3rem 0 0;
  padding: 0.4rem 0.6rem;
  border-left: 3px solid var(--accent, #d08b2c);
  background: rgba(208, 139, 44, 0.10);
  border-radius: 0 6px 6px 0;
  white-space: pre-wrap;
}

/* Question sets whose pictures come from the instructor's own machine. The
   dashed edge matches the instructor-only fields elsewhere: none of this is
   something the room can see. */
.local-set {
  border: 1px dashed var(--line, #d9d2c7);
  border-radius: 8px;
  padding: 0.7rem 0.9rem;
  margin: 0 0 0.7rem;
}

.local-set-path { margin: 0 0 0.2rem; font-weight: 600; }

.local-set-missing { margin: 0.5rem 0 0; font-size: 0.88rem; }
.local-set-missing > summary { cursor: pointer; }
.local-set-missing ul { margin: 0.3rem 0 0; padding-left: 1.2rem; }
.local-set-missing li { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.85rem; }
.local-set-missing.bad > summary { color: var(--danger, #a3341f); }

.local-alt {
  display: flex;
  gap: 0.8rem;
  align-items: flex-start;
  padding: 0.6rem 0;
  border-top: 1px solid var(--line, #d9d2c7);
}

.local-alt > div { flex: 1; min-width: 0; }
.local-alt-q { margin: 0 0 0.3rem; font-weight: 600; font-size: 0.92rem; }
.local-alt textarea { border-style: dashed; }

@media (max-width: 520px) {
  .local-alt { flex-direction: column; }
}
