/* ============================================================================
   class-app deck: the slide itself and every interaction block it can carry.

   THE COORDINATE SYSTEM. On the display surface a slide lives inside
   `.cl-stage__canvas`, a fixed 1920x1080 box with `container-type: inline-size`
   that is transform-scaled to whatever screen it is on. So EVERY display rule
   here sizes itself in `cqw` (1cqw = 19.2px) with a `clamp()` floor, never in
   `vw` and never behind a `@media` query. That is what makes the presenter's
   preview pixel-faithful to the projector: a `vw` rule looks perfect on the
   venue screen and silently breaks the phone preview, which is why it survives
   casual review.

   The phone surface is ordinary responsive CSS. Both surfaces of a block live
   next to each other in this file on purpose: the open-day app split its poll
   emphasis rules across two files and its own docs now say "change one, change
   the other".
   ============================================================================ */

/* ── The slide ─────────────────────────────────── */
.slide {
  position: relative;
  block-size: 100%;
  display: flex;
  flex-direction: column;
  gap: 2cqw;
  background: var(--bg-surface);
  color: var(--text-color);
  overflow: hidden;
}

.slide--display {
  padding: 6cqw 7cqw;
  justify-content: center;
}

.slide--phone {
  block-size: auto;
  gap: var(--space-md);
  padding: var(--space-lg);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

/* A slide with nothing on it. Wordless is worse than a sentence: a blank
   projector reads as a crash. */
.slide--blank {
  align-items: center;
  justify-content: center;
}

.slide__blank-note {
  font-family: var(--font-mono);
  font-size: clamp(13px, 1.4cqw, 28px);
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
}

.slide--phone .slide__blank-note {
  font-size: 13px;
}

.slide__title {
  font-family: var(--font-display);
  font-size: clamp(26px, 4.2cqw, 84px);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.025em;
  color: var(--text-color);
}

.slide--phone .slide__title {
  font-size: 22px;
  line-height: 1.25;
  letter-spacing: -0.02em;
}

.slide__body {
  font-size: clamp(15px, 2cqw, 40px);
  line-height: 1.6;
  color: var(--text-secondary);
  overflow-y: auto;
}

.slide--phone .slide__body {
  font-size: 15px;
}

/* Author-native markup lands in the body, so the common tags need house rules
   rather than the browser's defaults. */
.slide__body p + p {
  margin-block-start: 0.8em;
}

.slide__body ul,
.slide__body ol {
  /* RTL list indentation is padding-RIGHT, per the repo convention. */
  padding-right: 1.1em;
  display: flex;
  flex-direction: column;
  gap: 0.4em;
}

.slide__body strong {
  font-weight: 500;
  color: var(--text-color);
}

.slide__body code,
.slide__body pre {
  font-family: var(--font-mono);
  font-size: 0.9em;
  direction: ltr;
  /* An inline code identifier inside Hebrew narration is a Latin run in an RTL
     paragraph. `unicode-bidi: isolate` is what stops the surrounding Hebrew
     reordering it - this is a course full of identifiers, and a mis-ordered one
     is a line of code nobody can copy. */
  unicode-bidi: isolate;
}

.slide__body pre {
  display: block;
  padding: 0.8em 1em;
  background: var(--bg-secondary);
  border-radius: var(--radius-md);
  overflow-x: auto;
  text-align: left;
}

.slide__body a {
  color: var(--primary-color);
}

.slide__brandmark {
  position: absolute;
  inset-block-end: 3cqw;
  inset-inline-end: 3cqw;
  inline-size: 6cqw;
  opacity: 0.5;
  pointer-events: none;
}

/* A 9:16 phone card has no corner to spare. */
.slide--phone .slide__brandmark {
  display: none;
}

/* ── Build steps ───────────────────────────────── */
/*
   A slide body marks its build parts with `data-step="N"`; the renderer publishes
   the live step as `slide--step-N` on the root and these rules hide whatever is
   still ahead of it.

   THE DEFAULT IS VISIBLE AND THE RULES HIDE, not the other way round. If a step
   class is ever missing the slide shows too much, which a presenter can talk
   over; the inverse would paint a blank slide on a projector, which they cannot.

   Every rule below is exactly `.slide--step-N [data-step="M"]` - one class plus
   one attribute, identical specificity throughout. Do not add a rule at a
   different weight: an uneven cascade here hides a line with no error and no
   symptom until the moment it should have appeared.
*/
.slide [data-step] {
  transition:
    opacity 0.25s ease,
    transform 0.25s ease;
}

.slide--step-0 [data-step='1'],
.slide--step-0 [data-step='2'],
.slide--step-0 [data-step='3'],
.slide--step-0 [data-step='4'],
.slide--step-0 [data-step='5'],
.slide--step-0 [data-step='6'],
.slide--step-0 [data-step='7'],
.slide--step-1 [data-step='2'],
.slide--step-1 [data-step='3'],
.slide--step-1 [data-step='4'],
.slide--step-1 [data-step='5'],
.slide--step-1 [data-step='6'],
.slide--step-1 [data-step='7'],
.slide--step-2 [data-step='3'],
.slide--step-2 [data-step='4'],
.slide--step-2 [data-step='5'],
.slide--step-2 [data-step='6'],
.slide--step-2 [data-step='7'],
.slide--step-3 [data-step='4'],
.slide--step-3 [data-step='5'],
.slide--step-3 [data-step='6'],
.slide--step-3 [data-step='7'],
.slide--step-4 [data-step='5'],
.slide--step-4 [data-step='6'],
.slide--step-4 [data-step='7'],
.slide--step-5 [data-step='6'],
.slide--step-5 [data-step='7'],
.slide--step-6 [data-step='7'] {
  opacity: 0;
  transform: translateY(0.6cqw);
  /* Not display:none - the reveal must not reflow the lines already on screen. */
  pointer-events: none;
}

/* ── Poll and quiz ─────────────────────────────── */
.cl-poll {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.cl-poll--display {
  gap: 1.6cqw;
}

.cl-poll__prompt {
  font-size: 16px;
  font-weight: 500;
  color: var(--text-color);
}

.cl-poll--display .cl-poll__prompt {
  font-size: clamp(18px, 2.4cqw, 48px);
}

.cl-poll__options {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.cl-poll--display .cl-poll__options {
  gap: 1cqw;
}

.cl-poll__row {
  position: relative;
  inline-size: 100%;
  appearance: none;
  display: flex;
  align-items: center;
  gap: var(--space-md);
  text-align: start;
  padding: 12px 16px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--text-color);
  font-family: var(--font-body);
  font-size: 15px;
  cursor: pointer;
  overflow: hidden;
}

.cl-poll__row:disabled {
  cursor: default;
}

.cl-poll--display .cl-poll__row {
  padding: 1.4cqw 2cqw;
  font-size: clamp(15px, 1.9cqw, 38px);
  border-radius: 0.9cqw;
  gap: 1.5cqw;
}

/* The tally bar. Behind the label, not beside it: a row that grows sideways
   changes the reading order of the options every time somebody answers. */
.cl-poll__bar {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  background: var(--primary-light);
  transition: inline-size 0.4s ease;
  pointer-events: none;
}

.cl-poll__label {
  position: relative;
  flex: 1 1 auto;
  min-inline-size: 0;
  overflow-wrap: anywhere;
}

.cl-poll__tally {
  position: relative;
  font-family: var(--font-mono);
  font-size: 0.85em;
  color: var(--text-secondary);
  flex: none;
}

/* This viewer's own pick. Student surface only - the server sends no answer
   identity to the projector, so this class can never appear there. */
.cl-poll__row.is-mine {
  border-color: var(--primary-color);
}

/* The quiz's right answer. */
.cl-poll__row.is-correct {
  border-color: var(--primary-color);
  background: var(--primary-light);
}

.cl-poll__row.is-correct .cl-poll__label {
  font-weight: 500;
}

/* The crowd's pick on a poll. Suppressed on a quiz by the tally helper, where a
   wrong majority would compete with the right answer for the same emphasis. */
.cl-poll__row.is-leader {
  border-color: var(--accent-warm);
}

.cl-poll__meta {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text-tertiary);
}

.cl-poll--display .cl-poll__meta {
  font-size: clamp(12px, 1.2cqw, 24px);
}

.cl-poll__sep {
  padding-inline: 0.4em;
}

/* ── Word cloud ────────────────────────────────── */
.cl-cloud {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.cl-cloud__prompt {
  font-size: 16px;
  font-weight: 500;
  color: var(--text-color);
}

.cl-cloud--display .cl-cloud__prompt {
  font-size: clamp(18px, 2.4cqw, 48px);
}

.cl-cloud__words {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-sm) var(--space-md);
}

.cl-cloud--display .cl-cloud__words {
  gap: 1cqw 2cqw;
  justify-content: center;
}

/* Five weight steps. Light weights throughout: these are the words the room
   wrote, and a wall of bold Hebrew at 5cqw is a shout. */
.cl-cloud__word {
  font-weight: 300;
  line-height: 1.2;
  color: var(--text-color);
  overflow-wrap: anywhere;
}

.cl-cloud__word--r1 {
  font-size: 15px;
  color: var(--text-tertiary);
}
.cl-cloud__word--r2 {
  font-size: 18px;
  color: var(--text-secondary);
}
.cl-cloud__word--r3 {
  font-size: 22px;
}
.cl-cloud__word--r4 {
  font-size: 27px;
}
.cl-cloud__word--r5 {
  font-size: 33px;
  color: var(--primary-color);
}

.cl-cloud--display .cl-cloud__word--r1 {
  font-size: clamp(16px, 1.8cqw, 36px);
}
.cl-cloud--display .cl-cloud__word--r2 {
  font-size: clamp(20px, 2.6cqw, 52px);
}
.cl-cloud--display .cl-cloud__word--r3 {
  font-size: clamp(24px, 3.4cqw, 68px);
}
.cl-cloud--display .cl-cloud__word--r4 {
  font-size: clamp(28px, 4.2cqw, 84px);
}
.cl-cloud--display .cl-cloud__word--r5 {
  font-size: clamp(32px, 5cqw, 100px);
}

.cl-cloud__weight {
  font-family: var(--font-mono);
  font-size: 0.42em;
  color: var(--text-tertiary);
  padding-inline-start: 0.3em;
  vertical-align: super;
}

.cl-cloud__empty {
  font-size: 14px;
  color: var(--text-tertiary);
}

.cl-cloud--display .cl-cloud__empty {
  font-size: clamp(14px, 1.6cqw, 32px);
}

/* ── Group task ────────────────────────────────── */
.cl-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.cl-group__prompt {
  font-size: 16px;
  font-weight: 500;
  color: var(--text-color);
}

.cl-group--display .cl-group__prompt {
  font-size: clamp(18px, 2.4cqw, 48px);
}

.cl-group__meta {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text-tertiary);
}

.cl-group--display .cl-group__meta {
  font-size: clamp(13px, 1.4cqw, 28px);
}

.cl-group__submissions {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.cl-group--display .cl-group__submissions {
  gap: 1cqw;
}

.cl-group__submission {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
}

.cl-group__text {
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-color);
  min-inline-size: 0;
  overflow-wrap: anywhere;
}

.cl-group--display .cl-group__text {
  font-size: clamp(15px, 1.8cqw, 36px);
}

.cl-group--display .cl-group__badge {
  font-size: clamp(13px, 1.4cqw, 28px);
  min-inline-size: 3cqw;
  padding: 0.3cqw 1cqw;
}

.cl-group__mine {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md);
  background: var(--primary-light);
  border-radius: var(--radius-md);
}

.cl-group__mine-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  font-size: 18px;
  font-weight: 500;
  color: var(--text-color);
}

.cl-group__mode {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
}

.cl-group__members {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-md);
}

.cl-group__member {
  font-size: 15px;
  color: var(--text-color);
}

.cl-group__empty {
  font-size: 14px;
  color: var(--text-tertiary);
}

/* ── Sandbox ───────────────────────────────────── */
.cl-sandbox {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.cl-sandbox__eyebrow {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.cl-sandbox--display .cl-sandbox__eyebrow {
  font-size: clamp(12px, 1.1cqw, 22px);
}

.cl-sandbox__link,
.cl-sandbox__url {
  font-family: var(--font-mono);
  font-size: 16px;
  color: var(--primary-color);
  overflow-wrap: anywhere;
}

.cl-sandbox--display .cl-sandbox__url {
  font-size: clamp(18px, 2.2cqw, 44px);
}

/* ── Embed ─────────────────────────────────────── */
.cl-embed {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  min-block-size: 0;
  flex: 1 1 auto;
}

/* The address is printed above the frame on the display too. A refused frame and
   a frame with no network paint the same anonymous grey card, so the way out has
   to be something a person can read off the screen. */
.cl-embed__url {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text-tertiary);
  overflow-wrap: anywhere;
}

.cl-embed--display .cl-embed__url {
  font-size: clamp(13px, 1.2cqw, 24px);
}

.cl-embed__frame {
  inline-size: 100%;
  flex: 1 1 auto;
  min-block-size: 40cqw;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
}

/* ── Breakpoints: 1024 tablet, 767 mobile ────────
   Only the phone surface has any: the display surface is inside a container
   query and must never branch on the viewport. */
@media (max-width: 767px) {
  .slide--phone {
    padding: var(--space-md);
  }

  .cl-cloud__word--r4 {
    font-size: 24px;
  }

  .cl-cloud__word--r5 {
    font-size: 28px;
  }
}
