@import url('https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,400;0,600;0,700;1,400&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;1,8..60,400&family=JetBrains+Mono:wght@400;600&display=swap');

/* ============================================================================
 * Open Digital Learning — theme CSS, v0.2 (ODCC visual layer).
 *
 * Same catalogued component classes as v0.1; the visual design is now committed.
 * Rule unchanged: reference THEME TOKENS, never literal colours. Tokens are
 * generated from theme.json into --theme-colours-* / --theme-fonts-*.
 *
 * Brand rules encoded here:
 *   - Deep purple  (#3F264F) = brand surfaces, page/chapter-level headings, rules
 *   - Teal blue    (#32707E) = interaction: links, primary buttons, focus, state
 *   - Dark grey    (#3C3C3B) = body text
 *   - Serif        = learner-facing prose (task bodies, descriptions)
 *   - Sans         = interface (nav, headings, controls)
 *   - Mono         = metadata, badges, ordinals, counts
 *   - Geometry is square-ish (2-4px). Pills and heavy rounding read consumer,
 *     not institutional.
 * ==========================================================================*/

:root{
  --paper:      var(--theme-colours-paper);
  --ink:        var(--theme-colours-ink);
  --muted:      var(--theme-colours-muted);
  --line:       var(--theme-colours-line);
  --line-strong:var(--theme-colours-line-strong);
  --raise:      var(--theme-colours-raise);

  --brand:      var(--theme-colours-brand);
  --brand-soft: var(--theme-colours-brand-soft);
  --on-brand:   var(--theme-colours-on-brand);

  --accent:      var(--theme-colours-accent);
  --accent-hover:var(--theme-colours-accent-hover);
  --accent-soft: var(--theme-colours-accent-soft);
  --on-accent:   var(--theme-colours-on-accent);

  --note:   var(--theme-colours-note);
  --tip:    var(--theme-colours-tip);
  --warn:   var(--theme-colours-warn);
  --caution:var(--theme-colours-caution);
  /* Ochre #D1A75C is a FILL AND RULE colour only - as text it measures 2.24:1
     on paper, far below AA. This darker ochre (5.0:1 on paper, 4.65:1 on raise)
     is the text-safe member of the same family. Never set text to --caution. */
  --caution-text:var(--theme-colours-caution-text);

  --font-sans:  var(--theme-fonts-sans);
  --font-serif: var(--theme-fonts-serif);
  --font-mono:  var(--theme-fonts-mono);

  /* type scale */
  --t-meta:0.75rem; --t-sm:0.875rem; --t-base:1rem; --t-lead:1.125rem;
  --t-h3:1.375rem; --t-h2:1.75rem; --t-h1:2.25rem;

  /* structure */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px;
  --sp-6:32px; --sp-7:48px; --sp-8:64px;
  --radius:4px; --radius-sm:3px; --radius-pill:2px;
  --maxw:1120px; --prose:44rem;
  --rule:3px;             /* brand rule weight on headings */
}

/* ============ base ============ */
*{box-sizing:border-box}
body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--font-sans);
  font-size:var(--t-base);
  line-height:1.55;
  -webkit-font-smoothing:antialiased;
  text-wrap:pretty;
}
a{color:var(--accent);text-underline-offset:2px}
a:hover{color:var(--accent-hover)}
h1,h2,h3,h4{margin:0;line-height:1.2;font-weight:700;letter-spacing:-0.015em}
p{margin:0 0 var(--sp-4)}
button,input,textarea,select{font-family:inherit;color:inherit}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ============ form-control baseline — zero-specificity safety net ============ */
:where(input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not([type="file"]):not([type="range"]):not([type="color"]),textarea,select){
  background:var(--paper);
  color:var(--ink);
  border:1px solid var(--line-strong);
  border-radius:var(--radius-sm);
  padding:9px 11px;
  font:inherit;
}
:where(input,textarea,select):focus{outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent)}
:where(input,textarea)::placeholder{color:var(--muted)}

/* ============ app shell ============ */
.odl-app{min-height:100vh;display:flex;flex-direction:column}

.odl-head{background:var(--brand);color:var(--on-brand)}
.odl-bar{
  max-width:var(--maxw);margin:0 auto;
  display:flex;align-items:center;gap:var(--sp-6);
  padding:var(--sp-4);
}
.odl-brand{
  font-weight:700;font-size:1.0625rem;letter-spacing:-0.02em;
  color:var(--on-brand);text-decoration:none;
  display:flex;align-items:center;gap:var(--sp-3);
}
.odl-nav{display:flex;gap:var(--sp-5);flex:1}
.odl-navlink{
  color:var(--on-brand);text-decoration:none;
  font-size:var(--t-sm);font-weight:600;
  opacity:0.72;
  padding-bottom:2px;border-bottom:2px solid transparent;
}
.odl-navlink:hover{opacity:1;color:var(--on-brand)}
.odl-navlink.is-active{opacity:1;color:var(--on-brand);border-bottom-color:var(--on-brand)}
.odl-user{
  font-family:var(--font-mono);font-size:var(--t-meta);
  color:var(--on-brand);
  border:1px solid color-mix(in srgb, var(--on-brand) 40%, transparent);
  border-radius:var(--radius-sm);
  padding:4px 10px;white-space:nowrap;
}

.odl-main{
  max-width:var(--maxw);width:100%;margin:0 auto;
  padding:var(--sp-7) var(--sp-4) var(--sp-8);flex:1;
}
.odl-main-signin{max-width:var(--prose)}

.odl-foot{
  border-top:1px solid var(--line);color:var(--muted);
  font-size:var(--t-sm);
  display:flex;gap:var(--sp-4);flex-wrap:wrap;
  max-width:var(--maxw);width:100%;margin:0 auto;
  padding:var(--sp-5) var(--sp-4);
}
.odl-foot-note{color:var(--muted)}

/* ============ page + section headings ============ */
.odl-page-head{
  margin-bottom:var(--sp-6);
  padding-bottom:var(--sp-4);
  border-bottom:1px solid var(--line);
}
.odl-page-head h1{font-size:var(--t-h1);color:var(--brand)}
.odl-page-sub{
  color:var(--muted);margin:var(--sp-3) 0 0;
  font-family:var(--font-serif);font-size:var(--t-lead);line-height:1.5;
  max-width:var(--prose);
}
.odl-crumb{
  display:inline-block;margin-bottom:var(--sp-4);
  font-family:var(--font-mono);font-size:var(--t-meta);
  letter-spacing:0.04em;text-transform:uppercase;
  color:var(--accent);text-decoration:none;
}
.odl-crumb:hover{color:var(--accent-hover);text-decoration:underline}
.odl-meta{
  display:flex;gap:var(--sp-4);flex-wrap:wrap;
  font-family:var(--font-mono);color:var(--muted);
  font-size:var(--t-meta);margin-top:var(--sp-4);
}

.odl-category{margin-bottom:var(--sp-7)}
.odl-cat-head{
  margin-bottom:var(--sp-4);
  padding-left:var(--sp-4);
  border-left:var(--rule) solid var(--brand);
}
.odl-cat-head h2{font-size:var(--t-h2);color:var(--brand)}
.odl-cat-desc{
  color:var(--muted);margin:var(--sp-2) 0 0;
  font-family:var(--font-serif);max-width:var(--prose);
}

/* messages */
.odl-empty,.odl-error{
  color:var(--muted);font-family:var(--font-serif);
  padding:var(--sp-5);border:1px dashed var(--line-strong);border-radius:var(--radius);
}
.odl-error{
  color:var(--ink);background:var(--paper);
  border:1px solid var(--warn);border-left:var(--rule) solid var(--warn);border-radius:0 var(--radius) var(--radius) 0;
}

/* ============ programme grid + cards ============ */
.prog-grid{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(300px,1fr));
  gap:var(--sp-4);
}
.prog-card{
  border:1px solid var(--line);border-top:var(--rule) solid var(--brand);
  border-radius:0 0 var(--radius) var(--radius);
  background:var(--paper);
  padding:var(--sp-5);
  display:flex;flex-direction:column;gap:var(--sp-3);
  transition:border-color .12s, box-shadow .12s;
}
.prog-card:hover{box-shadow:0 1px 0 var(--line-strong), 0 6px 18px -12px rgba(63,38,79,.45)}
.prog-card-badges{display:flex;gap:var(--sp-2);flex-wrap:wrap}
.prog-card-title{font-size:var(--t-h3)}
.prog-card-title a{color:var(--brand);text-decoration:none}
.prog-card-title a:hover{color:var(--accent)}
.prog-card-desc{
  color:var(--muted);font-family:var(--font-serif);font-size:var(--t-sm);margin:0;
  display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden;
}
.prog-card .btn{margin-top:auto;align-self:flex-start}

/* ============ ordered lists — modules on a programme, tasks on a module ==== */
.module-list{
  list-style:none;margin:0;padding:0;
  display:flex;flex-direction:column;gap:1px;
  border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;
  background:var(--line);
}
.module-item{
  display:flex;align-items:center;gap:var(--sp-4);
  background:var(--paper);
  padding:var(--sp-4) var(--sp-5);
  text-decoration:none;color:var(--ink);
  border-left:var(--rule) solid transparent;
}
.module-item:hover{background:var(--accent-soft);border-left-color:var(--accent);color:var(--ink)}
.module-item.is-locked{background:var(--raise);color:var(--muted)}
.module-item.is-locked:hover{border-left-color:var(--line-strong);background:var(--raise)}
.module-item.is-complete,.module-item.is-done{border-left-color:var(--tip)}
.module-order{
  font-family:var(--font-mono);font-size:var(--t-sm);color:var(--brand);
  min-width:2.5ch;text-align:right;font-weight:600;
}
.module-item.is-locked .module-order{color:var(--muted)}
.module-item-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.module-item-title{display:block;font-weight:600}
.module-item-sub{
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  color:var(--muted);font-family:var(--font-serif);font-size:var(--t-sm);
}
.module-item-meta{
  display:flex;gap:var(--sp-3);flex-wrap:wrap;align-items:center;
  font-family:var(--font-mono);color:var(--muted);font-size:var(--t-meta);
}
.module-lock{
  font-family:var(--font-mono);font-size:var(--t-meta);color:var(--muted);
  letter-spacing:0.04em;text-transform:uppercase;white-space:nowrap;
}

/* ============ task detail ============ */
.task-header{
  border-bottom:1px solid var(--line);
  padding-bottom:var(--sp-5);margin-bottom:var(--sp-5);
}
.task-header h1{font-size:var(--t-h2);color:var(--brand)}
.task-badges{display:flex;gap:var(--sp-2);flex-wrap:wrap;margin-top:var(--sp-4)}
.task-body{max-width:var(--prose);font-family:var(--font-serif);font-size:var(--t-lead);line-height:1.62}
.task-body h2{
  font-family:var(--font-sans);font-size:var(--t-h3);color:var(--accent);
  margin:var(--sp-6) 0 var(--sp-3);
}
.task-body ul,.task-body ol{padding-left:var(--sp-5);margin:0 0 var(--sp-4)}
.task-body li{margin-bottom:var(--sp-2)}
.task-prompt,.task-signal{
  border-left:var(--rule) solid var(--line-strong);
  background:var(--raise);
  padding:var(--sp-4) var(--sp-5);
  color:var(--ink);margin:var(--sp-4) 0;
}
.task-signal{border-left-color:var(--accent);background:var(--accent-soft)}
.task-placeholder{
  border:1px dashed var(--line-strong);border-radius:var(--radius);
  color:var(--muted);font-family:var(--font-mono);font-size:var(--t-sm);
  padding:var(--sp-6);text-align:center;margin-top:var(--sp-6);
}

/* ============ enrolment panel ============ */
.enrol-panel{
  border:1px solid var(--line);border-left:var(--rule) solid var(--brand);
  border-radius:0 var(--radius) var(--radius) 0;
  background:var(--brand-soft);
  padding:var(--sp-5);margin:var(--sp-5) 0 var(--sp-6);
}
.enrol-panel.is-enrolled{
  display:flex;align-items:center;gap:var(--sp-3);
  background:var(--accent-soft);border-left-color:var(--accent);
}
.enrol-tick{color:var(--accent);font-weight:700;font-size:var(--t-lead);line-height:1}
.enrol-status{color:var(--ink);font-weight:600}
.enrol-lead{margin:0 0 var(--sp-4);color:var(--ink);font-family:var(--font-serif)}
.enrol-actions{display:flex;gap:var(--sp-3);align-items:center;flex-wrap:wrap}
.enrol-msg{color:var(--muted);font-size:var(--t-sm)}

/* ============ progress — native <progress value max>, never an inline style ==== */
.progress-line{display:flex;align-items:center;gap:var(--sp-3)}
progress.progress{
  appearance:none;-webkit-appearance:none;
  flex:1;width:100%;height:8px;border:0;display:block;
  background:var(--line);
  border-radius:var(--radius-pill);overflow:hidden;
  color:var(--accent);            /* IE/edge-legacy fill */
}
progress.progress::-webkit-progress-bar{background:var(--line);border-radius:var(--radius-pill)}
progress.progress::-webkit-progress-value{background:var(--accent)}
progress.progress::-moz-progress-bar{background:var(--accent)}
progress.progress.is-slim{height:4px}
.progress-pct{
  font-family:var(--font-mono);font-size:var(--t-meta);color:var(--muted);
  white-space:nowrap;font-variant-numeric:tabular-nums;min-width:4ch;text-align:right;
}

/* ============ completion marks ============ */
.done-tick{
  display:inline-flex;align-items:center;justify-content:center;
  width:18px;height:18px;flex:none;
  border:1px solid var(--tip);border-radius:var(--radius-pill);
  color:var(--tip);background:var(--paper);
  font-size:11px;line-height:1;font-weight:700;
}
.done-tick.is-solid{background:var(--tip);border-color:var(--tip);color:var(--paper)}
.done-tick.is-empty{border-color:var(--line-strong);color:transparent;background:var(--paper)}
.done-badge{
  display:inline-block;
  font-family:var(--font-mono);font-size:var(--t-meta);font-weight:600;
  letter-spacing:0.04em;text-transform:uppercase;line-height:1.5;
  padding:2px 7px;border-radius:var(--radius-pill);
  border:1px solid var(--tip);background:var(--paper);color:var(--tip);
  white-space:nowrap;
}
.task-progress{
  font-family:var(--font-mono);font-size:var(--t-meta);color:var(--muted);
  font-variant-numeric:tabular-nums;white-space:nowrap;
}
.task-progress.is-done{color:var(--tip);font-weight:600}

/* ============ stat tiles — /me and /cohort summaries ============ */
.stat-grid{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
  gap:var(--sp-4);
}
.stat{
  display:flex;flex-direction:column;gap:var(--sp-2);
  border:1px solid var(--line);border-top:var(--rule) solid var(--brand);
  border-radius:0 0 var(--radius) var(--radius);
  background:var(--paper);padding:var(--sp-4) var(--sp-5);
}
.stat-num{
  font-family:var(--font-mono);font-size:var(--t-h3);font-weight:600;
  color:var(--brand);line-height:1.15;font-variant-numeric:tabular-nums;
}
.stat-label{
  font-size:var(--t-sm);color:var(--muted);
  line-height:1.35;
}

/* ============ learner module breakdown ============ */
.me-modules{
  list-style:none;margin:0;padding:0;
  border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;
  display:flex;flex-direction:column;gap:1px;background:var(--line);
}
.me-modules li{
  display:flex;align-items:center;gap:var(--sp-3);
  background:var(--paper);padding:var(--sp-3) var(--sp-5);
}
.me-mod-name{flex:1;min-width:0;font-weight:600;font-size:var(--t-sm)}
.me-mod-name.is-done{color:var(--tip)}

/* ============ cohort dashboard table ============ */
.dash-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}
.dash-table{
  border-collapse:collapse;width:100%;
  font-size:var(--t-sm);
  border:1px solid var(--line);
}
.dash-table caption{
  caption-side:top;text-align:left;
  font-family:var(--font-mono);font-size:var(--t-meta);
  letter-spacing:0.04em;text-transform:uppercase;
  color:var(--muted);padding:0 0 var(--sp-3);
}
.dash-table th,.dash-table td{
  padding:var(--sp-2) var(--sp-4);
  border-bottom:1px solid var(--line);
  text-align:left;vertical-align:middle;
}
.dash-table thead th{
  background:var(--raise);color:var(--brand);
  font-family:var(--font-mono);font-size:var(--t-meta);font-weight:600;
  letter-spacing:0.04em;text-transform:uppercase;white-space:nowrap;
  border-bottom:1px solid var(--line-strong);
}
.dash-table tbody th{font-weight:600;color:var(--ink)}
.dash-table tbody tr:last-child th,.dash-table tbody tr:last-child td{border-bottom:0}
.dash-table td.is-num,.dash-table th.is-num{
  text-align:right;font-family:var(--font-mono);
  font-variant-numeric:tabular-nums;white-space:nowrap;
}
.dash-table .is-done{color:var(--tip)}
.dash-cell{display:inline-flex;align-items:center;gap:var(--sp-2);justify-content:flex-end}
.dash-note{
  color:var(--muted);font-family:var(--font-serif);font-size:var(--t-sm);
  border-left:var(--rule) solid var(--line-strong);
  padding:var(--sp-3) var(--sp-4);background:var(--raise);
  margin:var(--sp-4) 0 0;
}
.dash-note code,.task-body code,.callout code{
  font-family:var(--font-mono);font-size:0.9em;
  background:var(--paper);border:1px solid var(--line);
  border-radius:var(--radius-sm);padding:1px 5px;
}

/* ============ evidence submission ============ */
.task-submit{
  border-top:1px solid var(--line);
  margin-top:var(--sp-6);padding-top:var(--sp-5);
}
.task-submit h2{font-size:var(--t-h3);color:var(--brand);margin:0 0 var(--sp-4)}
.task-submit-field{margin:0 0 var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-2)}
.task-submit-field label{font-weight:600;font-size:var(--t-sm)}
.task-submit-field textarea{width:100%;resize:vertical;line-height:1.55}
.task-submit-actions{display:flex;gap:var(--sp-3);align-items:center;flex-wrap:wrap}
.task-submit-status{color:var(--muted);font-size:var(--t-sm)}

/* ============ callouts (authoring vocabulary, matches ODCC doc template) ==== */
.callout{
  border:1px solid var(--line);border-left:var(--rule) solid var(--line-strong);
  border-radius:0 var(--radius) var(--radius) 0;
  background:var(--raise);
  padding:var(--sp-4) var(--sp-5);margin:var(--sp-4) 0;
  font-family:var(--font-serif);
}
.callout-label{
  font-family:var(--font-mono);font-size:var(--t-meta);font-weight:600;
  letter-spacing:0.06em;text-transform:uppercase;
  display:block;margin-bottom:var(--sp-2);color:var(--muted);
}
.callout > :last-child{margin-bottom:0}
.callout.is-note{border-left-color:var(--note)}
.callout.is-note .callout-label{color:var(--note)}
.callout.is-tip{border-left-color:var(--tip)}
.callout.is-tip .callout-label{color:var(--tip)}
.callout.is-warning{border-left-color:var(--warn)}
.callout.is-warning .callout-label{color:var(--warn)}
.callout.is-important{border-left-color:var(--brand);background:var(--brand-soft)}
.callout.is-important .callout-label{color:var(--brand)}
.callout.is-caution{border-left-color:var(--caution)}
.callout.is-caution .callout-label{color:var(--caution-text)}

/* ============ badge ============ */
.badge{
  display:inline-block;
  font-family:var(--font-mono);
  font-size:var(--t-meta);font-weight:600;letter-spacing:0.04em;
  text-transform:uppercase;line-height:1.5;
  padding:2px 7px;border-radius:var(--radius-pill);
  border:1px solid var(--line-strong);
  background:var(--paper);color:var(--muted);
  white-space:nowrap;
}
/* access tiers + flags */
.badge.is-supervised,
.badge.is-paid,.badge.is-pro,.badge.is-premium{
  border-color:var(--brand);color:var(--brand);background:var(--brand-soft);
}
.badge.is-free{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}
/* any tier the data invents still reads as a tier, not as a stray grey chip */
.badge.is-tier{border-color:var(--brand);color:var(--brand);background:var(--brand-soft)}
.badge.is-tier.is-free{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}
.badge.is-draft{border-style:dashed;color:var(--muted)}
/* task descriptors — quietest tier, never competes with access tiers */
.badge.is-type,.badge.is-bloom,.badge.is-layer,
.badge.is-formative,.badge.is-duration{
  background:var(--raise);color:var(--ink);border-color:var(--line);
}

/* ============ button ============ */
.btn{
  display:inline-block;
  font-size:var(--t-sm);font-weight:600;letter-spacing:0.01em;
  padding:9px 18px;border-radius:var(--radius-sm);
  border:1px solid var(--accent);
  background:var(--accent);color:var(--on-accent);
  text-decoration:none;cursor:pointer;
  transition:background .12s, border-color .12s, color .12s;
}
.btn:hover,.btn.btn-primary:hover{background:var(--accent-hover);border-color:var(--accent-hover);color:var(--on-accent)}
.btn.is-secondary{background:var(--paper);color:var(--accent);border-color:var(--line-strong)}
.btn.is-secondary:hover{background:var(--accent-soft);border-color:var(--accent);color:var(--accent)}
.btn.is-quiet{background:transparent;border-color:transparent;color:var(--accent);padding-left:0;padding-right:0}
.btn.is-quiet:hover{background:transparent;text-decoration:underline;color:var(--accent-hover)}
.btn[disabled],.btn.is-disabled{
  opacity:1;cursor:default;
  background:var(--raise);color:var(--muted);border-color:var(--line);
}

/* ============ style-guide chrome (internal page only) ============ */
.sg-intro{
  color:var(--muted);font-family:var(--font-serif);font-size:var(--t-lead);
  max-width:var(--prose);margin-bottom:var(--sp-6);
}
.sg-demo{
  border:1px solid var(--line);border-radius:var(--radius);
  padding:var(--sp-5);margin:var(--sp-4) 0;
}
.sg-label{
  font-family:var(--font-mono);font-size:var(--t-meta);color:var(--muted);
  letter-spacing:0.06em;text-transform:uppercase;
  margin-bottom:var(--sp-4);
}
.sg-row{display:flex;gap:var(--sp-4);flex-wrap:wrap;align-items:flex-start}
.sg-full{margin:var(--sp-4) 0 0}
.sg-full textarea{width:100%}

/* ============ programme card facts line ============ */
.prog-card-facts{
  font-family:var(--font-mono);font-size:var(--t-meta);color:var(--muted);
  margin:0;letter-spacing:0.02em;
}

/* ============ programme overview (/p) ============ */
.odl-overview-facts{margin:var(--sp-6) 0}
.odl-overview-facts h2{
  font-size:var(--t-h3);color:var(--accent);margin:0 0 var(--sp-3);
}
.odl-facts{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;
}
.odl-facts li{
  background:var(--paper);padding:var(--sp-3) var(--sp-4);
  font-size:var(--t-sm);color:var(--ink);
}
.odl-facts li strong{color:var(--brand);font-weight:600}
.odl-cta{
  border-top:1px solid var(--line);
  margin-top:var(--sp-6);padding-top:var(--sp-5);
  display:flex;flex-direction:column;gap:var(--sp-3);align-items:flex-start;
}
.odl-cta-note{
  margin:0;color:var(--muted);font-size:var(--t-sm);font-family:var(--font-serif);
}

/* ============ /me programme sections ============ */
.me-prog{
  border:1px solid var(--line);border-top:var(--rule) solid var(--brand);
  border-radius:0 0 var(--radius) var(--radius);
  padding:var(--sp-5);margin:0 0 var(--sp-5);
  display:flex;flex-direction:column;gap:var(--sp-4);
}
.me-prog-head{
  display:flex;align-items:baseline;gap:var(--sp-3);flex-wrap:wrap;
}
.me-prog-head h2{font-size:var(--t-h3);color:var(--brand);flex:1;min-width:0}
.me-prog-actions{display:flex;gap:var(--sp-3);flex-wrap:wrap}

/* ============ sign-in page ============ */
.auth-page{max-width:24rem;margin:0 auto}
.auth-page h1{font-size:var(--t-h2);color:var(--brand);margin-bottom:var(--sp-5)}
.auth-error{
  color:var(--ink);background:var(--paper);
  border:1px solid var(--warn);border-left:var(--rule) solid var(--warn);
  border-radius:0 var(--radius) var(--radius) 0;
  padding:var(--sp-3) var(--sp-4);margin:0 0 var(--sp-4);
  font-family:var(--font-serif);
}
.login-form{display:flex;flex-direction:column;gap:var(--sp-4)}
.login-form .field{display:flex;flex-direction:column;gap:var(--sp-2)}
.login-form .field > span{font-weight:600;font-size:var(--t-sm)}
.login-form .field small{color:var(--muted);font-weight:400}
.login-form input{width:100%}
.login-form .btn{align-self:flex-start;margin-top:var(--sp-1)}

/* ============ auth chrome visibility (set by lazysite-chrome.js) ============ */
.odl-app.is-signed-out .odl-nav .requires-auth{display:none}

/* ============================================================================
 * ACCESS GATES (/modules, past the overview)
 *
 * A gate is a dead end, not an error: the learner has done nothing wrong, so no
 * rust and no alarm. Restriction gates carry the purple rule - access is a
 * structural fact about the programme. is-open is the inverse (reassurance that
 * nothing is collected), so it takes the teal wash and no padlock.
 *
 * Note the body/note colours: this component sits on a WASH, so secondary text
 * is --ink, never --muted (which fails AA on brand-soft at 4.31:1).
 * ==========================================================================*/
.odl-gate{
  border:1px solid var(--line);border-left:var(--rule) solid var(--brand);
  border-radius:0 var(--radius) var(--radius) 0;
  background:var(--brand-soft);
  padding:var(--sp-6) var(--sp-5);
  margin:var(--sp-5) 0 var(--sp-6);
  max-width:var(--prose);
}
.odl-gate-icon{
  font-size:1.5rem;line-height:1;margin-bottom:var(--sp-3);
  filter:grayscale(1);opacity:0.55;
}
.odl-gate-title{
  font-size:var(--t-h3);color:var(--brand);margin:0 0 var(--sp-3);
}
.odl-gate-body{
  font-family:var(--font-serif);font-size:var(--t-lead);line-height:1.55;
  color:var(--ink);margin:0 0 var(--sp-3);
}
.odl-gate-note{
  font-size:var(--t-sm);color:var(--ink);margin:0;
  padding-top:var(--sp-3);border-top:1px solid var(--line-strong);
}
.odl-gate.is-open{
  background:var(--accent-soft);border-left-color:var(--accent);
}
.odl-gate.is-open .odl-gate-title{color:var(--accent)}
.odl-gate > :last-child{margin-bottom:0}

/* ============================================================================
 * ROADMAP (P5a) - programmes that do not exist yet.
 *
 * These cards must NOT look interactive: no pointer, no hover, no teal, no
 * shadow. A dashed border and the raise fill say "not yet" before the status
 * badge is read. Real grid gaps, not a painted container.
 * ==========================================================================*/
.odl-roadmap{
  margin:var(--sp-8) 0 var(--sp-7);
  padding-top:var(--sp-6);border-top:1px solid var(--line);
}
.odl-roadmap-head{margin-bottom:var(--sp-5)}
.odl-roadmap-head h2{font-size:var(--t-h2);color:var(--brand);margin:0}
.odl-roadmap-head p{
  color:var(--muted);font-family:var(--font-serif);
  max-width:var(--prose);margin:var(--sp-2) 0 0;
}
.roadmap-grid{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(280px,1fr));
  gap:var(--sp-4);
}
.roadmap-card{
  border:1px dashed var(--line-strong);border-radius:var(--radius);
  background:var(--raise);
  padding:var(--sp-5);
  display:flex;flex-direction:column;gap:var(--sp-2);
  cursor:default;
}
.roadmap-card-cat{
  font-family:var(--font-mono);font-size:var(--t-meta);
  letter-spacing:0.04em;text-transform:uppercase;color:var(--muted);
}
.roadmap-card-title{font-size:var(--t-h3);color:var(--brand);margin:0}
.roadmap-card-summary{
  color:var(--muted);font-family:var(--font-serif);font-size:var(--t-sm);
  margin:0;
}

/* status badges - the quietest tier of all; ochre needs --caution-text as text */
.badge.is-status{background:var(--paper);border-color:var(--line-strong);color:var(--muted)}
.badge.is-status.is-planned{border-style:dashed;color:var(--muted)}
.badge.is-status.is-in-development{border-color:var(--caution);color:var(--caution-text)}
.badge.is-status.is-open{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}

/* ============================================================================
 * PRICING — ad-hoc per (programme, mode), and subscription packages.
 *
 * Money has to be scannable and comparable, so every amount is mono with
 * tabular figures: £120.00 and £45.00 must align on the decimal.
 *
 * SURFACE RESET: these cards appear standalone on paper AND nested inside
 * .odl-gate.is-subscription, which is a brand-soft wash. Rather than add
 * another wash exception, the cards carry their own --paper fill. That resets
 * the surface, so --muted is back to 5.11:1 and the tokens behave the same in
 * both contexts. Nest a card, reset its background.
 * ==========================================================================*/
.odl-price-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
  gap:var(--sp-3);
}
.odl-price{
  background:var(--paper);
  border:1px solid var(--line);border-top:var(--rule) solid var(--brand);
  border-radius:0 0 var(--radius) var(--radius);
  padding:var(--sp-4);
  display:flex;flex-direction:column;gap:var(--sp-1);
}
.odl-price-mode{
  font-family:var(--font-mono);font-size:var(--t-meta);
  letter-spacing:0.04em;text-transform:uppercase;color:var(--muted);
}
.odl-price-amount{
  font-family:var(--font-mono);font-size:var(--t-h3);font-weight:600;
  color:var(--brand);font-variant-numeric:tabular-nums;line-height:1.15;
}

.package-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
  gap:var(--sp-4);
}
.package-card{
  background:var(--paper);
  border:1px solid var(--line);border-top:var(--rule) solid var(--accent);
  border-radius:0 0 var(--radius) var(--radius);
  padding:var(--sp-5);
  display:flex;flex-direction:column;gap:var(--sp-3);
}
.package-card-head{
  display:flex;flex-direction:column;gap:var(--sp-1);
  padding-bottom:var(--sp-3);border-bottom:1px solid var(--line);
}
.package-card-name{font-size:var(--t-h3);font-weight:700;color:var(--brand);letter-spacing:-0.015em}
.package-card-price{
  font-family:var(--font-mono);font-size:var(--t-h3);font-weight:600;
  color:var(--accent);font-variant-numeric:tabular-nums;
}
/* the cadence rides with the amount but must not compete with it */
.package-card-cadence{
  font-size:var(--t-meta);font-weight:400;color:var(--muted);
  letter-spacing:0.04em;text-transform:uppercase;
}
.package-card-desc{
  font-family:var(--font-serif);font-size:var(--t-sm);color:var(--muted);margin:0;
}
.package-card-progs{
  list-style:none;margin:0;padding:0;
  display:flex;flex-direction:column;gap:var(--sp-2);
  margin-top:auto;padding-top:var(--sp-3);border-top:1px solid var(--line);
}
.package-card-progs li{
  font-size:var(--t-sm);color:var(--ink);
  padding-left:var(--sp-4);position:relative;
}
.package-card-progs li::before{
  content:"";position:absolute;left:0;top:0.55em;
  width:6px;height:6px;background:var(--accent);
}

/* the subscription gate carrying prices + packages */
.odl-subinfo{
  margin-top:var(--sp-5);padding-top:var(--sp-5);
  border-top:1px solid var(--line-strong);
  display:flex;flex-direction:column;gap:var(--sp-6);
}
.odl-subinfo-block{display:flex;flex-direction:column;gap:var(--sp-3)}
.odl-subinfo-head{
  font-size:var(--t-h3);color:var(--brand);margin:0;
}

/* entitlement: the learner already holds access. Forest, like completion -
   it is a confirmation, not an access tier, so it never uses purple or teal. */
.badge.is-entitled{
  border-color:var(--tip);color:var(--tip);background:var(--paper);
}

/* ============ narrow screens ============ */
@media (max-width:640px){
  :root{--t-h1:1.75rem; --t-h2:1.375rem; --t-h3:1.125rem}
  .odl-bar{flex-wrap:wrap;gap:var(--sp-3)}
  .odl-nav{order:3;flex-basis:100%;gap:var(--sp-4)}
  .prog-grid{grid-template-columns:1fr}
  .roadmap-grid{grid-template-columns:1fr}
  .package-grid{grid-template-columns:1fr}
  .odl-price-grid{grid-template-columns:1fr}
  .stat-grid{grid-template-columns:repeat(auto-fit,minmax(140px,1fr))}
  .odl-facts{grid-template-columns:1fr}
  .me-prog-head{gap:var(--sp-2)}
  .odl-main{padding:var(--sp-5) var(--sp-4) var(--sp-7)}
}

/* ============ evaluation report (P6) — structural, tokens only ==============
 * The /report page interleaves a GENERATED factual scaffold with the
 * EVALUATOR'S authored judgement, and a print view drops the app chrome and the
 * editing workspace so the report prints clean. No literal colours; dynamic
 * values ride on element attributes (the <progress>), never inline styles. */

.report-actions{display:flex;gap:var(--sp-3);flex-wrap:wrap;margin:0 0 var(--sp-5)}
.report-view{max-width:var(--prose)}
.report-view .odl-page-head h1{font-size:1.5rem}

/* at-a-glance strip — the headline summary above the report body */
.report-glance{
  border-left:3px solid var(--accent);
  background:var(--raise);
  padding:var(--sp-3) var(--sp-4);border-radius:var(--radius-sm);
  margin:0 0 var(--sp-5);color:var(--ink);
}

.report-section{margin:0 0 var(--sp-6)}
.report-section h2{font-size:1.2rem;letter-spacing:-0.01em;margin:0 0 var(--sp-3)}
.report-section h3{font-size:1rem;margin:var(--sp-5) 0 var(--sp-2)}

/* provenance tag — says whether a block is generated or authored */
.report-src-tag{
  font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.02em;
  color:var(--muted);text-transform:uppercase;margin:0 0 var(--sp-2);
}
.report-src-tag.is-authored{color:var(--accent)}

/* the evaluator-authored sections, once saved (or a placeholder if not) */
.report-judgement{
  border:1px solid var(--accent);border-radius:var(--radius);
  padding:var(--sp-4);background:var(--raise);
}
.report-authored{margin:0 0 var(--sp-4)}
.report-authored:last-child{margin-bottom:0}
.report-authored.is-empty{opacity:0.75}
.report-final-banner{
  border:1px solid var(--accent);border-radius:var(--radius-sm);
  color:var(--accent);font-weight:600;font-size:0.85rem;
  padding:var(--sp-2) var(--sp-3);margin:0 0 var(--sp-4);
}
.report-draft-banner{
  border:1px dashed var(--line);border-radius:var(--radius-sm);
  color:var(--muted);font-size:0.85rem;
  padding:var(--sp-2) var(--sp-3);margin:0 0 var(--sp-4);
}
.report-note{color:var(--muted);font-size:0.88rem;margin:0 0 var(--sp-3)}
.report-none{color:var(--muted)}
.report-outstanding{
  list-style:none;margin:var(--sp-2) 0 0;padding:0;
  display:flex;flex-direction:column;gap:var(--sp-1);
}
.report-outstanding li{
  padding:var(--sp-2) 0;border-bottom:1px solid var(--line);
  display:flex;gap:var(--sp-2);align-items:baseline;
}
.report-outstanding li:last-child{border-bottom:0}

/* paired-reflection cells wrap (the dash-table default is nowrap) */
.report-paired td{white-space:normal;vertical-align:top;min-width:15rem}
.report-paired th[scope="row"]{vertical-align:top}

/* the editing workspace — screen only, hidden in print */
.report-workspace{
  border-top:1px solid var(--line);
  margin-top:var(--sp-6);padding-top:var(--sp-5);
}
.report-workspace h2{font-size:1.15rem;margin:0 0 var(--sp-3)}
.report-workspace .task-submit-field textarea{width:100%;resize:vertical;line-height:1.5}

/* print view: drop app chrome + the workspace, keep the report body */
@media print{
  .odl-head,.odl-foot,.odl-crumb,.report-actions,.report-workspace,.dash-note{
    display:none !important;
  }
  .odl-main{padding:0}
  .report-view{max-width:none}
  .report-paired td{min-width:0}
  .report-section{break-inside:avoid}
}

/* live word-count readout on the evidence + interest fields (formative,
 * non-blocking): the count, and whether it meets the expected minimum.
 * Contract: demoed in the style guide as .wordcount / .is-met / .is-under. */
.wordcount-num{font-weight:600}
.wordcount.is-over{color:var(--note)}
.wordcount.is-none{color:var(--muted)}
.wordcount.is-met{color:var(--tip)}
.wordcount.is-under{color:var(--caution-text)}
.wordcount{font-family:var(--font-mono);font-size:0.78rem;margin-top:var(--sp-1);color:var(--muted)}
.wordcount .wordcount-num{font-weight:700}
.wordcount.is-under,.wordcount.is-over,.wordcount.is-none{color:var(--muted)}
.wordcount.is-met{color:var(--accent)}

/* ============ access-tier gate (P5a) — the overview->process boundary ============
 * The placeholder shown on /modules when a programme's access_tier blocks or
 * changes the process. Registered in the style guide as .odl-gate (+
 * .is-subscription / .is-internal / .is-open) with .odl-gate-icon/-title/-body/-note. */
.odl-gate{
  border:1px solid var(--line);
  border-left:3px solid var(--line-strong);
  border-radius:var(--radius);
  background:var(--raise);
  padding:var(--sp-6);
  margin:var(--sp-5) 0;
  max-width:var(--prose);
}
.odl-gate.is-subscription{border-left-color:var(--brand);background:var(--brand-soft)}
.odl-gate.is-internal{border-left-color:var(--caution)}
.odl-gate.is-open{border-left-color:var(--accent);background:var(--accent-soft)}
.odl-gate-icon{font-size:1.4rem;line-height:1;margin-bottom:var(--sp-3)}
.odl-gate-title{font-size:var(--t-h3);color:var(--brand);margin:0 0 var(--sp-3)}
.odl-gate.is-internal .odl-gate-title{color:var(--ink)}
.odl-gate-body{margin:0 0 var(--sp-3);color:var(--ink)}
.odl-gate-note{margin:0;color:var(--muted);font-size:var(--t-sm)}

/* ============ roadmap ("coming soon") on the catalogue (P5a) ============
 * Non-clickable future-programme cards. Registered in the style guide as
 * .odl-roadmap / .roadmap-grid / .roadmap-card (+ .roadmap-card-cat/-title/
 * -summary) and .badge.is-status modifiers. */
.odl-roadmap{margin-top:var(--sp-8);padding-top:var(--sp-6);border-top:1px solid var(--line)}
.odl-roadmap-head{margin-bottom:var(--sp-5)}
.odl-roadmap-head h2{font-size:var(--t-h2);color:var(--brand)}
.roadmap-grid{
  display:grid;
  grid-template-columns:repeat(auto-fill, minmax(15rem, 1fr));
  gap:var(--sp-4);
}
.roadmap-card{
  border:1px dashed var(--line-strong);
  border-radius:var(--radius);
  background:var(--paper);
  padding:var(--sp-5);
  display:flex;flex-direction:column;gap:var(--sp-3);
}
.roadmap-card-cat{
  font-family:var(--font-mono);font-size:var(--t-meta);color:var(--muted);
  letter-spacing:0.03em;
}
.roadmap-card-title{font-size:var(--t-h3);color:var(--ink);margin:0}
.roadmap-card-summary{margin:0;color:var(--muted);font-size:var(--t-sm)}
/* status chips on roadmap cards (planned / in-development / open) */
.badge.is-status{border-color:var(--line-strong);color:var(--muted);background:var(--raise)}
.badge.is-status.is-in-development{border-color:var(--brand);color:var(--brand);background:var(--brand-soft)}
.badge.is-status.is-open{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}

/* ============================================================================
 * P5(b) subscription + pricing components. Registered in the style guide as
 * .odl-subinfo (with .odl-subinfo-block / -head), the ad-hoc price rows
 * (.odl-price-grid > .odl-price with .odl-price-mode / -amount), the
 * .package-card (with -head / -name / -price / -cadence / -desc) laid out in a
 * .package-grid, and the .badge.is-entitled entitlement badge. Tokens only.
 * ========================================================================== */

/* subscription-info panel — sits inside .odl-gate.is-subscription, showing the
 * ad-hoc prices and the packages that include the programme (information only). */
.odl-subinfo{
  display:flex;flex-direction:column;gap:var(--sp-4);
  margin:var(--sp-4) 0;
}
.odl-subinfo-block{display:flex;flex-direction:column;gap:var(--sp-3)}
.odl-subinfo-head{
  font-family:var(--font-mono);font-size:var(--t-meta);
  letter-spacing:0.04em;text-transform:uppercase;color:var(--muted);
  margin:0;
}

/* ad-hoc price rows (supervised vs self-service) */
.odl-price-grid{display:flex;flex-direction:column;gap:var(--sp-2)}
.odl-price{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-4);
  padding:var(--sp-3) var(--sp-4);
  border:1px solid var(--line);border-radius:var(--radius-sm);
  background:var(--paper);
}
.odl-price-mode{
  font-family:var(--font-mono);font-size:var(--t-sm);
  letter-spacing:0.02em;color:var(--ink);text-transform:capitalize;
}
.odl-price-amount{font-weight:600;color:var(--brand);font-size:var(--t-lead)}

/* package cards — also used on the public /packages page */
.package-grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(15rem,1fr));
  gap:var(--sp-4);
}
.package-card{
  display:flex;flex-direction:column;gap:var(--sp-3);
  border:1px solid var(--line);border-radius:var(--radius);
  background:var(--paper);padding:var(--sp-5);
}
.package-card-head{
  display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--sp-3);flex-wrap:wrap;
}
.package-card-name{font-size:var(--t-h3);color:var(--ink)}
.package-card-price{font-weight:600;color:var(--brand);white-space:nowrap}
.package-card-cadence{
  font-family:var(--font-mono);font-size:var(--t-meta);
  font-weight:400;color:var(--muted);letter-spacing:0.02em;
}
.package-card-desc{margin:0;color:var(--muted);font-size:var(--t-sm)}
.package-card-progs{
  margin:0;padding:0;list-style:none;
  display:flex;flex-direction:column;gap:var(--sp-1);
}
.package-card-progs li{
  font-size:var(--t-sm);color:var(--ink);
  padding-left:var(--sp-4);position:relative;
}
.package-card-progs li::before{
  content:"›";position:absolute;left:0;color:var(--accent);
}

/* entitlement badge — shown when the signed-in learner holds access to a
 * subscription programme (a positive counterpart to the tier badge). */
.badge.is-entitled{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}

/* ============ task media — "Sources & materials" (task-media feature) ======
 * Media a task SHOWS, rendered on /t below the definition. Typed per media_type:
 * an image inline, a self-hosted video, and an authorised-source link card that
 * records the click. Tokens only; the link card carries the interactive accent. */
.task-media{
  border-top:1px solid var(--line);
  margin-top:var(--sp-6);padding-top:var(--sp-5);
}
.task-media h2{font-size:var(--t-h3);color:var(--brand);margin:0 0 var(--sp-4)}
.media-list{display:flex;flex-direction:column;gap:var(--sp-4)}
.media-item{margin:0}

/* image source */
.media-image{display:flex;flex-direction:column;gap:var(--sp-2)}
.media-img{
  display:block;max-width:100%;height:auto;
  border:1px solid var(--line);border-radius:var(--radius);background:var(--raise);
}
.media-caption{
  font-family:var(--font-serif);font-size:var(--t-sm);color:var(--muted);line-height:1.4;
}

/* video source (self-hosted; a non-file video falls back to a link card) */
.media-video{display:flex;flex-direction:column;gap:var(--sp-2)}
.media-player{
  display:block;width:100%;max-width:100%;
  border:1px solid var(--line);border-radius:var(--radius);background:var(--brand);
}

/* link card — the authorised source, and the fallback for a blocked image or a
 * non-file video. The whole card is the click target. */
.media-card{
  display:flex;align-items:center;gap:var(--sp-3);
  border:1px solid var(--line);border-left:var(--rule) solid var(--accent);
  border-radius:0 var(--radius) var(--radius) 0;
  background:var(--paper);
  padding:var(--sp-3) var(--sp-4);
  text-decoration:none;color:var(--ink);
  transition:background .12s, border-color .12s;
}
.media-link:hover{background:var(--accent-soft);border-left-color:var(--accent-hover)}
.media-link:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.media-link-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.media-link-caption{font-weight:600;font-size:var(--t-base);color:var(--accent);line-height:1.35}
.media-link:hover .media-link-caption{color:var(--accent-hover)}
.media-link-host{
  font-family:var(--font-mono);font-size:var(--t-meta);color:var(--muted);
  letter-spacing:0.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.media-link-go{flex:none;font-size:var(--t-lead);color:var(--accent);line-height:1}
.media-link:hover .media-link-go{color:var(--accent-hover)}

/* file-evidence placeholder (binary evidence deferred to the app's own host):
 * neutral bridge default; registered in the style guide for design to finalise. */
.evidence-file-note{border:1px dashed var(--line);border-radius:var(--radius-sm);padding:var(--sp-3) var(--sp-4);margin:0 0 var(--sp-4);color:var(--muted);font-size:var(--t-sm)}
.evidence-file-note strong{color:var(--ink)}
