/* CodeDev Teaching Portal
 *
 * One stylesheet, no framework, no CDN. The CSP is `default-src 'self'`, so an
 * external stylesheet or webfont would simply be blocked — everything the page
 * needs has to ship from this origin. That rules out an icon font and a display
 * face, so the visual identity is carried by colour, depth and spacing instead:
 * a brand gradient, a real elevation scale, and generous rhythm.
 *
 * Both colour schemes are first-class. The chrome (sidebar, present mode) stays
 * dark in either, because it frames content rather than presenting it.
 */

:root {
  /* surfaces, light */
  --bg: #f4f6fb;
  --surface: #ffffff;
  --surface-2: #f2f5fa;
  --surface-3: #e9eef7;
  --border: #dfe4ee;
  --border-strong: #c7cfdf;

  /* text, light */
  --text: #14171f;
  --text-muted: #59637a;
  --text-faint: #8b95a9;

  /* brand + state */
  --brand: #4f46e5;
  --brand-2: #7c3aed;
  --brand-grad: linear-gradient(135deg, #4f46e5 0%, #7c3aed 55%, #a855f7 100%);
  --accent: #2563eb;
  --accent-dark: #1d4ed8;
  --accent-soft: #e8effe;
  --success: #0f7a3d;
  --success-soft: #e2f7e9;
  --warning: #a15c00;
  --warning-soft: #fff2d5;
  --danger: #d1242f;
  --danger-soft: #ffe9e8;
  --info: #0e7490;
  --info-soft: #dff4fa;

  /* chrome — deliberately dark in both schemes */
  --sidebar: linear-gradient(180deg, #10141f 0%, #0a0d15 100%);
  --sidebar-line: rgba(255, 255, 255, .07);
  --sidebar-text: #b9c2d4;
  --sidebar-text-dim: #6b7488;

  /* shape + depth */
  --r-sm: 6px;
  --r: 10px;
  --r-lg: 14px;
  --radius: var(--r);            /* kept: older rules and templates use it */
  --shadow-sm: 0 1px 2px rgba(16, 20, 31, .05);
  --shadow: 0 1px 2px rgba(16, 20, 31, .05), 0 4px 12px rgba(16, 20, 31, .06);
  --shadow-lg: 0 12px 32px rgba(16, 20, 31, .12), 0 2px 8px rgba(16, 20, 31, .06);
  --ring: rgba(79, 70, 229, .35);

  --sidebar-width: 254px;
  --speed: 160ms;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0b0f17;
    --surface: #121826;
    --surface-2: #171e2e;
    --surface-3: #1e2739;
    --border: #242e42;
    --border-strong: #35415c;

    --text: #e7ecf5;
    --text-muted: #97a3b8;
    --text-faint: #6b7688;

    --brand: #6366f1;
    --brand-2: #8b5cf6;
    --brand-grad: linear-gradient(135deg, #6366f1 0%, #8b5cf6 55%, #c084fc 100%);
    --accent: #60a5fa;
    --accent-dark: #3b82f6;
    --accent-soft: #16233c;
    --success: #4ade80;
    --success-soft: #11251b;
    --warning: #e0b341;
    --warning-soft: #2a2214;
    --danger: #fb7185;
    --danger-soft: #2c151c;
    --info: #38bdf8;
    --info-soft: #10263440;

    --sidebar: linear-gradient(180deg, #070a11 0%, #05070c 100%);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .5);
    --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 6px 18px rgba(0, 0, 0, .38);
    --shadow-lg: 0 16px 40px rgba(0, 0, 0, .55), 0 2px 10px rgba(0, 0, 0, .4);
    --ring: rgba(99, 102, 241, .45);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text",
               "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 1.58;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* One visible focus treatment for everything, so keyboard users are never lost.
   :focus-visible rather than :focus, so a mouse click doesn't light it up. */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

h1, h2, h3, h4 { margin: 0 0 .5rem; line-height: 1.25; font-weight: 600; letter-spacing: -.015em; }
h1 { font-size: 1.55rem; }
h2 { font-size: 1.12rem; }
h3 { font-size: 1rem; }

code, kbd, .mono {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: .88em;
}
code {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 5px; padding: .08em .34em;
}

/* Figures line up in columns when they are tabular. */
.stat .n, table.tbl td.num, .num { font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------- layout --- */

.layout { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--sidebar-width);
  flex: 0 0 var(--sidebar-width);
  background: var(--sidebar);
  color: var(--sidebar-text);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  border-right: 1px solid rgba(255, 255, 255, .06);
}

.sidebar-brand {
  padding: 1.15rem 1.15rem 1.05rem;
  border-bottom: 1px solid var(--sidebar-line);
  display: flex;
  align-items: center;
  gap: .65rem;
}

.sidebar-brand strong { color: #fff; font-size: 1rem; letter-spacing: -.02em; }
.sidebar-brand span {
  display: block; font-size: .7rem; color: var(--sidebar-text-dim);
  font-weight: 500; letter-spacing: .02em;
}

.brand-mark {
  width: 32px; height: 32px; border-radius: 9px;
  background: var(--brand-grad);
  display: grid; place-items: center;
  color: #fff; font-weight: 700; font-size: .8rem; letter-spacing: .02em;
  flex: 0 0 32px;
  /* the glow is what stops the mark reading as a flat coloured square */
  box-shadow: 0 4px 14px rgba(124, 58, 237, .45), inset 0 1px 0 rgba(255, 255, 255, .25);
}

.sidebar-nav { padding: .6rem .55rem; flex: 1; }
.sidebar-section {
  padding: .95rem .7rem .35rem;
  font-size: .66rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--sidebar-text-dim);
  font-weight: 700;
}

.sidebar-nav a {
  position: relative;
  display: flex;
  align-items: center;
  gap: .65rem;
  padding: .48rem .7rem;
  margin: 1px 0;
  border-radius: 8px;
  color: var(--sidebar-text);
  font-size: .875rem;
  transition: background var(--speed) ease, color var(--speed) ease;
}
.sidebar-nav a:hover { background: rgba(255, 255, 255, .06); text-decoration: none; color: #fff; }
.sidebar-nav a.active {
  background: rgba(99, 102, 241, .16);
  color: #fff;
  font-weight: 550;
}
/* The bar is drawn rather than bordered so it can sit inside the rounded pill. */
.sidebar-nav a.active::before {
  content: ""; position: absolute; left: -.55rem; top: 50%;
  width: 3px; height: 18px; margin-top: -9px;
  border-radius: 0 3px 3px 0; background: var(--brand-grad);
}
.sidebar-nav .ico {
  width: 18px; text-align: center; flex: 0 0 18px;
  opacity: .7; font-size: .95em;
}
.sidebar-nav a.active .ico { opacity: 1; }

.sidebar-footer {
  border-top: 1px solid var(--sidebar-line);
  padding: .85rem 1.15rem 1rem;
  font-size: .8rem;
}
.sidebar-footer .who {
  color: #fff; font-weight: 550; display: block; text-decoration: none;
}
.sidebar-footer .who:hover, .sidebar-footer .who:focus-visible { text-decoration: underline; }
.sidebar-footer .role {
  color: var(--sidebar-text-dim); font-size: .7rem;
  text-transform: uppercase; letter-spacing: .07em; font-weight: 600;
}
.sidebar-footer form { margin: .6rem 0 0; }
.sidebar-footer button {
  width: 100%; background: rgba(255, 255, 255, .06); color: var(--sidebar-text);
  border: 1px solid rgba(255, 255, 255, .12); border-radius: 8px;
  padding: .4rem; font-size: .8rem; cursor: pointer; font-family: inherit;
  transition: background var(--speed) ease, color var(--speed) ease;
}
.sidebar-footer button:hover { background: rgba(255, 255, 255, .13); color: #fff; }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--border);
  padding: .8rem 1.9rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  position: sticky; top: 0; z-index: 20;
}
/* Browsers without color-mix still need an opaque bar rather than a see-through one. */
@supports not (background: color-mix(in srgb, red 50%, blue)) {
  .topbar { background: var(--surface); }
}
.topbar h1 { margin: 0; font-size: 1.28rem; }
.topbar .topbar-actions { display: flex; gap: .5rem; align-items: center; flex-shrink: 0; }

.content { padding: 1.9rem; flex: 1; max-width: 1400px; width: 100%; }

.breadcrumb { font-size: .8rem; color: var(--text-faint); margin-bottom: .3rem; }
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: var(--accent); }

/* -------------------------------------------------------------- cards --- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.2rem 1.3rem;
  margin-bottom: 1.2rem;
}
.card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; margin-bottom: .95rem;
  padding-bottom: .7rem; border-bottom: 1px solid var(--border);
}
.card-head h2, .card-head h3 { margin: 0; }
.card-head + .table-wrap { margin-top: -.2rem; }

/* Each of these stands on its own. They used to set only the column track and
   inherit `display: grid` from `.grid`, which meant one forgotten `grid` in a
   class attribute silently turned a row of four figures into four full-width
   bars stacked down the page — exactly what happened to the earnings page. */
.grid { display: grid; gap: 1rem; }
.grid-stats {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.grid-cards {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

.stat {
  position: relative; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 1.05rem 1.15rem; box-shadow: var(--shadow-sm);
}
/* A tinted wash instead of a flat panel — the difference between "a number in a
   box" and something that reads as a designed tile. */
.stat::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 130% at 100% 0%,
              color-mix(in srgb, var(--brand) 12%, transparent) 0%, transparent 60%);
}
.stat .n {
  font-size: 1.95rem; font-weight: 680; line-height: 1.1; letter-spacing: -.03em;
}
.stat .l {
  font-size: .72rem; color: var(--text-muted); text-transform: uppercase;
  letter-spacing: .09em; font-weight: 650;
}

.course-card {
  position: relative; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
  padding: 1.15rem 1.2rem; display: flex; flex-direction: column; gap: .6rem;
  transition: transform var(--speed) ease, box-shadow var(--speed) ease,
              border-color var(--speed) ease;
}
/* The rule across the top is the card's only ornament, and it is what makes a
   grid of them scan as a set rather than as a wall of rectangles. */
.course-card::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 3px;
  background: var(--brand-grad); opacity: .85;
}
.course-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
  border-color: var(--border-strong);
}
.course-card h3 { font-size: 1rem; margin: .15rem 0 0; }
.course-card h3 a { color: var(--text); }
.course-card h3 a:hover { color: var(--accent); text-decoration: none; }
.course-card .meta { font-size: .78rem; color: var(--text-muted); margin-top: auto; }

/* Courses are taught in a fixed order; the ordinal makes that order visible. */
.course-ord {
  display: inline-grid; place-items: center;
  min-width: 1.55rem; height: 1.55rem; padding: 0 .35rem;
  border-radius: 7px; font-size: .72rem; font-weight: 700;
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------ lessons --- */

.lesson-row {
  display: flex; align-items: center; gap: .9rem;
  padding: .7rem .55rem; border-bottom: 1px solid var(--border);
  border-radius: var(--r-sm);
  transition: background var(--speed) ease;
}
.lesson-row:hover { background: var(--surface-2); }
.lesson-row:last-child { border-bottom: 0; }
.lesson-num {
  flex: 0 0 2rem; height: 2rem; border-radius: 8px;
  display: grid; place-items: center;
  background: var(--surface-3); color: var(--text-muted);
  font-size: .8rem; font-weight: 650; font-variant-numeric: tabular-nums;
  border: 1px solid var(--border);
}
.lesson-row.done .lesson-num {
  background: var(--success-soft); color: var(--success);
  border-color: color-mix(in srgb, var(--success) 35%, transparent);
}
.lesson-body { min-width: 0; flex: 1; }
.lesson-body .t { font-weight: 550; }
.lesson-body .s { font-size: .78rem; color: var(--text-muted); }
.lesson-actions { display: flex; gap: .4rem; flex-shrink: 0; flex-wrap: wrap; }

/* ------------------------------------------------------------ buttons --- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  padding: .42rem .8rem; border-radius: 8px; cursor: pointer;
  font-size: .84rem; font-weight: 550; font-family: inherit; line-height: 1.4;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-sm);
  transition: background var(--speed) ease, border-color var(--speed) ease,
              transform var(--speed) ease, box-shadow var(--speed) ease;
  white-space: nowrap;
}
.btn:hover { background: var(--surface-2); text-decoration: none; border-color: var(--text-faint); }
.btn:active { transform: translateY(1px); box-shadow: none; }

.btn-primary {
  background: var(--brand-grad); color: #fff; border-color: transparent;
  box-shadow: 0 1px 2px rgba(16, 20, 31, .12), 0 4px 14px color-mix(in srgb, var(--brand) 35%, transparent);
}
.btn-primary:hover { filter: brightness(1.08); background: var(--brand-grad); border-color: transparent; }

.btn-danger { background: var(--danger); color: #fff; border-color: transparent; }
.btn-danger:hover { filter: brightness(1.08); background: var(--danger); }

.btn-sm { padding: .26rem .6rem; font-size: .78rem; }
.btn-block { display: flex; width: 100%; }
.btn:disabled { opacity: .55; cursor: not-allowed; transform: none; }

/* ------------------------------------------------------------- badges --- */

.badge {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .13rem .5rem; border-radius: 999px;
  font-size: .72rem; font-weight: 650; letter-spacing: .01em;
  background: var(--surface-3); color: var(--text-muted);
  border: 1px solid var(--border);
}
.badge-accent  { background: var(--accent-soft);  color: var(--accent);
                 border-color: color-mix(in srgb, var(--accent) 26%, transparent); }
.badge-success { background: var(--success-soft); color: var(--success);
                 border-color: color-mix(in srgb, var(--success) 30%, transparent); }
.badge-warning { background: var(--warning-soft); color: var(--warning);
                 border-color: color-mix(in srgb, var(--warning) 30%, transparent); }
.badge-danger  { background: var(--danger-soft);  color: var(--danger);
                 border-color: color-mix(in srgb, var(--danger) 30%, transparent); }

/* -------------------------------------------------------------- table --- */

.table-wrap { overflow-x: auto; border-radius: var(--r); }
table.tbl { width: 100%; border-collapse: collapse; font-size: .86rem; }
table.tbl th, table.tbl td {
  padding: .55rem .7rem; text-align: left; border-bottom: 1px solid var(--border);
  vertical-align: top;
}
table.tbl th {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .08em;
  color: var(--text-muted); font-weight: 700;
  background: var(--surface-2);
  position: sticky; top: 0;
  border-bottom: 1px solid var(--border-strong);
}
table.tbl tbody tr { transition: background var(--speed) ease; }
table.tbl tbody tr:hover { background: var(--surface-2); }
table.tbl tbody tr:last-child td { border-bottom: 0; }
table.tbl td.num { text-align: right; }

/* The invoice rate is stated, not typed. Without this it reads as an input the
   owner has failed to fill in, sitting a line above the ones that are. */
table.tbl td.fixed-rate {
  color: var(--text-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------- form --- */

.field { margin-bottom: .9rem; }
.field label { display: block; font-size: .8rem; font-weight: 600; margin-bottom: .28rem; }
.field .hint { font-size: .76rem; color: var(--text-muted); margin-top: .25rem; }

input[type=text], input[type=password], input[type=email], input[type=number],
input[type=date], input[type=search], input[type=url], select, textarea {
  width: 100%; padding: .5rem .65rem; font-size: .87rem; font-family: inherit;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--border-strong); border-radius: 8px;
  transition: border-color var(--speed) ease, box-shadow var(--speed) ease;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--ring);
}
textarea { resize: vertical; min-height: 72px; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: .9rem; }

/* A control sitting on one line beside a button — the invoice status picker —
   rather than a stray field stretched across the whole card. The 100% above is
   right for a stacked .field and wrong for anything inside a flex .row. */
.row > select, .row > input[type=text], .row > input[type=date] {
  width: auto; min-width: 11rem;
}

/* A short label/value list. account.html asked for class="table", which no rule
   in this file has ever matched, so the browser's default crushed the labels
   right up against their values in a column two words wide. */
.kv {
  display: grid; grid-template-columns: max-content 1fr;
  gap: .55rem 1.75rem; align-items: baseline; margin: 0;
}
.kv dt {
  font-size: .72rem; color: var(--text-muted); text-transform: uppercase;
  letter-spacing: .09em; font-weight: 650;
}
.kv dd { margin: 0; font-size: .9rem; }

/* In-page jump links. `scroll-margin-top` on the target keeps the card's title
   clear of the sticky topbar instead of landing underneath it. */
.jump { display: flex; flex-wrap: wrap; gap: .45rem; }
.chip {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .3rem .65rem; font-size: .8rem; font-weight: 600;
  color: var(--text); background: var(--surface); text-decoration: none;
  border: 1px solid var(--border); border-radius: 999px;
  transition: border-color var(--speed) ease, background var(--speed) ease;
}
.chip:hover { border-color: var(--brand); background: var(--surface-2); }
.chip-n {
  font-size: .72rem; font-weight: 700; color: var(--text-muted);
  background: var(--surface-3); border-radius: 999px; padding: 0 .4rem;
  font-variant-numeric: tabular-nums;
}
.card[id] { scroll-margin-top: 5rem; }

/* A fact the form states rather than a field it asks for. Styled as a quiet
   panel so it reads as settled — a bordered input would invite editing that
   the form deliberately no longer offers. */
.billed-to {
  display: flex; flex-direction: column; gap: .15rem;
  padding: .7rem .9rem; margin-bottom: 1rem;
  background: var(--surface-2); border: 1px solid var(--border);
  border-left: 3px solid var(--brand); border-radius: var(--radius);
}
.billed-to .l {
  font-size: .68rem; color: var(--text-muted); text-transform: uppercase;
  letter-spacing: .09em; font-weight: 650;
}
.billed-to .danger { color: var(--danger); }

/* ------------------------------------------------------------- flash ---- */

.flash {
  padding: .7rem .95rem; border-radius: var(--r); margin-bottom: .95rem;
  font-size: .86rem; border: 1px solid transparent;
  border-left-width: 3px;
}
.flash-success { background: var(--success-soft); color: var(--success); border-color: currentColor; }
.flash-error   { background: var(--danger-soft);  color: var(--danger);  border-color: currentColor; }
.flash-warning { background: var(--warning-soft); color: var(--warning); border-color: currentColor; }
.flash-info    { background: var(--accent-soft);  color: var(--accent);  border-color: currentColor; }

/* -------------------------------------------------------------- login --- */

.login-wrap {
  min-height: 100vh; display: grid; place-items: center; padding: 1.5rem;
  background:
    radial-gradient(60rem 32rem at 50% -12%, color-mix(in srgb, var(--brand) 16%, transparent), transparent 70%),
    var(--bg);
}
.login-card {
  width: 100%; max-width: 390px; background: var(--surface);
  border: 1px solid var(--border); border-radius: 16px;
  padding: 2.1rem 1.9rem; box-shadow: var(--shadow-lg);
}
.login-card .brand-mark { width: 46px; height: 46px; font-size: 1.1rem; margin: 0 auto 1rem; border-radius: 13px; }
.login-card h1 { text-align: center; font-size: 1.25rem; margin-bottom: .2rem; }
.login-card .sub { text-align: center; color: var(--text-muted); font-size: .84rem; margin-bottom: 1.4rem; }

/* -------------------------------------------------------------- notes --- */

/* The notes render in a sandboxed iframe carrying their own stylesheet, so the
   portal deliberately styles the frame and nothing inside it. Give it the whole
   viewport below the topbar — these documents run to hundreds of pages and are
   meant to be scrolled and filtered, not peeked at through a letterbox. */
.notes-frame {
  display: block; width: 100%; height: calc(100vh - 8.5rem);
  min-height: 30rem; border: 1px solid var(--border);
  border-radius: var(--r-lg); background: #0d1117; box-shadow: var(--shadow);
}

/* -------------------------------------------------------- toc poster --- */

/* The poster is a dark 2560px-wide PNG. Scaled to the card it is legible on a
   laptop and no more, which is why the whole thing is a link to the full size —
   the preview is there to say "here is the shape of the course", not to be read
   line by line. Its own dark background is kept rather than sitting it on the
   card colour, so the image ends where its border does in either theme. */
.toc-poster img {
  display: block; width: 100%; height: auto;
  border-radius: var(--r); border: 1px solid var(--border);
  background: #0d1117;
}
.toc-poster a:has(img) { display: block; }
.toc-poster a:has(img):hover img { border-color: var(--brand); }
.toc-poster a:has(img):focus-visible {
  outline: 2px solid var(--brand); outline-offset: 3px; border-radius: var(--r);
}
.toc-poster-actions { display: flex; gap: .5rem; flex-shrink: 0; }

/* ------------------------------------------------------------ present --- */

/* The deck viewer. Dark regardless of the reader's colour scheme: this is a
 * projector surface, and a light chrome around a slide washes the slide out.
 *
 * The chrome is deliberately temporary — the bar and the edge arrows fade out
 * after a few idle seconds and come back on the first mouse move. What is left
 * on screen while teaching is the slide. */

body.presenting { margin: 0; background: #05070c; overflow: hidden; }

.deck {
  position: fixed; inset: 0; background: #05070c;
  display: flex; flex-direction: column;
  font-family: inherit; color: #c9d1d9;
  /* Repeated arrow presses would otherwise select the bar's text. */
  user-select: none; -webkit-user-select: none;
}

/* ---- stage ---- */

.deck-stage {
  position: relative; flex: 1; min-height: 0;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden; cursor: pointer;
  /* The slide floats on a very slightly lit field rather than pure black, so
     a white slide has an edge instead of bleeding into the frame. */
  background:
    radial-gradient(ellipse at 50% 42%, rgba(99, 102, 241, .10), transparent 62%),
    #05070c;
}

.deck-slide {
  display: block; max-width: 100%; max-height: 100%;
  border-radius: 4px; background: #fff;
  box-shadow: 0 24px 70px rgba(0, 0, 0, .65), 0 0 0 1px rgba(255, 255, 255, .07);
}
.deck:not(.ready) .deck-slide, .deck.failed .deck-slide { display: none; }

/* Short and eased-out: long enough to read as a slide change, short enough
   that holding the arrow key does not feel like wading. */
.deck-slide.turning { animation: deck-turn .16s ease-out; }
@keyframes deck-turn {
  from { opacity: .35; transform: translateY(6px) scale(.995); }
  to   { opacity: 1;   transform: none; }
}

/* Pressing past either end. Without this the deck looks frozen. */
.deck-stage.nudge { animation: deck-nudge .22s ease-in-out; }
@keyframes deck-nudge {
  0%, 100% { transform: none; }
  35%      { transform: translateX(-5px); }
  70%      { transform: translateX(3px); }
}

@media (prefers-reduced-motion: reduce) {
  .deck-slide.turning, .deck-stage.nudge { animation: none; }
}

/* ---- edge navigation ---- */

.deck-nav {
  position: absolute; top: 0; bottom: 0; width: clamp(56px, 9vw, 110px);
  display: flex; align-items: center; justify-content: center;
  border: 0; padding: 0; cursor: pointer; color: #fff;
  background: linear-gradient(to right, rgba(0, 0, 0, .42), transparent);
  opacity: 0; transition: opacity .18s ease;
}
.deck-nav.prev { left: 0; }
.deck-nav.next { right: 0; background: linear-gradient(to left, rgba(0, 0, 0, .42), transparent); }
.deck-nav svg { width: 34px; height: 34px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; filter: drop-shadow(0 2px 6px rgba(0, 0, 0, .8)); }
.deck-nav:hover { opacity: 1; }
.deck-nav:focus-visible { opacity: 1; outline: 2px solid var(--brand); outline-offset: -4px; }
.deck-nav[disabled] { opacity: 0 !important; pointer-events: none; }
/* Present, but only just, until the pointer moves — enough to be found. */
.deck:not(.idle) .deck-nav { opacity: .42; }

/* ---- bar ---- */

.deck-bar {
  display: flex; align-items: center; gap: 1rem;
  padding: .45rem .9rem; font-size: .82rem;
  background: rgba(10, 14, 22, .92); backdrop-filter: blur(10px);
  border-top: 1px solid rgba(255, 255, 255, .08);
  transition: opacity .25s ease, transform .25s ease;
}
.deck.idle .deck-bar { opacity: 0; transform: translateY(100%); pointer-events: none; }

.deck-title {
  flex: 1; min-width: 0; font-weight: 600; color: #e6edf3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.deck-tools { display: flex; align-items: center; gap: .4rem; }

.deck-bar a, .deck-bar button {
  color: #c9d1d9; background: rgba(255, 255, 255, .07);
  border: 1px solid rgba(255, 255, 255, .13); border-radius: 7px;
  padding: .28rem .62rem; font-size: .8rem; cursor: pointer;
  font-family: inherit; line-height: 1.4;
}
.deck-bar a:hover, .deck-bar button:hover {
  background: rgba(255, 255, 255, .16); text-decoration: none; color: #fff;
}
.deck-bar #btn-overview[aria-expanded="true"] {
  background: var(--brand); border-color: var(--brand); color: #fff;
}
.deck-bar #btn-help { width: 1.85rem; padding: .28rem 0; text-align: center; }
.deck-timer { font-variant-numeric: tabular-nums; letter-spacing: .04em; }

.deck-count { display: flex; align-items: center; gap: .3rem; color: #8b949e; }
.deck-count input {
  width: 3.4rem; text-align: right; font-variant-numeric: tabular-nums;
  color: #e6edf3; background: rgba(255, 255, 255, .07);
  border: 1px solid rgba(255, 255, 255, .13); border-radius: 7px;
  padding: .28rem .4rem; font-size: .8rem; font-family: inherit;
}
.deck-count input:focus { outline: 2px solid var(--brand); outline-offset: 1px; }

/* ---- progress ---- */

.deck-progress { height: 3px; background: rgba(255, 255, 255, .07); flex: none; }
.deck-progress span {
  display: block; height: 100%; width: 0;
  background: var(--brand-grad); transition: width .18s ease;
}

/* ---- overview ---- */

.deck-overview {
  position: absolute; inset: 0; z-index: 20;
  display: flex; flex-direction: column;
  background: rgba(5, 7, 12, .96); backdrop-filter: blur(6px);
}
.deck-overview[hidden] { display: none; }
.deck-overview-head {
  display: flex; align-items: baseline; gap: .8rem;
  padding: .9rem 1.2rem; color: #e6edf3;
  border-bottom: 1px solid rgba(255, 255, 255, .08);
}
.deck-overview-head .small { color: #8b949e; }

.deck-grid {
  flex: 1; overflow-y: auto; padding: 1.1rem;
  display: grid; gap: .9rem;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}
.deck-thumb {
  position: relative; padding: 0; cursor: pointer; border-radius: 6px;
  background: rgba(255, 255, 255, .05);
  border: 2px solid transparent; transition: border-color .15s ease, transform .15s ease;
}
.deck-thumb:hover { transform: translateY(-2px); border-color: rgba(255, 255, 255, .3); }
.deck-thumb.current { border-color: var(--brand); }
.deck-thumb-frame {
  display: block; aspect-ratio: 16 / 9; border-radius: 4px; overflow: hidden;
  background: rgba(255, 255, 255, .06);
}
.deck-thumb-frame canvas { display: block; width: 100%; height: 100%; object-fit: contain; }
.deck-thumb-n {
  position: absolute; left: .35rem; bottom: .35rem;
  padding: .05rem .35rem; border-radius: 4px; font-size: .7rem;
  background: rgba(0, 0, 0, .72); color: #fff; font-variant-numeric: tabular-nums;
}

/* ---- overlays ---- */

.deck-help, .deck-error {
  position: absolute; inset: 0; z-index: 30;
  display: flex; align-items: center; justify-content: center;
  background: rgba(5, 7, 12, .82); backdrop-filter: blur(4px); padding: 1.2rem;
}
.deck-help[hidden], .deck-error[hidden] { display: none; }
.deck-help-card {
  max-width: 30rem; padding: 1.4rem 1.6rem; border-radius: 12px;
  background: #10141f; color: #e6edf3;
  border: 1px solid rgba(255, 255, 255, .12);
  box-shadow: 0 30px 80px rgba(0, 0, 0, .6);
}
.deck-help-card dl {
  display: grid; grid-template-columns: auto 1fr; gap: .45rem .9rem;
  margin: .9rem 0; align-items: baseline;
}
.deck-help-card dt { display: flex; gap: .25rem; }
.deck-help-card dd { margin: 0; color: #8b949e; font-size: .85rem; }
.deck-help-card kbd {
  display: inline-block; min-width: 1.4rem; text-align: center;
  padding: .1rem .35rem; font-size: .72rem; font-family: ui-monospace, monospace;
  background: rgba(255, 255, 255, .09); color: #e6edf3;
  border: 1px solid rgba(255, 255, 255, .16); border-radius: 5px;
}

/* Not `display:none` — the deck stays rendered underneath so returning from
   black is instant rather than a re-render. */
.deck-black { position: absolute; inset: 0; z-index: 40; background: #000; }
.deck-black[hidden] { display: none; }

/* ---- loading and fallback ---- */

.deck-loading {
  position: absolute; display: flex; align-items: center; gap: .6rem;
  color: #8b949e; font-size: .9rem;
}
.deck-loading[hidden] { display: none; }
.deck-spinner {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .18); border-top-color: var(--brand);
  animation: deck-spin .7s linear infinite;
}
@keyframes deck-spin { to { transform: rotate(360deg); } }

.deck-fallback { position: fixed; inset: 0; width: 100%; height: 100%; border: 0; }

/* ------------------------------------------------------------- utils ---- */

/* Reachable by a screen reader, invisible on screen. `display:none` would take
   it out of the accessibility tree too, which defeats the point. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.muted { color: var(--text-muted); }
.small { font-size: .8rem; }
.right { text-align: right; }
.center { text-align: center; }
.mt0 { margin-top: 0; } .mb0 { margin-bottom: 0; }
.mt1 { margin-top: .75rem; } .mb1 { margin-bottom: .75rem; }
.mt2 { margin-top: 1.5rem; }
.row { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.spread { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
.empty { text-align: center; padding: 2.75rem 1rem; color: var(--text-muted); }
.empty .big { font-size: 2.1rem; opacity: .4; display: block; margin-bottom: .4rem; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* A pack's bar sits above its sub-course table and spans the card. A leaf
   course has no table, so its bar shares a line with the button instead. */
.course-progress { margin: .1rem 0 .55rem; }

.course-foot { display: flex; align-items: center; gap: 1rem; margin-top: .2rem; }
.course-foot > .progress-bar,
.course-foot > div:first-child { flex: 1; min-width: 0; }
.course-foot > .btn { flex: none; }

.progress-bar { height: 6px; background: var(--surface-3); border-radius: 999px; overflow: hidden; }
.progress-bar > i {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--success), color-mix(in srgb, var(--success) 55%, #22d3ee));
  transition: width 320ms ease;
}

/* --------------------------------------------------------- responsive --- */

@media (max-width: 900px) {
  .layout { flex-direction: column; }
  .sidebar { width: 100%; flex: none; height: auto; position: static; }
  .sidebar-nav { display: flex; flex-wrap: wrap; padding: .4rem; }
  .sidebar-nav a { border-radius: 8px; padding: .4rem .7rem; }
  .sidebar-nav a.active::before { display: none; }
  .sidebar-section { display: none; }
  .topbar { padding: .75rem 1rem; flex-wrap: wrap; }
  .content { padding: 1rem; }
  .form-row { grid-template-columns: 1fr; }
  .notes-frame { height: calc(100vh - 12rem); }
}

/* Motion is decoration here; anyone who has asked for less should get none. */
/* ------------------------------------------------- earnings and tax ------ */

/* Two panels side by side on a desktop, stacked on anything narrower. The
   tax card is the taller of the two, so it goes first and the chart follows. */
.split-2 {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  gap: 16px;
  margin-bottom: 16px;
}

@media (max-width: 1000px) {
  .split-2 { grid-template-columns: minmax(0, 1fr); }
}

.money-hero {
  padding: 18px 0 10px;
}

.money-hero-figure {
  font-size: clamp(2rem, 5vw, 2.9rem);
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.05;
  background: var(--brand-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* The gradient text above disappears entirely if background-clip is not
   honoured, so the colour is restored wherever it is not. */
@supports not ((background-clip: text) or (-webkit-background-clip: text)) {
  .money-hero-figure { color: var(--brand); background: none; }
}

.money-hero-note {
  color: var(--text-muted);
  font-size: .875rem;
  margin-top: 6px;
  max-width: 46ch;
}

/* A bar chart made of divs. Deliberately not a charting library: the app's
   CSP is script-src 'self' with no CDN, and twelve numbers do not need one. */
.chart {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  height: 190px;
  padding-top: 12px;
}

.chart-col {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  height: 100%;
  min-width: 0;
}

.chart-bar {
  width: 100%;
  min-height: 2px;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  background: var(--brand-grad);
  transition: filter var(--speed) ease;
}

.chart-col:hover .chart-bar { filter: brightness(1.15); }

.chart-label {
  font-size: .7rem;
  color: var(--text-muted);
  margin-top: 6px;
  white-space: nowrap;
}

/* The derivation table: label on the left, money right-aligned, and the lines
   that are sums set apart so the arithmetic can be followed down the column. */
table.tbl.calc th {
  font-weight: 500;
  text-align: left;
  white-space: normal;
}

table.tbl.calc tr.calc-total th,
table.tbl.calc tr.calc-total td {
  font-weight: 700;
  border-top: 2px solid var(--border);
}

.steps {
  margin: 10px 0 0 1.1rem;
  padding: 0;
  font-size: .875rem;
  color: var(--text-muted);
}

.steps li { margin-bottom: 4px; }

.prewrap { white-space: pre-wrap; word-break: break-word; }

/* Destructive actions get a visible border so they never read as routine. */
.danger-zone { border-color: color-mix(in srgb, var(--danger) 40%, var(--border)); }

.danger-zone summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--danger);
}

@supports not (color: color-mix(in srgb, red 40%, blue)) {
  .danger-zone { border-color: var(--danger); }
}

/* ---------------------------------------------------------- dashboard --- */

/* The one band on the page that is allowed to be loud. It carries the single
   action most visits are here for, so it sits above the counts rather than
   among them. */
.hero {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap;
  padding: 1.5rem 1.65rem; margin-bottom: 1rem;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background:
    radial-gradient(120% 160% at 0% 0%,
      color-mix(in srgb, var(--brand) 22%, transparent) 0%, transparent 62%),
    var(--surface);
  box-shadow: var(--shadow-sm);
}
.hero-text { min-width: 0; }
.hero-eyebrow {
  font-size: .68rem; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--text-faint); margin-bottom: .35rem;
}
.hero h2 {
  margin: 0; font-size: 1.5rem; font-weight: 680; letter-spacing: -.025em;
}
.hero p { margin: .4rem 0 0; color: var(--text-muted); font-size: .92rem; }
.hero-actions { display: flex; gap: .55rem; flex-wrap: wrap; }

/* A stat that is also a link needs to say so without turning into a button. */
.stat-link {
  display: block; text-decoration: none; color: inherit;
  transition: transform var(--speed) ease, box-shadow var(--speed) ease;
}
.stat-link:hover {
  transform: translateY(-2px); box-shadow: var(--shadow);
  border-color: var(--border-strong);
}
.stat-link .n { color: var(--text); }

/* Money reads faster when the colour already says which direction it points:
   earned, owed, or reserved. */
.stat-success .n { color: var(--success); }
.stat-warning .n { color: var(--warning); }
.stat-info    .n { color: var(--info); }

.progress-row { padding: .6rem 0; border-bottom: 1px solid var(--border); }
.progress-row:last-child { border-bottom: 0; }
.progress-row .spread { margin-bottom: .4rem; gap: .75rem; }
.progress-row a { text-decoration: none; font-weight: 550; font-size: .92rem; }
.progress-row a:hover { text-decoration: underline; }

@media (max-width: 700px) {
  .hero { padding: 1.15rem 1.2rem; }
  .hero h2 { font-size: 1.25rem; }
  .hero-actions { width: 100%; }
  .hero-actions .btn { flex: 1; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
  .course-card:hover { transform: none; }
  .stat-link:hover { transform: none; }
}

@media print {
  .sidebar, .topbar, .lesson-actions, .btn { display: none !important; }
  .content { padding: 0; }
  .card { border: 0; box-shadow: none; }
  .course-card::before, .stat::after { display: none; }
}
