/* ── Interactive course-builder blocks — shared by the builder and the player.
   Config lives as JSON in data-* attributes so it survives the builder's
   innerHTML round-trip (blocks persist as {type, html}).

   Never put an inline `display` on .blk-editor-only — an inline style beats
   this rule and would leak builder chrome into the learner view. */

/* Every interactive block reads its accent from --blk-accent, so the generic
   Block Settings panel can retheme any block without touching its markup. */
.tb-block, .ac-block, .kc-block, .lb-block, .sc-block, .au-block, .im-block, .nt-block,
.hd-block, .ls-block, .tc-block, .st-block, .dv-block,
.vd-block, .gl-block, .qt-block, .rv-block, .pr-block,
.tr-block, .lg-block, .cn-block, .so-block, .it-block {
  --blk-accent: #2563eb;
  --blk-accent-dark: #1d4ed8;
}

.blk-editor-only { display: none !important; }
.blk-toolbar { align-items: center; gap: 10px; margin-bottom: 12px; }
.blk-edit-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 20px; border: 1px solid #dbe4ea;
  background: #fff; font-family: inherit; font-size: 12px; font-weight: 600;
  color: #3d4c5c; cursor: pointer;
}
.blk-edit-btn:hover { border-color: var(--blk-accent, #2563eb); color: var(--blk-accent, #2563eb); }

/* ── Tabs ─────────────────────────────────────────────── */
/* A card, like the accordion — the two are siblings and should read as such.
   Styled entirely from the existing .tb-strip / .tb-pane elements so tabs
   already saved in a lesson pick this up without a markup change (the
   .tb-strip-wrap + prev/next buttons are added automatically by
   tabsRender()/ensureTbNav() on first render, not baked into stored html). */
.tb-strip-wrap {
  display: flex; align-items: stretch;
  background: #f7f9fa;
  border: 1px solid #e5e9ec; border-bottom: none;
  border-radius: 12px 12px 0 0;
  overflow: hidden;   /* clips the strip's own square corners to match */
}
.tb-strip {
  display: flex; flex: 1; min-width: 0;
  overflow-x: auto; scroll-behavior: smooth;
}
.tb-strip::-webkit-scrollbar { height: 0; }
/* Rise-style: full-height chevron buttons that scroll the strip, shown only
   when there are enough tabs to overflow the block's width. */
.tb-nav-btn {
  flex: 0 0 auto; display: none; align-items: center; justify-content: center;
  width: 32px; border: none; background: var(--blk-accent, #2563eb); color: #fff;
  font-size: 15px; cursor: pointer;
}
.tb-nav-btn.show { display: flex; }
.tb-nav-btn:hover:not(:disabled) { background: var(--blk-accent-dark, #1d4ed8); }
.tb-nav-btn:disabled { opacity: .35; cursor: not-allowed; }
.tb-tab {
  /* A reasonable fixed-ish width: with few tabs they still grow to share the
     strip evenly (flex-grow:1); once enough tabs push past their min-width,
     the strip scrolls instead of squeezing labels unreadably thin. */
  flex: 1 1 130px; min-width: 130px;
  display: flex; align-items: center; justify-content: center;
  padding: 15px 14px; border: none; background: transparent;
  font-family: inherit; font-size: 15px; font-weight: 700; color: #8896a4;
  line-height: 1.35; text-align: center;
  /* `anywhere` (not `break-word`): it also shrinks the flex min-content size,
     so a long single word can never push the strip wider than its box. */
  cursor: pointer; white-space: normal; overflow-wrap: anywhere;
  border-right: 1px solid #e5e9ec;
  transition: background .12s ease, color .12s ease;
}
/* With 5+ tabs, cap each at exactly a quarter of the strip instead of
   however many happen to fit at 130px — so it's always "4 visible, scroll
   for the rest" regardless of how wide the block is, not a number that
   quietly shifts with label length or screen size. min-width still wins on
   narrow screens (mobile), where a strict 25% would otherwise squeeze
   labels unreadably thin — that just means fewer than 4 show there, same
   as the existing overflow behavior already handles. */
.tb-strip:has(.tb-tab:nth-child(5)) .tb-tab {
  flex: 0 0 25%; min-width: 130px;
}
/* 768px everywhere in this file, not an arbitrary value — it must match
   templates/lessons/player.html's own mobile breakpoint. A narrower value
   here previously created a 641-767px dead zone where the player already
   switched to its mobile layout but these blocks' own grids/floats hadn't
   collapsed yet, e.g. a "full width" image rendering at 100% of one column
   of a still-two-column split — half the canvas, not the full width. */
@media (max-width: 768px) {
  .tb-tab { font-size: 12.5px; padding: 11px 8px; }
}
/* No separator after the last tab — it would hang in the empty strip. */
.tb-tab:last-child { border-right: none; }
.tb-tab:hover:not(.active) { background: #f1f4f7; color: #3d4c5c; }
/* The active tab merges into the pane below it — that is what makes a tab
   strip read as tabs rather than as a row of links. */
.tb-tab.active {
  background: #fff; color: var(--blk-accent, #2563eb);
  box-shadow: inset 0 3px 0 var(--blk-accent, #2563eb);
}
.tb-pane {
  font-size: 16px; color: #3d4c5c; line-height: 1.8; min-height: 60px;
  background: #fff; padding: 22px 24px;
  border: 1px solid #e5e9ec; border-top: none;
  border-radius: 0 0 12px 12px;
}
/* Crossfade the pane when the learner switches tabs (added by tabsSwitch). */
@keyframes tb-fade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.tb-pane.tb-fade { animation: tb-fade .28s ease; }
@media (prefers-reduced-motion: reduce) { .tb-pane.tb-fade { animation: none; } }

/* ── Accordion ────────────────────────────────────────── */
.ac-list { border: 1px solid #e5e9ec; border-radius: 12px; overflow: hidden; }
.ac-item + .ac-item { border-top: 1px solid #e5e9ec; }
/* Rise-style accent bar down the left edge of the open item. An overlay
   pseudo-element, because the head/body child backgrounds would paint over
   anything drawn on the item box itself; absolute, so nothing shifts. */
.ac-item { position: relative; }
.ac-item::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--blk-accent, #2563eb); opacity: 0; transition: opacity .25s ease;
  pointer-events: none; z-index: 1;
}
.ac-item.open::before { opacity: 1; }
.ac-head {
  width: 100%; text-align: left; border: none; background: #f7f9fa;
  padding: 18px 22px; display: flex; justify-content: space-between; align-items: center;
  /* Title clearly outranks the 15px body (Rise runs ~1.25× body), but stays
     under the 20px H4 so standalone headings still outrank accordion rows. */
  cursor: pointer; font-family: inherit; font-size: 18px; font-weight: 700; color: #1b2733;
  transition: background .18s ease;
}
.ac-head:hover { background: #f1f4f7; }
/* The open item reads as ONE white panel — header merges with its body, and
   the accent bar outlines the whole unit (Rise-style). */
.ac-item.open .ac-head, .ac-item.open .ac-head:hover { background: #fff; }
.ac-head:focus-visible { outline: 2px solid var(--blk-accent, #2563eb); outline-offset: -2px; }
/* Rise-style +/× toggle: the plus rotates 45° when its item opens. */
.ac-head i { color: #8896a4; font-size: 18px; transition: transform .2s ease, color .2s ease; }
.ac-item.open .ac-head i { transform: rotate(45deg); color: var(--blk-accent, #2563eb); }
/* Smooth expand via grid-template-rows 0fr → 1fr (animates, unlike height:auto).
   The single child clips to 0 while collapsed, so its padding collapses too. */
.ac-body {
  display: grid; grid-template-rows: minmax(0, 0fr);
  transition: grid-template-rows .32s cubic-bezier(.4, 0, .2, 1);
  background: #fff; font-size: 15px; color: #3d4c5c; line-height: 1.75;
}
/* minmax(0, …) forces the track minimum to 0, so a collapsed row does not keep
   the child's padding (a bare 0fr resolves its auto-min to min-content). */
.ac-item.open .ac-body { grid-template-rows: minmax(0, 1fr); }
/* .ac-clip is the grid child: it clips, and carries NO padding of its own, so
   the row collapses to a true 0. The padding lives on .ac-pad, inside the clip. */
.ac-body > .ac-clip { overflow: hidden; min-height: 0; }
/* No top padding: the open header (now white) already provides the breathing
   room above, so the body starts right where the title's panel continues. */
.ac-pad { padding: 4px 22px 24px; }
@media (prefers-reduced-motion: reduce) {
  .ac-body { transition: none; }
}

/* ── Shared item media (Accordion items + Tabs panes) ─── */
/* Floated for left/right, so the row needs a clearfix. */
.blk-media::after { content: ''; display: block; clear: both; }
/* Rich item bodies: numbered/bulleted lists with a proper hanging indent. */
.blk-text ol, .blk-text ul { margin: 10px 0; padding-left: 26px; }
.blk-text li { margin: 6px 0; }
/* `inherit`, not a fixed dark: bold inside author-colored text keeps its color. */
.blk-text strong, .blk-text b { font-weight: 700; color: inherit; }
/* Single-image lightbox reuse: no prev/next arrows for a one-image set. */
.gl-lightbox.single .gl-lb-prev, .gl-lightbox.single .gl-lb-next { display: none; }

/* Per-item video (accordion/tabs): responsive 16:9 frame. */
.blk-video {
  position: relative; aspect-ratio: 16 / 9; margin: 14px 0 4px;
  border-radius: 10px; overflow: hidden; background: #000;
}
.blk-video video, .blk-video iframe {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
}
.blk-img { display: block; border-radius: 10px; max-width: 100%; }
/* Same Rise sizing as the Image block: natural size when smaller than the
   slot (small graphics never upscale to a blur). Big images take a generous
   85% of the block — substantial, with a little air on each side. */
.blk-img.a-full   { width: auto; max-width: 85%; margin: 0 auto 14px; }
.blk-img.a-center { width: auto; max-width: 70%; margin: 0 auto 14px; }
.blk-img.a-left   { width: 40%; float: left;  margin: 4px 18px 12px 0; }
.blk-img.a-right  { width: 40%; float: right; margin: 4px 0 12px 18px; }
/* Accordion items put the text first (Rise-style): a block image that follows
   the text carries its gap on top instead of underneath. */
.blk-text + .blk-img.a-full   { margin: 14px auto 0; }
.blk-text + .blk-img.a-center { margin: 14px auto 0; }
@media (max-width: 768px) {
  /* A 40% float on a phone leaves an unreadable column of text beside it. */
  .blk-img.a-left, .blk-img.a-right, .blk-img.a-center {
    width: auto; max-width: 100%; float: none; margin: 0 auto 14px;
  }
  .blk-text + .blk-img { margin: 14px auto 0; }
}

/* ── Knowledge Check ──────────────────────────────────── */
.kc-block { border: 1px solid #e5e9ec; border-radius: 16px; padding: 26px; background: #fafcfd; }
.kc-eyebrow { font-size: 10px; text-transform: uppercase; letter-spacing: 1px; color: var(--blk-accent, #2563eb); font-weight: 800; margin-bottom: 10px; }
/* Top-right pill announcing a must-pass question (Rise-style). */
.kc-require-badge {
  display: flex; align-items: center; gap: 6px;
  width: fit-content; margin-left: auto; margin-bottom: 10px;
  padding: 6px 12px; border-radius: 8px; background: #eef1f4;
  font-family: 'DM Sans', system-ui, sans-serif;
  font-size: 12.5px; font-weight: 600; color: #3d4c5c;
}
.kc-require-badge i { color: #64748b; font-size: 14px; }
.kc-question { font-size: 18px; font-weight: 700; color: #1b2733; margin-bottom: 18px; line-height: 1.5; }
.kc-options { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.kc-opt {
  display: flex; align-items: center; gap: 12px; padding: 13px 16px;
  border: 1px solid #e5e9ec; background: #fff; border-radius: 10px;
  cursor: pointer; font-size: 15px; color: #3d4c5c; transition: border-color .12s, background .12s;
}
.kc-opt:hover { border-color: #bfd7fe; }
.kc-opt input { accent-color: var(--blk-accent, #2563eb); flex-shrink: 0; }
/* States only appear AFTER the learner submits — the answer is never pre-revealed. */
.kc-block.answered .kc-opt { cursor: default; }
.kc-block.answered .kc-opt.correct { border-color: #86efac; background: #f0fdf4; color: #15803d; font-weight: 600; }
.kc-block.answered .kc-opt.wrong   { border-color: #fecaca; background: #fef2f2; color: #b91c1c; }
.kc-mark { margin-left: auto; font-size: 16px; display: none; }
.kc-block.answered .kc-opt.correct .kc-mark,
.kc-block.answered .kc-opt.wrong .kc-mark { display: inline-flex; }

.kc-btn {
  padding: 10px 22px; border-radius: 20px; border: none; background: var(--blk-accent, #2563eb); color: #fff;
  font-family: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
}
.kc-btn:hover { background: var(--blk-accent-dark, #1d4ed8); }
.kc-btn:disabled { opacity: .5; cursor: not-allowed; }
.kc-btn.kc-retry { background: #b45309; }
.kc-btn.kc-retry:hover { background: #92400e; }
.kc-feedback {
  display: none; margin-top: 14px; padding: 13px 16px; border-radius: 10px;
  font-size: 14px; line-height: 1.6;
}
.kc-feedback.show { display: block; }
.kc-feedback.ok  { background: #f0fdf4; border: 1px solid #bbf7d0; color: #15803d; }
.kc-feedback.bad { background: #fef2f2; border: 1px solid #fecaca; color: #b91c1c; }

/* ── Link button ──────────────────────────────────────── */
.lb-block { text-align: center; padding: 12px 0; }
.lb-btn {
  display: inline-block; padding: 13px 36px; border-radius: 26px;
  background: var(--blk-accent, #2563eb); color: #fff; font-size: 15px; font-weight: 700;
  text-decoration: none; letter-spacing: .2px;
}
.lb-btn:hover { background: var(--blk-accent-dark, #1d4ed8); color: #fff; }
/* No href — the author has not set a URL yet. Don't pretend it's clickable. */
.lb-btn:not([href]) { opacity: .55; cursor: not-allowed; }
.lb-btn:not([href]):hover { background: var(--blk-accent, #2563eb); }

/* ── Audio ────────────────────────────────────────────── */
.au-block { background: #fffbeb; border: 1px solid #fde68a; border-radius: 12px; padding: 18px; }
.au-head { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.au-head i { font-size: 26px; color: #d97706; flex-shrink: 0; }
.au-title { font-size: 14px; font-weight: 700; color: #92400e; }
.au-block audio { width: 100%; display: block; }
.au-caption { font-size: 12px; color: #92400e; margin-top: 8px; }
.au-empty { font-size: 13px; color: #92400e; }

/* ── AI Narration (attached to a paragraph/heading block) ───────────────
   Collapsed accordion, not an always-open card: a lesson with narration on
   several paragraphs would otherwise stack several always-expanded audio
   players and dominate the page. Same grid-template-rows collapse technique
   as the real Accordion block above, scoped under its own np- classes so it
   stays a single independent toggle (no "closes siblings" group behavior). */
.np-block { border: 1px solid #ddd6fe; border-radius: 12px; margin-top: 14px; overflow: hidden; }
.np-head {
  display: flex; align-items: center; gap: 8px; padding: 10px 14px;
  background: #f5f3ff; cursor: pointer; user-select: none;
  font-size: 12px; font-weight: 700; color: #6d28d9; text-transform: uppercase; letter-spacing: .4px;
  transition: background .15s ease;
}
.np-head:hover { background: #ede9fe; }
.np-head:focus-visible { outline: 2px solid var(--blk-accent, #2563eb); outline-offset: -2px; }
.np-head i.ti-microphone-2 { font-size: 15px; }
.np-head span { flex: 1; }
.np-head .np-chevron { color: #8b7fd6; font-size: 16px; transition: transform .2s ease; }
.np-block.open .np-head .np-chevron { transform: rotate(45deg); }
.np-body { display: grid; grid-template-rows: minmax(0, 0fr); transition: grid-template-rows .28s cubic-bezier(.4, 0, .2, 1); }
.np-block.open .np-body { grid-template-rows: minmax(0, 1fr); }
.np-body > .np-clip { overflow: hidden; min-height: 0; }
.np-pad { padding: 14px 16px; background: #faf9ff; }
.np-pad audio { width: 100%; display: block; }
@media (prefers-reduced-motion: reduce) { .np-body { transition: none; } }

/* ── Custom audio player (replaces native <audio controls>, whose look
   varies wildly across browsers — Firefox renders a dark pill, Chrome a
   light one, Safari yet another; this keeps it consistent everywhere) ── */
.ap-player { display: flex; align-items: center; gap: 10px; background: #f1f3f5; border-radius: 999px; padding: 6px 14px 6px 6px; }
.ap-btn { display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 50%; border: none; background: transparent; color: #475569; cursor: pointer; flex-shrink: 0; font-size: 15px; transition: background .15s; padding: 0; }
.ap-btn:hover { background: rgba(0,0,0,.06); }
.ap-play { width: 32px; height: 32px; font-size: 17px; color: #1e293b; }
.ap-time { font-size: 12.5px; color: #64748b; white-space: nowrap; font-variant-numeric: tabular-nums; min-width: 78px; }
.ap-seek { flex: 1; display: flex; align-items: center; height: 20px; cursor: pointer; }
.ap-seek-track { position: relative; width: 100%; height: 4px; background: #d8dce1; border-radius: 4px; }
.ap-seek-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0%; background: #94a3b8; border-radius: 4px; pointer-events: none; }
.ap-seek-thumb { position: absolute; top: 50%; left: 0%; width: 12px; height: 12px; border-radius: 50%; background: #475569; transform: translate(-50%,-50%); pointer-events: none; }
.ap-menu-wrap { position: relative; flex-shrink: 0; }
.ap-menu { display: none; position: absolute; bottom: calc(100% + 8px); right: 0; background: #fff; border: 1px solid #e2e4e8; border-radius: 10px; box-shadow: 0 8px 24px rgba(15,23,42,.14); padding: 6px; min-width: 110px; z-index: 20; }
.ap-menu.open { display: block; }
.ap-menu-label { font-size: 10px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: .5px; padding: 4px 8px; }
.ap-speed-opt { display: block; width: 100%; text-align: left; padding: 6px 8px; border: none; background: none; border-radius: 6px; font-size: 13px; color: #374151; cursor: pointer; }
.ap-speed-opt:hover { background: #f4f5f7; }
.ap-speed-opt.active { color: #2563eb; font-weight: 700; }

/* ── Scenario ─────────────────────────────────────────── */
.sc-block { background: #f0f6ff; border: 1px solid #bfdbfe; border-radius: 16px; padding: 24px; }
.sc-eyebrow { display: flex; align-items: center; gap: 8px; margin-bottom: 14px;
  font-size: 11px; text-transform: uppercase; letter-spacing: 1px; font-weight: 800; color: var(--blk-accent, #2563eb); }
.sc-prompt { font-size: 16px; color: #1e3a5f; line-height: 1.8; margin-bottom: 16px; }
.sc-options { display: flex; flex-direction: column; gap: 9px; }
.sc-opt {
  width: 100%; text-align: left; padding: 12px 16px; border-radius: 10px;
  border: 1px solid #bfdbfe; background: #fff; color: #1d4ed8;
  font-family: inherit; font-size: 14px; cursor: pointer; transition: .12s;
  display: flex; align-items: center; gap: 10px;
}
.sc-opt:hover { border-color: #60a5fa; }
.sc-opt .sc-mark { margin-left: auto; display: none; font-size: 16px; }
/* Outcome styling only after the learner picks — never pre-revealed. */
.sc-block.answered .sc-opt { cursor: default; }
.sc-block.answered .sc-opt.correct { border-color: #86efac; background: #f0fdf4; color: #15803d; font-weight: 600; }
.sc-block.answered .sc-opt.wrong { border-color: #fecaca; background: #fef2f2; color: #b91c1c; }
.sc-block.answered .sc-opt.correct .sc-mark,
.sc-block.answered .sc-opt.wrong .sc-mark { display: inline-flex; }
.sc-feedback { display: none; margin-top: 14px; padding: 13px 16px; border-radius: 10px; font-size: 14px; line-height: 1.6; }
.sc-feedback.show { display: block; }
.sc-feedback.ok { background: #f0fdf4; border: 1px solid #bbf7d0; color: #15803d; }
.sc-feedback.bad { background: #fef2f2; border: 1px solid #fecaca; color: #b91c1c; }
.sc-retry { margin-top: 12px; background: none; border: none; color: var(--blk-accent, #2563eb);
  font-family: inherit; font-size: 13px; font-weight: 600; cursor: pointer; display: none; }
.sc-block.answered .sc-retry { display: inline-block; }

/* ── Image ────────────────────────────────────────────── */
.im-block figure { margin: 0; }
/* Rise-style sizing: never upscale a small image (it renders at natural size,
   centered, sharp); big images fill the column width — tall is fine, exactly
   as Rise renders them. Click-to-zoom covers detail viewing. */
.im-block img {
  display: block; width: auto; max-width: 100%;
  margin: 0 auto; border-radius: 12px;
}
/* Full width: stretch the image (even a small one) to fill the column,
   instead of rendering it at natural size. */
.im-block.im-full img { width: 100%; }
/* "Full width" actually bleeds past the reading column to the pane's real
   edges (see imFitFull in blocks.js) — a JS measurement, since a fixed CSS
   trick can't know the pane's edges (a sidebar keeps it off-viewport-center).
   Until that JS runs, the browser has nothing to paint the image at but its
   plain in-column width, which is what it *would* show — then visibly jump
   to the bled width a moment later. Starting hidden (but still laid out, so
   the JS can measure it) and only revealing once the correct width/margin
   are already set skips straight to the final result instead of flashing
   the wrong one first. */
/* JS normally reveals this within milliseconds (see .im-fitted below) — but
   if blocks.js fails to load at all, this still forces it visible (at the
   plain in-column width, no bleed) after a beat rather than hiding a real,
   already-uploaded image forever. */
@keyframes im-reveal-fallback { to { visibility: visible; } }
.im-block.im-full .im-body { visibility: hidden; animation: im-reveal-fallback 0s 1.5s forwards; }
.im-block.im-full.im-fitted .im-body { visibility: visible; animation: none; }
.im-block figcaption { font-size: 13px; color: #8896a4; text-align: center; padding-top: 10px; }
/* The figcaption always exists in the DOM (see imRender) so the builder can
   wire it for inline editing; a genuinely empty one is invisible to a
   learner or in preview mode — only the builder's edit state shows it. */
.im-block figcaption:empty:not([contenteditable]) { display: none; padding: 0; }
.im-block figcaption[contenteditable] { outline: none; border-radius: 6px; cursor: text; min-height: 1.4em; }
.im-block figcaption[contenteditable]:hover { background: #f5f8fa; }
.im-block figcaption[contenteditable]:focus { background: #f0f6ff; }
.im-block figcaption[contenteditable]:empty::before { content: 'Add a caption…'; color: #a5b1bc; }
body.preview-mode .im-block figcaption { background: transparent !important; }
.im-empty {
  border: 2px dashed #cdd6dd; border-radius: 12px; padding: 44px 32px;
  text-align: center; color: #a5b1bc; font-size: 13px;
}

/* ── Headings (level is a real tag: h2 / h3 / h4) ─────── */
.hd-block { color: #1b2733; font-weight: 800; letter-spacing: -.3px; margin: 0; outline: none; }
h2.hd-block { font-size: 32px; line-height: 1.25; }
h3.hd-block { font-size: 25px; line-height: 1.3; }
h4.hd-block { font-size: 20px; line-height: 1.4; }

/* ── Lists (one structure, marker chosen by class) ────── */
.ls-block {
  /* The whole list is inset from the margin, Rise-style, so the markers don't
     hug the block edge. Numbered list re-indents its own items on top of this. */
  list-style: none; padding-left: 20px; margin: 0;
  font-size: 17px; color: #3d4c5c; line-height: 1.9; outline: none;
}
/* Hanging indent: marker inset, text hangs at a consistent point, and items get
   real vertical breathing room between them. */
.ls-block li { position: relative; padding-left: 38px; margin-bottom: 24px; }
.ls-block li:last-child { margin-bottom: 0; }
.ls-block li::before {
  position: absolute; left: 8px; top: 0;
  /* An explicit marker colour wins; otherwise follow the block accent. */
  color: var(--ls-color, var(--blk-accent, #2563eb)); font-weight: 700;
}
/* A drawn dot (not a glyph) so size and vertical centring on the first line are
   exact — top offset optically centres an 8px dot on a 17px/1.9 line. */
.ls-block.m-bullet li::before {
  content: ''; width: 8px; height: 8px; border-radius: 50%;
  background: var(--ls-color, var(--blk-accent, #2563eb));
  left: 8px; top: .68em;
}
.ls-block.m-check  li::before { content: '\2713'; font-size: 15px; top: .1em; }
.ls-block.m-arrow  li::before { content: '\2192'; top: .05em; }
/* Numbered list: a filled circular badge, not a bare "1." — with the indent and
   vertical rhythm to match. `min-width` + pill radius so 10+ doesn't get clipped. */
.ls-block.m-number { counter-reset: lsc; }
.ls-block.m-number li {
  counter-increment: lsc;
  padding-left: 62px;
  margin-bottom: 22px;
  line-height: 1.75;
}
.ls-block.m-number li:last-child { margin-bottom: 0; }
.ls-block.m-number li::before {
  content: counter(lsc);
  top: 3px;                       /* optical align with the first line of text */
  min-width: 34px; height: 34px; padding: 0 6px; border-radius: 17px;
  background: var(--ls-color, var(--blk-accent, #2563eb));
  color: #fff; font-size: 15px; font-weight: 700; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  box-sizing: border-box;
}

/* ── Two column ───────────────────────────────────────── */
.tc-block { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; }
.tc-block > div { font-size: 16px; color: #3d4c5c; line-height: 1.8; min-height: 60px; outline: none; }
@media (max-width: 768px) { .tc-block { grid-template-columns: 1fr !important; } }

/* ── Statement ────────────────────────────────────────── */
.st-block { text-align: center; }
.st-block .st-text { outline: none; }
.st-block.v-framed { padding: 34px 0; }
.st-block.v-framed .st-rule { width: 56px; height: 3px; background: var(--blk-accent, #2563eb); margin: 0 auto; border-radius: 2px; }
.st-block.v-framed .st-rule:first-child { margin-bottom: 26px; }
.st-block.v-framed .st-rule:last-child  { margin-top: 26px; }
.st-block.v-framed .st-text {
  font-size: 26px; font-weight: 800; color: #1b2733; line-height: 1.45;
  letter-spacing: -.3px; max-width: 560px; margin: 0 auto;
}
.st-block.v-dark { padding: 28px 32px; background: #1b2733; border-radius: 14px; }
.st-block.v-dark .st-rule { display: none; }
.st-block.v-dark .st-text { font-size: 22px; font-weight: 700; color: #fff; line-height: 1.55; }

/* ── Divider ──────────────────────────────────────────── */
.dv-block { padding: 16px 0; display: flex; align-items: center; gap: 18px; }
/* Without a number the two rules must join seamlessly — no phantom gap. */
.dv-block:not(.s-number) { gap: 0; }
.dv-block hr { flex: 1; border: none; border-top: 1px solid #dbe4ea; margin: 0; }
.dv-block.s-dashed hr { border-top-style: dashed; }
.dv-block.s-dotted hr { border-top-style: dotted; }
.dv-block .dv-num {
  width: 46px; height: 46px; border-radius: 50%; flex-shrink: 0;
  background: var(--blk-accent, #2563eb); color: #fff;
  display: none; align-items: center; justify-content: center;
  font-size: 18px; font-weight: 800; outline: none;
}
.dv-block.s-number .dv-num { display: flex; }

/* ── Note / Callout variants ──────────────────────────── */
.nt-block { display: flex; gap: 14px; padding: 20px 22px; border-radius: 0 12px 12px 0; border-left: 4px solid; }
.nt-block i { font-size: 22px; flex-shrink: 0; margin-top: 2px; }
.nt-body { font-size: 15px; line-height: 1.7; flex: 1; }
.nt-block.v-info    { background: #f0f6ff; border-color: #2563eb; }
.nt-block.v-info i, .nt-block.v-info .nt-body { color: #1e3a5f; }
.nt-block.v-info i  { color: #2563eb; }
.nt-block.v-success { background: #f0fdf4; border-color: #16a34a; }
.nt-block.v-success i { color: #16a34a; } .nt-block.v-success .nt-body { color: #14532d; }
.nt-block.v-warning { background: #fffbeb; border-color: #d97706; }
.nt-block.v-warning i { color: #d97706; } .nt-block.v-warning .nt-body { color: #78350f; }
.nt-block.v-danger  { background: #fef2f2; border-color: #dc2626; }
.nt-block.v-danger i { color: #dc2626; } .nt-block.v-danger .nt-body { color: #7f1d1d; }

/* ── Video ────────────────────────────────────────────── */
.vd-title { font-size: 17px; font-weight: 700; color: #1b2733; margin-bottom: 10px; line-height: 1.4; }
.vd-block { --vd-aspect: 16 / 9; }
.vd-block.a-16x9 { --vd-aspect: 16 / 9; }
.vd-block.a-4x3  { --vd-aspect: 4 / 3; }
.vd-block.a-1x1  { --vd-aspect: 1 / 1; }
.vd-body {
  position: relative; width: 100%; aspect-ratio: var(--vd-aspect);
  background: #0f172a; border-radius: 12px; overflow: hidden;
}
.vd-media { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }
video.vd-media { object-fit: contain; background: #000; }
.vd-empty {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  color: #94a3b8; font-size: 13px; text-align: center; padding: 20px;
  background: #f7f9fa; border: 2px dashed #dbe4ea; border-radius: 12px;
}
.vd-empty i { font-size: 34px; }
.vd-caption { margin-top: 10px; font-size: 13px; color: #8896a4; text-align: center; }

/* ── Gallery ──────────────────────────────────────────── */
/* `align-items: start` — a tile without a caption must not be stretched to the
   height of a captioned neighbour, which leaves a blank stub under its image. */
.gl-grid { --gl-cols: 3; display: grid; grid-template-columns: repeat(var(--gl-cols), 1fr); gap: 12px; align-items: start; }
@media (max-width: 768px) { .gl-grid { grid-template-columns: repeat(2, 1fr); } }
.gl-item {
  margin: 0; cursor: zoom-in; border-radius: 10px; overflow: hidden;
  background: #f1f5f9; outline: none;
}
.gl-item:focus-visible { box-shadow: 0 0 0 3px var(--blk-accent, #2563eb); }
.gl-item img { width: 100%; height: 160px; object-fit: cover; display: block; transition: transform .3s ease; }
.gl-item:hover img { transform: scale(1.04); }
.gl-item figcaption { padding: 8px 10px; font-size: 12px; color: #64748b; background: #fff; }
.gl-empty {
  grid-column: 1 / -1; padding: 34px; text-align: center; color: #94a3b8; font-size: 13px;
  border: 2px dashed #dbe4ea; border-radius: 10px;
}

.gl-lightbox {
  position: fixed; inset: 0; z-index: 9999; display: none;
  align-items: center; justify-content: center; gap: 8px;
  background: rgba(6, 12, 22, .92); padding: 40px 16px;
}
.gl-lightbox.open { display: flex; }
.gl-lb-fig { margin: 0; max-width: min(1100px, 90vw); max-height: 86vh; display: flex; flex-direction: column; gap: 12px; }
.gl-lb-fig img { max-width: 100%; max-height: 78vh; object-fit: contain; border-radius: 8px; display: block; margin: 0 auto; }
.gl-lb-fig figcaption { color: #cbd5e1; font-size: 14px; text-align: center; }
.gl-lightbox button {
  border: none; background: rgba(255, 255, 255, .1); color: #fff; cursor: pointer;
  border-radius: 50%; width: 44px; height: 44px; flex-shrink: 0;
  font-size: 26px; line-height: 1; display: flex; align-items: center; justify-content: center;
}
.gl-lightbox button:hover { background: rgba(255, 255, 255, .22); }
.gl-lb-close { position: absolute; top: 18px; right: 18px; font-size: 30px !important; }

/* ── Quote ────────────────────────────────────────────── */
.qt-block { padding: 26px 0; text-align: center; }
.qt-mark { font-size: 34px; color: var(--blk-accent, #2563eb); opacity: .3; display: block; margin-bottom: 12px; }
.qt-text {
  font-family: Merriweather, Georgia, serif; font-style: italic; font-weight: 300;
  font-size: 22px; color: #1b2733; line-height: 1.65;
  max-width: 600px; margin: 0 auto; border: none; padding: 0;
}
.qt-by { display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: 20px; }
.qt-avatar { width: 44px; height: 44px; border-radius: 50%; overflow: hidden; flex-shrink: 0; background: #e2e8f0; }
.qt-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.qt-meta { text-align: left; }
.qt-name { font-size: 14px; font-weight: 700; color: #1b2733; }
.qt-role { font-size: 12px; color: #8896a4; }
.qt-role:empty { display: none; }

/* ── Click to Reveal ──────────────────────────────────── */
.rv-list { --rv-cols: 2; display: grid; grid-template-columns: repeat(var(--rv-cols), 1fr); gap: 14px; }
@media (max-width: 768px) { .rv-list { grid-template-columns: 1fr; } }
.rv-item {
  display: block; width: 100%; text-align: left; cursor: pointer;
  font-family: inherit; padding: 20px 22px; min-height: 120px;
  background: #f7f9fa; border: 1px solid #e5e9ec; border-left: 3px solid var(--blk-accent, #2563eb);
  border-radius: 12px; transition: background .18s ease, box-shadow .18s ease;
}
.rv-item:hover { background: #fff; box-shadow: 0 4px 14px rgba(15, 23, 42, .07); }
.rv-title { display: block; font-size: 16px; font-weight: 700; color: #1b2733; line-height: 1.4; }
.rv-hint {
  display: flex; align-items: center; gap: 6px; margin-top: 10px;
  font-size: 12px; font-weight: 600; color: var(--blk-accent, #2563eb);
}
.rv-body {
  display: none; margin-top: 12px; padding-top: 12px; border-top: 1px solid #e5e9ec;
  font-size: 15px; color: #3d4c5c; line-height: 1.7; white-space: normal;
}
.rv-item.open { background: #fff; }
.rv-item.open .rv-hint { display: none; }
.rv-item.open .rv-body { display: block; }
.rv-empty { padding: 30px; text-align: center; color: #94a3b8; font-size: 13px; border: 2px dashed #dbe4ea; border-radius: 10px; }

@media (prefers-reduced-motion: reduce) {
  .gl-item img, .rv-item { transition: none; }
}

/* ── Process / Timeline ───────────────────────────────── */
.pr-empty { padding: 30px; text-align: center; color: #94a3b8; font-size: 13px; border: 2px dashed #dbe4ea; border-radius: 10px; }

/* Shared card body */
.pr-card .pr-label {
  font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: 1px;
  color: var(--blk-accent, #2563eb); margin-bottom: 6px;
}
.pr-card .pr-title { font-size: 17px; font-weight: 700; color: #1b2733; line-height: 1.4; }
.pr-card .pr-text { margin-top: 8px; font-size: 15px; color: #3d4c5c; line-height: 1.75; }
.pr-card .pr-img { width: 100%; max-height: 260px; object-fit: cover; border-radius: 10px; display: block; margin-top: 12px; }
/* .pr-label and .pr-text always render, even empty (the builder wires direct
   inline editing onto them) — collapse a genuinely empty one for a learner
   or in preview (preview strips the contenteditable attribute entirely), so
   its margin doesn't leave a stray gap where a step has no eyebrow label or
   no body text. */
.pr-card .pr-label:empty:not([contenteditable]),
.pr-card .pr-text:empty:not([contenteditable]) { display: none; margin: 0; }

/* Timeline — every step visible on a vertical rail */
.pr-rail { list-style: none; margin: 0; padding: 0; position: relative; }
.pr-rail::before {
  content: ''; position: absolute; left: 17px; top: 8px; bottom: 8px;
  width: 2px; background: #e2e8f0;
}
.pr-node { position: relative; padding: 0 0 26px 52px; }
.pr-node:last-child { padding-bottom: 0; }
.pr-dot {
  position: absolute; left: 0; top: 0; z-index: 1;
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--blk-accent, #2563eb); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 800;
  box-shadow: 0 0 0 4px #fff;
}
.v-timeline .pr-card {
  background: #f7f9fa; border: 1px solid #e5e9ec;
  border-radius: 12px; padding: 16px 20px;
}

/* Process — one step at a time */
.pr-steps { position: relative; margin-bottom: 22px; }
.pr-track {
  position: absolute; left: 18px; right: 18px; top: 17px; height: 2px;
  background: #e2e8f0; border-radius: 2px;
}
.pr-track-fill { height: 100%; background: var(--blk-accent, #2563eb); border-radius: 2px; transition: width .25s ease; }
.pr-chips { position: relative; display: flex; justify-content: space-between; }
.pr-chip {
  width: 36px; height: 36px; border-radius: 50%; flex-shrink: 0;
  border: 2px solid #e2e8f0; background: #fff; color: #94a3b8;
  font-family: inherit; font-size: 13px; font-weight: 800; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .18s ease, border-color .18s ease, color .18s ease;
}
.pr-chip:hover { border-color: var(--blk-accent, #2563eb); color: var(--blk-accent, #2563eb); }
.pr-chip.done { background: #fff; border-color: var(--blk-accent, #2563eb); color: var(--blk-accent, #2563eb); }
.pr-chip.active { background: var(--blk-accent, #2563eb); border-color: var(--blk-accent, #2563eb); color: #fff; }

/* overflow-x clip so a card sliding in from off-frame never adds a scrollbar. */
.pr-stage { min-height: 120px; overflow-x: hidden; }
@keyframes pr-in-next {
  from { opacity: 0; transform: translateX(90px); }
  to   { opacity: 1; transform: none; }
}
@keyframes pr-in-prev {
  from { opacity: 0; transform: translateX(-90px); }
  to   { opacity: 1; transform: none; }
}
.pr-stage .pr-card.pr-in-next { animation: pr-in-next .38s cubic-bezier(.22,.7,.28,1) both; }
.pr-stage .pr-card.pr-in-prev { animation: pr-in-prev .38s cubic-bezier(.22,.7,.28,1) both; }
.v-process .pr-card {
  background: #fafcfd; border: 1px solid #e5e9ec; border-left: 3px solid var(--blk-accent, #2563eb);
  border-radius: 12px; padding: 20px 24px;
}
.pr-nav { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 18px; }
.pr-count { font-size: 12px; font-weight: 600; color: #8896a4; white-space: nowrap; }
.pr-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 18px; border-radius: 20px; border: 1px solid #dbe4ea;
  background: #fff; color: #3d4c5c;
  font-family: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.pr-btn:hover:not(:disabled) { border-color: var(--blk-accent, #2563eb); color: var(--blk-accent, #2563eb); }
.pr-btn.primary { background: var(--blk-accent, #2563eb); border-color: var(--blk-accent, #2563eb); color: #fff; }
.pr-btn.primary:hover:not(:disabled) { background: var(--blk-accent-dark, #1d4ed8); color: #fff; }
.pr-btn:disabled { opacity: .4; cursor: not-allowed; }

@media (max-width: 768px) {
  .pr-node { padding-left: 46px; }
  .pr-chip { width: 30px; height: 30px; font-size: 12px; }
  .pr-track { top: 14px; left: 15px; right: 15px; }
}
@media (prefers-reduced-motion: reduce) {
  .pr-track-fill, .pr-chip { transition: none; }
  .pr-stage .pr-card.pr-in-next, .pr-stage .pr-card.pr-in-prev { animation: none; }
}

/* ── Continue / gate ──────────────────────────────────── */
/* Applied to whole sibling blocks, so it must beat their own display rules. */
.blk-gate-hidden { display: none !important; }

/* Added only when a learner clicks Continue, then removed on animationend, so
   a block can fade in again if the gate is relocked and reopened. */
@keyframes blk-gate-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
.blk-gate-in { animation: blk-gate-in .45s cubic-bezier(.2, .7, .3, 1) both; }

.cn-block { text-align: center; padding: 10px 0 22px; }
.cn-note { font-size: 14px; color: #8896a4; margin-bottom: 14px; line-height: 1.6; }
.cn-require { font-size: 13px; color: #b45309; font-weight: 600; margin-bottom: 10px; }
.cn-btn:disabled { opacity: .45; cursor: not-allowed; }
.cn-btn:disabled:hover { background: var(--blk-accent, #2563eb); }
.cn-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 190px; padding: 14px 38px; border: none; border-radius: 26px;
  background: var(--blk-accent, #2563eb); color: #fff;
  font-family: inherit; font-size: 15px; font-weight: 700; letter-spacing: .2px;
  cursor: pointer; transition: background .18s ease, transform .12s ease;
}
.cn-btn:hover { background: var(--blk-accent-dark, #1d4ed8); }
.cn-btn:active { transform: translateY(1px); }
/* Once clicked the gate is spent — keep the space but stop inviting clicks. */
.cn-block.done .cn-btn { display: none; }
.cn-block.done .cn-note { display: none !important; }
.cn-block.done { padding: 0; }
.cn-locked {
  margin-top: 12px; font-size: 12px; color: #92400e;
  background: #fffbeb; border: 1px dashed #fde68a; border-radius: 8px;
  padding: 8px 14px; justify-content: center;
}

/* ── Table ────────────────────────────────────────────── */
.tr-wrap { overflow-x: auto; border: 1px solid #e5e9ec; border-radius: 12px; }
.tr-table { width: 100%; border-collapse: collapse; font-size: 15px; }
.tr-table caption {
  caption-side: top; text-align: left; padding: 14px 18px 10px;
  font-size: 13px; font-weight: 600; color: #8896a4;
}
/* The caption always exists in the DOM (see trRender) so the builder can wire
   it for inline editing; a genuinely empty one is invisible to a learner or
   in preview mode — only the builder's edit state shows it. */
.tr-table caption:empty:not([contenteditable]) { display: none; padding: 0; }
.tr-table caption[contenteditable]:empty::before { content: 'Add a caption…'; color: #a5b1bc; }
.tr-table th, .tr-table td {
  padding: 13px 18px; text-align: left; border-bottom: 1px solid #eef2f5;
  color: #3d4c5c; line-height: 1.6; vertical-align: top;
}
.tr-table thead th {
  background: var(--blk-accent, #2563eb); color: #fff;
  font-size: 13px; font-weight: 700; letter-spacing: .3px;
  text-transform: uppercase; white-space: nowrap; border-bottom: none;
}
.tr-table tbody th[scope="row"] { font-weight: 700; color: #1b2733; }
.tr-table tbody tr:last-child th,
.tr-table tbody tr:last-child td { border-bottom: none; }
.tr-table.striped tbody tr:nth-child(even) { background: #f8fafb; }
.tr-table tbody tr:hover { background: #f1f6fd; }
.tr-empty {
  padding: 30px; text-align: center; color: #94a3b8; font-size: 13px;
  border: 2px dashed #dbe4ea; border-radius: 10px;
}

/* ── Labeled Graphic ──────────────────────────────────── */
/* The stage must NOT clip: a pin sits at translate(-50%,-50%) so one at x=0 or
   x=100 would be sliced in half, and a tooltip on a short image would be cut
   off. Round the image itself instead. */
.lg-stage { position: relative; display: block; line-height: 0; }
.lg-img { width: 100%; height: auto; display: block; border-radius: 12px; }
.lg-empty {
  padding: 44px 20px; text-align: center; color: #94a3b8; font-size: 13px;
  border: 2px dashed #dbe4ea; border-radius: 12px; line-height: 1.6;
}
.lg-hint { margin-top: 10px; font-size: 12px; color: #94a3b8; text-align: center; }

.lg-hot { position: absolute; width: 0; height: 0; }
/* Rise-style marker: solid accent dot with a "+", floating in a soft
   translucent white ring. The + rotates to an × while its tip is open. */
.lg-pin {
  position: absolute; left: 0; top: 0; transform: translate(-50%, -50%);
  width: 30px; height: 30px; border-radius: 50%; border: none;
  background: var(--blk-accent, #2563eb); color: #fff;
  font-family: inherit; font-size: 18px; font-weight: 600; line-height: 1;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  box-shadow: 0 0 0 7px rgba(255, 255, 255, .55), 0 2px 10px rgba(15, 23, 42, .3);
  transition: transform .18s ease, background .18s ease;
  z-index: 2;
}
.lg-pin:hover { transform: translate(-50%, -50%) scale(1.1); }
.lg-hot.open .lg-pin {
  transform: translate(-50%, -50%) scale(1.1) rotate(45deg);
  background: var(--blk-accent-dark, #1d4ed8);
}
/* Pulse only while idle, so an answered pin stops demanding attention. */
.lg-pin::after {
  content: ''; position: absolute; inset: -6px; border-radius: 50%;
  border: 2px solid var(--blk-accent, #2563eb); opacity: .55;
  animation: lg-pulse 2.2s ease-out infinite;
}
.lg-hot.open .lg-pin::after { animation: none; opacity: 0; }
@keyframes lg-pulse {
  0%   { transform: scale(.85); opacity: .55; }
  70%  { transform: scale(1.5);  opacity: 0; }
  100% { transform: scale(1.5);  opacity: 0; }
}

.lg-tip {
  display: none; position: absolute; z-index: 3;
  left: 0; bottom: 26px; transform: translateX(-50%);
  width: 240px; max-height: min(320px, 60vh); padding: 14px 16px; line-height: 1.6;
  background: #fff; border: 1px solid #e5e9ec; border-radius: 10px;
  box-shadow: 0 10px 30px rgba(15, 23, 42, .18);
  text-align: left; flex-direction: column;
}
.lg-hot.open .lg-tip { display: flex; }
.lg-hot.t-left  .lg-tip { transform: translateX(-22px); }
.lg-hot.t-right .lg-tip { transform: translateX(-218px); }
.lg-hot.t-below .lg-tip { bottom: auto; top: 26px; }
.lg-tip-title { font-size: 14px; font-weight: 700; color: #1b2733; margin-bottom: 4px; flex-shrink: 0; }
.lg-tip-body { font-size: 13px; color: #3d4c5c; overflow-y: auto; padding-right: 4px; }

@media (max-width: 768px) {
  .lg-tip { width: 200px; }
  .tr-table th, .tr-table td { padding: 11px 14px; font-size: 14px; }
}
@media (prefers-reduced-motion: reduce) {
  .lg-pin, .lg-pin::after, .cn-btn { transition: none; animation: none; }
  /* The reveal must still *reveal* — drop the motion, keep the end state. */
  .blk-gate-in { animation: none; opacity: 1; transform: none; }
}

/* ── Sorting activity ─────────────────────────────────── */
.so-block { border: 1px solid #e5e9ec; border-radius: 16px; padding: 24px; background: #fafcfd; }
.so-prompt { font-size: 16px; font-weight: 600; color: #1b2733; line-height: 1.6; margin-bottom: 16px; }

.so-pool {
  display: flex; flex-wrap: wrap; gap: 10px; min-height: 62px;
  padding: 12px; margin-bottom: 18px;
  border: 2px dashed #cdd8e1; border-radius: 12px; background: #fff;
  transition: border-color .15s ease, background .15s ease;
}
.so-pool:empty::before {
  content: 'All cards sorted.'; color: #a5b1bc; font-size: 13px; align-self: center; margin: 0 auto;
}

.so-cats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.so-cat { display: flex; flex-direction: column; }
.so-cat-name {
  font-size: 12px; font-weight: 800; letter-spacing: .6px; text-transform: uppercase;
  color: var(--blk-accent, #2563eb); margin-bottom: 8px; text-align: center;
}
.so-drop {
  flex: 1; min-height: 110px; padding: 12px;
  display: flex; flex-direction: column; gap: 8px; align-items: stretch;
  border: 2px dashed #cdd8e1; border-radius: 12px; background: #fff;
  transition: border-color .15s ease, background .15s ease;
}
.so-drop.over, .so-pool.over { border-color: var(--blk-accent, #2563eb); background: #f0f6ff; }

.so-chip {
  font-family: inherit; font-size: 14px; font-weight: 600; color: #1b2733;
  padding: 11px 15px; border-radius: 10px; text-align: left;
  border: 1px solid #dbe4ea; background: #fff; cursor: grab;
  box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
  transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
.so-pool .so-chip { flex: 0 0 auto; }
.so-chip:hover { border-color: var(--blk-accent, #2563eb); }
.so-chip.dragging { opacity: .45; cursor: grabbing; }
.so-chip.selected {
  border-color: var(--blk-accent, #2563eb);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, .18);
  transform: translateY(-1px);
}

/* Marking only appears after Check answers — never before. */
.so-block.answered .so-chip { cursor: default; }
.so-block.answered .so-chip.correct { border-color: #86efac; background: #f0fdf4; color: #15803d; }
.so-block.answered .so-chip.wrong   { border-color: #fecaca; background: #fef2f2; color: #b91c1c; }

.so-actions { display: flex; align-items: center; gap: 12px; margin-top: 18px; }
.so-check {
  padding: 10px 22px; border-radius: 20px; border: none;
  background: var(--blk-accent, #2563eb); color: #fff;
  font-family: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
}
.so-check:hover { background: var(--blk-accent-dark, #1d4ed8); }
.so-block.answered .so-check { display: none; }
.so-retry {
  display: none; background: none; border: none; color: var(--blk-accent, #2563eb);
  font-family: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.so-block.answered .so-retry { display: inline-block; }

.so-feedback { display: none; margin-top: 14px; padding: 13px 16px; border-radius: 10px; font-size: 14px; line-height: 1.6; }
.so-feedback.show { display: block; }
.so-feedback.ok  { background: #f0fdf4; border: 1px solid #bbf7d0; color: #15803d; }
.so-feedback.bad { background: #fef2f2; border: 1px solid #fecaca; color: #b91c1c; }

.so-empty { padding: 30px; text-align: center; color: #94a3b8; font-size: 13px; border: 2px dashed #dbe4ea; border-radius: 10px; }

@media (prefers-reduced-motion: reduce) {
  .so-chip, .so-drop, .so-pool { transition: none; }
}

/* ── Image & Text ─────────────────────────────────────── */
.it-inner {
  display: grid; grid-template-columns: 1fr 1fr; gap: 34px; align-items: center;
}
/* Layout 'right' just reorders the image column. */
.it-block.it-right .it-media { order: 2; }
.it-media img { width: 100%; border-radius: 12px; display: block; }
.it-media .it-empty {
  aspect-ratio: 3 / 2; border: 2px dashed #dbe4ea; border-radius: 12px;
  display: flex; align-items: center; justify-content: center; color: #c3ccd4;
}
.it-media .it-empty i { font-size: 40px; }
.it-caption { margin-top: 9px; font-size: 13px; color: #8896a4; text-align: center; }
.it-heading { font-size: 22px; font-weight: 800; color: #1b2733; line-height: 1.3; letter-spacing: -.3px; margin-bottom: 12px; }
.it-para { font-size: 16px; color: #3d4c5c; line-height: 1.85; }
@media (max-width: 768px) {
  .it-inner { grid-template-columns: 1fr; gap: 18px; }
  /* Stack on mobile: image always on top, regardless of the left/right choice. */
  .it-block.it-right .it-media { order: 0; }
}

/* ── Paragraph with Heading ───────────────────────────── */
.ph-block .ph-heading {
  font-size: 27px; font-weight: 800; color: #1b2733; line-height: 1.28;
  letter-spacing: -.3px; margin: 0 0 12px; outline: none;
}
.ph-block .ph-body {
  font-size: 17px; color: #3d4c5c; line-height: 1.85; min-height: 40px; outline: none;
}

/* ── Interaction micro-animations ─────────────────────── */
/* Keyframes, not transitions: several of these elements emerge from
   display:none, where transitions never fire. */

/* Feedback banners (knowledge check / scenario / sorting) slide up softly. */
@keyframes blk-feedback-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.kc-feedback.show, .sc-feedback.show, .so-feedback.show {
  animation: blk-feedback-in .3s ease both;
}

/* Marking: the right answer pops, a wrong pick shakes — replayable because
   `answered` is re-applied on every check/retry cycle. */
@keyframes blk-correct-pop {
  0% { transform: scale(1); } 45% { transform: scale(1.03); } 100% { transform: scale(1); }
}
@keyframes blk-wrong-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-5px); } 40% { transform: translateX(5px); }
  60% { transform: translateX(-3px); } 80% { transform: translateX(3px); }
}
.kc-block.answered .kc-opt.correct,
.sc-block.answered .sc-opt.correct,
.so-block.answered .so-chip.correct { animation: blk-correct-pop .35s ease; }
.kc-block.answered .kc-opt.wrong,
.sc-block.answered .sc-opt.wrong,
.so-block.answered .so-chip.wrong { animation: blk-wrong-shake .4s ease; }

/* Lightbox: backdrop fades, the figure zooms up into place. */
@keyframes gl-lb-bg { from { opacity: 0; } to { opacity: 1; } }
@keyframes gl-lb-zoom {
  from { opacity: 0; transform: scale(.92); }
  to   { opacity: 1; transform: none; }
}
.gl-lightbox.open { animation: gl-lb-bg .22s ease; }
.gl-lightbox.open .gl-lb-fig { animation: gl-lb-zoom .3s cubic-bezier(.22, .7, .28, 1); }

/* Sorting: a chip settles with a small pop when dropped into a column. */
@keyframes so-placed {
  0% { transform: scale(.92); opacity: .65; }
  60% { transform: scale(1.04); }
  100% { transform: scale(1); opacity: 1; }
}
.so-chip.placed { animation: so-placed .28s ease; }

@media (prefers-reduced-motion: reduce) {
  .kc-feedback.show, .sc-feedback.show, .so-feedback.show,
  .kc-block.answered .kc-opt.correct, .sc-block.answered .sc-opt.correct,
  .so-block.answered .so-chip.correct,
  .kc-block.answered .kc-opt.wrong, .sc-block.answered .sc-opt.wrong,
  .so-block.answered .so-chip.wrong,
  .gl-lightbox.open, .gl-lightbox.open .gl-lb-fig,
  .so-chip.placed { animation: none; }
}

/* ── Reading typography (Rise-style) ──────────────────────
   Long-form lesson text reads in a serif with darker ink; structure —
   headings, tabs, buttons, labels — stays in the platform sans. Applied at
   the container so every text block inherits it, then re-asserted for the
   structural elements that must not. */
.lp-ablock, .block-content, .lp-lesson-desc {
  font-family: 'Merriweather', Georgia, 'Times New Roman', serif;
}
.lp-ablock .hd-block, .block-content .hd-block,
.lp-ablock .ph-heading, .block-content .ph-heading,
.lp-ablock .st-text, .block-content .st-text,
.lp-ablock .ac-head, .block-content .ac-head,
.lp-ablock .tb-tab, .block-content .tb-tab,
.lp-ablock .kc-eyebrow, .block-content .kc-eyebrow,
.lp-ablock .kc-question, .block-content .kc-question,
.lp-ablock .kc-feedback, .block-content .kc-feedback,
.lp-ablock button, .block-content button,
.lp-ablock .cn-note, .block-content .cn-note,
.lp-ablock .cn-locked, .block-content .cn-locked,
.lp-ablock .cn-require, .block-content .cn-require,
.lp-ablock .lb-btn, .block-content .lb-btn,
.lp-ablock .pr-label, .block-content .pr-label,
.lp-ablock .pr-title, .block-content .pr-title,
.lp-ablock .qt-name, .block-content .qt-name,
.lp-ablock .qt-role, .block-content .qt-role,
.lp-ablock .rv-title, .block-content .rv-title,
.lp-ablock .lg-tip-title, .block-content .lg-tip-title,
.lp-ablock .tr-table th, .block-content .tr-table th,
.lp-ablock .so-block, .block-content .so-block,
.lp-ablock .vd-title, .block-content .vd-title,
.lp-ablock figcaption, .block-content figcaption,
.lp-ablock .vd-caption, .block-content .vd-caption,
.lp-ablock .dv-block, .block-content .dv-block {
  font-family: 'DM Sans', system-ui, sans-serif;
}
/* The plain "Paragraph" block has no CSS class of its own — it's a bare
   contenteditable div carrying font-size/color/line-height purely as an
   inline style attribute (see BLOCK_TEMPLATES.paragraph in editor.html).
   That's fine in the builder canvas, which loads content raw, but the real
   learner-facing player renders everything through the server-side
   sanitizer, which strips `style` outright (a deliberate defense against
   arbitrary CSS injection) — leaving this block with none of its sizing and
   falling back to the browser's tiny default text. Restore it here, keyed
   off the one thing that does survive sanitization: the *absence* of a
   class, which uniquely identifies this block type among all the others
   (every other block wraps itself in a named class). */
.lp-ablock > div:not([class]), .block-content > div:not([class]) {
  font-size: 17px !important; line-height: 1.8 !important; color: #26323d !important;
}
.ph-block .ph-body, .ls-block, .blk-text, .tc-block > div,
.kc-opt, .tr-table td, .pr-card .pr-text, .qt-text, .rv-body,
.lg-tip-body, .lp-lesson-desc {
  color: #26323d;
}
/* The page-wide `* { margin:0; padding:0 }` reset strips the browser's own
   list indent too, so a bullet/numbered list typed inline (via the format
   toolbar) inside any rich-text body sits flush left with no marker room.
   Restore a proper hanging indent — same treatment as accordion/tab bodies. */
.ph-block .ph-body ul, .ph-block .ph-body ol,
.lp-ablock > div:not([class]) ul, .lp-ablock > div:not([class]) ol,
.block-content > div:not([class]) ul, .block-content > div:not([class]) ol,
.tc-block > div ul, .tc-block > div ol,
.pr-card .pr-text ul, .pr-card .pr-text ol,
.qt-text ul, .qt-text ol,
.rv-body ul, .rv-body ol,
.lg-tip-body ul, .lg-tip-body ol,
.lp-lesson-desc ul, .lp-lesson-desc ol {
  margin: 10px 0; padding-left: 26px;
}
.ph-block .ph-body li, .lp-ablock > div:not([class]) li, .block-content > div:not([class]) li,
.tc-block > div li, .pr-card .pr-text li, .qt-text li, .rv-body li,
.lg-tip-body li, .lp-lesson-desc li {
  margin: 6px 0;
}

/* ── Headings and titles in pure black ─────────────────────
   Maximum contrast against the serif reading text. The dark-canvas statement
   keeps its white text (higher-specificity rule, untouched). */
.hd-block, .ph-block .ph-heading, .it-heading,
.ac-head, .kc-question, .vd-title, .rv-title,
.pr-card .pr-title, .lg-tip-title, .qt-name,
.st-block.v-framed .st-text, .so-prompt,
.tr-table tbody th[scope="row"] {
  color: #000;
}

/* ── Dual measure (Rise-style) ─────────────────────────────
   Reading-text blocks hold a ~70ch column; media and interactive blocks
   (images, video, gallery, accordion, tabs, quizzes, tables, steppers…) use
   the full wide canvas. Both center on the same axis, so media reads as
   deliberately bleeding beyond the text column. */
.lp-ablock > div:not([class]), .block-content > div:not([class]),
.lp-ablock > .ph-block,  .block-content > .ph-block,
.lp-ablock > .hd-block,  .block-content > .hd-block,
.lp-ablock > .ls-block,  .block-content > .ls-block,
.lp-ablock > .qt-block,  .block-content > .qt-block,
.lp-ablock > .dv-block,  .block-content > .dv-block {
  max-width: 716px; margin-left: auto; margin-right: auto;
}
