/* vc/css/main.css
   ===========================================================================
   COMPONENT STYLES
   ---------------------------------------------------------------------------
   Loaded AFTER theme.css. Consumes --vc-* tokens only. There is deliberately
   not a single colour literal in this file: every colour in the module resolves
   through the bridge in theme.css, which is what makes light, dark, and system
   one attribute flip rather than a stylesheet swap.

   Class names are carried over from the prototype verbatim wherever the ported
   canvas engines depend on them (.canvas-frame, .control, .metric, .readout,
   .scrub-row, .step-btn, .entry-block, and the button family). Renaming any of
   those would turn the Step 2 port into a rewrite, so they stay as they are.

   Section map
     1  base and layout
     2  campus bar
     3  buttons and controls
     4  cards and grids
     5  reading typography
     6  note variants          the load bearing pedagogical device
     7  tables and figures
     8  canvas, metrics, readout
     9  timeline entry blocks
    10  footer
    11  responsive and motion
   =========================================================================== */


/* ============================ 1. BASE AND LAYOUT ========================== */

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
    margin: 0;
    background-color: var(--vc-bg);
    color: var(--vc-text);
    font-family: var(--vc-font);
    font-size: 16px;
    line-height: 1.7;
    transition: background-color 0.3s, color 0.3s;
}

/* Long unbroken strings, mostly ids and slugs in captions, must not be allowed
   to push the document wider than the viewport at 375 pixels. */
body, p, li, td, th, h1, h2, h3 { overflow-wrap: break-word; }

.wrap {
    max-width: 1140px;
    margin: 0 auto;
    padding: var(--vc-space-lg) 2rem 3rem;
}

.title-row { margin-bottom: var(--vc-space-lg); }

h1 {
    margin: 0 0 0.4rem;
    font-size: clamp(26px, 5vw, 38px);
    line-height: 1.15;
    color: var(--vc-heading);
}

.subtitle {
    margin: 0;
    max-width: 62ch;
    color: var(--vc-text-soft);
}

/* Keyboard and stylus users get a way past the campus bar. Visually hidden
   until focused, then it lands as a normal sized control. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 10;
    min-height: var(--vc-hit);
    padding: 0.7rem 1.1rem;
    background-color: var(--vc-accent);
    color: var(--vc-bg);
    border-radius: 0 0 10px 0;
    text-decoration: none;
    font-weight: 600;
}
.skip-link:focus { left: 0; }


/* ============================== 2. CAMPUS BAR ============================= */
/* Standalone tree, so there is no Explore dropdown and no cross application
   link. A campus bar with ../ or ../portal/ entries would dangle from a folder
   on disk. Brand mark, wordmark, and the theme controls, which is exactly the
   prototype's header. */

.campus-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--vc-space-md);
    flex-wrap: wrap;
    padding: 0.75rem 2rem;
    background-color: var(--vc-header);
    border-bottom: 1px solid var(--vc-border);
}

/* Left cluster of the campus bar. The bar itself is space-between with exactly
   two children, so the brand and the Explore dropdown have to travel together
   in one box, otherwise a third child would spread all three across the row. */
.campus-left {
    display: flex;
    align-items: center;
    gap: var(--vc-space-md);
    flex-wrap: wrap;
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    min-height: var(--vc-hit);
    color: var(--vc-heading);
    text-decoration: none;
    font-weight: 600;
}
.brand:hover { color: var(--vc-accent); }

/* A CSS drawn mark rather than an image file, so the header issues no network
   request of any kind and cannot 404 from a folder on disk. */
.brand-mark {
    display: inline-grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border-radius: 7px;
    background-color: var(--vc-accent);
    color: var(--vc-bg);
    font-weight: 700;
    font-size: 15px;
}


/* ------------------------------------------------- nav row and dropdowns -- */
/* Zero JavaScript. A dropdown opens on :hover for a pointer and on
   :focus-within for a keyboard or a stylus tab, which is the same mechanism the
   campus shells use. Nothing here needs a script to work, so the menus are live
   from the first paint rather than waiting on Step 3.

   The nav row is position: relative because the mobile rule below re-anchors
   every panel to it. */
.nav-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    padding: 0.5rem 2rem;
    background-color: var(--vc-header);
    border-bottom: 1px solid var(--vc-border);
}

.nav-dropdown { position: relative; }

.nav-dropdown-trigger {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-height: var(--vc-hit);
    padding: 0 0.95rem;
    border: 1px solid var(--vc-border);
    border-radius: 8px;
    background-color: var(--vc-bg);
    color: var(--vc-text);
    font-family: inherit;
    font-size: 14.5px;
    font-weight: 500;
    cursor: pointer;
    transition: var(--vc-fast);
}
.nav-dropdown-trigger:hover { background-color: var(--vc-panel-hover); border-color: var(--vc-accent); }
.nav-dropdown:focus-within .nav-dropdown-trigger { border-color: var(--vc-accent); }
.nav-dropdown-caret { font-size: 10px; color: var(--vc-text-soft); }

.nav-dropdown-menu {
    position: absolute;
    top: calc(100% + 0.4rem);
    left: 0;
    z-index: 30;
    min-width: 232px;
    padding: 0.3rem;
    border: 1px solid var(--vc-border);
    border-radius: 10px;
    background-color: var(--vc-panel);
    box-shadow: 0 8px 24px var(--vc-shadow);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity var(--vc-fast);
}
.nav-dropdown:hover > .nav-dropdown-menu,
.nav-dropdown:focus-within > .nav-dropdown-menu {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
}

/* Transparent bridge across the trigger to panel gap. Because the panel is a
   descendant of .nav-dropdown, hovering the bridge keeps the ancestor :hover
   alive, so the menu no longer snaps shut mid traversal. */
.nav-dropdown-menu::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: -0.4rem;
    height: 0.4rem;
}

.nav-dropdown-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    width: 100%;
    min-height: var(--vc-hit);
    padding: 0 0.75rem;
    border: none;
    border-radius: 7px;
    background-color: transparent;
    color: var(--vc-text);
    font-family: inherit;
    font-size: 14.5px;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    transition: var(--vc-fast);
}
.nav-dropdown-item:hover { background-color: var(--vc-panel-hover); color: var(--vc-accent); }
.nav-dropdown-item[aria-pressed="true"] {
    background-color: var(--vc-accent);
    color: var(--vc-bg);
    font-weight: 600;
}
.nav-dropdown-item .item-note {
    font-size: 12px;
    color: var(--vc-text-soft);
}
.nav-dropdown-item:hover .item-note { color: inherit; }
.nav-dropdown-item[aria-pressed="true"] .item-note { color: inherit; }

.nav-dropdown-heading {
    padding: 0.45rem 0.75rem 0.3rem;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--vc-text-soft);
}

/* The classroom this shell IS. Accent text rather than the filled treatment the
   theme buttons use, because it is a statement of location and not a control in
   a pressed state. */
.nav-dropdown-item.explore-active {
    color: var(--vc-accent);
    font-weight: 600;
}

/* A coming soon classroom is a span carrying no href, never a disabled anchor.
   A disabled anchor is still a keyboard target and still shows a destination in
   the status bar, which promises something that does not exist. */
.nav-dropdown-item.explore-locked {
    color: var(--vc-muted);
    cursor: default;
}
.nav-dropdown-item.explore-locked:hover {
    background-color: transparent;
    color: var(--vc-muted);
}

/* Right edge panels are right aligned through container scoped selectors, so
   specificity decides rather than source order. */
.theme-dropdown .nav-dropdown-menu {
    left: auto;
    right: 0;
    min-width: 210px;
}


/* ======================= 3. BUTTONS AND CONTROLS ========================== */
/* Every control resolves its minimum height from --vc-hit. The owner works with
   a stylus, so a small hit target is a defect rather than a nitpick. */

.btn {
    min-height: var(--vc-hit);
    padding: 0 1.3rem;
    border: 1px solid var(--vc-accent);
    border-radius: 10px;
    background-color: var(--vc-accent);
    color: var(--vc-bg);
    font-family: inherit;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    transition: var(--vc-fast);
}
.btn:hover { background-color: var(--vc-accent-hover); border-color: var(--vc-accent-hover); }

.btn-ghost { background-color: transparent; color: var(--vc-accent); }
.btn-ghost:hover { background-color: var(--vc-accent-soft); }
.btn-ghost[aria-pressed="true"] { background-color: var(--vc-accent); color: var(--vc-bg); }

.back-btn {
    min-height: var(--vc-hit);
    padding: 0 1.1rem;
    margin-bottom: var(--vc-space-md);
    border: 1px solid var(--vc-border);
    border-radius: 10px;
    background-color: var(--vc-bg);
    color: var(--vc-text);
    font-family: inherit;
    font-size: 15px;
    cursor: pointer;
}
.back-btn:hover { background-color: var(--vc-panel-hover); border-color: var(--vc-accent); }

.route-nav {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: var(--vc-space-lg);
}
.route-btn {
    min-height: var(--vc-hit);
    padding: 0 1.15rem;
    border: 1px solid var(--vc-border);
    border-radius: 10px;
    background-color: var(--vc-panel);
    color: var(--vc-text);
    font-family: inherit;
    font-size: 15px;
    font-weight: 500;
    cursor: pointer;
    transition: var(--vc-fast);
}
.route-btn:hover { background-color: var(--vc-panel-hover); border-color: var(--vc-accent); }
.route-btn[aria-current="page"] {
    background-color: var(--vc-accent);
    border-color: var(--vc-accent);
    color: var(--vc-bg);
    font-weight: 600;
}

.btn:focus-visible,
.btn-ghost:focus-visible,
.route-btn:focus-visible,
.theme-btn:focus-visible,
.back-btn:focus-visible,
.step-btn:focus-visible,
.sandbox-card:focus-visible,
.module-card:focus-visible,
a:focus-visible {
    outline: 3px solid var(--vc-accent-hover);
    outline-offset: 2px;
}

.control-row {
    display: flex;
    flex-wrap: wrap;
    gap: 1.4rem;
    align-items: flex-end;
    margin-bottom: var(--vc-space-md);
}

/* A wrapping row of related buttons inside one control, used by the ecosystem
   map's dataset switch. */
.btn-row { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.btn-row .btn { font-size: 14px; padding: 0 0.9rem; }
.control { flex: 1 1 230px; min-width: 210px; }
.control label {
    display: block;
    margin-bottom: 0.3rem;
    font-size: 13px;
    font-weight: 600;
    color: var(--vc-text-soft);
}
.control output { color: var(--vc-accent); font-weight: 700; }

/* Stylus sized range input. Thick track, oversized thumb, and a tall
   transparent hit area so a tablet pen lands on it without pixel hunting. */
input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: var(--vc-hit);
    margin: 0;
    background: transparent;
    cursor: pointer;
    touch-action: none;
}
input[type="range"]::-webkit-slider-runnable-track {
    height: 12px;
    border-radius: 6px;
    background-color: var(--vc-border);
}
input[type="range"]::-moz-range-track {
    height: 12px;
    border-radius: 6px;
    background-color: var(--vc-border);
}
input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: var(--vc-thumb);
    height: var(--vc-thumb);
    margin-top: calc((12px - var(--vc-thumb)) / 2);
    border-radius: 50%;
    border: 3px solid var(--vc-bg);
    background-color: var(--vc-accent);
    box-shadow: 0 1px 4px var(--vc-shadow);
}
input[type="range"]::-moz-range-thumb {
    width: var(--vc-thumb);
    height: var(--vc-thumb);
    border-radius: 50%;
    border: 3px solid var(--vc-bg);
    background-color: var(--vc-accent);
}
input[type="range"]:focus-visible::-webkit-slider-thumb {
    outline: 3px solid var(--vc-accent-hover);
    outline-offset: 2px;
}


/* ========================= 4. CARDS AND GRIDS ============================= */

.toc-grid {
    display: grid;
    gap: var(--vc-space-md);
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

.sandbox-card,
.module-card {
    padding: var(--vc-space-md) 1.15rem;
    border: 1px solid var(--vc-border);
    border-left: 5px solid var(--vc-accent);
    border-radius: 10px;
    background-color: var(--vc-panel);
    cursor: pointer;
    transition: var(--vc-fast);
}
.sandbox-card:hover,
.module-card:hover {
    background-color: var(--vc-panel-hover);
    box-shadow: 0 4px 12px var(--vc-shadow);
}
.sandbox-card h3,
.module-card h3 {
    margin: 0 0 0.35rem;
    font-size: 18px;
    color: var(--vc-heading);
}
.sandbox-card p,
.module-card p {
    margin: 0;
    font-size: 14.5px;
    color: var(--vc-text-soft);
}

.workspace-intro { margin-bottom: var(--vc-space-md); }

.badge {
    display: inline-block;
    padding: 0.2rem 0.6rem;
    border-radius: 999px;
    background-color: var(--vc-accent-soft);
    color: var(--vc-accent);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.checkpoint-body {
    padding: 1.15rem;
    border: 1px solid var(--vc-border);
    border-radius: 12px;
    background-color: var(--vc-panel);
}


/* ======================= 5. READING TYPOGRAPHY ============================ */
/* The module reader in Step 4 mounts its blocks inside .reading. */

/* ONE COLUMN. EVERY BLOCK SHARES ITS EDGES.

   An earlier version of this file capped prose at a 72 character measure and
   let figures run to the full page width. That was defensible in isolation and
   wrong on the page: paragraphs stopped at roughly 600 pixels while charts ran
   to 1076, so the left edge held but the right edge jumped on every figure and
   the document read as two documents interleaved.

   So the whole module view is one measured column now, sized by the widest
   thing that has to fit in it rather than by the text.

   THE WIDTH IS DERIVED, NOT CHOSEN. From vcMountCanvas:

     avail = frame.clientWidth - 16       the 0.5rem frame padding
     it scrolls when minWidth > avail

   frame.clientWidth is the figure width less the 1px border on each side, so a
   column of width W gives an engine W - 18 pixels to draw in. The widest floor
   in the registry is the referral network at 680, so the column must be at
   least 698. At 760 the engines get 742, which clears every floor with 62
   pixels to spare and is the smallest round number that does.

   That headroom is the budget for any new engine: a floor above 742 will
   scroll, and the answer then is to widen this one value, not to special case
   the block.

   The cost is a longer line of text, about 86 characters rather than 72. That
   is past the comfortable measure and it is a deliberate trade: a flush column
   down the centre of the page reads better than an optimal measure that keeps
   stepping sideways. */

/* THE MEASURED COLUMN. Every single column view shares it, so the left and
   right edges of the application never move as a student navigates. The class
   is stamped on the container by the router from the route name.

   NOTE ON THE NAMING. The standalone interactive route parses as "lab", not
   "sandbox", so the class is .view-lab. There is no .view-sandbox and a rule
   written against one would silently match nothing. */
.view-module,
.view-module-quiz,
.view-readings,
.view-review,
.view-glossary,
.view-corrections,
.view-timeline,
.view-lab,
.view-downloads,
.view-settings,
.view-dashboard {
    max-width: 760px;
    margin: 0 auto;
}

/* Left at the full wrap width on purpose. These three are grids rather than
   documents, and their layouts get better as they get wider:

     .view-index           the module card grid, three across on a desktop
     .view-interactives    the same grid over the engine registry
     .view-fund-simulator  twelve deal cards and a results table

   Constraining them would not make the application more consistent. It would
   make three dashboards into three long narrow lists. */

/* The container carries the cap, so nothing inside it sets its own. Any block
   that did would be the one that broke the flush edge again. */
.reading { max-width: none; }
.view-module .view-lede,
.view-readings .view-lede,
.view-review .view-lede,
.view-glossary .view-lede,
.view-corrections .view-lede,
.view-timeline .view-lede,
.view-lab .view-lede,
.view-module .quiz,
.view-module-quiz .quiz,
.view-review .quiz { max-width: none; }
.reading h2 {
    margin: var(--vc-space-lg) 0 0.6rem;
    font-size: 24px;
    line-height: 1.25;
    color: var(--vc-heading);
}
.reading h3 {
    margin: var(--vc-space-md) 0 0.4rem;
    font-size: 19px;
    line-height: 1.3;
    color: var(--vc-heading);
}
.reading p { margin: 0 0 var(--vc-space-md); }
.reading ul,
.reading ol { margin: 0 0 var(--vc-space-md); padding-left: 1.4rem; }
.reading li { margin-bottom: 0.4rem; }

/* Span styles from schema section 3.2a. A term span whose ref resolves becomes
   a glossary link, and one whose ref does not resolve renders as plain text, so
   a retired glossary entry degrades instead of breaking the reading. */
.span-strong { font-weight: 700; color: var(--vc-heading); }
.span-em { font-style: italic; }
.span-term,
.span-figure {
    color: var(--vc-accent);
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: 3px;
    cursor: pointer;
}
.span-term:hover,
.span-figure:hover { text-decoration-style: solid; }

.caption {
    margin: 0.25rem 0 0;
    font-size: 13px;
    color: var(--vc-text-soft);
}


/* =========================== 6. NOTE VARIANTS ============================= */
/* The load bearing pedagogical device. lecture is what was said in class and
   context is what was added afterward, and a student must never be unsure which
   is which. Those two are therefore separated on three independent channels:

       hue        accent against success
       fill       tinted against plain panel
       rule       5px against 3px

   plus the label text itself. Hue alone would fail a colour blind reader and
   fail again in a greyscale print, so it is never the only signal.

   Expected markup, built by views-module.js in Step 4:

       <aside class="note lecture">
         <p class="note-label">From the lecture notes</p>
         <p class="note-text">...</p>
       </aside> */

.note {
    margin: 0 0 var(--vc-space-md);
    padding: 0.85rem 1.1rem;
    border-left: 3px solid var(--vc-border);
    border-radius: 0 8px 8px 0;
    background-color: var(--vc-panel);
}
.note-label {
    margin: 0 0 0.3rem;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--vc-text-soft);
}
.note-text { margin: 0; }
.note-text + .note-text { margin-top: 0.6rem; }

/* lecture, the primary voice. Tinted fill and the heaviest rule. */
.note.lecture {
    border-left-width: 5px;
    border-left-color: var(--vc-accent);
    background-color: var(--vc-accent-soft);
}
.note.lecture .note-label { color: var(--vc-accent); }

/* context, added afterward. Plain panel fill and a lighter rule, so it reads as
   a different register rather than as more lecture material. */
.note.context {
    border-left-color: var(--vc-success);
    background-color: var(--vc-panel);
}
.note.context .note-label { color: var(--vc-success); }

.note.principle {
    border-left-width: 5px;
    border-left-color: var(--vc-principle);
}
.note.principle .note-label { color: var(--vc-principle); }

.note.definition {
    border-left-color: var(--vc-muted);
}
.note.definition .note-label { color: var(--vc-text-soft); }

.note.correction {
    border-left-color: var(--vc-error);
}
.note.correction .note-label { color: var(--vc-error); }

.note.caution {
    border-left-color: var(--vc-warning);
}
.note.caution .note-label { color: var(--vc-warning); }

/* An unknown block type, or a figure or sandbox id with no registered engine,
   renders one of these instead of throwing. */
.note.placeholder {
    border-left-style: dashed;
    border-left-color: var(--vc-muted);
    color: var(--vc-text-soft);
    font-size: 14.5px;
}


/* ======================= 7. TABLES AND FIGURES ============================ */
/* A wide table scrolls inside its own container. The document body must never
   scroll horizontally, at any of the three target widths. */

.table-scroll {
    overflow-x: auto;
    margin: 0 0 var(--vc-space-md);
}
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 15px;
}
.data-table caption {
    caption-side: bottom;
    padding-top: 0.5rem;
    font-size: 13px;
    color: var(--vc-text-soft);
    text-align: left;
}
.data-table th,
.data-table td {
    padding: 0.6rem 0.75rem;
    border-bottom: 1px solid var(--vc-border);
    text-align: left;
    vertical-align: top;
}
.data-table th {
    color: var(--vc-heading);
    font-weight: 600;
    border-bottom-width: 2px;
}
.data-table tbody tr:hover { background-color: var(--vc-panel); }

.figure { margin: 0 0 var(--vc-space-md); }


/* ================== 8. CANVAS, METRICS, AND READOUT ====================== */
/* The 0.5rem frame padding is load bearing: vcMountCanvas derives its drawing
   width from frame.clientWidth minus 16, which is that padding on both sides at
   the 16px root size. Changing it desynchronises the backing store. */

canvas {
    display: block;
    width: 100%;
    height: auto;
    touch-action: none;
}
.canvas-frame {
    margin: var(--vc-space-md) 0 0.5rem;
    padding: 0.5rem;
    border: 1px solid var(--vc-border);
    border-radius: 10px;
    background-color: var(--vc-bg);
}

/* Applied by the kernel only when an engine declares a minWidth and the
   container is narrower than it. The dense node diagrams are unreadable when
   squeezed to 375 pixels, so they hold their width and scroll inside their own
   frame instead. The document body still never scrolls horizontally, which is
   the same treatment .table-scroll gives a wide table. */
.canvas-frame.scrollable { overflow-x: auto; }
.canvas-frame.scrollable canvas { width: auto; max-width: none; }

.metric-grid {
    display: grid;
    gap: var(--vc-space-md);
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    margin-top: var(--vc-space-md);
}
.metric { padding-top: 0.55rem; border-top: 3px solid var(--vc-accent); }
.metric .k {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--vc-text-soft);
}
.metric .v {
    font-size: 27px;
    font-weight: 700;
    line-height: 1.2;
    color: var(--vc-heading);
}
.metric.alt { border-top-color: var(--vc-error); }
.metric.alt .v { color: var(--vc-error); }

.readout {
    margin-top: var(--vc-space-md);
    padding: 0.85rem 1rem;
    border-left: 5px solid var(--vc-accent);
    border-radius: 8px;
    background-color: var(--vc-accent-soft);
    color: var(--vc-text);
    font-size: 15px;
}


/* ====================== 9. TIMELINE ENTRY BLOCKS ========================== */
/* Ported alongside the timeline engine in Step 2. The notes and context rules
   deliberately match the lecture and context note variants above, because they
   carry the same distinction. */

.scrub-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: var(--vc-space-md);
}
.scrub-row input[type="range"] { flex: 1 1 auto; }

.step-btn {
    flex: 0 0 auto;
    min-width: var(--vc-hit);
    min-height: var(--vc-hit);
    border: 1px solid var(--vc-border);
    border-radius: 10px;
    background-color: var(--vc-bg);
    color: var(--vc-text);
    font-family: inherit;
    font-size: 20px;
    cursor: pointer;
}
.step-btn:hover:not(:disabled) { background-color: var(--vc-panel-hover); border-color: var(--vc-accent); }
.step-btn:disabled { opacity: 0.35; cursor: default; }

.entry-year {
    font-size: 14px;
    font-weight: 700;
    letter-spacing: 0.08em;
    color: var(--vc-accent);
}
.entry-title {
    margin: 0.1rem 0 0.7rem;
    font-size: 21px;
    color: var(--vc-heading);
}
.entry-block {
    margin-top: 0.9rem;
    padding-left: 0.9rem;
    border-left: 3px solid var(--vc-border);
}
.entry-block.notes { border-left-color: var(--vc-accent); }
.entry-block.context { border-left-color: var(--vc-success); }
.entry-block .lbl {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--vc-text-soft);
}
.entry-block p { margin: 0.25rem 0 0; }


/* ============================== 10. FOOTER =============================== */
/* One compact horizontal line carrying branding, navigation, and copyright. A
   multi column footer grid was tried on the campus root pages and reverted the
   same day, so this is deliberately a single wrapping flex row. */

footer {
    padding: var(--vc-space-md) 2rem;
    border-top: 1px solid var(--vc-border);
    background-color: var(--vc-header);
}
.footer-main {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.5rem 1.1rem;
    font-size: 13.5px;
    color: var(--vc-text-soft);
}
.footer-main a { color: var(--vc-text-soft); text-decoration: none; }
.footer-main a:hover { color: var(--vc-accent); text-decoration: underline; }
.footer-main .sep { color: var(--vc-muted); }
.footer-brand {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 600;
    color: var(--vc-heading);
}


/* ========================= 12. VIEW FURNITURE ============================ */

.view-header { margin-bottom: var(--vc-space-lg); }
.view-header h1 { margin-bottom: 0.5rem; }
.view-lede {
    margin: 0;
    max-width: 68ch;
    color: var(--vc-text-soft);
}

.empty-state {
    padding: 1.4rem 1.25rem;
    border: 1px dashed var(--vc-border);
    border-radius: 12px;
    background-color: var(--vc-panel);
}
.empty-title {
    margin: 0 0 0.4rem;
    font-size: 17px;
    font-weight: 600;
    color: var(--vc-heading);
}
.empty-body { margin: 0; color: var(--vc-text-soft); }

.badge-done {
    background-color: var(--vc-success);
    color: var(--vc-bg);
}
.badge-pending {
    background-color: transparent;
    border: 1px solid var(--vc-muted);
    color: var(--vc-text-soft);
}
.badge-quiet {
    background-color: transparent;
    border: 1px solid var(--vc-border);
    color: var(--vc-text-soft);
}

.progress-track {
    height: 8px;
    border-radius: 999px;
    background-color: var(--vc-border);
    overflow: hidden;
}
.progress-fill {
    height: 100%;
    width: 0;
    border-radius: 999px;
    background-color: var(--vc-accent);
    transition: width var(--vc-fast);
}


/* ============================ 13. THE INDEX ============================== */

.index-progress { margin-bottom: var(--vc-space-lg); }
.index-progress-label {
    margin: 0 0 0.45rem;
    font-size: 14px;
    color: var(--vc-text-soft);
}

/* The due surface. Present only when something is actually due, so it can
   afford to be prominent without ever nagging. */
.review-card {
    display: flex;
    align-items: center;
    gap: 1rem;
    min-height: var(--vc-hit);
    margin-bottom: var(--vc-space-lg);
    padding: 0.9rem 1.15rem;
    border: 1px solid var(--vc-accent);
    border-radius: 12px;
    background-color: var(--vc-accent-soft);
    text-decoration: none;
    transition: var(--vc-fast);
}
.review-card:hover { box-shadow: 0 4px 12px var(--vc-shadow); }
.review-count {
    font-size: 30px;
    font-weight: 700;
    line-height: 1;
    color: var(--vc-accent);
}
.review-body { display: flex; flex-direction: column; gap: 0.15rem; }
.review-title { font-weight: 600; color: var(--vc-heading); }
.review-note { font-size: 13px; color: var(--vc-text-soft); }
.review-quiet {
    margin: 0 0 var(--vc-space-lg);
    font-size: 14px;
    color: var(--vc-text-soft);
}

.session-group { margin-bottom: var(--vc-space-lg); }
.session-heading {
    margin: 0 0 var(--vc-space-md);
    padding-bottom: 0.4rem;
    border-bottom: 1px solid var(--vc-border);
    font-size: 20px;
    color: var(--vc-heading);
}

/* The cards are real anchors, so they are bookmarkable and middle clickable
   rather than div elements pretending to be links. */
.module-card,
.sandbox-card {
    display: block;
    color: inherit;
    text-decoration: none;
}
.module-card-head {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    margin-bottom: 0.5rem;
}
.module-meta {
    margin-top: 0.5rem;
    font-size: 13px;
    color: var(--vc-text-soft);
}


/* ======================== 14. THE MODULE READER ========================== */

.objectives {
    margin-bottom: var(--vc-space-lg);
    padding: 1rem 1.15rem;
    border-left: 4px solid var(--vc-principle);
    border-radius: 0 10px 10px 0;
    background-color: var(--vc-panel);
}
.objectives-heading {
    margin: 0 0 0.5rem;
    font-size: 15px;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--vc-heading);
}
.objective-list { margin: 0; padding-left: 1.3rem; }
.objective-list li { margin-bottom: 0.3rem; }

.module-quiz-cta {
    margin: var(--vc-space-lg) 0 var(--vc-space-md);
    padding-top: var(--vc-space-md);
    border-top: 1px solid var(--vc-border);
}
.module-quiz-cta .btn { display: inline-flex; align-items: center; text-decoration: none; }

.module-complete-row { margin-bottom: var(--vc-space-lg); }

.module-nav {
    display: flex;
    justify-content: space-between;
    gap: var(--vc-space-md);
    padding-top: var(--vc-space-md);
    border-top: 1px solid var(--vc-border);
}
.module-nav-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--vc-hit);
    max-width: 46%;
    color: var(--vc-accent);
    text-decoration: none;
    font-size: 14.5px;
}
.module-nav-link:hover { text-decoration: underline; }
.module-nav-link.next { text-align: right; }

.inline-check {
    margin: var(--vc-space-md) 0;
    padding: 1rem 1.15rem;
    border: 1px solid var(--vc-accent);
    border-radius: 12px;
    background-color: var(--vc-panel);
}
.inline-check-label {
    margin: 0 0 0.6rem;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--vc-accent);
}

.figure { margin: var(--vc-space-md) 0; }
.figure figcaption { margin-top: 0.3rem; }


/* =========================== 15. THE QUIZ =============================== */

.quiz { max-width: 68ch; }
.quiz-title { margin: 0 0 var(--vc-space-md); font-size: 24px; color: var(--vc-heading); }

.quiz-progress { margin-bottom: var(--vc-space-md); }
.quiz-progress-label {
    display: block;
    margin-bottom: 0.4rem;
    font-size: 13px;
    font-weight: 600;
    color: var(--vc-text-soft);
}

.quiz-question { margin-bottom: var(--vc-space-md); }
.quiz-context {
    margin: 0 0 0.4rem;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--vc-text-soft);
}
.quiz-prompt {
    margin: 0 0 var(--vc-space-md);
    font-size: 18px;
    line-height: 1.5;
    color: var(--vc-heading);
}

.quiz-choices { display: flex; flex-direction: column; gap: 0.55rem; }

/* Every choice is a full width target whose height resolves from --vc-hit. The
   owner works with a stylus, so a cramped choice list is a defect rather than a
   density preference. */
.quiz-choice {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    width: 100%;
    min-height: var(--vc-hit);
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--vc-border);
    border-radius: 10px;
    background-color: var(--vc-panel);
    color: var(--vc-text);
    font-family: inherit;
    font-size: 15.5px;
    line-height: 1.45;
    text-align: left;
    cursor: pointer;
    transition: var(--vc-fast);
}
.quiz-choice:hover:not(:disabled) {
    border-color: var(--vc-accent);
    background-color: var(--vc-panel-hover);
}
.quiz-choice:disabled { cursor: default; }

.quiz-choice-key {
    flex: 0 0 auto;
    display: inline-grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 1px solid var(--vc-border);
    background-color: var(--vc-bg);
    font-size: 12.5px;
    font-weight: 700;
    color: var(--vc-text-soft);
}
.quiz-choice-text { flex: 1 1 auto; }

/* Graded states. Colour is never the only signal: the correct choice also gains
   a heavier border and the chosen wrong one a struck key, so the feedback
   survives a colourblind reader. */
.quiz-choice.correct {
    border-color: var(--vc-success);
    border-width: 2px;
    background-color: var(--vc-panel);
}
.quiz-choice.correct .quiz-choice-key {
    border-color: var(--vc-success);
    color: var(--vc-success);
    font-weight: 700;
}
.quiz-choice.wrong {
    border-color: var(--vc-error);
    border-width: 2px;
}
.quiz-choice.wrong .quiz-choice-key {
    border-color: var(--vc-error);
    color: var(--vc-error);
    text-decoration: line-through;
}

.quiz-feedback {
    margin-top: var(--vc-space-md);
    padding: 0.85rem 1rem;
    border-left: 5px solid var(--vc-border);
    border-radius: 0 8px 8px 0;
    background-color: var(--vc-panel);
}
.quiz-feedback.correct { border-left-color: var(--vc-success); }
.quiz-feedback.wrong { border-left-color: var(--vc-error); }
.quiz-verdict {
    margin: 0 0 0.35rem;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}
.quiz-feedback.correct .quiz-verdict { color: var(--vc-success); }
.quiz-feedback.wrong .quiz-verdict { color: var(--vc-error); }
.quiz-rationale { margin: 0; }

.quiz-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: var(--vc-space-md);
}

.quiz-summary {
    padding: 1.25rem;
    border: 1px solid var(--vc-border);
    border-radius: 12px;
    background-color: var(--vc-panel);
}
.quiz-summary h3 { margin: 0 0 0.5rem; color: var(--vc-heading); }
.quiz-score {
    margin: 0 0 0.4rem;
    font-size: 27px;
    font-weight: 700;
    color: var(--vc-accent);
}
.quiz-verdict-pass { margin: 0 0 0.6rem; color: var(--vc-success); font-weight: 600; }
.quiz-verdict-work { margin: 0 0 0.6rem; color: var(--vc-text-soft); font-weight: 600; }
.quiz-schedule-note { margin: 0; color: var(--vc-text-soft); font-size: 14.5px; }
.quiz-missed-heading {
    margin: var(--vc-space-md) 0 0.5rem;
    font-size: 14px;
    font-weight: 700;
    letter-spacing: 0.03em;
    color: var(--vc-heading);
}
.quiz-missed-item {
    padding: 0.75rem 0.9rem;
    margin-bottom: 0.5rem;
    border-left: 3px solid var(--vc-error);
    border-radius: 0 8px 8px 0;
    background-color: var(--vc-bg);
}
.quiz-missed-prompt { margin: 0 0 0.35rem; font-weight: 600; color: var(--vc-heading); }

/* Structural hooks. These carry no appearance of their own, but they are
   declared so this stylesheet documents the complete class vocabulary the views
   use, and so an unstyled class in a view is always a mistake rather than
   possibly deliberate. */
.quiz-stage,
.quiz-feedback-slot,
.quiz-missed,
.correction-list { display: block; }


/* ========================= 16. REFERENCE VIEWS =========================== */

.review-intro { margin-bottom: var(--vc-space-lg); }

.ref-links { margin: 0.4rem 0 0; font-size: 13px; }
.ref-link { color: var(--vc-accent); text-decoration: none; }
.ref-link:hover { text-decoration: underline; }

.glossary-list { margin: 0; }
.glossary-term {
    margin-top: var(--vc-space-md);
    font-size: 17px;
    font-weight: 700;
    color: var(--vc-heading);
}
.glossary-def {
    margin: 0.25rem 0 0;
    padding: 0 0 0.9rem 0.9rem;
    border-left: 3px solid var(--vc-border);
}
.glossary-def p { margin: 0; }

.correction-entry {
    padding: 0.9rem 1.1rem;
    margin-bottom: 0.6rem;
    border-left: 4px solid var(--vc-error);
    border-radius: 0 10px 10px 0;
    background-color: var(--vc-panel);
}
.correction-pair { margin: 0 0 0.3rem; }
.correction-heard {
    color: var(--vc-text-soft);
    text-decoration: line-through;
}
.correction-arrow { color: var(--vc-text-soft); font-size: 13px; }
.correction-correct { font-weight: 700; color: var(--vc-heading); }
.correction-note { margin: 0; font-size: 14.5px; color: var(--vc-text-soft); }

.reading-list { display: flex; flex-direction: column; gap: var(--vc-space-md); }
.reading-entry {
    padding: 1.1rem 1.25rem;
    border: 1px solid var(--vc-border);
    border-left: 5px solid var(--vc-accent);
    border-radius: 10px;
    background-color: var(--vc-panel);
}
.reading-title { margin: 0 0 0.2rem; font-size: 18px; color: var(--vc-heading); }
.reading-author { margin: 0 0 0.5rem; font-size: 14px; color: var(--vc-text-soft); }
.reading-desc { margin: 0 0 0.75rem; }
.reading-link { display: inline-flex; align-items: center; text-decoration: none; }
.reading-nolink { margin: 0; font-size: 13px; color: var(--vc-text-soft); font-style: italic; }


/* ==================== 11. RESPONSIVE AND MOTION ========================== */

@media (max-width: 768px) {
    .campus-bar,
    .nav-row,
    .wrap,
    footer {
        padding-left: 1rem;
        padding-right: 1rem;
    }
    .control { min-width: 100%; }

    /* The prose caps are measured in characters, so below this width the
       viewport is already narrower than the cap and they stop biting on their
       own. Nothing to override here any more. */

    /* The mobile dropdown sheet rule. Every dropdown container goes static, so
       its panel re-anchors to the positioned .nav-row and spans that row edge
       to edge. No trigger position can then push a panel past the viewport,
       and this matters for the hidden state too: a visibility: hidden box still
       widens the document in Chromium, which is exactly how a header dropdown
       produces a horizontal scrollbar on a phone. */
    .nav-dropdown { position: static; }
    .nav-dropdown-menu,
    .theme-dropdown .nav-dropdown-menu {
        left: 0;
        right: 0;
        min-width: 0;
    }

    /* The theme dropdown lives in the campus bar, which is its own positioned
       row, so it re-anchors there rather than to the nav row. */
    .campus-bar { position: relative; }
}

@media (max-width: 420px) {
    /* Let the triggers share the row rather than overflow it. */
    .nav-row .nav-dropdown { flex: 1 1 auto; }
    .nav-row .nav-dropdown-trigger { width: 100%; justify-content: space-between; }
}

/* ===========================================================================
   THE LBO DRILL, THE DEBATE, AND THE FUND SIMULATOR
   ---------------------------------------------------------------------------
   Every colour below is a token. There is not one literal in this section, so
   all three surfaces follow the theme without a single dark mode override of
   their own.

   EVERY CONTROL MEETS --vc-hit. The drill steppers, the allocation steppers,
   and the submit buttons are all at or above 48px, because the point of the
   stylus target is that a student can do the whole drill without reaching for
   a keyboard.
   =========================================================================== */

/* ------------------------------------------------------------- the drill -- */

.drill {
    margin: var(--vc-space-lg) 0;
    padding: 1.15rem;
    border: 1px solid var(--vc-border);
    border-left: 3px solid var(--vc-accent);
    border-radius: 12px;
    background-color: var(--vc-panel);
}
.drill-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
}
.drill-label {
    margin: 0;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--vc-accent);
}
.drill-counter {
    margin: 0;
    font-size: 13px;
    color: var(--vc-text-soft);
}
.drill-prompt { margin: 0.7rem 0 0; }

.drill-facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 0.35rem 1.1rem;
    margin: 1rem 0;
    padding: 0.85rem 1rem;
    border-radius: 10px;
    background-color: var(--vc-bg);
}
.drill-stat,
.fund-stat {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.2rem 0;
}
.drill-stat-label,
.fund-stat-label {
    font-size: 14px;
    color: var(--vc-text-soft);
}
.drill-stat-value,
.fund-stat-value {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    text-align: right;
    color: var(--vc-heading);
}
.fund-stat.is-positive .fund-stat-value { color: var(--vc-success); }
.fund-stat.is-negative .fund-stat-value { color: var(--vc-error); }

.drill-ask {
    margin: 0 0 1rem;
    font-size: 15px;
    color: var(--vc-text-soft);
}

/* The schedule the J curve drill hands the student. Figures are right aligned
   and tabular so a column of them can actually be added up by eye, which is
   the whole task. */
.drill-schedule:empty { display: none; }
.drill-schedule { margin: 0 0 1rem; }
.drill-table { font-size: 14.5px; }
.drill-table td,
.drill-table th { padding: 0.35rem 0.6rem; }
.drill-table td + td,
.drill-table th + th {
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.drill-table caption {
    margin-bottom: 0.5rem;
    font-size: 13px;
    text-align: left;
    color: var(--vc-text-soft);
}

.drill-verdict-note {
    margin: 0.5rem 0 0;
    font-size: 14px;
    color: var(--vc-text-soft);
}

.drill-input-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}
.drill-input-label {
    flex: 0 0 auto;
    font-weight: 600;
}
.drill-input {
    width: 7rem;
    min-height: var(--vc-hit);
    padding: 0 0.75rem;
    border: 1px solid var(--vc-border);
    border-radius: 8px;
    background-color: var(--vc-bg);
    color: var(--vc-text);
    font-family: inherit;
    font-size: 18px;
    font-variant-numeric: tabular-nums;
    text-align: center;
}
.drill-input:focus-visible {
    outline: 2px solid var(--vc-accent);
    outline-offset: 2px;
}
.drill-input[aria-invalid="true"] { border-color: var(--vc-error); }
.drill-unit { color: var(--vc-text-soft); }

/* Square at the full hit target, so a stylus has the same area to land on as
   a finger does. */
.drill-step,
.fund-step {
    min-width: var(--vc-hit);
    min-height: var(--vc-hit);
    padding: 0 0.6rem;
    font-size: 13px;
}

.drill-buttons { margin-top: 0.9rem; }
.drill-submit,
.drill-reveal,
.drill-again { min-height: var(--vc-hit); }

.drill-verdict:empty { display: none; }
.drill-verdict {
    margin-top: 0.9rem;
    padding: 0.7rem 0.9rem;
    border-radius: 8px;
    border-left: 3px solid var(--vc-border);
}
.drill-verdict.is-correct {
    border-left-color: var(--vc-success);
    background-color: var(--vc-panel-hover);
}
.drill-verdict.is-wrong {
    border-left-color: var(--vc-error);
    background-color: var(--vc-panel-hover);
}
.drill-verdict-text { margin: 0; }

.drill-step-list {
    margin: 1.1rem 0 0;
    padding-left: 1.3rem;
}
.drill-step-list li { margin-bottom: 0.6rem; }
.drill-step-label {
    display: block;
    font-weight: 700;
    color: var(--vc-heading);
}
.drill-step-work {
    display: block;
    font-size: 15px;
    color: var(--vc-text-soft);
}

.drill-attribution {
    margin-top: 1.1rem;
    padding: 0.9rem 1rem;
    border-radius: 10px;
    background-color: var(--vc-bg);
}
.drill-attribution-head {
    margin: 0 0 0.5rem;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--vc-text-soft);
}
.drill-attribution .drill-stat:last-of-type {
    margin-top: 0.35rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--vc-border);
}
.drill-attribution-note {
    margin: 0.7rem 0 0;
    font-size: 14px;
    color: var(--vc-text-soft);
}

/* --------------------------------------------------- the prediction gate -- */

.prediction { margin: var(--vc-space-lg) 0; }

.prediction-gate {
    padding: 1.1rem 1.25rem;
    border: 1px solid var(--vc-principle);
    border-radius: 12px;
    background-color: var(--vc-panel);
}
.prediction-label {
    margin: 0 0 0.4rem;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--vc-principle);
}
.prediction-prompt {
    margin: 0 0 0.5rem;
    font-size: 17px;
    font-weight: 600;
    color: var(--vc-heading);
}
.prediction-hint {
    margin: 0 0 0.9rem;
    font-size: 14px;
    color: var(--vc-text-soft);
}
.prediction-choices { display: flex; flex-direction: column; gap: 0.5rem; }
.prediction-choice { text-align: left; }

/* Once answered the gate becomes a short record of what was committed to, and
   it stays visible above the interactive so the contradiction is legible. */
.prediction-summary {
    padding: 0.85rem 1.1rem;
    border-left: 3px solid var(--vc-muted);
    border-radius: 0 10px 10px 0;
    background-color: var(--vc-panel);
}
.prediction-summary.is-correct { border-left-color: var(--vc-success); }
.prediction-summary.is-wrong { border-left-color: var(--vc-warning); }
.prediction-summary-label {
    margin: 0;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--vc-text-soft);
}
.prediction-summary.is-correct .prediction-summary-label { color: var(--vc-success); }
.prediction-summary.is-wrong .prediction-summary-label { color: var(--vc-warning); }
.prediction-summary-choice {
    margin: 0.25rem 0 0;
    font-weight: 600;
    color: var(--vc-heading);
}
.prediction-summary-actual {
    margin: 0.35rem 0 0;
    font-weight: 600;
    color: var(--vc-heading);
}
.prediction-summary-why {
    margin: 0.5rem 0 0;
    font-size: 14.5px;
    color: var(--vc-text-soft);
}
.prediction-summary-general { font-size: 14px; }

.prediction-stage:empty { display: none; }

/* The reveal is a fade rather than a slide, because the interactive under it
   may be a canvas mid layout and moving it would cost a reflow on first paint.
   Suppressed entirely under reduced motion by the global rule at the end. */
@keyframes vc-reveal {
    from { opacity: 0; }
    to   { opacity: 1; }
}
.prediction-gate.just-revealed,
.prediction-gate.just-revealed + .prediction-stage {
    animation: vc-reveal 0.35s ease both;
}

/* --------------------------------------------------------- the quotation -- */

/* Deliberately unlike a note. A note is the course speaking and sits in a
   filled box. A quotation is somebody else speaking, so it is set open, with a
   rule down the left and larger type, and its citation is part of the block
   rather than a footnote somewhere else. */
.quotation {
    margin: var(--vc-space-lg) 0;
    padding: 0.2rem 0 0.2rem 1.15rem;
    border-left: 3px solid var(--vc-accent);
}
.quotation-text { margin: 0; }
.quotation-text p {
    margin: 0;
    font-size: 18px;
    line-height: 1.6;
    color: var(--vc-heading);
}
.quotation-cite {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    margin-top: 0.6rem;
}
.quotation-speaker {
    font-weight: 700;
    font-size: 14.5px;
    color: var(--vc-text);
}
.quotation-source {
    font-size: 14px;
    color: var(--vc-text-soft);
}
/* The course answering back. Set in the ordinary voice and size, so a
   quotation can be given at full strength and then contested without either
   side being visually softened. */
.quotation-gloss {
    margin: 0.7rem 0 0;
    font-size: 15px;
    color: var(--vc-text-soft);
}

/* ------------------------------------------------------------ the debate -- */

.debate { margin: var(--vc-space-lg) 0; }
.debate-label {
    margin: 0 0 0.25rem;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--vc-text-soft);
}
.debate-question { margin: 0 0 0.9rem; }

.debate-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: var(--vc-space-md);
}
.debate-position {
    display: flex;
    flex-direction: column;
    padding: 1rem 1.1rem;
    border: 1px solid var(--vc-border);
    border-radius: 12px;
    background-color: var(--vc-panel);
}
/* The two sides are told apart by position and by border hue, never by one
   being tinted more favourably than the other. Both use a neutral information
   colour rather than success against error, because the layout must not cast a
   vote the prose has not cast. */
.debate-position:first-child { border-top: 3px solid var(--vc-principle); }
.debate-position:last-child  { border-top: 3px solid var(--vc-warning); }

.debate-claim {
    margin: 0;
    font-size: 17px;
    font-weight: 700;
    color: var(--vc-heading);
}
.debate-holder {
    margin: 0.2rem 0 0.7rem;
    font-size: 14px;
    color: var(--vc-text-soft);
}
.debate-body { margin: 0 0 0.9rem; }
.debate-source {
    margin-top: auto;
    font-size: 14px;
    color: var(--vc-accent);
}
.debate-verdict { margin-top: var(--vc-space-md); }

/* ------------------------------------------- the video and micro practice -- */

/* TWO PANELS IN THE 760px COLUMN, NOT A BREAKOUT.
   The column budget is the one in section 5 and this block lives inside it like
   every other. With the 1rem grid gap the two tracks resolve to about 396px and
   344px, so the player draws at 396 by 223 and the practice beside it keeps a
   comfortable measure for a four choice question. Widening this block past the
   column would restore the stepping right edge that section 5 exists to prevent.

   MOBILE FIRST, DELIBERATELY. The default is one column, so the stack is what a
   phone gets without a query having to undo anything. The side by side rule is
   the exception and only applies once the viewport can actually hold the wrap
   padding plus the full 760px column, which is 824px, so 880 clears it with
   room and the two panels never share a row while being squeezed. */

.video-practice { margin: var(--vc-space-lg) 0; }

.video-practice-label {
    margin: 0 0 0.25rem;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--vc-accent);
}
.video-practice-title {
    margin: 0 0 0.4rem;
    font-size: 19px;
    line-height: 1.3;
    color: var(--vc-heading);
}
.video-practice-lede {
    margin: 0 0 var(--vc-space-md);
    color: var(--vc-text-soft);
}

.video-practice-grid {
    display: grid;
    gap: var(--vc-space-md);
    /* minmax(0, ...) rather than a bare fraction, so a wide player or a long
       unbroken word inside a choice cannot force a track past its share and
       push the column. */
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
}

@media (min-width: 880px) {
    .video-practice-grid {
        grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    }
}

/* --------------------------------------------------------- the player ----- */

.video-panel { margin: 0; }

/* A ratio box, not aspect-ratio, because the frame must hold its shape before
   the player inside it has reported a size of its own. 56.25 percent is 9 over
   16. The frame keeps its border and radius whether the player loads or not, so
   an offline reader sees a deliberate empty stage rather than a collapsed gap. */
.video-frame {
    position: relative;
    height: 0;
    padding-bottom: 56.25%;
    overflow: hidden;
    border: 1px solid var(--vc-border);
    border-radius: 10px;
    background-color: var(--vc-panel);
}
.video-embed {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

/* Deliberately the quotation's citation, because it is doing the same job:
   somebody else is speaking and the course is reporting it. */
.video-cite {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem 0.5rem;
    margin-top: 0.55rem;
}
.video-speaker {
    font-weight: 700;
    font-size: 14.5px;
    color: var(--vc-text);
}
.video-source {
    font-size: 14px;
    color: var(--vc-text-soft);
}

.video-watch-for {
    margin: 0.7rem 0 0;
    padding: 0.6rem 0.85rem;
    border-left: 3px solid var(--vc-principle);
    border-radius: 0 8px 8px 0;
    background-color: var(--vc-panel);
}
.video-watch-for-label {
    margin: 0;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--vc-principle);
}
.video-watch-for-list {
    margin: 0.35rem 0 0;
    padding-left: 1.2rem;
    font-size: 14.5px;
}
.video-watch-for-list li { margin-bottom: 0.25rem; }
.video-watch-for-list li:last-child { margin-bottom: 0; }

/* Same treatment as .reading-interested, because it is the same rule. */
.video-interested {
    margin: 0.7rem 0 0;
    padding: 0.6rem 0.85rem;
    border-left: 3px solid var(--vc-warning);
    border-radius: 0 8px 8px 0;
    background-color: var(--vc-panel-hover);
}
.video-interested-label {
    margin: 0;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--vc-warning);
}
.video-interested-note {
    margin: 0.2rem 0 0;
    font-size: 14px;
    color: var(--vc-text-soft);
}

/* A full hit target, because it is the whole of the fallback path for a reader
   who is offline or who declines a third party frame. */
.video-away {
    display: inline-flex;
    align-items: center;
    min-height: var(--vc-hit);
    margin-top: 0.5rem;
    padding: 0 0.85rem;
    border: 1px solid var(--vc-border);
    border-radius: 999px;
    background-color: var(--vc-bg);
    color: var(--vc-accent);
    font-size: 14px;
    text-decoration: none;
    transition: var(--vc-fast);
}
.video-away:hover {
    background-color: var(--vc-accent-soft);
    border-color: var(--vc-accent);
}

/* ------------------------------------------------- the practice panel ----- */

.practice-panel {
    padding: 1rem 1.1rem;
    border: 1px solid var(--vc-accent);
    border-radius: 12px;
    background-color: var(--vc-panel);
}
.practice-label {
    margin: 0 0 0.5rem;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--vc-accent);
}
.practice-prompt {
    margin: 0 0 0.75rem;
    font-size: 14.5px;
    color: var(--vc-text-soft);
}

/* The quiz engine is mounted unchanged, so the overrides are the two things a
   narrow panel needs and nothing else: the measure cap comes off, because the
   panel is already narrower than 68 characters, and the title furniture goes
   because the panel carries its own label. */
.practice-panel .quiz { max-width: none; }
.practice-panel .quiz-prompt { font-size: 16.5px; }
.practice-panel .quiz-progress { margin-bottom: 0.75rem; }
.practice-panel.is-single .quiz-progress { display: none; }

/* THE 48px HIT BOX, RESTATED AT THE POINT OF USE.
   .quiz-choice already resolves its minimum from --vc-hit, and it is declared
   again here because this is the narrowest place a choice is ever rendered and
   the temptation to tighten the padding to fit one more line would silently
   take the target below the floor. The owner works with a stylus: a choice a pen
   can miss is a defect, not a density preference. The key stays circular and
   full size for the same reason, since it is inside the same tap area. */
.practice-panel .quiz-choice {
    min-height: var(--vc-hit);
    padding: 0.65rem 0.85rem;
    gap: 0.6rem;
    font-size: 15px;
}
.practice-panel .quiz-choices { gap: 0.5rem; }
.practice-panel .quiz-choice-key {
    width: 28px;
    height: 28px;
}
.practice-panel .quiz-actions .btn,
.practice-panel .quiz-summary .btn { min-height: var(--vc-hit); }
.practice-panel .quiz-summary {
    padding: 0.9rem 1rem;
    background-color: var(--vc-bg);
}

/* ---------------------------------------------------- interested sources -- */

.reading-interested {
    margin: 0.7rem 0;
    padding: 0.6rem 0.85rem;
    border-left: 3px solid var(--vc-warning);
    border-radius: 0 8px 8px 0;
    background-color: var(--vc-panel-hover);
}
.reading-interested-label {
    margin: 0;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--vc-warning);
}
.reading-interested-note {
    margin: 0.2rem 0 0;
    font-size: 14px;
    color: var(--vc-text-soft);
}

/* Sits directly above the link button, so the terms of access are read before
   the click rather than discovered after it. */
.reading-access {
    margin: 0.7rem 0 0.4rem;
    font-size: 14px;
    color: var(--vc-text-soft);
}

/* ------------------------------------ the readings inside a module -- */

/* Set off from the module body above it, because it is source material rather
   than more course text and a student should be able to see the reading end. */
.module-readings {
    margin-top: var(--vc-space-lg);
    padding-top: var(--vc-space-lg);
    border-top: 1px solid var(--vc-border);
}
.module-readings-head { margin: 0 0 0.3rem; font-size: 20px; }
.module-readings-lede {
    margin: 0 0 var(--vc-space-md);
    color: var(--vc-text-soft);
}

/* ------------------------------------------- interactives on a reading -- */

.reading-engines { margin: 0.8rem 0 0.2rem; }
.reading-engines-label {
    margin: 0 0 0.4rem;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--vc-text-soft);
}
.reading-engine-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}
/* Chips rather than buttons, so they read as related material and do not
   compete with the one link that actually reaches the reading. */
.reading-engine {
    display: inline-flex;
    align-items: center;
    min-height: var(--vc-hit);
    padding: 0 0.85rem;
    border: 1px solid var(--vc-border);
    border-radius: 999px;
    background-color: var(--vc-bg);
    color: var(--vc-accent);
    font-size: 14px;
    text-decoration: none;
    transition: var(--vc-fast);
}
.reading-engine:hover {
    background-color: var(--vc-accent-soft);
    border-color: var(--vc-accent);
}

/* ---------------------------------------------------- the fund simulator -- */

.fund-panel {
    margin: var(--vc-space-lg) 0;
    padding: 1.15rem;
    border: 1px solid var(--vc-border);
    border-radius: 12px;
    background-color: var(--vc-panel);
}
.fund-panel-head { margin: 0 0 0.75rem; font-size: 20px; }
.fund-note { margin: 0 0 1rem; color: var(--vc-text-soft); }

.fund-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 0.2rem 1.1rem;
    margin: 1rem 0 0.6rem;
    padding: 0.85rem 1rem;
    border-radius: 10px;
    background-color: var(--vc-bg);
}

.fund-bar {
    height: 10px;
    margin-bottom: 1rem;
    border-radius: 999px;
    background-color: var(--vc-panel-hover);
    overflow: hidden;
}
.fund-bar-fill {
    height: 100%;
    border-radius: 999px;
    background-color: var(--vc-accent);
    transition: width var(--vc-fast);
}
.fund-bar-fill.is-best { background-color: var(--vc-success); }

.fund-deal-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: var(--vc-space-sm);
    margin-bottom: var(--vc-space-md);
}
.fund-deal {
    padding: 0.85rem 1rem;
    border: 1px solid var(--vc-border);
    border-radius: 10px;
    background-color: var(--vc-bg);
}
.fund-deal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}
.fund-deal-name { margin: 0; font-size: 16px; }
.fund-deal-meta {
    margin: 0.2rem 0 0.7rem;
    font-size: 14px;
    color: var(--vc-text-soft);
}
.fund-deal-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}
.fund-deal-amount {
    flex: 1 1 auto;
    min-width: 6rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    text-align: center;
    color: var(--vc-heading);
}
.fund-fill { min-height: var(--vc-hit); font-size: 13px; }

.fund-close,
.fund-again { min-height: var(--vc-hit); margin-top: var(--vc-space-sm); }
.fund-close[disabled] { opacity: 0.5; cursor: not-allowed; }
.fund-reset {
    min-height: var(--vc-hit);
    margin-top: var(--vc-space-lg);
    color: var(--vc-text-soft);
}

/* A position that returned nothing is muted rather than painted in the error
   colour. Half the portfolio going to zero is the expected result here, not a
   fault, and six red rows would say the opposite of what the panel teaches. */
.fund-results .is-zero td { color: var(--vc-muted); }

.fund-lesson {
    margin-top: var(--vc-space-md);
    padding: 1rem 1.1rem;
    border-left: 3px solid var(--vc-accent);
    border-radius: 0 10px 10px 0;
    background-color: var(--vc-bg);
}
.fund-lesson-head {
    margin: 0 0 0.6rem;
    font-size: 15px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--vc-text-soft);
}
.fund-lesson-line { margin: 0 0 0.7rem; }
.fund-lesson-line:last-child { margin-bottom: 0; }

@media (max-width: 520px) {
    /* Stack the drill input so the field and both steppers stay full size
       rather than shrinking below the hit target to share one row. */
    .drill-input-row { gap: 0.4rem; }
    .drill-input-label { flex: 1 0 100%; }
    .drill-input { flex: 1 1 auto; width: auto; }
}

/* ==================== 14. THE COMPANION DOWNLOAD ========================== */
/* Offered on the readings page as a full card and at the foot of every module
   as a compact row. Same component, two densities, because on a module it must
   not compete with the quiz call to action sitting directly above it.

   No colour literal appears here. Every value resolves through the --vc-*
   bridge, so the block follows the campus palette in both themes without a
   single per theme rule of its own. */

.companion-download {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.55rem;
    margin: var(--vc-space-lg) 0;
    padding: 1.05rem 1.2rem;
    border: 1px solid var(--vc-accent);
    border-left-width: 3px;
    border-radius: 10px;
    background-color: var(--vc-accent-soft);
}

.companion-title {
    margin: 0;
    color: var(--vc-heading);
    font-size: 16px;
    font-weight: 700;
}

.companion-body {
    margin: 0;
    max-width: 62ch;
    color: var(--vc-text);
    font-size: 14.5px;
    line-height: 1.55;
}

/* The action row. Side by side on desktop, stacked on a phone.
   flex-wrap alone would wrap the second button under the first at an awkward
   width, so the narrow case is handled explicitly below rather than left to
   whatever the content happens to measure. */
.companion-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 0.6rem;
    margin-top: 0.15rem;
}

/* The control is an anchor rather than a button, so it needs the inline flex
   and the line height that .btn assumes from a real button element. Without
   them the label sits off centre inside the 48px hit target. */
.companion-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    line-height: 1;
    text-align: center;
}

/* The size note under the buttons. Quiet, because it answers a question the
   reader only asks on a metered connection. */
.companion-meta {
    margin: 0.1rem 0 0;
    color: var(--vc-text-soft);
    font-size: 12.5px;
}

/* The compact form: tighter, quieter ground, and the body text stands down to
   secondary so the eye still reaches the navigation underneath it. */
.companion-compact {
    gap: 0.4rem;
    margin: var(--vc-space-md) 0;
    padding: 0.85rem 1rem;
    border-color: var(--vc-border);
    border-left-color: var(--vc-accent);
    background-color: var(--vc-panel);
}
.companion-compact .companion-title { font-size: 15px; }
.companion-compact .companion-body {
    font-size: 13.5px;
    color: var(--vc-text-soft);
}

@media (max-width: 560px) {
    /* Stacked, each button full width. Two half width buttons at this size
       would each hold a wrapped two line label and neither would read as the
       primary action. Stacking also keeps both above the 48px hit floor
       without shrinking the type. */
    .companion-actions { flex-direction: column; }
    .companion-link {
        width: 100%;
        line-height: 1.3;
        padding-top: 0.6rem;
        padding-bottom: 0.6rem;
    }
}


/* ======================= 15. DOWNLOADS AND SETTINGS ======================= */
/* Two document shaped views added in the parity sweep. No colour literal here
   either: every value resolves through the --vc-* bridge. */

.download-group {
    margin: var(--vc-space-lg) 0 0.6rem;
    font-size: 15px;
    font-weight: 700;
    color: var(--vc-heading);
}

.download-list {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--vc-border);
    border-radius: 10px;
    overflow: hidden;
}

/* A row rather than a card. Twenty one cards is a wall; twenty one rows is a
   list you can scan down the left edge. */
.download-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--vc-space-md);
    padding: 0.7rem 0.9rem;
    border-top: 1px solid var(--vc-border);
    background-color: var(--vc-bg);
}
.download-row:first-child { border-top: none; }
.download-row:hover { background-color: var(--vc-panel); }

.download-text { min-width: 0; }
.download-title {
    margin: 0;
    font-size: 14.5px;
    font-weight: 600;
    color: var(--vc-text);
}
.download-meta {
    margin: 0.15rem 0 0;
    font-size: 12.5px;
    color: var(--vc-text-soft);
}
.download-link {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    line-height: 1;
    padding: 0 1rem;
}
.download-missing {
    flex: 0 0 auto;
    font-size: 13px;
    color: var(--vc-muted);
}

/* ---- settings ---- */

.settings-section {
    margin: var(--vc-space-lg) 0;
    padding: 1rem 1.15rem;
    border: 1px solid var(--vc-border);
    border-radius: 10px;
    background-color: var(--vc-bg);
}
.settings-title {
    margin: 0 0 0.3rem;
    font-size: 16px;
    font-weight: 700;
    color: var(--vc-heading);
}
.settings-lede {
    margin: 0 0 0.7rem;
    font-size: 13.5px;
    color: var(--vc-text-soft);
}

.settings-stat {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--vc-space-md);
    padding: 0.4rem 0;
    border-top: 1px solid var(--vc-border);
}
.settings-stat:first-of-type { border-top: none; }
.settings-stat-label { font-size: 14px; color: var(--vc-text); }
.settings-stat-value {
    font-size: 14px;
    font-weight: 700;
    color: var(--vc-heading);
    font-variant-numeric: tabular-nums;
}

.settings-note,
.settings-ok {
    margin: 0.7rem 0 0;
    font-size: 13px;
    color: var(--vc-text-soft);
}
.settings-ok { margin-top: 0; }

/* The storage warnings. These carry the only real bad news in the interface,
   so they get a border weight nothing else on the page has. */
.settings-alert {
    padding: 0.7rem 0.9rem;
    border-left: 3px solid var(--vc-muted);
    border-radius: 0 8px 8px 0;
    background-color: var(--vc-panel);
}
.settings-alert-title {
    margin: 0 0 0.25rem;
    font-size: 14px;
    font-weight: 700;
}
.settings-alert p:last-child { margin-bottom: 0; font-size: 13.5px; }
.settings-alert-error {
    border-left-color: var(--vc-error);
}
.settings-alert-error .settings-alert-title { color: var(--vc-error); }
.settings-alert-warning {
    border-left-color: var(--vc-warning);
}
.settings-alert-warning .settings-alert-title { color: var(--vc-warning); }

.settings-chart { margin-top: 0.5rem; }
.vc-chart-canvas { display: block; width: 100%; }

/* Armed state for the two step reset. The colour change is doing real work
   here: it is the only signal that the next press is the destructive one. */
.settings-danger { border-color: var(--vc-error); color: var(--vc-error); }
.settings-danger:hover { background-color: var(--vc-error); color: var(--vc-bg); }
.settings-danger.is-armed {
    background-color: var(--vc-error);
    color: var(--vc-bg);
    border-color: var(--vc-error);
}
.settings-status {
    margin: 0.5rem 0 0;
    font-size: 13px;
    color: var(--vc-text-soft);
}

@media (max-width: 560px) {
    /* The row's button drops under its label rather than squeezing the title
       into two or three words. */
    .download-row {
        flex-direction: column;
        align-items: stretch;
        gap: 0.5rem;
    }
    .download-link { width: 100%; padding-top: 0.55rem; padding-bottom: 0.55rem; }
}


/* ========================== 16. THE DASHBOARD ============================= */
/* A diagnostic surface. The visual weight follows the diagnostic weight: the
   conceptual gap section is the loudest thing on the page and the counts are
   the quietest, because that is the order in which they are useful. */

.dash-section {
    margin: var(--vc-space-lg) 0;
    padding: 1rem 1.15rem;
    border: 1px solid var(--vc-border);
    border-radius: 10px;
    background-color: var(--vc-bg);
}
.dash-title {
    margin: 0 0 0.3rem;
    font-size: 16px;
    font-weight: 700;
    color: var(--vc-heading);
}
.dash-lede {
    margin: 0 0 0.8rem;
    max-width: 62ch;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--vc-text-soft);
}
.dash-note {
    margin: 0.7rem 0 0;
    font-size: 13px;
    color: var(--vc-text-soft);
}
.dash-footnote {
    margin: var(--vc-space-lg) 0 0;
    font-size: 12.5px;
    color: var(--vc-text-soft);
}
.dash-chart { margin: 0.3rem 0 0.2rem; }

/* ---- due today ---- */
.dash-due {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    flex-wrap: wrap;
}
.dash-due-number {
    font-size: 44px;
    font-weight: 700;
    line-height: 1;
    color: var(--vc-accent);
    font-variant-numeric: tabular-nums;
}
.dash-due-unit { font-size: 14.5px; color: var(--vc-text); }
.dash-cta {
    display: inline-flex;
    align-items: center;
    margin-top: 0.9rem;
    text-decoration: none;
    line-height: 1;
}

/* ---- conceptual gaps ---- */
.dash-gap-list {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    margin-top: 0.9rem;
}
.dash-gap {
    padding: 0.7rem 0.85rem;
    border-left: 3px solid var(--vc-error);
    border-radius: 0 8px 8px 0;
    background-color: var(--vc-panel);
}
.dash-gap-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.dash-gap-name {
    font-size: 14.5px;
    font-weight: 700;
    color: var(--vc-heading);
}
/* The reach flag is the single most diagnostic mark on the page: it is what
   separates a hard question from a hole in the foundation. */
.dash-gap-flag {
    background-color: var(--vc-error);
    color: var(--vc-bg);
    border-color: var(--vc-error);
}
.dash-gap-detail {
    margin: 0.25rem 0 0;
    font-size: 13px;
    color: var(--vc-text-soft);
}
.dash-gap-modules { margin: 0.35rem 0 0; font-size: 13px; }
.dash-gap-modules-label { color: var(--vc-text-soft); }
.dash-module-link { color: var(--vc-accent); text-decoration: none; }
.dash-module-link:hover { text-decoration: underline; }

/* ---- drills ---- */
.dash-drill-list {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--vc-border);
    border-radius: 8px;
    overflow: hidden;
    margin-top: 0.9rem;
}
.dash-drill {
    padding: 0.7rem 0.85rem;
    border-top: 1px solid var(--vc-border);
}
.dash-drill:first-child { border-top: none; }
.dash-drill-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--vc-space-md);
}
.dash-drill-name { font-size: 14px; font-weight: 600; color: var(--vc-text); }
.dash-drill-rate {
    font-size: 14px;
    font-weight: 700;
    color: var(--vc-heading);
    font-variant-numeric: tabular-nums;
}
.dash-drill-thin { font-weight: 400; color: var(--vc-muted); }
.dash-drill-detail,
.dash-drill-split {
    margin: 0.2rem 0 0;
    font-size: 12.5px;
    color: var(--vc-text-soft);
}
.dash-drill-verdict {
    margin: 0.35rem 0 0;
    font-size: 13px;
    color: var(--vc-text);
    font-weight: 600;
}
.dash-drill-link {
    display: inline-block;
    margin-top: 0.4rem;
    font-size: 12.5px;
    color: var(--vc-accent);
    text-decoration: none;
}
.dash-drill-link:hover { text-decoration: underline; }

@media (max-width: 560px) {
    .dash-due-number { font-size: 36px; }
    .dash-cta { width: 100%; justify-content: center; padding-top: 0.6rem; padding-bottom: 0.6rem; }
    .dash-drill-head { flex-direction: column; align-items: flex-start; gap: 0.1rem; }
}


/* =========================== 17. THE IDENTITY ============================= */
/* Minimal by requirement and by constraint. The initial is CSS drawn for the
   same reason the brand mark beside it is: a remote profile photo would put a
   third party request on every signed in page load and undo the point of
   loading the identity client only on demand.

   No colour literal here either. */

.campus-right {
    display: flex;
    align-items: center;
    gap: var(--vc-space-md);
    flex-wrap: wrap;
}

.identity-mount { position: relative; }
.identity-dropdown { position: relative; }

/* The signed out control is an anchor before identity.js runs and a button
   afterwards, so both are given the trigger's shape explicitly rather than
   inheriting a button default one of them does not have. */
.identity-trigger {
    display: inline-flex;
    align-items: center;
    min-height: var(--vc-hit);
    padding: 0 0.95rem;
    border: 1px solid var(--vc-border);
    border-radius: 8px;
    background-color: var(--vc-bg);
    color: var(--vc-text);
    font-family: inherit;
    font-size: 14.5px;
    font-weight: 500;
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    transition: var(--vc-fast);
}
.identity-trigger:hover {
    background-color: var(--vc-panel-hover);
    border-color: var(--vc-accent);
}

/* The signed in state: an initial in a circle, sized to the same hit floor as
   every other control in the bar. */
.identity-avatar-btn {
    display: inline-grid;
    place-items: center;
    width: var(--vc-hit);
    height: var(--vc-hit);
    padding: 0;
    border: 1px solid var(--vc-border);
    border-radius: 50%;
    background-color: var(--vc-bg);
    cursor: pointer;
    transition: var(--vc-fast);
}
.identity-avatar-btn:hover { border-color: var(--vc-accent); }
.identity-initial {
    display: inline-grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background-color: var(--vc-accent);
    color: var(--vc-bg);
    font-weight: 700;
    font-size: 14px;
    line-height: 1;
}

/* Right aligned through a container scoped selector, so specificity decides
   rather than source order. Same rule the theme panel follows. */
.identity-dropdown .nav-dropdown-menu {
    left: auto;
    right: 0;
    min-width: 288px;
    padding: 0.7rem;
}

.identity-panel-title {
    margin: 0 0 0.3rem;
    font-size: 14.5px;
    font-weight: 700;
    color: var(--vc-heading);
}
.identity-panel-note {
    margin: 0 0 0.5rem;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--vc-text-soft);
}
.identity-panel-quiet { color: var(--vc-muted); }

.identity-name {
    margin: 0;
    font-size: 14.5px;
    font-weight: 700;
    color: var(--vc-heading);
}
.identity-email {
    margin: 0.1rem 0 0.5rem;
    font-size: 12.5px;
    color: var(--vc-text-soft);
    overflow-wrap: anywhere;
}

.identity-go {
    width: 100%;
    justify-content: center;
    display: inline-flex;
    align-items: center;
    line-height: 1;
}
.identity-gis-slot:not(:empty) { margin-top: 0.5rem; }
.identity-status:empty { display: none; }
.identity-status {
    margin: 0.5rem 0 0;
    font-size: 12.5px;
    color: var(--vc-text-soft);
}

.identity-settings { gap: 0.45rem; justify-content: flex-start; }
.identity-signout { color: var(--vc-error); }
.identity-signout:hover { color: var(--vc-error); }

@media (max-width: 768px) {
    /* The dropdown sheet rule the rest of the bar follows: below the
       breakpoint the container goes static so the panel re-anchors to the
       header row instead of being pushed off the right edge by its trigger. */
    .identity-dropdown { position: static; }
    .identity-dropdown .nav-dropdown-menu { left: 0; right: 0; min-width: 0; }
}


@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; }
    /* The reveal fade is decorative. Nothing depends on it having run, so it
       is dropped outright rather than shortened. */
    *, *::before, *::after { animation: none !important; }
    html { scroll-behavior: auto; }
}
