/* =====================================================================
   OTC Learning Platform — catalogue, account and admin layer.

   Built on the tokens the course engine already defines in engine.css
   (--bg, --gold, --panel, --border and the rest), so the shop and the
   lessons look like one product in all three themes rather than two
   applications bolted together.
   ===================================================================== */

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--selection); }

a { color: var(--gold-bright); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { margin: 0 0 .35em; line-height: 1.2; font-weight: 700; }
h1 { font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2.1rem); }
h2 { font-size: 1.25rem; }
p  { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

.lede  { color: var(--text-soft); }
.hint  { color: var(--muted); font-size: .875rem; margin: .4em 0 0; }
.hint.small { font-size: .8rem; }
.mono  { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.centred { text-align: center; }
.icon  { flex-shrink: 0; vertical-align: -.2em; }

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ---------------------------------------------------------------------
   Layout
   --------------------------------------------------------------------- */
#app { max-width: 1180px; margin: 0 auto; padding: 28px 20px 72px; }
body.bare #app { max-width: none; padding: 0; }

#site-header {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 18px;
  padding: 10px 20px;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}
/* Same leak as .theme-toggle below: engine.css styles .brand for the course
   player's sidebar, where the logo sits above a vertical nav and so carries
   padding-bottom: 20px and margin-bottom: 12px. In a horizontal header that
   is 32px of nothing, and it was making the header 85px tall on a phone. */
#site-header .brand {
  padding: 0; margin: 0;
  display: flex; align-items: center; flex-shrink: 0;
}
#site-header .brand img { height: 34px; width: auto; display: block; }

.site-nav { display: flex; gap: 4px; margin-left: 8px; }
.site-nav a {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 14px; border-radius: 999px;
  color: var(--text-soft); font-size: .9rem; font-weight: 600;
  text-decoration: none;
}
.site-nav a:hover { background: var(--overlay-2); color: var(--text); }
.site-nav a.on { background: var(--gold-wash); color: var(--gold-bright); }

.header-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }

/* engine.css positions .theme-toggle as fixed at the top right, which was
   right when the course player owned the whole page. Inside this header it
   floated over the user's name and cut it off, so the positioning is reset
   here. The player's own overlay controls are left alone. */
#site-header .theme-toggle {
  position: static; z-index: auto;
  box-shadow: none; backdrop-filter: none;
  display: flex; gap: 2px;
  background: var(--switch-bg); border: 1px solid var(--border);
  padding: 3px; border-radius: 999px;
}
#site-header .theme-toggle button {
  border: 0; background: none; cursor: pointer; font-size: 14px;
  width: 30px; height: 30px; border-radius: 999px; line-height: 1;
  display: grid; place-items: center;
}
#site-header .theme-toggle button:hover { background: var(--overlay-2); }
#site-header .theme-toggle button.on {
  background: linear-gradient(135deg, var(--gold), var(--gold-2));
  box-shadow: 0 2px 8px var(--gold-soft);
}

.user-menu { position: relative; }
.user-btn {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 12px 5px 5px; border-radius: 999px;
  background: var(--overlay-1); border: 1px solid var(--border);
  color: var(--text); cursor: pointer; font: inherit; font-size: .875rem;
}
.user-btn:hover { background: var(--overlay-hover); }
.avatar {
  width: 30px; height: 30px; border-radius: 999px; flex-shrink: 0;
  display: grid; place-items: center;
  background: linear-gradient(140deg, var(--gold), var(--gold-2));
  color: var(--dark-ink); font-weight: 800; font-size: .75rem;
}
.user-name { max-width: 190px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }

.menu {
  position: absolute; right: 0; top: calc(100% + 8px); min-width: 200px;
  background: var(--panel-solid); border: 1px solid var(--border);
  border-radius: 12px; padding: 6px; z-index: 50;
  box-shadow: 0 18px 40px rgba(0,0,0,.35);
}
.menu a, .menu button {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 12px; border-radius: 8px; border: 0; background: none;
  color: var(--text); font: inherit; font-size: .9rem; text-align: left;
  cursor: pointer; text-decoration: none;
}
.menu a:hover, .menu button:hover { background: var(--overlay-2); text-decoration: none; }

/* The theme switcher inside the user menu. It exists for phones: the
   header's copy is hidden below 620px, and until this was added a
   learner on a phone could not change the theme anywhere in the site,
   including inside a lesson. Hidden on desktop, where the header's own
   switcher is a few pixels away.

   engine.css positions .theme-toggle as fixed at the top right, from
   when the course player owned the whole page, so that has to be undone
   here the same way #site-header does it. The buttons override .menu
   button, which would otherwise stretch each one to the full width. */
.menu .theme-row { display: none; }
@media (max-width: 620px) {
  .menu .theme-row {
    display: block;
    border-top: 1px solid var(--border);
    margin: 6px -6px -6px; padding: 8px 12px 10px;
  }
}
.menu .theme-row-label {
  display: block; font-size: .72rem; text-transform: uppercase;
  letter-spacing: .08em; color: var(--muted); font-weight: 700;
  margin-bottom: 7px;
}
.menu .theme-toggle {
  position: static; z-index: auto; box-shadow: none; backdrop-filter: none;
  display: flex; gap: 6px; padding: 0; border: 0; background: none;
}
.menu .theme-toggle button {
  width: 42px; height: 42px; padding: 0; flex: 0 0 auto;
  border-radius: 12px; background: var(--switch-bg);
  border: 1px solid var(--border); font-size: 17px;
  display: grid; place-items: center;
}
.menu .theme-toggle button.on, .menu .theme-toggle button.active {
  background: linear-gradient(135deg, var(--gold), var(--gold-2));
  border-color: transparent; box-shadow: 0 2px 10px var(--gold-soft);
}

.page-head {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 16px; flex-wrap: wrap; margin-bottom: 24px;
}
.section-h {
  font-size: .8rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--muted); font-weight: 700; margin: 32px 0 14px;
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
}
/* The course count beside a category heading. Quiet, and not uppercase,
   so the heading itself stays the thing being read. */
.section-count {
  text-transform: none; letter-spacing: 0; font-weight: 600;
  font-size: .78rem; color: var(--muted); opacity: .72;
}
.back-link { display: inline-flex; align-items: center; gap: 6px;
  color: var(--muted); font-size: .9rem; margin-bottom: 18px; }

/* ---------------------------------------------------------------------
   Buttons, fields
   --------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 42px; padding: 0 20px; border-radius: 11px;
  border: 1px solid transparent; cursor: pointer;
  font: inherit; font-size: .925rem; font-weight: 650;
  text-decoration: none; transition: filter .16s, background .16s, transform .1s;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn-lg { min-height: 50px; padding: 0 26px; font-size: 1rem; }
.btn-primary {
  background: linear-gradient(140deg, var(--gold), var(--gold-2));
  color: var(--dark-ink);
}
.btn-primary:hover { filter: brightness(1.07); }
.btn-primary.danger { background: var(--red); color: #fff; }
.btn-ghost {
  background: var(--overlay-1); border-color: var(--border); color: var(--text);
}
.btn-ghost:hover { background: var(--overlay-hover); }
.btn-block { width: 100%; }
.btn-sm { min-height: 32px; padding: 0 13px; font-size: .84rem; }

.field { margin-bottom: 16px; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.label { display: block; font-size: .84rem; font-weight: 650;
  color: var(--text-soft); margin-bottom: 6px; }
.req { color: var(--gold); margin-left: 3px; }
.opt { color: var(--muted); font-weight: 400; font-size: .82em; }

.input {
  width: 100%; min-height: 44px; padding: 10px 13px;
  background: var(--input-bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 11px;
  font: inherit; font-size: .95rem;
}
.input:focus { outline: none; border-color: var(--gold-soft);
  box-shadow: 0 0 0 3px var(--gold-wash); }
.input:disabled { opacity: .6; }
.input::placeholder { color: var(--muted); }

.code-input {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.15rem; letter-spacing: .13em; text-transform: uppercase;
  text-align: center;
}

/* ---------------------------------------------------------------------
   Notes, chips
   --------------------------------------------------------------------- */
.note {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 12px 14px; border-radius: 11px; margin: 14px 0;
  background: var(--accent-chip); border: 1px solid var(--accent-chip-border);
  color: var(--text-soft); font-size: .89rem;
}
.note.bad  { background: color-mix(in srgb, var(--red) 12%, transparent);
             border-color: color-mix(in srgb, var(--red) 34%, transparent);
             color: var(--text); }
.note.warn { background: var(--gold-wash); border-color: var(--gold-soft); }
.note.good { background: color-mix(in srgb, var(--green) 12%, transparent);
             border-color: color-mix(in srgb, var(--green) 32%, transparent); }

.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: 999px;
  background: var(--gold-wash); border: 1px solid var(--gold-soft);
  color: var(--gold-bright); font-size: .74rem; font-weight: 700;
  letter-spacing: .02em; white-space: nowrap;
}
.chip.ghost { background: var(--overlay-1); border-color: var(--border);
  color: var(--muted); }
.chip.good  { background: color-mix(in srgb, var(--green) 14%, transparent);
  border-color: color-mix(in srgb, var(--green) 36%, transparent); color: var(--green); }
.chip.warn  { background: var(--gold-wash); border-color: var(--gold-soft);
  color: var(--gold-bright); }
.chip.bad   { background: color-mix(in srgb, var(--red) 14%, transparent);
  border-color: color-mix(in srgb, var(--red) 36%, transparent); color: var(--red); }

/* ---------------------------------------------------------------------
   Course cards
   --------------------------------------------------------------------- */
.course-grid {
  display: grid; gap: 18px;
  grid-template-columns: repeat(auto-fill, minmax(285px, 1fr));
}

.course-card {
  display: flex; flex-direction: column; gap: 10px;
  padding: 20px; border-radius: var(--radius);
  background: linear-gradient(155deg, var(--card-grad-a), var(--card-grad-b));
  border: 1px solid var(--border); cursor: pointer;
  transition: transform .16s, border-color .16s, box-shadow .16s;
}
.course-card:hover, .course-card:focus-visible {
  transform: translateY(-3px);
  border-color: var(--gold-soft);
  box-shadow: 0 16px 36px rgba(0,0,0,.28);
}
.course-card.owned {
  background: linear-gradient(155deg, var(--card-grad-c), var(--card-grad-d));
}
.course-top { display: flex; gap: 6px; flex-wrap: wrap; }
.course-card h3 { font-size: 1.1rem; margin: 4px 0 0; }
.course-strap { color: var(--text-soft); font-size: .9rem; margin: 0; }
.course-meta {
  display: flex; gap: 14px; flex-wrap: wrap; margin-top: auto;
  padding-top: 8px; color: var(--muted); font-size: .82rem;
}
.course-meta span { display: inline-flex; align-items: center; gap: 5px; }
.course-price { font-size: 1.3rem; font-weight: 800; color: var(--gold-bright);
  margin: 6px 0 2px; }
.course-price .free { color: var(--green); }
.course-foot { font-size: .82rem; color: var(--muted); margin: 0;
  display: flex; align-items: center; gap: 5px; }
.course-foot.warn { color: var(--gold-bright); }
.course-foot.bad  { color: var(--red); }

.bar { display: block; height: 6px; border-radius: 999px;
  background: var(--track-bg); overflow: hidden; margin: 4px 0; }
.bar span { display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--gold-2), var(--gold)); }

.skel-card {
  height: 236px; border-radius: var(--radius);
  background: linear-gradient(90deg, var(--overlay-1), var(--overlay-2), var(--overlay-1));
  background-size: 200% 100%; animation: shimmer 1.3s infinite;
}
@keyframes shimmer { to { background-position: -200% 0; } }

/* ---------------------------------------------------------------------
   Course detail
   --------------------------------------------------------------------- */
.detail-grid { display: grid; grid-template-columns: 1fr 330px; gap: 32px;
  align-items: start; }
.detail-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.detail-strap { font-size: 1.08rem; color: var(--gold-bright); font-weight: 600; }
.detail-facts { display: flex; gap: 28px; flex-wrap: wrap; margin: 22px 0;
  padding: 16px 0; border-top: 1px solid var(--divider);
  border-bottom: 1px solid var(--divider); }
.detail-facts div { display: flex; flex-direction: column; }
.detail-facts b { font-size: 1.3rem; color: var(--gold-bright); }
.detail-facts span { font-size: .8rem; color: var(--muted); }

.unit-list { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 2px; }
.unit-list li {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 14px; border-radius: 11px; background: var(--overlay-1);
}
.unit-list li.locked { opacity: .62; }
.unit-num {
  width: 30px; height: 30px; flex-shrink: 0; border-radius: 8px;
  display: grid; place-items: center; background: var(--overlay-2);
  color: var(--muted); font-size: .8rem; font-weight: 700;
}
.unit-body { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.unit-body b { font-size: .93rem; font-weight: 650; }
.unit-body span { font-size: .82rem; color: var(--muted); }

.panel {
  padding: 22px; border-radius: var(--radius);
  background: var(--panel-solid); border: 1px solid var(--border);
}
.panel.wide { margin-bottom: 20px; }
.buy-panel { position: sticky; top: 82px; }
.panel-badge { display: flex; align-items: center; gap: 8px; font-weight: 700;
  color: var(--green); margin-bottom: 8px; }
.buy-price { font-size: 1.9rem; font-weight: 800; color: var(--gold-bright);
  margin: 0 0 4px; }
.buy-price .free { color: var(--green); }

/* ---------------------------------------------------------------------
   Promotional pricing

   The old price stays on the card, struck through. A discount with
   nothing to compare it against is just a price, and a learner deciding
   whether to spend 25,000 francs is entitled to see what it was.

   .was is deliberately quiet and .now carries the weight, so the figure
   they will actually be asked for is the one the eye lands on.
   --------------------------------------------------------------------- */
.course-price.has-offer,
.buy-price.has-offer {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px;
}
.was {
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
  color: var(--muted);
  font-weight: 600;
  font-size: .62em;
}
.course-price .now, .buy-price .now { color: var(--gold-bright); }
.off {
  display: inline-flex; align-items: center;
  padding: 2px 9px; border-radius: 999px;
  font-size: .5em; font-weight: 800; letter-spacing: .03em;
  background: color-mix(in srgb, var(--green) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--green) 38%, transparent);
  color: var(--green);
  white-space: nowrap;
}
.offer-ends { color: var(--green); margin-top: 2px; }

/* In the buy dialog header the type is much smaller, so the same
   elements need their own scale rather than an em of the parent. */
.modal-sub .was { font-size: .9rem; }
.modal-sub .off { font-size: .68rem; padding: 1px 8px; }

/* The admin list: a struck-through price beside the discounted one. */
.order-row .hint .was { font-size: .95em; margin-right: 4px; }
.chip.sale {
  background: color-mix(in srgb, var(--green) 14%, transparent);
  border-color: color-mix(in srgb, var(--green) 36%, transparent);
  color: var(--green);
}
.buy-panel .btn { margin-top: 10px; }

/* ---------------------------------------------------------------------
   Authentication
   --------------------------------------------------------------------- */
.auth-wrap { min-height: 100vh; display: grid; grid-template-columns: 1fr 1fr; }
.auth-card {
  display: flex; flex-direction: column; justify-content: center;
  padding: 48px min(8vw, 72px); max-width: 560px; width: 100%;
  margin: 0 auto;
}
.auth-logo { height: 42px; width: auto; align-self: flex-start; margin-bottom: 28px; }
.auth-card.centred { text-align: center; align-items: center; }
.auth-card .btn { margin-top: 8px; }
.auth-switch { margin-top: 22px; font-size: .9rem; color: var(--muted); }

.auth-aside {
  display: flex; flex-direction: column;
  padding: 48px min(6vw, 60px);
  background: var(--hero-grad);
  border-left: 1px solid var(--border);
  overflow-y: auto;
  position: relative; isolation: isolate;

  /* How the photograph reads through the panel, per theme.

     The washed file is deliberately pale, mean 202 of 255, which is
     right on the three light themes and exactly wrong on midnight: at
     any opacity worth having it lifts the dark panel to a mid grey and
     the light text on top loses its contrast. Measured, midnight came
     out at 1.00 against text that needs 4.5.

     So dark themes darken the picture before fading it, and light
     themes leave it alone. Default is midnight, matching engine.css
     where `:root` and [data-theme="midnight"] share a block. */
  --aside-shot: .5;
  --aside-shot-filter: brightness(.30) saturate(.8);
}
[data-theme="paper"]    .auth-aside,
[data-theme="daylight"] .auth-aside,
[data-theme="lavender"] .auth-aside {
  --aside-shot: .75;
  --aside-shot-filter: none;
}
/* Centred when there is room, scrollable from the top when there is not.
   justify-content: center would push the heading above the top edge on a
   short viewport, where scrolling cannot reach it. margin-block: auto on
   the inner block centres without ever doing that. */
.auth-aside-inner { margin-block: auto; width: 100%; }
/* The photograph fills the whole panel and sits behind the text.

   It stays an <img> rather than becoming a CSS background for two
   reasons: the src can carry ?v=ASSET_VERSION, which prepare-deploy
   stamps and a stylesheet URL could not, and loading="lazy" keeps a
   phone from fetching it while the panel is display:none. z-index -1
   with isolation on the parent puts it behind .auth-aside-inner
   without either of them needing a stacking context of its own. */
.auth-shot {
  position: absolute; inset: 0; z-index: -1;
  width: 100%; height: 100%;
  object-fit: cover; object-position: 64% center;
  opacity: var(--aside-shot);
  filter: var(--aside-shot-filter);
  pointer-events: none; user-select: none;
}

/* The scrim, between the photograph and the words.

   A washed photograph is still a photograph: the monitor in it is full
   of small high-contrast detail, and body text laid straight over that
   is unreadable however pale the picture is. This lays the panel's own
   background colour back over the image, heaviest where the text
   starts and clearing towards the right edge so the picture still
   reads. color-mix against --bg means it follows the theme without a
   per-theme rule, the way the sticky header at the top of this file
   already does.

   It comes after .auth-shot in the document, so at the same z-index it
   paints above the image and still below .auth-aside-inner. */
.auth-aside::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(100deg,
    color-mix(in srgb, var(--bg) 95%, transparent) 0%,
    color-mix(in srgb, var(--bg) 90%, transparent) 45%,
    color-mix(in srgb, var(--bg) 72%, transparent) 78%,
    color-mix(in srgb, var(--bg) 40%, transparent) 100%);
  pointer-events: none;
}
.auth-aside h2 { font-size: 1.5rem; color: var(--gold-bright); }
.auth-lede { color: var(--text); font-weight: 600; margin-top: 6px; }
.auth-points { list-style: none; margin: 22px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 12px; }
.auth-points li { display: flex; gap: 12px; align-items: flex-start;
  color: var(--text-soft); font-size: .95rem; }
.auth-points .icon { color: var(--gold); margin-top: 2px; flex: 0 0 auto; }
.auth-cta { margin-top: 22px; color: var(--gold-bright); font-weight: 600; }

.steps { padding-left: 20px; color: var(--text-soft); font-size: .92rem;
  display: flex; flex-direction: column; gap: 8px; }
.steps code { background: var(--overlay-2); padding: 1px 6px; border-radius: 5px;
  font-size: .88em; }

.big-icon {
  width: 68px; height: 68px; border-radius: 999px; margin: 0 auto 16px;
  display: grid; place-items: center;
  background: var(--gold-wash); color: var(--gold); border: 1px solid var(--gold-soft);
}
.big-icon.good { background: color-mix(in srgb, var(--green) 13%, transparent);
  border-color: color-mix(in srgb, var(--green) 34%, transparent); color: var(--green); }

/* ---------------------------------------------------------------------
   Orders, payment methods
   --------------------------------------------------------------------- */
.method-list { display: flex; flex-direction: column; gap: 7px; margin: 16px 0; }
.method {
  display: flex; align-items: center; gap: 12px; cursor: pointer;
  padding: 12px 14px; border-radius: 11px;
  background: var(--overlay-1); border: 1px solid var(--border);
}
.method:hover { background: var(--overlay-hover); }
.method:has(input:checked) { border-color: var(--gold-soft); background: var(--gold-wash); }
.method input { width: 18px; height: 18px; accent-color: var(--gold); flex-shrink: 0; }
.method span { display: flex; flex-direction: column; }
.method b  { font-size: .93rem; font-weight: 650; }
.method em { font-style: normal; font-size: .82rem; color: var(--muted); }

.order-list { display: flex; flex-direction: column; gap: 10px; }
.order-row {
  display: flex; justify-content: space-between; align-items: center; gap: 16px;
  padding: 16px 18px; border-radius: 13px; flex-wrap: wrap;
  background: var(--panel-solid); border: 1px solid var(--border);
}
.order-row > div:first-child { display: flex; flex-direction: column; gap: 2px; }
.order-row .mono { font-size: .82rem; color: var(--gold-bright); }
.order-right { display: flex; align-items: center; gap: 12px; }

.ref { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.4rem; font-weight: 700; color: var(--gold-bright);
  letter-spacing: .05em; margin: 0 0 4px; }

.summary { display: grid; grid-template-columns: auto 1fr; gap: 8px 18px;
  margin: 18px 0; font-size: .92rem; }
.summary dt { color: var(--muted); }
.summary dd { margin: 0; text-align: right; }

/* ---------------------------------------------------------------------
   Empty states, loading
   --------------------------------------------------------------------- */
/* ---------------------------------------------------------------------
   Tables

   These had no styling at all. The Code requests screen was rendering a
   raw HTML table: no cell padding, no row separators, and cells falling
   back to vertical-align: middle, which is what threw the columns out of
   line. A row where one cell is one line and the next is three centres
   them against each other, so nothing sat on the same baseline.
   ------------------------------------------------------------------ */

.table-wrap {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
  overflow-x: auto;              /* narrow screens scroll, never squash */
  -webkit-overflow-scrolling: touch;
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: .93rem;
}

.table th {
  text-align: left;
  vertical-align: bottom;
  white-space: nowrap;
  padding: 13px 16px;
  font-size: .78rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
  background: var(--overlay-1);
}

/* Top, not middle. This is the alignment fix. */
.table td {
  vertical-align: top;
  padding: 14px 16px;
  border-bottom: 1px solid var(--divider);
  line-height: 1.45;
  /* A work address can be fifty characters with no space in it. Without
     this the browser sizes that column to the whole address and squeezes
     every other column, so a two-word name wraps while an email does not. */
  overflow-wrap: break-word;
}
.table td:first-child { min-width: 11ch; }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--overlay-1); }

/* The first line of every cell should sit on the same line as the first
   line of its neighbours, so nothing above it may add leading. */
.table td > :first-child { margin-top: 0; }
.table td .hint { margin: 3px 0 0; }
.table td code {
  display: inline-block;
  padding: 2px 7px; border-radius: 6px;
  background: var(--overlay-2); font-size: .85rem;
}

/* The action column. Buttons had only whitespace between them and sat
   flush against the date. */
.table td:last-child { text-align: right; }
.table td:last-child .btn { margin: 0 0 0 8px; }
.table td:last-child .btn:first-child { margin-left: 0; }
.table td:last-child .hint { text-align: right; }

.table .nowrap { white-space: nowrap; }
.strong { font-weight: 650; }
.muted  { color: var(--muted); }

@media (max-width: 620px) {
  .table th, .table td { padding: 11px 12px; }
}

/* ---------------------------------------------------------------------
   Waiting for approval

   A code that needs approval creates no order, so this is the only place
   a learner can see that their request exists and is being dealt with.
   ------------------------------------------------------------------ */

.waiting-box { border-color: var(--gold-soft); }
.waiting-box h3 {
  display: flex; align-items: center; gap: 9px;
  margin: 0 0 8px; font-size: 17px;
}
.waiting-box h3 svg { color: var(--gold); flex: none; }
.waiting-box > .lede { margin: 0 0 16px; }

.waiting-list { list-style: none; padding: 0; margin: 0 0 4px; }
.waiting-list li {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap;
  padding: 13px 0;
  border-top: 1px solid var(--divider);
}
.waiting-list li > div { display: flex; flex-direction: column; gap: 2px; }
.waiting-list .hint { margin: 0; }

.empty { text-align: center; padding: 64px 20px; }
.empty p { color: var(--muted); max-width: 42ch; margin: 0 auto 18px; }
.loading { display: grid; place-items: center; padding: 56px; }

.spin {
  display: inline-block; width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: spin .7s linear infinite; vertical-align: -2px;
}
.spin.big { width: 30px; height: 30px; border-width: 3px; color: var(--gold); }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------------------------------------------------------------------
   Toasts and modal
   --------------------------------------------------------------------- */
.toast-stack {
  position: fixed; right: 18px; bottom: 18px; z-index: 200;
  display: flex; flex-direction: column; gap: 9px; max-width: min(400px, calc(100vw - 36px));
}
.toast {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 13px 15px; border-radius: 12px;
  background: var(--panel-solid); border: 1px solid var(--border);
  box-shadow: 0 14px 34px rgba(0,0,0,.34);
  font-size: .9rem; animation: toast-in .2s ease-out;
}
.toast.out { opacity: 0; transform: translateY(6px); transition: .2s; }
.toast span { flex: 1; }
.toast.success { border-color: color-mix(in srgb, var(--green) 42%, transparent); }
.toast.success .icon { color: var(--green); }
.toast.error   { border-color: color-mix(in srgb, var(--red) 42%, transparent); }
.toast.error .icon { color: var(--red); }
.toast.warning .icon { color: var(--gold); }
.toast.info .icon { color: var(--accent); }
.toast-close { background: none; border: 0; color: var(--muted); cursor: pointer;
  padding: 0; display: grid; place-items: center; }
@keyframes toast-in { from { opacity: 0; transform: translateY(10px); } }

.modal-scrim {
  position: fixed; inset: 0; z-index: 150;
  background: rgba(4, 8, 18, .66); backdrop-filter: blur(3px);
  display: grid; place-items: center; padding: 20px;
  animation: fade .14s ease-out;
}
@keyframes fade { from { opacity: 0; } }
.modal {
  width: 100%; max-width: 560px; max-height: calc(100vh - 40px);
  display: flex; flex-direction: column;
  background: var(--panel-solid); border: 1px solid var(--border);
  border-radius: 17px; box-shadow: 0 26px 70px rgba(0,0,0,.5);
  animation: rise .18s ease-out;
}
@keyframes rise { from { opacity: 0; transform: translateY(14px); } }
.modal.narrow { max-width: 430px; }
.modal.wide   { max-width: 760px; }
.modal-head {
  display: flex; justify-content: space-between; align-items: flex-start; gap: 14px;
  padding: 20px 22px 14px; border-bottom: 1px solid var(--divider);
}
.modal-head h2 { font-size: 1.12rem; margin: 0; }
.modal-sub { margin: 4px 0 0; color: var(--muted); font-size: .88rem; }
.modal-x { background: none; border: 0; color: var(--muted); cursor: pointer;
  padding: 4px; border-radius: 7px; display: grid; place-items: center; }
.modal-x:hover { background: var(--overlay-2); color: var(--text); }
.modal-body { padding: 20px 22px; overflow-y: auto; }
.modal-foot {
  display: flex; justify-content: flex-end; gap: 9px;
  padding: 15px 22px 20px; border-top: 1px solid var(--divider);
}
.modal-foot .btn-block { width: 100%; }

/* ---------------------------------------------------------------------
   Admin
   --------------------------------------------------------------------- */
.head-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.tabs { display: flex; gap: 4px; margin-bottom: 20px;
  border-bottom: 1px solid var(--border); }
.tab-btn {
  padding: 10px 16px; border: 0; background: none; cursor: pointer;
  color: var(--muted); font: inherit; font-size: .92rem; font-weight: 650;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tab-btn:hover { color: var(--text); }
.tab-btn.on { color: var(--gold-bright); border-bottom-color: var(--gold); }

.filters {
  display: grid; gap: 12px; margin-bottom: 20px;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  padding: 16px; border-radius: 13px;
  background: var(--overlay-1); border: 1px solid var(--border);
}
.filters .field { margin: 0; }

/* Reconciliation: the figures to check against a bank statement */
.recon {
  display: grid; grid-template-columns: 240px 1fr; gap: 22px;
  padding: 20px; margin-bottom: 20px; border-radius: 13px;
  background: var(--panel-solid); border: 1px solid var(--border);
}
.recon-total { display: flex; flex-direction: column; gap: 2px;
  padding-right: 22px; border-right: 1px solid var(--divider); }
.recon-total b { font-size: 1.7rem; color: var(--gold-bright); }
.recon-methods { display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.recon-methods div { display: flex; flex-direction: column; }
.recon-methods span { font-size: .8rem; color: var(--muted); }
.recon-methods b { font-size: 1.02rem; }
.recon-methods em { font-style: normal; font-size: .76rem; color: var(--muted); }

.preview-picker {
  display: grid; gap: 4px; max-height: 210px; overflow-y: auto;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  padding: 12px; border-radius: 11px;
  background: var(--overlay-1); border: 1px solid var(--border);
}
.check-inline { display: flex; align-items: center; gap: 9px; cursor: pointer;
  font-size: .86rem; padding: 3px 0; }
.check-inline input { width: 16px; height: 16px; accent-color: var(--gold);
  flex-shrink: 0; }

.order-row .hint { font-size: .8rem; }
code { background: var(--overlay-2); padding: 1px 6px; border-radius: 5px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .88em; }

/* ---------------------------------------------------------------------
   Learning view
   The engine draws its own layout from engine.css. These are only the
   pieces the platform adds around it: a way back to the catalogue and
   the course name in the sidebar.
   --------------------------------------------------------------------- */
/* Shown across the top when someone is reading a free preview unit
   without having enrolled. */
.preview-bar {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; gap: 14px;
  padding: 10px 18px;
  background: var(--gold-wash); border-bottom: 1px solid var(--gold-soft);
  color: var(--text); font-size: .88rem;
}
.preview-bar > span { flex: 1; min-width: 0; }
.preview-bar .icon { color: var(--gold); flex-shrink: 0; }
.app.has-preview-bar { min-height: calc(100vh - 48px); }

@media (max-width: 620px) {
  .preview-bar { flex-wrap: wrap; gap: 8px; padding: 9px 13px; font-size: .82rem; }
  .preview-bar .btn { width: 100%; }
}

/* Preview and owned units on the course page open; locked ones do not. */
.unit-list li.openable { padding: 0; }
.unit-list li.openable .unit-link {
  display: flex; align-items: center; gap: 14px; width: 100%;
  padding: 12px 14px; border-radius: 11px;
  color: inherit; text-decoration: none;
}
/* Listed but locked: shown in full so a prospective student can read the
   whole syllabus, dimmed so it is obvious they cannot open it yet. */
.unit-list li.listed { opacity: .78; }
.unit-list li.listed .unit-num { background: transparent; color: var(--muted); }

.unit-list li.openable { cursor: pointer; }
.unit-list li.openable .unit-num {
  background: var(--gold-wash); color: var(--gold-bright);
}
.unit-list li.openable:hover { background: var(--overlay-2); }
.unit-list li.openable:hover .unit-go { color: var(--gold-bright); }
.unit-go { margin-left: auto; color: var(--muted); }

.learn-back {
  display: flex; align-items: center; gap: 7px;
  padding: 10px 14px; margin-bottom: 4px;
  color: var(--muted); font-size: .84rem; font-weight: 600;
}
.learn-back:hover { color: var(--gold-bright); text-decoration: none; }

.learn-course-name {
  padding: 0 14px 12px;
  color: var(--gold-bright); font-size: .92rem; font-weight: 700;
  line-height: 1.3;
}

/* ---------------------------------------------------------------------
   Demonstration mode
   Deliberately hard to ignore, so invented data is never mistaken for
   real learner records.
   --------------------------------------------------------------------- */
#demo-banner {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 190;
  display: flex; align-items: center; gap: 14px;
  padding: 10px 18px;
  background: var(--gold); color: var(--dark-ink);
  font-size: .86rem; font-weight: 500;
  box-shadow: 0 -2px 14px rgba(0,0,0,.25);
}
#demo-banner span { flex: 1; min-width: 0; }
#demo-banner button {
  flex-shrink: 0; cursor: pointer; border: 0;
  background: rgba(0,0,0,.17); color: var(--dark-ink);
  border-radius: 999px; padding: 5px 16px; min-height: 30px;
  font: inherit; font-size: .84rem; font-weight: 700;
}
#demo-banner button:hover { background: rgba(0,0,0,.29); }
body:has(#demo-banner) #app { padding-bottom: 76px; }

.demo-accounts {
  margin-top: 24px; padding: 16px;
  border-radius: 13px; background: var(--overlay-1);
  border: 1px dashed var(--gold-soft);
}
.demo-accounts > b { font-size: .84rem; color: var(--gold-bright); }
.demo-accounts button {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  width: 100%; margin-top: 6px; padding: 9px 12px;
  border-radius: 9px; border: 1px solid var(--border);
  background: var(--panel-solid); color: var(--text);
  font: inherit; font-size: .84rem; cursor: pointer; text-align: left;
}
.demo-accounts button:hover { border-color: var(--gold-soft); background: var(--gold-wash); }
.demo-accounts button span { font-family: ui-monospace, Menlo, monospace;
  font-size: .8rem; }
.demo-accounts button em { font-style: normal; color: var(--muted);
  font-size: .76rem; text-align: right; }

/* ---------------------------------------------------------------------
   Responsive
   --------------------------------------------------------------------- */
@media (max-width: 900px) {
  .detail-grid { grid-template-columns: 1fr; }
  .buy-panel { position: static; }
  .auth-wrap { grid-template-columns: 1fr; }
  .auth-aside { display: none; }
  .auth-card { max-width: 480px; padding: 36px 24px; }
}

@media (max-width: 620px) {
  #app { padding: 20px 14px 64px; }

  /* The header ran 102px past the right edge of a 375px screen, which put
     "Create account" — the first thing a new learner with an access code
     has to press — entirely off it. Three things fix it, in order of how
     much room they buy:

     1. The theme toggle goes. At 102px it is the widest item here and the
        least important, and the course player carries its own, so a learner
        can still switch theme where they actually read.
     2. The header may wrap. A safety net: if the contents ever grow again,
        they drop to a second row instead of sliding off the screen.
     3. min-width: 0 lets the flex children shrink rather than forcing the
        row wider than its container. */
  #site-header {
    padding: 9px 13px; gap: 10px;
    flex-wrap: wrap; row-gap: 8px;
  }
  #site-header .brand img { height: 28px; }
  /* The direct child only. The user menu hangs off this same header, so
     the unscoped selector also hid the switcher inside it, which is the
     one this breakpoint exists to fall back on. */
  #site-header .header-right > .theme-toggle { display: none; }
  #site-header .site-nav { min-width: 0; margin-left: 0; }

  /* Nav links become icon-only. The labels are bare text nodes rather than
     wrapped in a span, which is why the old `.site-nav a span` rule never
     did anything — so the text is collapsed with font-size instead. The
     icons carry explicit width and height attributes, so they survive it.
     Every destination stays reachable; only the words go. */
  .site-nav a {
    font-size: 0; gap: 0; padding: 8px 10px;
  }
  .site-nav a svg, .site-nav a .icon { font-size: 1rem; }

  .header-right { gap: 8px; min-width: 0; }
  .header-right .btn { padding-left: 13px; padding-right: 13px; white-space: nowrap; }

  .user-name { display: none; }
  .site-nav a { padding: 8px 11px; }
  .site-nav a span { display: none; }
  .field-row { grid-template-columns: 1fr; }
  .course-grid { grid-template-columns: 1fr; }
  .order-row { flex-direction: column; align-items: flex-start; }
  .order-right { width: 100%; justify-content: space-between; }
  .toast-stack { left: 13px; right: 13px; bottom: 13px; max-width: none; }
  .modal-foot { flex-direction: column-reverse; }
  .modal-foot .btn { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; transition-duration: .001ms !important;
  }
}

/* ---------------------------------------------------------------------
   Payment instructions
   Shown after an order is placed and again from My orders. The merchant
   code and the account name are the two things a student must get right,
   so both are large and unambiguous.
   --------------------------------------------------------------------- */
.pay-box {
  margin: 18px 0; padding: 18px;
  border-radius: 13px;
  background: var(--overlay-1); border: 1px solid var(--border);
}
.pay-lead { display: flex; gap: 9px; align-items: flex-start;
  margin: 0 0 14px; font-size: .93rem; color: var(--text-soft); }
.pay-lead .icon { flex-shrink: 0; margin-top: 3px; color: var(--gold); }

.pay-details {
  display: grid; grid-template-columns: auto 1fr; gap: 9px 18px;
  margin: 0 0 4px; font-size: .93rem; align-items: center;
}
.pay-details dt { color: var(--muted); }
.pay-details dd { margin: 0; text-align: right; }

.pay-code {
  display: inline-block;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.18rem; font-weight: 700; letter-spacing: .08em;
  color: var(--gold-bright);
  background: var(--gold-wash); border: 1px solid var(--gold-soft);
  border-radius: 8px; padding: 3px 12px;
}

.pay-qr { margin-top: 16px; text-align: center; }
.pay-qr img {
  display: block; margin: 0 auto 8px;
  width: 100%; max-width: 232px; aspect-ratio: 1 / 1;
  object-fit: contain;
  /* A QR must be scanned off the screen, so it always sits on white
     regardless of the theme the learner is using. */
  background: #fff; padding: 10px; border-radius: 12px;
}
.pay-qr span { font-size: .82rem; color: var(--muted); }

@media (max-width: 520px) {
  .pay-details { grid-template-columns: 1fr; gap: 3px; }
  .pay-details dd { text-align: left; margin-bottom: 8px; }
}

/* ---------- Touch devices ----------
   Width is the wrong test for a thumb: a 700px tablet is still touched.
   These target pointer capability instead, so a tablet gets full-size
   controls even though it is wider than the mobile breakpoint. */
@media (hover: none) and (pointer: coarse) {
  #site-header .theme-toggle button { width: 40px; height: 40px; font-size: 16px; }
  .header-right .btn { min-height: 42px; }
  .site-nav a { min-height: 42px; }
  /* The logo is a link home. Free to enlarge — the 42px buttons beside it
     already set the header's height, so this costs no vertical space. */
  #site-header .brand { min-height: 42px; }
}

/* ---------------------------------------------------------------------
   Course card: artwork, rating, learner count.

   The image sits above the chips and is purely decorative — alt is empty
   and it is aria-hidden, because the course title immediately below says
   everything the picture does. A missing file removes its own wrapper
   (see Catalogue.cardImage), so a course with no artwork gets a shorter
   card rather than a broken icon.
   --------------------------------------------------------------------- */
.course-art {
  margin: -18px -18px 14px;
  border-radius: 14px 14px 0 0;
  overflow: hidden;
  background: var(--overlay-1);
  aspect-ratio: 16 / 9;
}
.course-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.course-proof {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin: 10px 0 0;
}
.course-stars {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-size: 14px;
  line-height: 1;
}
.course-stars .star { color: var(--border); }
.course-stars .star.on { color: #f5b301; }
/* Half star: the gold half is painted with a clipped overlay rather than a
   second glyph, so it lines up at any font size. */
.course-stars .star.half {
  position: relative;
  color: var(--border);
}
.course-stars .star.half::before {
  content: '★';
  position: absolute;
  left: 0; top: 0;
  width: 50%;
  overflow: hidden;
  color: #f5b301;
}
.course-stars b {
  margin-left: 6px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--text-soft);
}
.course-learners {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12.5px;
  color: var(--muted);
}
.course-learners svg { flex-shrink: 0; }
.course-proof-note {
  margin: 5px 0 0;
  font-size: 12px;
  color: var(--muted);
}

@media (max-width: 620px) {
  .course-art { margin: -14px -14px 12px; }
  .course-proof { gap: 10px; }
}

/* ---------------------------------------------------------------------
   English Evaluation Test
   ------------------------------------------------------------------ */
.placement-bar { margin: 8px 0 20px; }
.placement-bar .hint { margin-top: 8px; }

.placement-intro .plain-list { list-style: none; padding: 0; margin: 0 0 22px; }
.placement-intro .plain-list li {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 9px 0; color: var(--text-soft); line-height: 1.5;
}
.placement-intro .plain-list svg { flex: none; margin-top: 3px; color: var(--muted); }

/* ---------- The intro screen ----------
   This is the first thing a new learner sees after registering, and for
   many it is the first page of ours they read properly. It gets a hero
   with the mark on it and an answer to the question they are actually
   asking, which is "why am I being tested". */

.pl-hero {
  text-align: center;
  padding: 34px 24px 30px;
  margin-bottom: 22px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--hero-grad);
}
/* The mark is dark navy artwork with no light version, so on the
   midnight theme it disappeared into the panel. It sits on its own white
   plate instead, which reads on all three themes. */
.pl-hero-logo {
  height: 52px; width: auto; display: block;
  margin: 0 auto 18px;
  background: #fff;
  padding: 9px 15px;
  border-radius: 12px;
  box-sizing: content-box;
}
.pl-hero h1 { margin: 0 0 10px; line-height: 1.2; }
.pl-hero .lede {
  margin: 0 auto; max-width: 62ch;
  color: var(--text-soft); line-height: 1.6;
}

/* Two columns on a desktop, stacked on a phone. The why comes first in
   the markup so a phone reads the reason before the mechanics. */
.pl-grid {
  display: grid; gap: 18px;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
}
@media (max-width: 860px) {
  .pl-grid { grid-template-columns: 1fr; }
}

.pl-grid .card { margin-bottom: 0; }
.pl-grid h2 {
  font-size: 18px; font-weight: 700; margin: 0 0 14px;
}
.pl-why p {
  color: var(--text-soft); line-height: 1.65; margin: 0 0 14px;
}
.pl-note {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 18px 0 0; padding: 13px 15px;
  border: 1px solid var(--accent-chip-border);
  background: var(--accent-chip);
  border-radius: 12px;
  color: var(--text-soft); line-height: 1.55; font-size: 14.5px;
}
.pl-note svg { flex: none; margin-top: 2px; color: var(--accent); }

.pl-steps { list-style: none; padding: 0; margin: 0 0 20px; }
.pl-steps li {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 0 0 18px;
}
.pl-steps li:last-child { padding-bottom: 0; }
/* Same gradient as btn-primary rather than flat --gold. On the lavender
   theme --gold is a mid purple and --dark-ink is white, and white on
   flat #8b5cf6 was too weak to read at this size. */
.pl-step-n {
  flex: none;
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 14px; font-weight: 700; line-height: 1;
  color: var(--dark-ink);
  background: linear-gradient(140deg, var(--gold), var(--gold-2));
}
.pl-steps div { display: flex; flex-direction: column; gap: 3px; }
.pl-steps b { font-size: 15px; }
.pl-steps span { color: var(--muted); line-height: 1.55; font-size: 14.5px; }

.pl-facts {
  list-style: none; padding: 16px 0 0; margin: 0 0 20px;
  border-top: 1px solid var(--divider);
  display: grid; gap: 10px;
}
.pl-facts li {
  display: flex; align-items: center; gap: 9px;
  color: var(--muted); font-size: 14.5px;
}
.pl-facts svg { flex: none; }

.pl-skip-note { margin: 12px 0 0; line-height: 1.5; }

.placement-q .q-text {
  font-size: 1.15rem; line-height: 1.45; margin: 4px 0 20px; color: var(--text);
}
.q-options { display: grid; gap: 10px; }

/* Full width, generously tall, because a good share of these learners
   are answering on a phone with one thumb. */
.q-option {
  display: flex; align-items: center; gap: 14px; width: 100%;
  padding: 15px 16px; min-height: 56px; text-align: left;
  font: inherit; font-size: 15.5px; color: var(--text); cursor: pointer;
  background: var(--overlay-1); border: 1px solid var(--border);
  border-radius: 12px; transition: border-color .12s, background .12s;
}
.q-option:hover { border-color: var(--gold-soft); background: var(--gold-wash); }
.q-option:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 2px; }
.q-letter {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center; font-weight: 800; font-size: 13px;
  background: var(--overlay-2); color: var(--muted);
}
.q-option:hover .q-letter { background: var(--gold-soft); color: var(--dark-ink); }

.listen-box {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding-bottom: 16px; margin-bottom: 16px;
  border-bottom: 1px solid var(--divider);
}

.result-card { display: grid; gap: 20px; }
.result-score { display: flex; align-items: baseline; gap: 10px; }
.result-score b { font-size: 2.6rem; line-height: 1; color: var(--gold-bright); }
.result-score span { color: var(--muted); font-size: .95rem; }
.result-breakdown { display: grid; gap: 10px; margin: 0; }
.result-breakdown > div {
  display: flex; justify-content: space-between; gap: 16px;
  padding-bottom: 9px; border-bottom: 1px solid var(--divider);
}
.result-breakdown dt { color: var(--text-soft); margin: 0; }
.result-breakdown dd { margin: 0; font-weight: 700; color: var(--text); }

/* The level shown on the account page and after registration. */
.level-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 11px; border-radius: 999px; font-weight: 800; font-size: 12.5px;
  background: var(--gold-wash); color: var(--gold-bright);
  border: 1px solid var(--gold-soft);
}

/* Level distribution on the admin evaluation screen. A small column
   chart rather than a table: staff are reading the shape of an intake,
   not the exact numbers, and the numbers sit above each column anyway. */
.level-summary { margin-bottom: 18px; }
.level-summary .section-h { margin-top: 0; }
.level-bars {
  display: grid; grid-template-columns: repeat(5, 1fr);
  gap: 12px; align-items: end; height: 150px;
}
.level-bar { display: flex; flex-direction: column; align-items: center;
  gap: 7px; height: 100%; justify-content: flex-end; }
.level-bar-n { font-weight: 800; font-size: .95rem; color: var(--text); }
.level-bar-track {
  width: 100%; flex: 1; display: flex; align-items: flex-end;
  background: var(--overlay-1); border-radius: 8px; overflow: hidden;
}
.level-bar-track span {
  width: 100%; min-height: 3px; border-radius: 8px;
  background: linear-gradient(180deg, var(--gold), var(--gold-2));
}
.level-bar-l { font-size: .8rem; font-weight: 700; color: var(--muted);
  letter-spacing: .04em; }

/* ---------------------------------------------------------------------
   LEARNERS SCREEN

   Five numbers above a table, and a module breakdown that unfolds
   inside a row of it. The row of numbers wraps rather than scrolls,
   because on a phone five figures side by side are unreadable and five
   stacked in two rows are not.
   ------------------------------------------------------------------ */

.lrn-kpis {
  display: flex; flex-wrap: wrap; gap: 10px;
}
.lrn-kpi {
  flex: 1 1 120px;
  padding: 12px 14px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--overlay-1);
}
.lrn-kpi .v { font-size: 1.5rem; font-weight: 800; line-height: 1.1; }
.lrn-kpi .v.bad { color: var(--red); }
.lrn-kpi .l { margin-top: 2px; font-size: .78rem; color: var(--muted); }

/* The unfolded detail. It sits in a td that spans the whole table, so
   it needs its own padding back: the cell padding would otherwise put
   the nested table hard against the outer one's border. */
/* text-align is reset because this cell IS the last cell of its row, so
   `.table td:last-child { text-align: right }` applies to it and then
   cascades into the nested table, right-aligning every module title. */
.table tr.lrn-detail-row > td {
  background: var(--overlay-1); padding: 14px 16px; text-align: left;
}
.lrn-detail-row .table-wrap { background: var(--panel); }
.lrn-detail-foot {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 10px; margin-top: 12px;
}
.lrn-detail-foot .hint { margin: 0; display: flex; align-items: center; gap: 6px; }

/* The progress cell has a figure above a bar, and the bar's own margin
   pushes the pair off the baseline of its neighbours. */
.table td .bar { margin: 5px 0 0; }
