/* ==========================================================================
   COURSE PLAYER — stylesheet
   --------------------------------------------------------------------------
   Palette is lifted from the course itself: Modules 1 and 2 already share
   #19d8ff cyan on a #050b16 navy, with #4fd18b green and #f2b93a amber for
   state. Module 3's violet appears only as the accent for Module 3's own
   card, so the menu reads as a map of the course rather than a theme of
   its own. Typeface is Roboto throughout, as in every module.
   ========================================================================== */

:root {
  --bg:        #030910;
  --bg-lift:   #071322;
  --panel:     rgba(7, 18, 32, .93);
  --line:      rgba(25, 216, 255, .16);
  --line-soft: rgba(215, 226, 240, .10);

  --cyan:      #19d8ff;
  --cyan-dim:  rgba(25, 216, 255, .22);
  --green:     #4fd18b;
  --amber:     #f2b93a;
  --violet:    #a78bfa;

  --text:      #ffffff;
  --muted:     #9fb2c6;
  --dim:       #6a7d92;

  --font: "Roboto", Arial, Helvetica, sans-serif;
  --mono: "Roboto Mono", ui-monospace, Consolas, monospace;

  --chrome-in:  cubic-bezier(.16, .84, .3, 1);
  --safe-t: env(safe-area-inset-top,  0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  /* The player itself never scrolls. Each module already sets overflow:hidden
     on its own body, so there is exactly one non-scrolling surface and no
     possibility of a second scrollbar. */
  overflow: hidden;
  overscroll-behavior: none;
}

/* The course fills the viewport. dvh tracks the mobile URL bar as it
   collapses; the svh fallback keeps older browsers from over-shooting. */
#player {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100svh;
  height: 100dvh;
  background: var(--bg);
  overflow: hidden;
}

/* ---------------------------------------------------------------- STAGE */
#stage { position: absolute; inset: 0; }

#moduleFrame {
  display: block;              /* kills the inline-element baseline gap */
  width: 100%;
  height: 100%;
  border: 0;
  background: var(--bg);
  opacity: 0;
  transition: opacity .45s ease;
}
#moduleFrame.is-ready { opacity: 1; }

/* ------------------------------------------------------------- LOADING */
#loader {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--bg);
  z-index: 20;
  transition: opacity .4s ease, visibility .4s;
}
#loader[hidden] { display: grid; opacity: 0; visibility: hidden; pointer-events: none; }
.loader-inner { text-align: center; }
.loader-ring {
  width: 46px; height: 46px; margin: 0 auto 18px;
  border: 2px solid var(--cyan-dim);
  border-top-color: var(--cyan);
  border-radius: 50%;
  animation: spin 1s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loader-label {
  font-family: var(--mono);
  font-size: 11px; letter-spacing: .22em; text-transform: uppercase;
  color: var(--dim);
}

/* ================================================================ CHROME
   Two controls only: the menu, and the volume. No Next, no Previous, no
   Replay — every module already owns its own forward navigation and the
   player must not compete with it. Both controls sit above the module and
   fade back when the pointer is elsewhere so the course stays the subject.
   ==================================================================== */
.chrome {
  position: absolute;
  z-index: 30;
  opacity: .55;
  transition: opacity .3s ease;
}
.chrome:hover, .chrome:focus-within, #player.chrome-awake .chrome { opacity: 1; }

/* One row, top-left: the course menu and the settings button.

   The controls were top-right once — over the AURA badge in the Final Mission
   and the insight panel in Device Security — and then bottom-left, where they
   covered the narration every module prints along the foot of the frame. The
   top-left corner is the only one the player has always owned, and the row
   only fits there because it is now two buttons instead of six. */
#chromeBar {
  top:  calc(14px + var(--safe-t));
  left: calc(14px + var(--safe-l));
  display: flex;
  align-items: center;
  gap: 8px;
}

#settingsScrim { position: absolute; inset: 0; z-index: 29; background: transparent; }
/* The coach marks aim at the settings button and open downwards — onto the
   popover. They wait rather than argue with it. */
#player.settings-open #fsHint,
#player.settings-open #notesHint { display: none !important; }
#settingsScrim[hidden] { display: none; }

/* The settings popover hangs under the row it belongs to. */
#volumeControl {
  position: absolute;
  z-index: 31;
  top:  calc(62px + var(--safe-t));
  left: calc(14px + var(--safe-l));
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: rgba(5, 13, 24, .92);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  box-shadow: 0 18px 44px rgba(0, 0, 0, .5);
  opacity: 1;
  animation: chromePop .16s var(--chrome-in) both;
}
#volumeControl[hidden] { display: none; }
@keyframes chromePop {
  from { opacity: 0; transform: translateY(-6px) }
  to   { opacity: 1; transform: translateY(0) }
}
/* A square button with no label — the one control that is always on screen. */
.ctl-icon { padding: 0; width: 40px; justify-content: center; }
.ctl-icon .ctl-dot { right: 6px; top: 6px; }
#settingsButton[aria-expanded="true"] {
  border-color: var(--cyan);
  background: rgba(25, 216, 255, .12);
}
/* Every control moved behind this button, so on a first run the card that
   explains it is only half an introduction — a learner reads the card and then
   has to find the thing it points at. The button says "me" while the card is
   up, and stops the moment it is dismissed.

   A ring that travels outward rather than a colour that throbs: on a dark
   screen full of cyan already, motion away from the button is what the eye
   catches, and it does not turn the control a different colour from the one
   the learner will see for the rest of the course. */
@keyframes ctlAttention {
  0%   { box-shadow: 0 0 0 0 rgba(25, 216, 255, .55), 0 0 0 0 rgba(25, 216, 255, .3) }
  70%  { box-shadow: 0 0 0 9px rgba(25, 216, 255, 0),  0 0 0 16px rgba(25, 216, 255, 0) }
  100% { box-shadow: 0 0 0 0 rgba(25, 216, 255, 0),    0 0 0 0 rgba(25, 216, 255, 0) }
}
#settingsButton.attention {
  border-color: rgba(25, 216, 255, .75);
  background: rgba(25, 216, 255, .1);
  animation: ctlAttention 1.9s cubic-bezier(.25, .6, .3, 1) infinite;
}
/* The chrome dims to .55 when the pointer is elsewhere. While the button is
   asking to be noticed it does not dim, or the pulse is the only bright thing
   and the button under it is not. */
#player.chrome-hinting .chrome { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  #settingsButton.attention { animation: none; box-shadow: 0 0 0 2px rgba(25, 216, 255, .6); }
}
/* Inside the popover the slider is always out: there is no room for a
   hover-reveal in a panel that only exists while it is being used. */
#volumeControl #volumeRange { width: 92px; opacity: 1; }

/* A phone in landscape is about 390px tall. The popover holds six controls in
   a row; below this it wraps to two, which is still far less than the bottom
   strip it replaced. */
@media (max-width: 560px) {
  #volumeControl { flex-wrap: wrap; max-width: calc(100vw - 28px); }
}

.ctl {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 40px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: rgba(5, 13, 24, .74);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  color: var(--text);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color .2s ease, background .2s ease, transform .15s ease;
}
.ctl:hover { border-color: var(--cyan); background: rgba(9, 24, 42, .9); }
.ctl:active { transform: translateY(1px); }
.ctl svg { width: 17px; height: 17px; flex: none; }
.ctl .ctl-text { white-space: nowrap; }

/* Visible focus ring on every interactive element, keyboard and mouse alike. */
:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 3px;
  border-radius: 8px;
}

/* menu button carries the current module number as a quiet progress cue */
#menuButton .ctl-step {
  font-size: 10px;
  color: var(--cyan);
  padding-left: 9px;
  border-left: 1px solid var(--line);
}

/* text-language switch — shows the language you are reading, not the one you
   would move to, so it reads as a state rather than a button label. Hidden
   outright when the course offers only one language. */
#langButton .ctl-text { color: var(--cyan); letter-spacing: .18em; }
.ctl[hidden] { display: none; }

/* captions toggle — clearly on or off at a glance, since it is a setting
   rather than a momentary action */
#captionButton[aria-pressed="true"]{border-color:var(--cyan);color:var(--cyan);
  background:rgba(25,216,255,.12);box-shadow:0 0 18px rgba(25,216,255,.2)}
#captionButton[aria-pressed="false"]{opacity:.8}

/* volume slider reveals on hover/focus so idle chrome stays minimal */
/* Left to the browser this was a grey 3px line with a default round thumb —
   the only piece of stock OS UI anywhere in the player. Drawn by hand now, in
   the same cyan-on-smoked-glass language as the buttons beside it. */
#volumeRange {
  width: 0;
  opacity: 0;
  height: 16px;
  margin: 0;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  transition: width .3s var(--chrome-in), opacity .25s ease;
}
#volumeRange::-webkit-slider-runnable-track {
  height: 4px; border-radius: 3px;
  background: linear-gradient(90deg, var(--cyan) 0 var(--vol, 100%), rgba(255,255,255,.14) var(--vol, 100%) 100%);
  box-shadow: inset 0 0 0 1px rgba(120,190,255,.14);
}
#volumeRange::-moz-range-track {
  height: 4px; border-radius: 3px; background: rgba(255,255,255,.14);
  box-shadow: inset 0 0 0 1px rgba(120,190,255,.14);
}
#volumeRange::-moz-range-progress { height: 4px; border-radius: 3px; background: var(--cyan); }
#volumeRange::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 13px; height: 13px; margin-top: -4.5px; border-radius: 50%;
  background: #eafaff; border: 1px solid rgba(25,216,255,.9);
  box-shadow: 0 0 10px rgba(25,216,255,.75), 0 1px 3px rgba(0,0,0,.6);
  transition: transform .14s ease, box-shadow .18s ease;
}
#volumeRange::-moz-range-thumb {
  width: 13px; height: 13px; border-radius: 50%; border: 1px solid rgba(25,216,255,.9);
  background: #eafaff; box-shadow: 0 0 10px rgba(25,216,255,.75), 0 1px 3px rgba(0,0,0,.6);
}
#volumeRange:hover::-webkit-slider-thumb { transform: scale(1.14); box-shadow: 0 0 16px rgba(25,216,255,.95), 0 1px 3px rgba(0,0,0,.6); }
#volumeRange:active::-webkit-slider-thumb { transform: scale(1.02); }
#volumeRange:focus-visible { outline: none; }
#volumeRange:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px rgba(25,216,255,.34), 0 0 14px rgba(25,216,255,.8); }
#volumeRange:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 3px rgba(25,216,255,.34), 0 0 14px rgba(25,216,255,.8); }
/* The slider no longer hides: see #volumeControl #volumeRange above. */

/* ================================================================== MENU */
#menuScrim {
  position: absolute;
  inset: 0;
  z-index: 40;
  background: rgba(1, 5, 11, .68);
  -webkit-backdrop-filter: blur(7px);
  backdrop-filter: blur(7px);
  opacity: 0;
  visibility: hidden;
  transition: opacity .35s ease, visibility .35s;
}
#player.menu-open #menuScrim { opacity: 1; visibility: visible; }

#menu {
  position: absolute;
  z-index: 50;
  top: 0; left: 0; bottom: 0;
  width: min(430px, 92vw);
  padding: calc(22px + var(--safe-t)) 22px calc(22px + var(--safe-b)) calc(22px + var(--safe-l));
  background: var(--panel);
  -webkit-backdrop-filter: blur(22px);
  backdrop-filter: blur(22px);
  border-right: 1px solid var(--line);
  box-shadow: 34px 0 90px rgba(0, 0, 0, .55);
  display: flex;
  flex-direction: column;
  gap: 18px;
  overflow-y: auto;
  overscroll-behavior: contain;
  transform: translateX(-102%);
  transition: transform .42s var(--chrome-in);
}
#player.menu-open #menu { transform: none; }

/* the technical seam that ties the panel to the course's visual language */
#menu::before {
  content: "";
  position: absolute;
  inset: 0 0 0 auto;
  width: 1px;
  background: linear-gradient(180deg, transparent, var(--cyan), transparent);
  opacity: .5;
  pointer-events: none;
}

.menu-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
.menu-title { font-size: 19px; font-weight: 700; letter-spacing: .01em; margin: 0; }
.menu-sub {
  margin: 5px 0 0;
  font-family: var(--mono);
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--dim);
}
#menuClose {
  flex: none;
  width: 36px; height: 36px; padding: 0;
  display: grid; place-items: center;
  border: 1px solid var(--line-soft);
  border-radius: 10px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: color .2s, border-color .2s;
}
#menuClose:hover { color: var(--text); border-color: var(--cyan); }

/* overall progress rail */
.menu-progress { display: flex; align-items: center; gap: 10px; }
.rail { flex: 1; display: flex; gap: 4px; }
.rail i {
  flex: 1; height: 3px; border-radius: 2px;
  background: rgba(255, 255, 255, .1);
  transition: background .4s ease, box-shadow .4s ease;
}
.rail i.done { background: var(--green); box-shadow: 0 0 10px rgba(79, 209, 139, .5); }
.rail i.current { background: var(--cyan); box-shadow: 0 0 10px rgba(25, 216, 255, .55); }
.menu-count { font-family: var(--mono); font-size: 10px; letter-spacing: .14em; color: var(--dim); }

/* ------------------------------------------------------------ MODULE CARD */
.mod-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 11px; }

.mod {
  position: relative;
  width: 100%;
  text-align: left;
  display: grid;
  grid-template-columns: 42px 1fr;
  gap: 14px;
  padding: 15px 15px 15px 13px;
  border: 1px solid var(--line-soft);
  border-radius: 14px;
  background: rgba(10, 22, 38, .5);
  color: inherit;
  font: inherit;
  cursor: pointer;
  transition: border-color .22s, background .22s, transform .18s var(--chrome-in);
}
.mod:hover:not(.is-locked) { border-color: var(--cyan); background: rgba(13, 31, 52, .78); transform: translateX(3px); }
.mod:disabled, .mod.is-locked { cursor: not-allowed; }

.mod-badge {
  width: 42px; height: 42px;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  border-radius: 11px;
  font-family: var(--mono); font-size: 13px; font-weight: 600;
  color: var(--cyan);
  background: rgba(25, 216, 255, .06);
}
.mod-name { font-size: 15px; font-weight: 600; margin: 0 0 3px; display: flex; align-items: baseline; gap: 9px; }
.mod-mins {
  font-family: var(--mono); font-size: 9.5px; font-weight: 500;
  letter-spacing: .12em; color: var(--dim); white-space: nowrap;
}
.mod-summary { margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--muted); }

.mod-state {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 9px;
  font-family: var(--mono); font-size: 9px;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--dim);
}
.mod-state::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
}

/* --- state: completed --- */
.mod.is-done { border-color: rgba(79, 209, 139, .34); }
.mod.is-done .mod-badge { color: var(--green); border-color: rgba(79, 209, 139, .4); background: rgba(79, 209, 139, .08); }
.mod.is-done .mod-state { color: var(--green); }

/* --- state: current --- */
.mod.is-current { border-color: var(--cyan); background: rgba(13, 33, 55, .8); }
.mod.is-current .mod-badge { box-shadow: 0 0 0 3px rgba(25, 216, 255, .12); }
.mod.is-current .mod-state { color: var(--cyan); }

/* --- state: available --- */
.mod.is-open .mod-state { color: var(--amber); }

/* --- state: locked --- */
.mod.is-locked { opacity: .44; background: rgba(10, 18, 30, .34); }
.mod.is-locked .mod-badge { color: var(--dim); border-color: var(--line-soft); background: transparent; }
.mod.is-locked:hover { border-color: var(--line-soft); transform: none; }

/* module 3 keeps its own violet identity in the badge */
.mod[data-id="module-3"]:not(.is-locked):not(.is-done) .mod-badge { color: var(--violet); border-color: rgba(167, 139, 250, .38); background: rgba(167, 139, 250, .08); }

.menu-foot {
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.menu-note { font-size: 10px; line-height: 1.6; color: var(--dim); margin: 0; }
#resetProgress {
  flex: none;
  padding: 8px 11px;
  border: 1px solid var(--line-soft);
  border-radius: 9px;
  background: transparent;
  color: var(--dim);
  font-family: var(--mono); font-size: 9px; letter-spacing: .14em; text-transform: uppercase;
  cursor: pointer;
  transition: color .2s, border-color .2s;
}
#resetProgress:hover { color: var(--amber); border-color: rgba(242, 185, 58, .45); }

/* ============================================================ RESUME ASK */
#resumeAsk {
  position: absolute;
  left: 50%;
  /* clears the captions/volume chrome now sitting in the bottom-left */
  bottom: calc(78px + var(--safe-b));
  transform: translate(-50%, 16px);
  z-index: 62;
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  max-width: min(620px, 92vw);
  padding: 16px 20px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: rgba(6, 16, 29, .97);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  box-shadow: 0 22px 60px rgba(0, 0, 0, .5);
  opacity: 0; visibility: hidden;
  transition: opacity .3s ease, transform .3s var(--chrome-in), visibility .3s;
}
#resumeAsk.show { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
#resumeAsk .ask-text { margin: 0; flex: 1 1 240px; font-size: 14px; line-height: 1.45; }
#resumeAsk .ask-actions { display: flex; gap: 9px; flex: none; }
#resumeAsk button {
  height: 38px; padding: 0 16px;
  border-radius: 10px; cursor: pointer;
  font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase;
  transition: border-color .2s, background .2s, color .2s;
}
#resumeAsk .ask-yes { border: 1px solid var(--cyan); background: rgba(25,216,255,.14); color: #fff; }
#resumeAsk .ask-yes:hover { background: rgba(25,216,255,.24); }
#resumeAsk .ask-no { border: 1px solid var(--line-soft); background: transparent; color: var(--muted); }
#resumeAsk .ask-no:hover { color: var(--text); border-color: var(--muted); }
@media (max-width: 620px) { #resumeAsk { flex-direction: column; align-items: stretch; text-align: center; } }

/* ========================================================== COURSE ENDING */
#courseEnd {
  position: absolute; inset: 0; z-index: 70;
  display: grid; place-items: center;
  padding: calc(28px + var(--safe-t)) 24px calc(28px + var(--safe-b));
  background: radial-gradient(circle at 50% 32%, rgba(25,216,255,.09), transparent 48%),
              linear-gradient(180deg, rgba(3,9,16,.97), rgba(2,6,13,.99));
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  opacity: 0; visibility: hidden;
  transition: opacity .55s ease, visibility .55s;
  overflow-y: auto;
}
#courseEnd.show { opacity: 1; visibility: visible; }
.ce-inner { width: min(760px, 100%); text-align: center; }
.ce-kick {
  font-family: var(--mono); font-size: 11px; font-weight: 600;
  letter-spacing: .3em; text-transform: uppercase; color: var(--cyan); margin-bottom: 14px;
}
.ce-title { margin: 0 0 14px; font-size: clamp(28px, 4.4vw, 44px); font-weight: 700; }
.ce-sub { margin: 0 auto 34px; max-width: 560px; font-size: 14.5px; line-height: 1.6; color: var(--muted); }
.ce-rows { display: flex; flex-direction: column; gap: 8px; margin-bottom: 30px; text-align: left; }
.ce-row {
  display: grid; grid-template-columns: 34px 1fr auto auto; align-items: baseline; gap: 14px;
  padding: 15px 18px;
  border: 1px solid var(--line-soft); border-radius: 12px;
  background: rgba(9, 22, 38, .55);
}
.ce-num { font-family: var(--mono); font-size: 11px; color: var(--dim); }
.ce-name { font-size: 14.5px; font-weight: 600; }
.ce-val { font-size: 21px; font-weight: 700; color: var(--cyan); }
.ce-lab {
  font-family: var(--mono); font-size: 9px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--dim); min-width: 108px; text-align: right;
}
.ce-takeaway {
  margin: 0 0 30px; padding: 15px 18px;
  border: 1px solid rgba(79,209,139,.28); border-radius: 12px;
  background: rgba(79,209,139,.06);
  font-size: 13.5px; line-height: 1.55; color: #d3e8dd;
}
.ce-takeaway strong { color: var(--green); }
.ce-actions { display: flex; justify-content: center; gap: 11px; flex-wrap: wrap; }
.ce-btn {
  height: 44px; padding: 0 26px; border-radius: 11px; cursor: pointer;
  border: 1px solid var(--cyan); background: rgba(25,216,255,.14); color: #fff;
  font-family: var(--mono); font-size: 11px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase;
  transition: background .2s, border-color .2s, color .2s;
}
.ce-btn:hover { background: rgba(25,216,255,.26); }
.ce-btn.ghost { border-color: var(--line-soft); background: transparent; color: var(--muted); }
.ce-btn.ghost:hover { color: var(--text); border-color: var(--muted); }
@media (max-width: 620px) {
  .ce-row { grid-template-columns: 28px 1fr auto; }
  .ce-lab { grid-column: 2 / -1; text-align: left; min-width: 0; }
}

/* ============================================================ PREVIEW
   The trailer that closes the free preview build. Nothing here is reachable
   in the full course — the player only builds it when course.config.js
   carries a `preview` block.

   It sits above everything the player owns except the size gate, because a
   30-second 16:9 film on a 390px phone needs the same "turn it sideways"
   advice the modules do.
   ==================================================================== */
#previewEnd {
  position: absolute; inset: 0; z-index: 72;
  background: #000;
  opacity: 0; transition: opacity .5s ease;
}
#previewEnd.show { opacity: 1; }
#previewEnd[hidden] { display: none; }
/* contain, never cover: the trailer's text is composed to the 16:9 frame and
   cropping it would cut the words this whole film was written around. */
#previewVideo { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; background: #000; }

/* Sits over the resting frame so white type on black does not fight the
   button. Only painted once the film has finished. */
#previewVeil {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(0,0,0,.18), rgba(0,0,0,.62));
  opacity: 0; transition: opacity .6s ease;
}
#previewEnd.rest #previewVeil { opacity: 1; }

/* Skip. Deliberately quiet — present for the second viewing, not shouting
   during the first. Gone once the film has reached its end. */
#previewSkipBar {
  position: absolute; z-index: 2;
  right: calc(16px + var(--safe-r)); bottom: calc(16px + var(--safe-b));
  transition: opacity .35s ease;
}
#previewEnd.rest #previewSkipBar { opacity: 0; pointer-events: none; }
#previewSkipBar button {
  height: 34px; padding: 0 16px; border-radius: 9px; cursor: pointer;
  border: 1px solid rgba(215, 226, 240, .22); background: rgba(4, 10, 18, .55);
  color: var(--muted);
  font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  transition: color .2s, border-color .2s, background .2s;
}
#previewSkipBar button:hover { color: #fff; border-color: var(--muted); background: rgba(9, 20, 34, .8); }

/* The muted-autoplay fallback. Top-left, where the chrome the learner already
   knows lives, and it removes itself the moment sound is on. */
#previewSound {
  position: absolute; z-index: 3;
  left: calc(16px + var(--safe-l)); top: calc(16px + var(--safe-t));
  display: inline-flex; align-items: center; gap: 9px;
  height: 38px; padding: 0 16px 0 13px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--cyan); background: rgba(25, 216, 255, .16); color: #fff;
  font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
#previewSound[hidden] { display: none; }
#previewSound:hover { background: rgba(25, 216, 255, .3); }
#previewSound svg { width: 17px; height: 17px; flex: none; }

/* The line on the resting frame. Centred low so it never covers the card the
   film ends on, and quieter than that card: it is an instruction, not the
   last word. */
#previewDone {
  position: absolute; z-index: 3; left: 0; right: 0; margin: 0;
  bottom: 13%;
  padding: 0 24px;
  text-align: center;
  font-family: var(--mono); font-size: clamp(11px, 1.15vw, 14px); font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase; color: var(--muted);
  opacity: 0; transform: translateY(8px);
  transition: opacity .8s ease .4s, transform .8s ease .4s;
}
#previewDone[hidden] { display: none; }
#previewEnd.rest #previewDone { opacity: 1; transform: none; }

/* The player's own chrome would sit on top of the film. It steps away, the
   same way it does for the size gate. */
#player.preview-playing .chrome,
#player.preview-playing #toast { opacity: 0 !important; pointer-events: none; }
/* The coach marks are not .chrome and are drawn above it, so hiding the row
   alone left "Open in fullscreen" floating over the film. They are dismissed
   in script as well; this is the belt to that pair of braces. */
#player.preview-playing #fsHint,
#player.preview-playing #notesHint { display: none !important; }

@media (max-width: 620px) {
  #previewDone { letter-spacing: .12em; }
  #previewSound span { display: none; }
  #previewSound { padding: 0 11px; }
}
@media (prefers-reduced-motion: reduce) {
  #previewEnd, #previewVeil, #previewDone { transition-duration: .01ms; }
  #previewDone { transform: none; }
}

/* ============================================================== TOAST */
#toast {
  position: absolute;
  left: 50%;
  bottom: calc(76px + var(--safe-b));
  transform: translate(-50%, 14px);
  z-index: 60;
  max-width: min(460px, 90vw);
  padding: 12px 18px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(6, 16, 29, .95);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  font-size: 13px;
  color: var(--text);
  opacity: 0;
  visibility: hidden;
  transition: opacity .3s ease, transform .3s var(--chrome-in), visibility .3s;
}
#toast.show { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
#toast.good { border-color: rgba(79, 209, 139, .5); }

/* ===================================================== TOO SMALL TO PLAY

   Above everything, including the notes scrim, because it is a statement about
   the frame itself rather than a panel inside it — and there is no point
   letting a learner open Notes over a course they cannot read.

   Sized in vmin rather than px: at 390x219 a card built on the width alone is
   a tall column in a short frame, and the thing it is complaining about is
   precisely the height. */
#sizeGate {
  position: absolute; inset: 0; z-index: 95;
  display: grid; place-items: center;
  padding: 16px;
  background: radial-gradient(circle at 50% 42%, rgba(9, 28, 44, .97), rgba(2, 7, 14, .99) 70%);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  text-align: center;
}
#sizeGate[hidden] { display: none; }
.size-card {
  max-width: min(420px, 92%);
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(6px, 1.6vmin, 12px);
}
.size-icon {
  width: clamp(26px, 7vmin, 38px); height: clamp(26px, 7vmin, 38px);
  color: rgba(46, 209, 243, .85);
}
.size-card h2 {
  margin: 0; font-size: clamp(15px, 3.4vmin, 20px); font-weight: 700;
  letter-spacing: .01em; color: #fff;
}
.size-card p {
  margin: 0; font-size: clamp(12px, 2.6vmin, 14px); line-height: 1.45; color: #b8cfe0;
}
.size-card p[hidden] { display: none; }
.size-icon[hidden] { display: none; }
#sizeGateGo {
  margin-top: clamp(2px, 1vmin, 8px);
  font: inherit; font-size: clamp(11px, 2.3vmin, 12.5px); font-weight: 600;
  cursor: pointer; padding: 8px 15px; border-radius: 9px; color: #9fc2d8;
  border: 1px solid rgba(120, 160, 185, .35); background: transparent;
}
#sizeGateGo:hover { color: #d6f4ff; border-color: rgba(46, 209, 243, .5); }
/* While it is up, the chrome is noise: the controls belong to a course that is
   not readable yet, and on a 219px stage they take a fifth of the screen. */
#player.too-small .chrome,
#player.too-small #fsHint,
#player.too-small #notesHint { display: none !important; }

/* ========================================================== RESPONSIVE */

/* Tablet — the panel gets a little tighter. */
@media (max-width: 900px) {
  #menu { width: min(390px, 94vw); }
}

/* Phone — the menu becomes a full sheet, and the chrome loses its labels so
   two 40px targets never crowd a narrow status bar. */
@media (max-width: 620px) {
  #menu {
    width: 100%;
    border-right: 0;
    border-radius: 0;
  }
  .ctl .ctl-text { display: none; }
  .ctl { padding: 0 12px; }
  #chromeBar     { top: calc(10px + var(--safe-t)); left: calc(10px + var(--safe-l)); }
  #volumeControl { top: calc(58px + var(--safe-t)); left: calc(10px + var(--safe-l)); }
  /* The labels are gone at this width, so the settings button's square is the
     same size as its neighbours again. */
  .ctl-icon { width: 40px; }
  .mod-summary { font-size: 11.5px; }
}

/* Short landscape phones: tighten the cards so all three still fit. */
@media (max-height: 460px) {
  .mod { padding: 11px 12px; }
  #menu { gap: 13px; }
}

/* Coarse pointers get no hover-reveal: the slider is always out, and the
   chrome never dims, because there is no hover state to bring it back. */
@media (hover: none) {
  .chrome { opacity: 1; }
}

/* ===================================================== REDUCED MOTION */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  #menu { transition: none; }
  .loader-ring { animation: none; border-top-color: var(--cyan-dim); }
}

/* ============================================================ PRINT */
@media print { .chrome, #menu, #menuScrim, #toast { display: none !important; } }

/* =========================================================== NOTES
   Player-level, because notes belong to the course rather than to whichever
   module happens to be on screen. The panel holds the module while it is
   open — see setPaused in module-bridge.js — so nothing plays on underneath
   while the learner is writing.
   ================================================================== */
.ctl-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--cyan); flex: none; margin-left: -2px;
  box-shadow: 0 0 8px rgba(46, 209, 243, .8);
}
.ctl-dot[hidden] { display: none; }

#notesScrim {
  position: absolute; inset: 0; z-index: 78;
  background: rgba(2, 7, 14, .62);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  animation: notesFade .18s ease both;
}
#notesScrim[hidden], #notesPanel[hidden], #notesHint[hidden], #fsHint[hidden] { display: none; }
@keyframes notesFade { from { opacity: 0 } to { opacity: 1 } }

/* Left, not right. The Notes button lives in the bottom-left cluster with the
   language and caption controls, so a panel opening on the far side of the
   screen came from nowhere; and the right edge is where several modules put
   their own panels (Module 3's insight, Module 4's notifications). */
#notesPanel {
  position: absolute; z-index: 80;
  left: 22px; top: 76px; bottom: 22px;
  width: min(430px, calc(100vw - 44px));
  display: flex; flex-direction: column;
  border-radius: 16px; overflow: hidden;
  border: 1px solid rgba(120, 196, 255, .22);
  background: linear-gradient(180deg, rgba(10, 20, 34, .97), rgba(6, 13, 24, .97));
  box-shadow: 0 26px 70px rgba(0, 0, 0, .6), 0 0 0 1px rgba(3, 9, 16, .9);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  animation: notesIn .22s cubic-bezier(.2, .8, .3, 1) both;
}
@keyframes notesIn { from { opacity: 0; transform: translateX(14px) } to { opacity: 1; transform: none } }

.notes-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 17px 18px 14px; border-bottom: 1px solid rgba(255, 255, 255, .07);
}
.notes-head h2 { margin: 0; font-size: 15px; font-weight: 700; letter-spacing: .01em; }
.notes-sub { margin: 5px 0 0; font-size: 11.5px; line-height: 1.45; color: var(--muted, #8695ae); }
#notesClose {
  flex: none; width: 30px; height: 30px; border-radius: 8px; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, .1); background: rgba(255, 255, 255, .03);
  color: var(--muted, #8695ae); display: grid; place-items: center;
}
#notesClose:hover { color: #fff; border-color: rgba(120, 196, 255, .5); }

#notesText {
  flex: 1; min-height: 0; resize: none;
  padding: 16px 18px; border: 0; outline: none;
  background: transparent; color: #e8edf7;
  font: inherit; font-size: 14px; line-height: 1.62;
}
#notesText::placeholder { color: #5f6d86; }

.notes-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 14px 14px; border-top: 1px solid rgba(255, 255, 255, .07);
}
.notes-status { font-size: 11px; color: var(--muted, #8695ae); }
.notes-actions { display: flex; gap: 8px; }
.notes-ghost, .notes-primary {
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  padding: 8px 13px; border-radius: 9px; border: 1px solid rgba(255, 255, 255, .12);
  background: rgba(255, 255, 255, .03); color: #cfe0f0;
  transition: border-color .15s, background .15s, color .15s;
}
.notes-ghost:hover { color: #ffb4bf; border-color: rgba(255, 130, 150, .5) }
.notes-primary { border-color: rgba(46, 209, 243, .45); color: #d6f4ff; background: rgba(46, 209, 243, .1) }
.notes-primary:hover { background: rgba(46, 209, 243, .2); border-color: var(--cyan) }

/* First-run coach mark, shown once. */
#notesHint {
  position: absolute; z-index: 82; right: 22px; top: 74px;
  width: min(320px, calc(100vw - 44px)); padding: 15px 16px 13px;
  border-radius: 13px; border: 1px solid rgba(46, 209, 243, .4);
  background: linear-gradient(180deg, rgba(9, 28, 44, .98), rgba(6, 16, 28, .98));
  box-shadow: 0 20px 50px rgba(0, 0, 0, .55);
  animation: notesIn .3s cubic-bezier(.2, .8, .3, 1) both;
}
/* Position and --arrow are both set from the live position of the Notes
   button; see aimNotesHint. .points-down flips the tail to the lower edge for
   a button that sits below the card. */
#notesHint:before {
  content: ""; position: absolute; top: -6px; left: var(--arrow, 224px);
  width: 11px; height: 11px; transform: rotate(45deg);
  background: rgba(9, 28, 44, .98);
  border-left: 1px solid rgba(46, 209, 243, .4); border-top: 1px solid rgba(46, 209, 243, .4);
}
#notesHint.points-down:before {
  top: auto; bottom: -6px;
  border-left: 0; border-top: 0;
  border-right: 1px solid rgba(46, 209, 243, .4);
  border-bottom: 1px solid rgba(46, 209, 243, .4);
}
#notesHint p { margin: 0 0 11px; font-size: 12.5px; line-height: 1.5; color: #c8dcea }
#notesHint strong { color: #fff }
#notesHintOk {
  font: inherit; font-size: 11.5px; font-weight: 600; cursor: pointer;
  padding: 7px 13px; border-radius: 8px; color: #d6f4ff;
  border: 1px solid rgba(46, 209, 243, .45); background: rgba(46, 209, 243, .1);
}
#notesHintOk:hover { background: rgba(46, 209, 243, .22) }

/* The fullscreen coach mark borrows the Notes card's shape, tail and aiming
   code, but not its paragraph: this one is an offer, not an explanation, so it
   is the offer and a way to decline it and nothing else. It sizes to its
   content rather than to a fixed width — "Öppna i helskärm" and "Open in
   fullscreen" are not the same length. */
#fsHint {
  position: absolute; z-index: 83; right: 22px; top: 74px;
  width: max-content; max-width: calc(100vw - 44px); padding: 9px 10px;
  border-radius: 13px; border: 1px solid rgba(46, 209, 243, .4);
  background: linear-gradient(180deg, rgba(9, 28, 44, .98), rgba(6, 16, 28, .98));
  box-shadow: 0 20px 50px rgba(0, 0, 0, .55);
  animation: notesIn .3s cubic-bezier(.2, .8, .3, 1) both;
}
#fsHint:before {
  content: ""; position: absolute; top: -6px; left: var(--arrow, 250px);
  width: 11px; height: 11px; transform: rotate(45deg);
  background: rgba(9, 28, 44, .98);
  border-left: 1px solid rgba(46, 209, 243, .4); border-top: 1px solid rgba(46, 209, 243, .4);
}
#fsHint.points-down:before {
  top: auto; bottom: -6px;
  border-left: 0; border-top: 0;
  border-right: 1px solid rgba(46, 209, 243, .4);
  border-bottom: 1px solid rgba(46, 209, 243, .4);
}
.hint-actions { display: flex; align-items: center; gap: 6px }
.hint-primary, .hint-ghost {
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  padding: 8px 14px; border-radius: 8px; white-space: nowrap;
}
.hint-primary {
  color: #d6f4ff;
  border: 1px solid rgba(46, 209, 243, .45); background: rgba(46, 209, 243, .1);
}
.hint-primary:hover { background: rgba(46, 209, 243, .22) }
/* Just a way out. It carries no label because the card no longer says enough
   to need one — declining an offer this short is a dismissal, not a choice. */
.hint-ghost {
  color: var(--muted, #8695ae); font-size: 15px; line-height: 1;
  padding: 8px 11px;
  border: 1px solid rgba(255, 255, 255, .1); background: transparent;
}
.hint-ghost:hover { color: #fff; border-color: rgba(120, 196, 255, .4) }

@media (max-width: 620px) {
  #notesPanel { right: 12px; left: 12px; top: 68px; bottom: 12px; width: auto }
  #notesHint  { right: 12px; left: 12px; width: auto }
}
@media (prefers-reduced-motion: reduce) {
  #notesPanel, #notesHint, #fsHint, #notesScrim { animation: none }
}

.notes-where-mod {
  display: block; margin-top: 3px;
  font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
  color: rgba(46, 209, 243, .85);
}

/* ==================================================== POPOVER MENUS
   Used by the language picker. A list rather than a toggle, so the learner
   can see the choices and so a third language is a config change and not a
   redesign.

   It drops DOWN from its control. It used to rise, because the controls used
   to be a strip along the bottom of the frame and there was nothing but course
   above them. The chrome now sits in the top-left, so rising sent the list off
   the top of the screen and cut it in half. Down is the direction with room in
   it — and openLangMenu() measures after painting and flips back up on the rare
   screen too short for the drop, so neither edge can clip it.
   ==================================================================== */
.ctl-wrap { position: relative; display: inline-flex; }
.pop-menu {
  position: absolute; top: calc(100% + 8px); bottom: auto; left: 0; z-index: 90;
  margin: 0; padding: 5px; list-style: none;
  min-width: 176px; border-radius: 12px;
  /* A short screen scrolls the list rather than growing past the frame. */
  max-height: min(260px, 62vh); overflow-y: auto; overscroll-behavior: contain;
  border: 1px solid rgba(120, 196, 255, .24);
  background: linear-gradient(180deg, rgba(10, 20, 34, .98), rgba(6, 13, 24, .98));
  box-shadow: 0 18px 44px rgba(0, 0, 0, .55);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  animation: popIn .16s cubic-bezier(.2, .8, .3, 1) both;
}
/* The fallback, chosen by measurement, not by hope. */
.pop-menu.drop-up { top: auto; bottom: calc(100% + 8px);
  animation: popInUp .16s cubic-bezier(.2, .8, .3, 1) both; }
.pop-menu[hidden] { display: none; }
@keyframes popIn   { from { opacity: 0; transform: translateY(-5px) } to { opacity: 1; transform: none } }
@keyframes popInUp { from { opacity: 0; transform: translateY(5px) }  to { opacity: 1; transform: none } }
.pop-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 10px; border: 0; border-radius: 8px; cursor: pointer;
  background: transparent; color: #cfe0f0; font: inherit; font-size: 13px;
  text-align: left; transition: background .13s, color .13s;
}
.pop-item:hover, .pop-item:focus-visible { background: rgba(46, 209, 243, .12); color: #fff; outline: none; }
.pop-item.is-on { color: #fff; }
.pop-code {
  flex: none; min-width: 30px; padding: 2px 6px; border-radius: 5px;
  border: 1px solid rgba(255, 255, 255, .14); background: rgba(255, 255, 255, .04);
  font-family: var(--mono, monospace); font-size: 10.5px; letter-spacing: .08em; text-align: center;
}
.pop-item.is-on .pop-code { border-color: rgba(46, 209, 243, .6); color: var(--cyan, #2ed1f3); }
.pop-label { flex: 1; }
.pop-tick { opacity: 0; color: var(--cyan, #2ed1f3); font-size: 12px; }
.pop-item.is-on .pop-tick { opacity: 1; }

/* The subtitle size step sits beside CC and only exists while CC is on. */
.ctl-sub .ctl-text { font-family: var(--mono, monospace); font-size: 11px; letter-spacing: .06em; }
.ctl[hidden] { display: none; }
