@charset "UTF-8";
/* ==========================================================================
   learn.pranaymahendrakar.com — site.css
   One hand-written stylesheet. No framework, no preprocessor, no web fonts.

   Contents
   01. Design tokens (light)
   02. Dark theme  (media query + manual override)
   03. Reset and base elements
   04. Utilities
   05. Page shell, grid, page furniture
   06. Site header, nav, theme toggle
   07. Search box and type-ahead panel
   08. Sidebar and mobile drawer
   09. Table of contents
   10. Prose typography
   11. Code blocks and output blocks
   12. Callouts
   13. Level tabs and level panels
   14. Cards, grids, chips
   15. Breadcrumbs, pager, next-up, prereq, topic lists
   16. Buttons
   17. Home page
   18. Listing and document pages
   19. Footer
   20. Motion, print, forced colours
   21. Authorship — byline and /about profile
   22. Authorship lists inside .prose
   23. LeetCode listing and solution pages
   24. Share row and "Learn this your way"
   24b. Present mode
   25. Practice — /practice

   Every selector below targets markup that a real template prints. The three
   files that produce it are views/partials/*.php, views/pages/*.php and
   app/Markdown.php — the last of which documents its markup contract in its
   own header. If a class name changes there, it changes here in the same
   commit, or the rule becomes dead weight and the element loses its styling
   silently.

   Every colour pair used for text was measured against WCAG 2.1:
   body text >= 4.5:1, functional control borders >= 3:1, in BOTH themes.
   Measured ratios are noted beside each token. Do not eyeball replacements.
   ========================================================================== */

/* ==========================================================================
   01. DESIGN TOKENS — light theme
   ========================================================================== */

:root {
  color-scheme: light;

  /* -- Surfaces ---------------------------------------------------------- */
  --bg:            #ffffff;
  --bg-soft:       #f6f7f9;   /* header, sidebar, cards on white           */
  --bg-sunken:     #eef0f4;   /* kbd, inline chips, wells                  */
  --bg-code:       #f2f4f7;   /* code block background                     */
  --bg-hover:      #eef1f6;   /* row / list hover                          */

  /* -- Lines -------------------------------------------------------------
     --rule        decorative hairline (cards, table rules) — exempt from
                   the 3:1 rule because it never carries meaning alone.
     --edge        functional control boundary (inputs, secondary buttons)
                   3.72:1 on #fff, 3.47:1 on --bg-soft, 3.26:1 on --bg-sunken
     --------------------------------------------------------------------- */
  --rule:          #e2e5ea;
  --rule-strong:   #d5dae1;
  --edge:          #7d8592;

  /* -- Ink ---------------------------------------------------------------
     --ink        16.56:1 on --bg
     --ink-soft    6.39:1 on --bg,  5.96:1 on --bg-soft
     --ink-faint   5.24:1 on --bg,  4.89:1 on --bg-soft, 4.60:1 on --bg-sunken
     --------------------------------------------------------------------- */
  --ink:           #1b1f24;
  --ink-soft:      #57606a;
  --ink-faint:     #646d79;
  --ink-quote:     #3d444d;   /* 9.85:1 on --bg */

  /* -- Accent — restrained documentation blue, never neon -----------------
     --accent      6.39:1 on --bg, 5.63:1 on --accent-soft
     --on-accent   6.39:1 on --accent
     --------------------------------------------------------------------- */
  --accent:        #0b57d0;
  --accent-hover:  #0842a0;   /* 9.14:1 on --bg */
  --accent-soft:   #eaf1fe;
  --accent-line:   #b9d1fb;
  --on-accent:     #ffffff;

  /* -- Level colours ------------------------------------------------------
     Colour is ALWAYS paired with a text label and a distinct dot shape.
     --------------------------------------------------------------------- */
  --green:         #0d7a44;   /* 5.41:1 on --bg | white 5.41:1 on it       */
  --green-soft:    #e6f4ec;
  --green-on:      #ffffff;
  --amber:         #8a5300;   /* 6.33:1 on --bg | white 6.33:1 on it       */
  --amber-soft:    #fdf3e2;
  --amber-on:      #ffffff;
  --red:           #b3261e;   /* 6.54:1 on --bg | white 6.54:1 on it       */
  --red-soft:      #fdecea;
  --red-on:        #ffffff;

  /* -- Callout accents (reuse level hues) --------------------------------- */
  --note:          var(--accent);
  --note-soft:     var(--accent-soft);
  --warn:          var(--red);
  --warn-soft:     var(--red-soft);
  --tip:           var(--green);
  --tip-soft:      var(--green-soft);

  /* -- Marks and keys ----------------------------------------------------- */
  --mark-bg:       #fff3bf;   /* --ink 14.85:1 on it */
  --mark-ink:      var(--ink);
  --kbd-bg:        #eef0f4;
  --kbd-ink:       var(--ink);

  /* -- Terminal (output blocks) — deliberately identical in both themes ----
     A dark slab reads as "machine output" everywhere, which is exactly the
     distinction we want against the light-tinted code block.
     --term-ink 13.29:1 | --term-dim 7.21:1 | --term-accent 10.13:1
     --------------------------------------------------------------------- */
  --term-bg:       #12171e;
  --term-bar:      #1b2230;
  --term-bar-ink:  #c3cbd6;   /* 9.74:1 on --term-bar */
  --term-ink:      #d8dee6;
  --term-dim:      #9aa5b4;
  --term-accent:   #59d99a;
  --term-edge:     #2b3446;

  /* -- Elevation ---------------------------------------------------------- */
  --shadow-sm:  0 1px 2px rgba(16, 24, 40, .06);
  --shadow:     0 2px 6px rgba(16, 24, 40, .07), 0 1px 2px rgba(16, 24, 40, .05);
  --shadow-lg:  0 12px 32px rgba(16, 24, 40, .13), 0 2px 8px rgba(16, 24, 40, .07);

  /* -- Typography ---------------------------------------------------------
     System stack only. A web font is a render-blocking round trip on a
     2G connection, and most of this audience arrives on mobile data.
     --------------------------------------------------------------------- */
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans",
               "Helvetica Neue", Arial, "Noto Sans Devanagari", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
               "Liberation Mono", "Noto Sans Mono", "Courier New", monospace;

  --fs-body:   clamp(1rem, .972rem + .12vw, 1.0625rem);
  --fs-sm:     .875rem;
  --fs-xs:     .78125rem;
  --fs-h1:     clamp(1.8rem, 1.36rem + 1.9vw, 2.5rem);
  --fs-h2:     clamp(1.35rem, 1.19rem + .7vw, 1.7rem);
  --fs-h3:     clamp(1.15rem, 1.08rem + .3vw, 1.3rem);
  --fs-h4:     1.0625rem;

  --lh-body:   1.7;    /* the calm, readable measure this site is built on */
  --lh-tight:  1.28;
  --lh-code:   1.65;

  --measure:   72ch;   /* prose line length cap */

  /* -- Space -------------------------------------------------------------- */
  --sp-1: .25rem;  --sp-2: .5rem;   --sp-3: .75rem;  --sp-4: 1rem;
  --sp-5: 1.5rem;  --sp-6: 2rem;    --sp-7: 3rem;    --sp-8: 4rem;

  /* -- Radii -------------------------------------------------------------- */
  --r-sm: 6px;  --r: 10px;  --r-lg: 14px;  --r-pill: 999px;

  /* -- Layout metrics ----------------------------------------------------- */
  --header-h:   3.75rem;
  --sidebar-w:  17rem;
  --toc-w:      14rem;
  --gutter:     clamp(1rem, .5rem + 2vw, 2.5rem);
  --max-w:      88rem;

  --ring:       2px solid var(--accent);
  --ring-off:   2px;
  --speed:      .16s;
}

/* ==========================================================================
   02. DARK THEME
   Two blocks on purpose:
     a) system preference, opted out of by an explicit data-theme="light"
     b) explicit data-theme="dark", which must win even in a light OS
   KEEP THESE TWO BLOCKS IN SYNC. Change one, change the other.
   ========================================================================== */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --bg:            #0e1117;
    --bg-soft:       #151a21;
    --bg-sunken:     #0a0d12;
    --bg-code:       #161b22;
    --bg-hover:      #1d2430;

    --rule:          #262c36;
    --rule-strong:   #333b47;
    --edge:          #6e7885;   /* 4.22:1 on --bg, 3.86:1 on --bg-code */

    --ink:           #e6e9ee;   /* 15.53:1 on --bg */
    --ink-soft:      #a3adba;   /*  8.32:1 on --bg */
    --ink-faint:     #8b96a5;   /*  6.30:1 on --bg, 5.77:1 on --bg-code */
    --ink-quote:     #c8cfd9;   /* 12.04:1 on --bg */

    --accent:        #79a8ff;   /*  7.97:1 on --bg, 6.62:1 on --accent-soft */
    --accent-hover:  #a9c6ff;
    --accent-soft:   #16233b;
    --accent-line:   #2b4370;
    --on-accent:     #0b1220;   /*  7.90:1 on --accent */

    --green:         #4fd18b;   /*  9.74:1 on --bg */
    --green-soft:    #0f2a1e;
    --green-on:      #08130c;   /*  9.76:1 on --green */
    --amber:         #f2b544;   /* 10.32:1 on --bg */
    --amber-soft:    #2e2210;
    --amber-on:      #1a1200;   /* 10.15:1 on --amber */
    --red:           #ff8078;   /*  7.75:1 on --bg */
    --red-soft:      #34151a;
    --red-on:        #2a0805;   /*  7.58:1 on --red */

    --mark-bg:       #3a2f0b;
    --mark-ink:      #ffe08a;   /* 10.23:1 on --mark-bg */
    --kbd-bg:        #1d2430;
    --kbd-ink:       var(--ink);

    --shadow-sm:  0 1px 2px rgba(0, 0, 0, .5);
    --shadow:     0 2px 6px rgba(0, 0, 0, .55), 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-lg:  0 14px 36px rgba(0, 0, 0, .62), 0 2px 8px rgba(0, 0, 0, .45);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --bg:            #0e1117;
  --bg-soft:       #151a21;
  --bg-sunken:     #0a0d12;
  --bg-code:       #161b22;
  --bg-hover:      #1d2430;

  --rule:          #262c36;
  --rule-strong:   #333b47;
  --edge:          #6e7885;

  --ink:           #e6e9ee;
  --ink-soft:      #a3adba;
  --ink-faint:     #8b96a5;
  --ink-quote:     #c8cfd9;

  --accent:        #79a8ff;
  --accent-hover:  #a9c6ff;
  --accent-soft:   #16233b;
  --accent-line:   #2b4370;
  --on-accent:     #0b1220;

  --green:         #4fd18b;
  --green-soft:    #0f2a1e;
  --green-on:      #08130c;
  --amber:         #f2b544;
  --amber-soft:    #2e2210;
  --amber-on:      #1a1200;
  --red:           #ff8078;
  --red-soft:      #34151a;
  --red-on:        #2a0805;

  --mark-bg:       #3a2f0b;
  --mark-ink:      #ffe08a;
  --kbd-bg:        #1d2430;
  --kbd-ink:       var(--ink);

  --shadow-sm:  0 1px 2px rgba(0, 0, 0, .5);
  --shadow:     0 2px 6px rgba(0, 0, 0, .55), 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-lg:  0 14px 36px rgba(0, 0, 0, .62), 0 2px 8px rgba(0, 0, 0, .45);
}

/* ==========================================================================
   03. RESET AND BASE
   ========================================================================== */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* anchor links must not land under the sticky header */
  scroll-padding-top: calc(var(--header-h) + 1rem);
  tab-size: 2;
}

body {
  min-height: 100vh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-wrap: break-word;
}

/* [hidden] must beat any display value we set on a component. */
[hidden] { display: none !important; }

img, picture, svg, video, canvas {
  display: block;
  max-width: 100%;
  height: auto;
}

svg { fill: none; stroke: currentColor; flex: none; }

input, button, textarea, select {
  font: inherit;
  color: inherit;
}

button { background: none; border: 0; }

a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: .16em;
  text-decoration-thickness: .07em;
  text-decoration-color: color-mix(in srgb, currentColor 42%, transparent);
  transition: color var(--speed) ease, text-decoration-color var(--speed) ease;
}
a:hover {
  color: var(--accent-hover);
  text-decoration-color: currentColor;
}

hr {
  height: 1px;
  border: 0;
  background: var(--rule);
  margin-block: var(--sp-6);
}

::selection {
  background: var(--accent-soft);
  color: var(--ink);
}

/* One consistent, high-contrast focus ring everywhere. Never remove it. */
:focus-visible {
  outline: var(--ring);
  outline-offset: var(--ring-off);
  border-radius: var(--r-sm);
}
:focus:not(:focus-visible) { outline: none; }

/* ==========================================================================
   04. UTILITIES
   ========================================================================== */

.visually-hidden,
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  top: 0; left: var(--sp-4);
  z-index: 200;
  transform: translateY(-120%);
  padding: var(--sp-2) var(--sp-4);
  background: var(--accent);
  color: var(--on-accent);
  border-radius: 0 0 var(--r) var(--r);
  text-decoration: none;
  font-weight: 600;
  box-shadow: var(--shadow);
  transition: transform var(--speed) ease;
}
.skip-link:focus { transform: translateY(0); color: var(--on-accent); }

.container {
  width: 100%;
  max-width: var(--max-w);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.stack > * + * { margin-top: var(--sp-4); }
.text-soft  { color: var(--ink-soft); }
.text-faint { color: var(--ink-faint); }
.text-sm    { font-size: var(--fs-sm); }
.nowrap     { white-space: nowrap; }

/* .js-only carries no styles on purpose. It marks a control that needs
   JavaScript to do anything; the <noscript> block in views/layout.php is what
   retires those controls when there is none. Giving it a display value here
   would override each component's own. */

/* Inline SVG from lai_icon() in views/helpers.php.
   The reset sets svg { display: block } so a standalone icon does not sit on a
   text baseline and inherit line-height. An icon printed inside a sentence
   needs the opposite, and would otherwise break the line it is on. Every
   caller today happens to be a flex container, where neither value matters —
   this rule is what keeps that from being luck. */
.icon {
  display: inline-block;
  vertical-align: -0.14em;
}

/* body carries `page page--<id> layout--<mode>` from views/layout.php. Nothing
   styles them: they exist as stable hooks for a future page-specific override,
   and as the fastest way to tell from devtools which branch of layout.php ran.
   The shell modifiers on .shell do the actual layout work. */

/* ==========================================================================
   05. PAGE SHELL AND GRID

   views/layout.php prints:

     <div class="shell [shell--sidebar] [shell--toc]">
        [aside.sidebar]  <main id="main" class="main">  [aside.toc--rail]

   The modifiers are added only when that column actually has content, so the
   grid never reserves space for a sidebar that is not there. min-width:0 on
   the middle track is what stops a wide code block from pushing the whole
   page sideways.
   ========================================================================== */

.shell {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.5rem, 1rem + 2vw, 3.5rem);
  align-items: start;
  width: 100%;
  max-width: var(--max-w);
  margin-inline: auto;
  padding-inline: var(--gutter);
  padding-block: var(--sp-6) var(--sp-8);
}

.shell > * { min-width: 0; }

.shell--sidebar            { grid-template-columns: var(--sidebar-w) minmax(0, 1fr); }
.shell--toc                { grid-template-columns: minmax(0, 1fr) var(--toc-w); }
.shell--sidebar.shell--toc { grid-template-columns: var(--sidebar-w) minmax(0, 1fr) var(--toc-w); }

.main { min-width: 0; }
.main:focus { outline: none; }

/* The TOC rail is the first thing to go: it is a convenience, the lesson is not. */
@media (max-width: 1200px) {
  .shell--toc,
  .shell--sidebar.shell--toc { grid-template-columns: var(--sidebar-w) minmax(0, 1fr); }
  .toc--rail { display: none; }
}

@media (max-width: 900px) {
  .shell,
  .shell--sidebar,
  .shell--toc,
  .shell--sidebar.shell--toc {
    grid-template-columns: minmax(0, 1fr);
    padding-block: var(--sp-5) var(--sp-7);
  }
  .shell--sidebar.shell--toc { grid-template-columns: minmax(0, 1fr); }
}

/* Generic page furniture used by every listing and document page. */

.page-head {
  padding-block: var(--sp-2) var(--sp-6);
  border-bottom: 1px solid var(--rule);
  margin-bottom: var(--sp-6);
}
.page-head__eyebrow {
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--sp-2);
}
.page-head__eyebrow a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  text-decoration: none;
  color: inherit;
}
.page-head__eyebrow a:hover { color: var(--accent); }
.page-head__title {
  font-size: var(--fs-h1);
  line-height: var(--lh-tight);
  letter-spacing: -.022em;
  font-weight: 700;
  text-wrap: balance;
}
.page-head__emoji { margin-inline-end: var(--sp-2); }
.page-head__lead {
  margin-top: var(--sp-3);
  max-width: var(--measure);
  color: var(--ink-soft);
  font-size: 1.0625em;
}
.page-head__facts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-4);
  font-size: var(--fs-sm);
  color: var(--ink-faint);
}
.page-head__dot { color: var(--rule-strong); }
.page-head__actions { margin-top: var(--sp-5); }
.page-head__search { margin-top: var(--sp-5); max-width: 34rem; }

.section-title {
  font-size: var(--fs-h2);
  line-height: var(--lh-tight);
  letter-spacing: -.015em;
  font-weight: 700;
  margin-top: var(--sp-7);
  margin-bottom: var(--sp-3);
  text-wrap: balance;
}
.section-lead {
  max-width: var(--measure);
  margin-bottom: var(--sp-5);
  color: var(--ink-soft);
}
.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
}
.section-head .section-title { margin-bottom: var(--sp-2); }
.section-more {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.section-more:hover { text-decoration: underline; }

.empty {
  padding: var(--sp-5);
  border: 1px dashed var(--rule-strong);
  border-radius: var(--r);
  background: var(--bg-soft);
  color: var(--ink-soft);
  max-width: var(--measure);
}

/* ==========================================================================
   06. SITE HEADER, NAV, THEME TOGGLE
   views/partials/header.php
   ========================================================================== */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: saturate(1.6) blur(10px);
  border-bottom: 1px solid var(--rule);
}
@supports not (backdrop-filter: blur(1px)) {
  .site-header { background: var(--bg); }
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--header-h);
  max-width: var(--max-w);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}
.brand:hover { color: var(--ink); }
.brand__mark { color: var(--accent); flex: none; }
.brand__text { display: flex; flex-direction: column; line-height: 1.15; }
.brand__name {
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: -.015em;
}
.brand__note {
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--ink-faint);
  white-space: nowrap;
}
/* Below the drawer breakpoint the header is brand + three controls; the second
   brand line is the first thing that can go to keep them all on one row. */
@media (max-width: 560px) { .brand__note { display: none; } }

/* min-width:0 lets the link row give ground before the row overflows. */
.site-nav { margin-inline-start: auto; min-width: 0; }
.site-nav__list {
  display: flex;
  align-items: center;
  /* Translated labels run wider than the English they replace — Tamil pushed
     this row 285px past the viewport and carried the language and theme
     controls off screen with it, which is exactly when a reader needs them.
     Wrapping the links inside the nav keeps the controls where they were. */
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--sp-1);
  list-style: none;
  padding: 0;
}

/* The header gained two things at once: the language control, and a brand that
   grew a second line. Between the drawer breakpoint and a genuinely wide screen
   that is enough to push the last links onto a second row, so the links give
   back a little of their own padding rather than the row growing taller. */
@media (min-width: 1151px) and (max-width: 1600px) {
  .site-nav__link { padding-inline: calc(var(--sp-2) - 1px); gap: calc(var(--sp-2) - 2px); }
  .site-nav__list { gap: 0; }
}

.site-nav__link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  color: var(--ink-soft);
  font-size: var(--fs-sm);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}
.site-nav__link:hover { background: var(--bg-hover); color: var(--ink); }
.site-nav__link.is-current,
.site-nav__link[aria-current="page"] {
  color: var(--accent);
  background: var(--accent-soft);
  font-weight: 600;
}
.site-nav__icon { display: inline-flex; }

/* The search field is the elastic part of this row: it gives ground first
   so the brand line and the controls after it keep their places. Without
   the shrink it held 19rem and pushed the language and theme buttons onto
   a second line at ordinary laptop widths. */
.site-header__search { width: min(19rem, 30vw); min-width: 8.5rem; flex: 0 1 auto; }

/* Cross-site link to the author's main site. Monospace and outlined so it reads
   as a way OUT of this site rather than another section of it — the primary nav
   items are sans-serif and borderless, so the contrast carries the meaning. */
.mainsite {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  flex: none;
  padding: .45rem .8rem;
  border: 1px solid var(--edge);
  border-radius: var(--r-sm);
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition: border-color var(--speed) ease, color var(--speed) ease,
              background var(--speed) ease;
}
.mainsite:hover,
.mainsite:focus-visible {
  border-color: var(--accent);
  background: var(--bg-hover);
  color: var(--ink);
}

.mainsite__arrow { transition: transform var(--speed) ease; }
.mainsite:hover .mainsite__arrow,
.mainsite:focus-visible .mainsite__arrow { transform: translateX(2px); }

@media (prefers-reduced-motion: reduce) {
  .mainsite__arrow { transition: none; }
  .mainsite:hover .mainsite__arrow,
  .mainsite:focus-visible .mainsite__arrow { transform: none; }
}

/* One of the two is always hidden, so a screen reader never meets both. */
.site-nav__item--external { display: none; }
@media (max-width: 1150px) {
  .mainsite--header { display: none; }
  .site-nav__item--external { display: block; }
  .site-nav__link--external { font-family: var(--font-mono); color: var(--ink-soft); }
}

/* Icon buttons ----------------------------------------------------------- */

.nav-toggle,
.theme-toggle {
  display: inline-grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  flex: none;
  border-radius: var(--r-sm);
  color: var(--ink-soft);
  cursor: pointer;
}
.nav-toggle:hover,
.theme-toggle:hover { background: var(--bg-hover); color: var(--ink); }

/* Both icon layers occupy the same cell, so the button never resizes. */
.nav-toggle > span,
.theme-toggle__icon { grid-area: 1 / 1; }

.nav-toggle { display: none; margin-inline-start: auto; }
.nav-toggle__close { display: none; }
:root[data-nav="open"] .nav-toggle__open  { display: none; }
:root[data-nav="open"] .nav-toggle__close { display: grid; }

/* Three states, one button: the icon shows what is in force right now. */
.theme-toggle__icon { display: none; }
.theme-toggle__icon--auto { display: grid; }
:root[data-theme-pref="light"] .theme-toggle__icon--auto  { display: none; }
:root[data-theme-pref="light"] .theme-toggle__icon--light { display: grid; }
:root[data-theme-pref="dark"]  .theme-toggle__icon--auto  { display: none; }
:root[data-theme-pref="dark"]  .theme-toggle__icon--dark  { display: grid; }

/* Narrow: the nav becomes a sheet under the header bar. The threshold is the
   width at which brand + links + search + the two icon controls stop fitting
   on one line, not a round number. */
@media (max-width: 1150px) {
  .nav-toggle { display: inline-grid; }
  .site-header__search { display: none; }
  .site-nav {
    display: none;
    position: absolute;
    inset-inline: 0;
    top: 100%;
    margin: 0;
    padding: var(--sp-3) var(--gutter) var(--sp-4);
    background: var(--bg);
    border-bottom: 1px solid var(--rule);
    box-shadow: var(--shadow);
  }
  :root[data-nav="open"] .site-nav { display: block; }
  .site-nav__list { flex-direction: column; align-items: stretch; gap: var(--sp-1); }
  .site-nav__link { padding: var(--sp-3); font-size: 1rem; }
  /* Without JavaScript the toggle cannot work, so show the nav unconditionally. */
  :root:not([data-js]) .nav-toggle { display: none; }
  :root:not([data-js]) .site-nav {
    display: block;
    position: static;
    padding: 0 0 var(--sp-3);
    box-shadow: none;
    border-bottom: 0;
  }
  :root:not([data-js]) .site-header__inner { flex-wrap: wrap; }
}

/* ==========================================================================
   07. SEARCH BOX AND TYPE-AHEAD PANEL
   views/partials/search-box.php + assets/js/site.js
   ========================================================================== */

.search { position: relative; }

.search__box {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding-inline-start: var(--sp-3);
  background: var(--bg-soft);
  border: 1px solid var(--edge);
  border-radius: var(--r-pill);
  transition: border-color var(--speed) ease, background var(--speed) ease;
}
.search__box:focus-within {
  background: var(--bg);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.search__icon { display: inline-flex; color: var(--ink-faint); flex: none; }

.search__input {
  flex: 1 1 auto;
  min-width: 0;
  padding-block: var(--sp-2);
  background: none;
  border: 0;
  font-size: var(--fs-sm);
}
.search__input:focus { outline: none; }
.search__input::-webkit-search-cancel-button { -webkit-appearance: none; }
.search__input::placeholder { color: var(--ink-faint); }

.search__submit {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  margin: 3px;
  border-radius: var(--r-pill);
  color: var(--ink-soft);
  cursor: pointer;
}
.search__submit:hover { background: var(--bg-hover); color: var(--ink); }
.search__submit-text { font-size: var(--fs-sm); font-weight: 600; }

/* Hero variant: the search box IS the call to action, so it is bigger. */
.search--hero .search__box {
  padding-inline-start: var(--sp-4);
  border-radius: var(--r-lg);
  background: var(--bg);
  box-shadow: var(--shadow);
}
.search--hero .search__input { padding-block: var(--sp-3); font-size: 1.0625rem; }
.search--hero .search__submit {
  background: var(--accent);
  color: var(--on-accent);
  border-radius: var(--r);
  padding-inline: var(--sp-4);
}
.search--hero .search__submit:hover { background: var(--accent-hover); color: var(--on-accent); }
.search--inline .search__box { background: var(--bg); }

/* Type-ahead dropdown ----------------------------------------------------- */

.search__panel {
  position: absolute;
  z-index: 60;
  inset-inline: 0;
  top: calc(100% + var(--sp-2));
  max-height: min(28rem, 70vh);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-1);
  background: var(--bg);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r);
  box-shadow: var(--shadow-lg);
}

.search__item {
  display: grid;
  gap: 2px;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  color: var(--ink);
  text-decoration: none;
}
.search__item:hover,
.search__item[aria-selected="true"] { background: var(--accent-soft); color: var(--ink); }
.search__item[aria-selected="true"] { box-shadow: inset 3px 0 0 var(--accent); }

.search__title  { font-weight: 600; font-size: var(--fs-sm); }
.search__meta   { font-size: var(--fs-xs); color: var(--ink-faint); text-transform: capitalize; }
.search__snippet {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  line-height: 1.45;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.search__meta:empty, .search__snippet:empty { display: none; }
.search__snippet mark {
  background: var(--mark-bg);
  color: var(--mark-ink);
  border-radius: 2px;
  padding-inline: 1px;
}

.search__all {
  display: block;
  padding: var(--sp-2) var(--sp-3);
  margin-top: var(--sp-1);
  border-top: 1px solid var(--rule);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
}
.search__all:hover,
.search__all[aria-selected="true"] { background: var(--accent-soft); }

.search__empty {
  padding: var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--ink-soft);
}

/* ==========================================================================
   08. SIDEBAR AND MOBILE DRAWER
   views/partials/sidebar.php — native <details>, so it collapses with no JS.
   ========================================================================== */

.sidebar {
  position: sticky;
  top: calc(var(--header-h) + var(--sp-4));
  max-height: calc(100vh - var(--header-h) - var(--sp-6));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-inline-end: var(--sp-2);
  font-size: var(--fs-sm);
}

.sidebar__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}
.sidebar__title {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.sidebar__toggle { display: none; }

/* Three levels, one column: areas, the sections inside the reader's area, and
   the lessons inside the reader's section. Only the branch the reader is on is
   printed in full — see the comment at the top of views/partials/sidebar.php.
   Indentation and weight carry the hierarchy; the hairline is the tree line. */
.sidebar__areas,
.sidebar__sections { list-style: none; padding: 0; }
.sidebar__area-item + .sidebar__area-item { border-top: 1px solid var(--rule); }

.sidebar__sections {
  margin-inline-start: var(--sp-3);
  padding-bottom: var(--sp-2);
  border-inline-start: 1px solid var(--rule);
}

.sidebar__summary {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-1);
  list-style: none;
  cursor: pointer;
  border-radius: var(--r-sm);
  font-weight: 600;
  color: var(--ink);
}
.sidebar__summary::-webkit-details-marker { display: none; }
.sidebar__summary:hover { background: var(--bg-hover); }
.sidebar__emoji { flex: none; }
.sidebar__label { flex: 1 1 auto; min-width: 0; }
.sidebar__count {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--ink-faint);
  background: var(--bg-sunken);
  border-radius: var(--r-pill);
  padding: 1px var(--sp-2);
}
.sidebar__marker { color: var(--ink-faint); transition: transform var(--speed) ease; }
.sidebar__area[open]    > .sidebar__summary .sidebar__marker,
.sidebar__section[open] > .sidebar__summary .sidebar__marker { transform: rotate(180deg); }
.sidebar__section.is-active > .sidebar__summary { color: var(--accent); }
.sidebar__area.is-active > .sidebar__summary--area { color: var(--accent); }
.sidebar__summary--area { font-weight: 700; }
.sidebar__section > .sidebar__summary { padding-inline-start: var(--sp-3); }

.sidebar__links {
  list-style: none;
  padding: 0 0 var(--sp-3);
  margin-inline-start: var(--sp-3);
  border-inline-start: 1px solid var(--rule);
}
.sidebar__link {
  display: block;
  padding: var(--sp-2) var(--sp-3);
  margin-inline-start: -1px;
  border-inline-start: 2px solid transparent;
  color: var(--ink-soft);
  text-decoration: none;
  line-height: 1.4;
}
a.sidebar__link:hover { background: var(--bg-hover); color: var(--ink); }
.sidebar__link.is-current,
.sidebar__link[aria-current="page"] {
  color: var(--accent);
  border-inline-start-color: var(--accent);
  background: var(--accent-soft);
  font-weight: 600;
}
.sidebar__link--overview { font-weight: 600; color: var(--ink); }
.sidebar__link--soon {
  color: var(--ink-faint);
  cursor: default;
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
}
.sidebar__soon {
  font-style: normal;
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-faint);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-pill);
  padding: 0 var(--sp-2);
  flex: none;
}

/* An area, collapsed to a link: the same row as an open area's <summary>, so
   the fourteen of them read as one list whichever state they are in. These
   come after .sidebar__link on purpose — they override it. */
.sidebar__link--area {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-1);
  margin-inline-start: 0;
  border-inline-start: 0;
  border-radius: var(--r-sm);
  font-weight: 700;
  color: var(--ink);
  line-height: 1.35;
}
a.sidebar__link--area:hover { background: var(--bg-hover); color: var(--accent); }

/* A section inside the reader's own area. */
.sidebar__link--section {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-weight: 500;
}

.sidebar__foot { margin-top: var(--sp-4); }
.sidebar__all {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  font-weight: 600;
  text-decoration: none;
}
.sidebar__all:hover { text-decoration: underline; }

.drawer-backdrop { display: none; }

/* Below the sidebar breakpoint the whole thing slides in over the page. */
@media (max-width: 900px) {
  .sidebar {
    position: static;
    max-height: none;
    overflow: visible;
    padding: var(--sp-3);
    background: var(--bg-soft);
    border: 1px solid var(--rule);
    border-radius: var(--r);
  }
  .sidebar__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--edge);
    border-radius: var(--r-pill);
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--ink);
    cursor: pointer;
  }
  .sidebar__chevron { transition: transform var(--speed) ease; }
  :root[data-drawer="open"] .sidebar__chevron { transform: rotate(180deg); }

  /* JS present: collapsed until the button is pressed. JS absent: always open,
     because nothing could ever open it. */
  :root[data-js] .sidebar__body { display: none; }
  :root[data-js][data-drawer="open"] .sidebar__body { display: block; }
}

@media (prefers-reduced-motion: reduce) {
  .sidebar__marker, .sidebar__chevron { transition: none; }
}

/* ==========================================================================
   09. TABLE OF CONTENTS
   views/partials/toc.php — 'rail' in the right column, 'mobile' as <details>.
   ========================================================================== */

.toc { font-size: var(--fs-sm); }

.toc--rail {
  position: sticky;
  top: calc(var(--header-h) + var(--sp-4));
  max-height: calc(100vh - var(--header-h) - var(--sp-6));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-inline-start: var(--sp-4);
  border-inline-start: 1px solid var(--rule);
}
.toc__title {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--sp-2);
}
.toc__list { list-style: none; padding: 0; }
.toc__item--h3 { padding-inline-start: var(--sp-3); }
.toc__link {
  display: block;
  padding: var(--sp-1) 0;
  color: var(--ink-soft);
  text-decoration: none;
  line-height: 1.4;
}
.toc__link:hover { color: var(--accent); }
.toc__link[aria-current="true"] { color: var(--accent); font-weight: 600; }

.toc__top {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  margin-top: var(--sp-4);
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  text-decoration: none;
}
.toc__top:hover { color: var(--accent); }
.toc__top-icon { transform: rotate(90deg); }

/* Mobile variant: a closed disclosure above the body text. */
.toc--mobile {
  margin-bottom: var(--sp-5);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-soft);
}
.toc--mobile .toc__list { padding: 0 var(--sp-4) var(--sp-3); }
.toc__summary {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  list-style: none;
  cursor: pointer;
  font-weight: 600;
}
.toc__summary::-webkit-details-marker { display: none; }
.toc__count {
  margin-inline-start: auto;
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  background: var(--bg-sunken);
  border-radius: var(--r-pill);
  padding: 1px var(--sp-2);
}

/* The rail already carries the same links on a wide screen. */
@media (min-width: 1201px) { .toc--mobile { display: none; } }

/* ==========================================================================
   10. PROSE TYPOGRAPHY
   Everything app/Markdown.php emits lives inside .prose.
   ========================================================================== */

.prose { max-width: var(--measure); }
.prose > * + * { margin-top: var(--sp-4); }

.prose h2, .prose h3, .prose h4 {
  line-height: var(--lh-tight);
  letter-spacing: -.015em;
  font-weight: 700;
  text-wrap: balance;
  scroll-margin-top: calc(var(--header-h) + 1rem);
}
.prose h2 { font-size: var(--fs-h2); margin-top: var(--sp-7); }
.prose h3 { font-size: var(--fs-h3); margin-top: var(--sp-6); }
.prose h4 { font-size: var(--fs-h4); margin-top: var(--sp-5); }
.prose h2 + *, .prose h3 + *, .prose h4 + * { margin-top: var(--sp-3); }

/* The "#" beside a heading: invisible until you go looking for it. */
.heading-anchor {
  margin-inline-start: .4em;
  color: var(--ink-faint);
  text-decoration: none;
  opacity: 0;
  transition: opacity var(--speed) ease;
}
:is(h1, h2, h3, h4, h5, h6):hover .heading-anchor,
.heading-anchor:focus-visible { opacity: 1; }

.prose ul, .prose ol { padding-inline-start: 1.4em; }
.prose li + li { margin-top: var(--sp-2); }
.prose li > ul, .prose li > ol { margin-top: var(--sp-2); }
.prose ul { list-style: disc; }
.prose ol { list-style: decimal; }

.task-list { list-style: none; padding-inline-start: 0; }
.task-item { display: flex; align-items: baseline; gap: var(--sp-2); }
.task-item input { margin: 0; flex: none; accent-color: var(--accent); }

.prose blockquote {
  padding: var(--sp-1) var(--sp-4);
  border-inline-start: 3px solid var(--accent-line);
  color: var(--ink-quote);
  font-style: italic;
}
.prose blockquote > * + * { margin-top: var(--sp-3); }

.prose img { border-radius: var(--r); margin-inline: auto; }

.prose kbd {
  font-family: var(--font-mono);
  font-size: .85em;
  background: var(--kbd-bg);
  color: var(--kbd-ink);
  border: 1px solid var(--rule-strong);
  border-bottom-width: 2px;
  border-radius: var(--r-sm);
  padding: 1px .4em;
}

.prose mark { background: var(--mark-bg); color: var(--mark-ink); padding-inline: 2px; border-radius: 2px; }

/* Inline code — distinct from a code block, and never a link colour. */
.prose :not(pre) > code {
  font-family: var(--font-mono);
  font-size: .875em;
  background: var(--bg-sunken);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  padding: .1em .35em;
  overflow-wrap: break-word;
}

/* Tables scroll inside their own box; the page never scrolls sideways. */
.table-wrap {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  border: 1px solid var(--rule);
  border-radius: var(--r);
}
.table-wrap table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}
.table-wrap th, .table-wrap td {
  padding: var(--sp-2) var(--sp-3);
  text-align: start;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}
.table-wrap thead th {
  background: var(--bg-soft);
  font-weight: 700;
  white-space: nowrap;
}
.table-wrap tbody tr:last-child td { border-bottom: 0; }

/* ==========================================================================
   11. CODE BLOCKS AND OUTPUT BLOCKS

   app/Markdown.php prints exactly this, and nothing else:

     div.code-block[data-lang]
       div.code-block__bar   > span.code-block__title
                             + button.code-block__copy[data-copy]
       pre.code-block__body  > code.language-*

     div.output-block
       div.output-block__bar  > span.output-block__label
       pre.output-block__body > samp

   The bar is optional on a code block (no language, no title = no bar).
   ========================================================================== */

.code-block,
.output-block {
  border-radius: var(--r);
  overflow: hidden;
  border: 1px solid var(--rule-strong);
}
.prose > .code-block,
.prose > .output-block { margin-top: var(--sp-5); }

.code-block { background: var(--bg-code); }

.code-block__bar,
.output-block__bar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--rule-strong);
  font-size: var(--fs-xs);
}

.code-block__bar { background: var(--bg-soft); }
.code-block__title {
  font-family: var(--font-mono);
  color: var(--ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.code-block__copy {
  margin-inline-start: auto;
  flex: none;
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--edge);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--ink-soft);
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--speed) ease;
}
.code-block:hover .code-block__copy,
.code-block:focus-within .code-block__copy,
.code-block__copy:focus-visible { opacity: 1; }
.code-block__copy:hover { background: var(--bg-hover); color: var(--ink); }
.code-block__copy[data-state="done"] {
  border-color: var(--green);
  color: var(--green);
  background: var(--green-soft);
  opacity: 1;
}
.code-block__copy[data-state="fail"] {
  border-color: var(--amber);
  color: var(--amber);
  background: var(--amber-soft);
  opacity: 1;
}
/* Touch devices have no hover, so it must always be reachable. */
@media (hover: none) {
  .code-block__copy { opacity: 1; }
}
/* Without JavaScript the button cannot copy anything. */
:root:not([data-js]) .code-block__copy { display: none; }

.code-block__body,
.output-block__body {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding: var(--sp-4);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: var(--lh-code);
  tab-size: 4;
}
.code-block__body code,
.output-block__body samp {
  font: inherit;
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
  white-space: pre;
}

/* The dark slab reads as "machine output" in both themes. That contrast with
   the light code block is the whole point, so it does not follow the theme. */
.output-block {
  background: var(--term-bg);
  border-color: var(--term-edge);
  color: var(--term-ink);
}
.output-block__bar {
  background: var(--term-bar);
  border-bottom-color: var(--term-edge);
}
.output-block__label {
  color: var(--term-bar-ink);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.output-block__label::before {
  content: "▸ ";
  color: var(--term-accent);
}

/* The verbatim message on an error page — a slab, not an interactive block. */
.code-block--error {
  display: block;
  padding: var(--sp-4);
  overflow-x: auto;
  background: var(--bg-code);
  border: 1px solid var(--red);
  border-inline-start-width: 3px;
  border-radius: var(--r);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: var(--lh-code);
}
.code-block--error code { white-space: pre-wrap; background: none; border: 0; padding: 0; }

/* ==========================================================================
   11b. PYTHON PLAYGROUND (assets/js/playground.js)

   Injected markup only — with JavaScript off none of these elements exist
   and this whole section is dead weight by design, never a visible change:

     .code-block[data-lang=python][data-editing?]
       .code-block__bar > .code-block__run[data-run] + .code-block__edit + copy
       .code-block__body            (hidden while data-editing)
       textarea.code-block__editor  (present while editing)
       .run-block[data-state=loading|running|done|error|stopped|fatal]
         .run-block__bar  > .run-block__label + .run-block__status
                          + .run-block__stop
         .run-block__body > span (stdout) / span.run-block__err (stderr)

   The output panel wears the terminal tokens, same visual language as the
   authored output-block: "this dark slab is what the machine said."
   ========================================================================== */

/* Run is the invitation, so unlike Copy it is always visible. */
.code-block__run {
  margin-inline-start: auto;
  flex: none;
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--green);
  border-radius: var(--r-sm);
  background: var(--green-soft);
  color: var(--green);
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--speed) ease, color var(--speed) ease;
}
.code-block__run::before { content: "▶ "; font-size: .85em; }
.code-block__run:hover:not(:disabled),
.code-block__run:focus-visible { background: var(--green); color: var(--green-on); }
.code-block__run:disabled { opacity: .45; cursor: default; }

/* Edit follows Copy's hover-reveal manners; touch always shows it. */
.code-block__edit {
  flex: none;
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--edge);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--ink-soft);
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--speed) ease;
}
.code-block:hover .code-block__edit,
.code-block:focus-within .code-block__edit,
.code-block[data-editing] .code-block__edit,
.code-block__edit:focus-visible { opacity: 1; }
.code-block__edit:hover { background: var(--bg-hover); color: var(--ink); }
@media (hover: none) {
  .code-block__edit { opacity: 1; }
}

/* Run pushes itself right; Copy's own auto margin must then be cancelled,
   or the two auto margins split the space and Run floats mid-bar. Together:
   title …………………… [Run] [Edit] [Copy]. */
.code-block__run ~ .code-block__copy { margin-inline-start: 0; }

/* The editor replaces the pre — same metrics, so nothing jumps. */
.code-block[data-editing] .code-block__body { display: none; }
.code-block__editor {
  display: block;
  inline-size: 100%;
  padding: var(--sp-4);
  border: 0;
  border-block-start: 1px dashed var(--edge);
  border-radius: 0;
  background: var(--bg-code);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: var(--lh-code);
  tab-size: 4;
  white-space: pre;
  overflow-x: auto;
  resize: vertical;
}
.code-block__editor:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ---------------------------------------------------------- output panel */
.run-block {
  border-block-start: 1px solid var(--term-edge);
  background: var(--term-bg);
  color: var(--term-ink);
}
.run-block__bar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-block-end: 1px solid var(--term-edge);
  background: var(--term-bar);
  font-size: var(--fs-xs);
}
.run-block__label {
  color: var(--term-bar-ink);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.run-block__label::before { content: "▸ "; color: var(--term-accent); }

/* Status text carries the state; colour only ever restates it.
   On --term-bar #1b2230: --term-dim 6.36:1, --term-accent 8.92:1,
   #ffa198 (error red) 8.10:1 — all comfortably past 4.5:1. */
.run-block__status { margin-inline-start: auto; color: var(--term-dim); text-align: end; }
.run-block[data-state="done"]    .run-block__status { color: var(--term-accent); }
.run-block[data-state="error"]   .run-block__status,
.run-block[data-state="fatal"]   .run-block__status { color: #ffa198; }

/* A quiet spinner while downloading or running. The global
   prefers-reduced-motion rule in §20 freezes it automatically. */
.run-block[data-state="loading"] .run-block__status::before,
.run-block[data-state="running"] .run-block__status::before {
  content: "";
  display: inline-block;
  inline-size: .7em;
  block-size: .7em;
  margin-inline-end: var(--sp-2);
  border: 2px solid var(--term-edge);
  border-block-start-color: var(--term-accent);
  border-radius: 50%;
  vertical-align: -.05em;
  animation: run-spin .7s linear infinite;
}
@keyframes run-spin { to { transform: rotate(360deg); } }

.run-block__stop {
  flex: none;
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid #ffa198;
  border-radius: var(--r-sm);
  background: transparent;
  color: #ffa198;
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
}
.run-block__stop:hover,
.run-block__stop:focus-visible { background: #ffa198; color: var(--term-bg); }

.run-block__body {
  margin: 0;
  padding: var(--sp-4);
  max-block-size: 24rem;
  overflow: auto;
  overscroll-behavior: contain;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: var(--lh-code);
  white-space: pre;
}
.run-block__body:empty { display: none; }
.run-block__err { color: #ffa198; }

/* matplotlib figures (assets/js/playground.js appends these after the body).
   The panel is the dark terminal slab in both themes, and matplotlib draws on
   white, so each figure sits on its own white card — the same contrast in
   light and dark, no theme-dependent surprises in the plot colours. */
.run-block__figs {
  padding: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.run-block__figs:empty { display: none; }
.run-block__fig { margin: 0; }
.run-block__fig img {
  display: block;
  max-width: 100%;
  height: auto;
  background: #ffffff;
  border-radius: var(--r-sm);
}

@media (forced-colors: active) {
  .code-block__run, .code-block__edit, .run-block__stop { border: 1px solid ButtonBorder; }
  .run-block { border-block-start: 1px solid ButtonBorder; }
  .run-block__fig img { border: 1px solid ButtonBorder; }
}
@media print {
  .code-block__run, .code-block__edit, .run-block__bar .run-block__stop { display: none; }
  .run-block { background: #eee !important; color: #000 !important; }
}


/* ==========================================================================
   12. CALLOUTS
   app/Markdown.php: aside.callout.callout--NAME > p.callout__label
                                                 + div.callout__body
   ========================================================================== */

.callout {
  --hue: var(--accent);
  --hue-soft: var(--accent-soft);
  padding: var(--sp-4);
  border: 1px solid color-mix(in srgb, var(--hue) 34%, transparent);
  border-inline-start: 3px solid var(--hue);
  border-radius: var(--r);
  background: var(--hue-soft);
}
.prose > .callout { margin-top: var(--sp-5); }

.callout__label {
  font-weight: 700;
  font-size: var(--fs-sm);
  letter-spacing: .01em;
  color: var(--hue);
  margin-bottom: var(--sp-2);
}
.callout__body > * + * { margin-top: var(--sp-3); }
.callout__body :is(h2, h3, h4) { font-size: var(--fs-h4); margin-top: var(--sp-4); }

.callout--note,
.callout--info      { --hue: var(--accent); --hue-soft: var(--accent-soft); }
.callout--tip,
.callout--example,
.callout--remember  { --hue: var(--green);  --hue-soft: var(--green-soft); }
.callout--warning,
.callout--caution,
.callout--important { --hue: var(--amber);  --hue-soft: var(--amber-soft); }
.callout--danger    { --hue: var(--red);    --hue-soft: var(--red-soft); }

/* A ::: block with a name we do not recognise still needs to look deliberate. */
.block {
  padding: var(--sp-4);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--bg-soft);
}
.block__label { font-weight: 700; font-size: var(--fs-sm); margin-bottom: var(--sp-2); }
.block > * + * { margin-top: var(--sp-3); }

/* A nested ::: beginner inside a level block, rendered in place. */
.level { display: block; }
.level + .level { margin-top: var(--sp-6); }

/* ==========================================================================
   13. LEVEL TABS AND LEVEL PANELS  (BUILD_SPEC §6 — the signature feature)

   views/partials/level-tabs.php prints all three panels, always. Which one is
   visible is decided HERE and nowhere else:

     no data-js       tabs hidden, panels stacked, each under its own label.
                      This is what a crawler with JS off gets, and it is the
                      whole reason every level is indexable.
     data-js +
     data-level=x     tabs shown, only the matching panel displayed.

   assets/js/site.js moves <html data-level> and nothing more. It never sets
   the `hidden` attribute on a panel — [hidden] is display:none !important, so
   a panel marked that way could not be shown again.
   ========================================================================== */

.levels { margin-top: var(--sp-6); }

.levels__intro {
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  margin-bottom: var(--sp-3);
  max-width: var(--measure);
}

.level-tabs {
  display: none;              /* shown only once data-js is present */
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--rule);
  margin-bottom: var(--sp-5);
}
:root[data-js] .level-tabs { display: flex; }

.level-tab {
  --lv: var(--green);
  --lv-on: var(--green-on);
  --lv-soft: var(--green-soft);
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 var(--sp-2);
  align-items: center;
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-soft);
  text-align: start;
  cursor: pointer;
  transition: background var(--speed) ease, color var(--speed) ease, border-color var(--speed) ease;
}
.level-tab:hover { background: var(--bg-hover); color: var(--ink); }

.level-tab--beginner   { --lv: var(--green); --lv-on: var(--green-on); --lv-soft: var(--green-soft); }
.level-tab--developer  { --lv: var(--amber); --lv-on: var(--amber-on); --lv-soft: var(--amber-soft); }
.level-tab--researcher { --lv: var(--red);   --lv-on: var(--red-on);   --lv-soft: var(--red-soft); }

.level-tab__dot   { grid-row: 1 / span 2; font-size: 1.1em; line-height: 1; }
.level-tab__label { font-weight: 600; font-size: var(--fs-sm); }
.level-tab__hint  { font-size: var(--fs-xs); color: var(--ink-faint); }

/* Colour is never the only signal: the selected tab is also filled and bolder,
   and it is the only one with aria-selected="true" for a screen reader. */
.level-tab[aria-selected="true"] {
  background: var(--lv);
  border-color: var(--lv);
  color: var(--lv-on);
  font-weight: 700;
}
.level-tab[aria-selected="true"] .level-tab__hint { color: inherit; opacity: .85; }

.level-panel { scroll-margin-top: calc(var(--header-h) + 1rem); }
.level-panel:focus { outline: none; }
.level-panel + .level-panel { margin-top: var(--sp-7); }

/* The stacked, no-JavaScript presentation. */
.level-panel {
  border-inline-start: 3px solid var(--lv, var(--rule-strong));
  padding-inline-start: var(--sp-4);
}
.level-panel--beginner   { --lv: var(--green); }
.level-panel--developer  { --lv: var(--amber); }
.level-panel--researcher { --lv: var(--red); }

.level-panel__label {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-h3);
  font-weight: 700;
  color: var(--lv);
  margin-bottom: var(--sp-4);
}

/* With JavaScript the stack becomes one panel at a time. */
:root[data-js] .level-panel {
  display: none;
  border-inline-start: 0;
  padding-inline-start: 0;
  margin-top: 0;
}
:root[data-js] .level-panel__label { display: none; }
:root[data-js][data-level="beginner"]   .level-panel--beginner,
:root[data-js][data-level="developer"]  .level-panel--developer,
:root[data-js][data-level="researcher"] .level-panel--researcher {
  display: block;
}

/* A lesson with no developer or researcher block must not go blank when the
   stored preference points at a level it does not have. site.js corrects this
   on load; :has() gets it right before the script even runs. */
:root[data-js][data-level="developer"]
  .levels:not(:has(.level-panel--developer)) .level-panel--beginner,
:root[data-js][data-level="researcher"]
  .levels:not(:has(.level-panel--researcher)) .level-panel--beginner {
  display: block;
}

/* A single-level document has nothing to switch between. */
.levels--single .level-panel {
  border-inline-start: 0;
  padding-inline-start: 0;
}
:root[data-js] .levels--single .level-panel { display: block; }

@media (max-width: 560px) {
  .level-tab { padding-inline: var(--sp-3); }
  .level-tab__hint { display: none; }
}

/* ==========================================================================
   14. CARDS, GRIDS, CHIPS
   views/partials/card.php, section-card.php
   ========================================================================== */

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
  gap: var(--sp-4);
  list-style: none;
  padding: 0;
}
.card-grid--tight    { grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr)); }
.card-grid--sections { grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr)); }
.card-grid--paths,
.card-grid--projects { grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr)); }

.card {
  position: relative;
  display: flex;
  gap: var(--sp-3);
  height: 100%;
  padding: var(--sp-4);
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  transition: border-color var(--speed) ease, box-shadow var(--speed) ease, transform var(--speed) ease;
}
.card:hover {
  border-color: var(--accent-line);
  box-shadow: var(--shadow);
}
.card:has(.card__link:focus-visible) {
  outline: var(--ring);
  outline-offset: var(--ring-off);
}

.card__icon { flex: none; font-size: 1.35rem; line-height: 1.2; color: var(--accent); }
.card__body { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; flex: 1 1 auto; }

.card__eyebrow {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.card__title {
  font-size: var(--fs-h4);
  line-height: var(--lh-tight);
  font-weight: 700;
  letter-spacing: -.01em;
}
/* One tab stop per card: the title link is stretched over the whole box. */
.card__link { color: var(--ink); text-decoration: none; }
.card__link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.card__link:focus-visible { outline: none; }
.card:hover .card__link { color: var(--accent); }

.card__summary,
.card__snippet { font-size: var(--fs-sm); color: var(--ink-soft); line-height: 1.55; }
.card__snippet mark {
  background: var(--mark-bg);
  color: var(--mark-ink);
  border-radius: 2px;
  padding-inline: 1px;
}

.card__tags { display: flex; flex-wrap: wrap; gap: var(--sp-1); list-style: none; padding: 0; }
.card__tag {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  background: var(--bg-sunken);
  border-radius: var(--r-pill);
  padding: 1px var(--sp-2);
}

.card__meta {
  margin-top: auto;
  padding-top: var(--sp-1);
  font-size: var(--fs-xs);
  color: var(--ink-faint);
}
.card__dot { margin-inline: var(--sp-1); color: var(--rule-strong); }

.card__badge {
  position: absolute;
  top: var(--sp-3);
  inset-inline-end: var(--sp-3);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: var(--r-pill);
  padding: 1px var(--sp-2);
}
.card--soon { background: var(--bg-soft); border-style: dashed; }
.card--soon:hover { box-shadow: none; border-color: var(--rule-strong); }
.card--soon .card__title { color: var(--ink-faint); }
.card__badge--soon { color: var(--ink-faint); background: var(--bg-sunken); }

/* Section card ------------------------------------------------------------ */

.section-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  height: 100%;
  padding: var(--sp-5);
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  transition: border-color var(--speed) ease, box-shadow var(--speed) ease;
}
.section-card:hover { border-color: var(--accent-line); box-shadow: var(--shadow); }

.section-card__head { display: flex; align-items: flex-start; gap: var(--sp-3); }
.section-card__emoji { font-size: 1.6rem; line-height: 1.1; flex: none; }
.section-card__headings { min-width: 0; }
.section-card__num {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--ink-faint);
}
.section-card__title { font-size: var(--fs-h3); line-height: var(--lh-tight); font-weight: 700; }
.section-card__link { color: var(--ink); text-decoration: none; }
.section-card:hover .section-card__link { color: var(--accent); }
.section-card__summary { font-size: var(--fs-sm); color: var(--ink-soft); }

.section-card__topics { list-style: none; padding: 0; font-size: var(--fs-sm); }
.section-card__topic + .section-card__topic { margin-top: var(--sp-1); }
.section-card__topic a { text-decoration: none; }
.section-card__topic a:hover { text-decoration: underline; }
.section-card__topic--soon { color: var(--ink-faint); }

.section-card__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-top: auto;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--rule);
  font-size: var(--fs-xs);
  color: var(--ink-faint);
}
.section-card__of { color: var(--ink-faint); }
.section-card__more {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  font-weight: 600;
  font-size: var(--fs-sm);
  text-decoration: none;
}
.section-card__more:hover { text-decoration: underline; }

/* Area card --------------------------------------------------------------
   views/partials/area-card.php — one of the dozen groups the curriculum is
   browsed by. It is a section card with the section list moved inside it: the
   heading goes to the area's block on /learn/, each name under it goes
   straight to that section. An area has no page of its own and never will.
   ------------------------------------------------------------------------- */

.area-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr));
  gap: var(--sp-4);
  list-style: none;
  padding: 0;
}
.area-grid__item { min-width: 0; }

.area-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  height: 100%;
  min-width: 0;
  padding: var(--sp-5);
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  transition: border-color var(--speed) ease, box-shadow var(--speed) ease;
}
.area-card:hover { border-color: var(--accent-line); box-shadow: var(--shadow); }

.area-card__head { display: flex; align-items: flex-start; gap: var(--sp-3); }
.area-card__emoji { font-size: 1.6rem; line-height: 1.1; flex: none; }
.area-card__title {
  font-size: var(--fs-h3);
  line-height: var(--lh-tight);
  font-weight: 700;
  min-width: 0;
}
.area-card__link { color: var(--ink); text-decoration: none; }
.area-card:hover .area-card__link { color: var(--accent); }
.area-card__summary { font-size: var(--fs-sm); color: var(--ink-soft); }

.area-card__sections {
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: var(--fs-sm);
}
.area-card__section { line-height: 1.5; }
.area-card__section + .area-card__section { margin-top: var(--sp-1); }
.area-card__section a { color: var(--ink-soft); text-decoration: none; }
.area-card__section a:hover { color: var(--accent); text-decoration: underline; }
.area-card__section--rest { color: var(--ink-faint); }

.area-card__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-1);
  margin-top: auto;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--rule);
  font-size: var(--fs-xs);
  color: var(--ink-faint);
}
.area-card__dot { color: var(--rule-strong); }

/* Chips ------------------------------------------------------------------- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--ink-soft);
  font-size: var(--fs-sm);
  text-decoration: none;
  white-space: nowrap;
}
a.chip:hover { border-color: var(--accent-line); background: var(--accent-soft); color: var(--accent); }
.chip--tag { font-family: var(--font-mono); font-size: var(--fs-xs); }
.chip--prereq { font-weight: 600; }
.chip--flat { background: var(--bg-sunken); border-color: transparent; color: var(--ink-faint); }
.chip__count { font-size: var(--fs-xs); color: var(--ink-faint); }

/* The filter a reader has chosen. Not colour alone: the chip is filled and the
   text is bold, so it still reads as "on" in forced colours and in greyscale. */
.chip.is-current,
a.chip[aria-current] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}
.chip.is-current .chip__count,
a.chip[aria-current] .chip__count { color: var(--on-accent); }
a.chip.is-current:hover,
a.chip[aria-current]:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--on-accent); }

/* ==========================================================================
   15. BREADCRUMBS, PAGER, NEXT-UP, PREREQ, TOPIC LISTS
   ========================================================================== */

.breadcrumbs { margin-bottom: var(--sp-4); font-size: var(--fs-sm); }
.breadcrumbs__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1);
  list-style: none;
  padding: 0;
}
.breadcrumbs__item { display: inline-flex; align-items: center; gap: var(--sp-1); }
.breadcrumbs__link { color: var(--ink-faint); text-decoration: none; }
.breadcrumbs__link:hover { color: var(--accent); text-decoration: underline; }
.breadcrumbs__step { color: var(--ink-faint); }
.breadcrumbs__current {
  color: var(--ink-soft);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: min(100%, 26rem);
}
.breadcrumbs__sep { display: inline-flex; color: var(--rule-strong); }

/* Pager ------------------------------------------------------------------- */

.pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-3);
  margin-top: var(--sp-7);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--rule);
}
.pager__link {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  color: var(--ink);
  text-decoration: none;
}
.pager__link:hover { border-color: var(--accent-line); background: var(--bg-soft); color: var(--ink); }
.pager__link--next { text-align: end; justify-content: flex-end; }
.pager__link--empty { border: 0; pointer-events: none; }
.pager__arrow { color: var(--ink-faint); flex: none; }
.pager__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pager__label {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.pager__title { font-weight: 600; line-height: var(--lh-tight); }
.pager__section { font-size: var(--fs-xs); color: var(--ink-faint); }

@media (max-width: 640px) {
  .pager { grid-template-columns: minmax(0, 1fr); }
  .pager__link--empty { display: none; }
  .pager__link--next { text-align: start; justify-content: flex-start; flex-direction: row-reverse; }
}

/* Next-up ----------------------------------------------------------------- */

.next-up { margin-top: var(--sp-7); }
.next-up__title { font-size: var(--fs-h2); line-height: var(--lh-tight); font-weight: 700; }
.next-up__note { margin-top: var(--sp-2); color: var(--ink-soft); font-size: var(--fs-sm); }
.next-up__list { margin-top: var(--sp-4); }

/* Prereq ------------------------------------------------------------------ */

.prereq {
  margin-top: var(--sp-5);
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-soft);
  border: 1px solid var(--rule);
  border-radius: var(--r);
}
.prereq__label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--sp-2);
}
.prereq__list { display: flex; flex-wrap: wrap; gap: var(--sp-2); list-style: none; padding: 0; }

/* Ordered topic list — views/partials/topic-list.php ----------------------- */

.topics { list-style: none; padding: 0; counter-reset: topic; }
.topics__item { border-bottom: 1px solid var(--rule); }
.topics__item:first-child { border-top: 1px solid var(--rule); }

.topics__link {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-2);
  color: var(--ink);
  text-decoration: none;
}
a.topics__link:hover { background: var(--bg-hover); }
a.topics__link:hover .topics__title { color: var(--accent); }
.topics__link--soon { color: var(--ink-faint); cursor: default; }

.topics__num {
  flex: none;
  width: 2ch;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.topics__title { flex: 1 1 auto; min-width: 0; font-weight: 500; }
.topics__arrow { flex: none; color: var(--ink-faint); }
.topics__soon {
  flex: none;
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: .05em;
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-pill);
  padding: 0 var(--sp-2);
}
.topics__item.is-current .topics__link {
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent);
}
.topics__item.is-current .topics__title { color: var(--accent); font-weight: 700; }
.topics__more { margin-top: var(--sp-3); font-size: var(--fs-sm); }
.topics__more a { display: inline-flex; align-items: center; gap: var(--sp-1); text-decoration: none; font-weight: 600; }
.topics__more a:hover { text-decoration: underline; }

/* ==========================================================================
   16. BUTTONS
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-5);
  border-radius: var(--r);
  border: 1px solid transparent;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--speed) ease, border-color var(--speed) ease, color var(--speed) ease;
}
.btn--primary { background: var(--accent); color: var(--on-accent); }
.btn--primary:hover { background: var(--accent-hover); color: var(--on-accent); }
.btn--ghost { border-color: var(--edge); color: var(--ink); background: var(--bg); }
.btn--ghost:hover { background: var(--bg-hover); color: var(--ink); }

/* ==========================================================================
   17. HOME PAGE
   views/pages/home.php
   ========================================================================== */

.home > section + section { margin-top: var(--sp-8); }

.hero {
  padding-block: var(--sp-6) var(--sp-7);
  border-bottom: 1px solid var(--rule);
}
.hero__inner { max-width: 46rem; }
.hero__eyebrow {
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--accent);
}
.hero__title {
  margin-top: var(--sp-3);
  font-size: var(--fs-h1);
  line-height: var(--lh-tight);
  letter-spacing: -.025em;
  font-weight: 800;
  text-wrap: balance;
}
.hero__lead {
  margin-top: var(--sp-4);
  color: var(--ink-soft);
  font-size: 1.0625em;
  max-width: var(--measure);
}
.hero__search { margin-top: var(--sp-5); max-width: 36rem; }
.hero__chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-4);
}
.hero__chips-label { font-size: var(--fs-sm); color: var(--ink-faint); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-5); }
.hero__promise { margin-top: var(--sp-4); font-size: var(--fs-sm); color: var(--ink-faint); }

.hero__byline { margin-top: var(--sp-2); font-size: var(--fs-sm); color: var(--ink-faint); }
.hero__byline a {
  color: var(--ink-soft);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid var(--accent-line);
}
.hero__byline a:hover,
.hero__byline a:focus-visible { color: var(--accent-hover); border-bottom-color: currentColor; }

.levels-intro__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: var(--sp-4);
  list-style: none;
  padding: 0;
  margin-top: var(--sp-5);
}
.levels-intro__item {
  --lv: var(--green);
  padding: var(--sp-4);
  background: var(--bg);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--lv);
  border-radius: var(--r);
}
.levels-intro__item--beginner   { --lv: var(--green); }
.levels-intro__item--developer  { --lv: var(--amber); }
.levels-intro__item--researcher { --lv: var(--red); }
.levels-intro__dot { font-size: 1.35rem; line-height: 1; }
.levels-intro__name { margin-top: var(--sp-2); font-size: var(--fs-h4); font-weight: 700; color: var(--lv); }
.levels-intro__hint { font-size: var(--fs-sm); color: var(--ink-faint); }
.levels-intro__body { margin-top: var(--sp-3); font-size: var(--fs-sm); color: var(--ink-soft); }

.home-topics .area-grid,
.home-topics .card-grid,
.home-paths .card-grid { margin-top: var(--sp-5); }
.home-topics .section-lead { margin-top: var(--sp-2); }

.recent-strip {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
  gap: var(--sp-4);
  list-style: none;
  padding: 0;
  margin-top: var(--sp-5);
}

.start-zero {
  padding: var(--sp-6);
  background: var(--bg-soft);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
}
.start-zero .section-title { margin-top: 0; }
.start-zero__steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: var(--sp-4);
  list-style: none;
  padding: 0;
}
.start-zero__step {
  padding: var(--sp-4);
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: var(--r);
}
.start-zero__num {
  display: inline-grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 700;
  font-size: var(--fs-sm);
}
.start-zero__name { margin-top: var(--sp-2); font-size: var(--fs-h4); font-weight: 700; }
.start-zero__step p { margin-block: var(--sp-2); font-size: var(--fs-sm); color: var(--ink-soft); }
.start-zero__cta { margin-top: var(--sp-5); }
.start-zero__note {
  margin-top: var(--sp-4);
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  max-width: var(--measure);
}

/* ==========================================================================
   18. LISTING AND DOCUMENT PAGES
   ========================================================================== */

/* /learn/ ----------------------------------------------------------------- */

.jump { margin-bottom: var(--sp-6); }
.jump__title {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--sp-2);
}
.jump__list { display: flex; flex-wrap: wrap; gap: var(--sp-2); list-style: none; padding: 0; }

/* The area filter. Fourteen ordinary links, one per area, plus "All areas".
   They wrap rather than scroll: a row that scrolls sideways hides half its
   options on the phone where most of these readers are. */
.areabar {
  margin-bottom: var(--sp-6);
  padding: var(--sp-4);
  background: var(--bg-soft);
  border: 1px solid var(--rule);
  border-radius: var(--r);
}
.areabar__title {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--sp-3);
}
.areabar__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  list-style: none;
  padding: 0;
  min-width: 0;
}
.areabar__list > li { min-width: 0; }
.areabar .chip { max-width: 100%; white-space: normal; }

/* On a phone the same fifteen chips wrap to two thirds of a screen and push
   the curriculum below the fold, so there they become one row that scrolls
   inside itself. The container scrolls; the page body never does. */
@media (max-width: 700px) {
  .areabar { padding: var(--sp-3); }
  .areabar__list {
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    padding-bottom: var(--sp-2);
    scroll-snap-type: x proximity;
  }
  /* flex:none or the chips shrink to fit instead of scrolling. */
  .areabar__list > li { flex: none; scroll-snap-align: start; }
  .areabar .chip { white-space: nowrap; max-width: none; }
}

.learn-index__filtered {
  margin-bottom: var(--sp-6);
  font-size: var(--fs-sm);
  color: var(--ink-soft);
}
.learn-index__filtered strong { color: var(--ink); }

/* One area: a heading, its sections, and the way into all of its lessons. */
.area-block {
  margin-bottom: var(--sp-7);
  padding-top: var(--sp-5);
  border-top: 2px solid var(--rule-strong);
  scroll-margin-top: calc(var(--header-h) + 1rem);
}
.area-block:first-of-type { padding-top: 0; border-top: 0; }
.area-block__head { margin-bottom: var(--sp-4); }
.area-block__title {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-h2);
  line-height: var(--lh-tight);
  font-weight: 800;
  letter-spacing: -.015em;
}
.area-block__emoji { flex: none; }
.area-block__summary { margin-top: var(--sp-2); color: var(--ink-soft); max-width: var(--measure); }
.area-block__facts {
  margin-top: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--ink-faint);
}
.area-block__dot { color: var(--rule-strong); margin-inline: var(--sp-1); }
/* Inside a filtered area: jump to one of its sections without scrolling past
   the others. Anchors only — no new address, nothing to load. */
.sectionbar { margin-top: var(--sp-4); }

.area-block__more { margin-top: var(--sp-4); }
.area-block__more a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  font-weight: 600;
  font-size: var(--fs-sm);
  text-decoration: none;
}
.area-block__more a:hover { text-decoration: underline; }

/* A section, as one scannable row rather than a heading with a lesson list
   under it. 115 rows is a page a reader can run an eye down; 115 lesson lists
   was the wall this replaced. Two columns once there is room for them, so the
   whole curriculum is a handful of screens instead of thirty.
   The count sits at the end of the row and never wraps under the title. */
.section-rows {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
  column-gap: var(--sp-5);
  list-style: none;
  padding: 0;
  border-top: 1px solid var(--rule);
}
.section-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  min-width: 0;
  padding: var(--sp-2) var(--sp-2);
  border-bottom: 1px solid var(--rule);
  scroll-margin-top: calc(var(--header-h) + 1rem);
}
.section-row:hover { background: var(--bg-hover); }
.section-row__title {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  font-size: var(--fs-body);
  line-height: 1.45;
  font-weight: 600;
  min-width: 0;
}
.section-row__emoji { flex: none; }
.section-row__title a { color: var(--ink); text-decoration: none; }
.section-row__title a:hover { color: var(--accent); text-decoration: underline; }
.section-row__count {
  flex: none;
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  white-space: nowrap;
}

.learn-block { margin-bottom: var(--sp-6); scroll-margin-top: calc(var(--header-h) + 1rem); }
.area-block .learn-block__title { font-size: var(--fs-h3); }
.learn-block__head { margin-bottom: var(--sp-4); }
.learn-block__num {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--ink-faint);
}
.learn-block__title {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-h2);
  line-height: var(--lh-tight);
  font-weight: 700;
}
.learn-block__title a { color: var(--ink); text-decoration: none; }
.learn-block__title a:hover { color: var(--accent); }
.learn-block__summary { margin-top: var(--sp-2); color: var(--ink-soft); max-width: var(--measure); }
.learn-block__count { margin-top: var(--sp-2); font-size: var(--fs-sm); color: var(--ink-faint); }
.learn-index__more { padding-top: var(--sp-5); border-top: 1px solid var(--rule); }
.learn-index__more .card-grid { margin-top: var(--sp-4); }

/* /learn/{section}/ ------------------------------------------------------- */

.section-page__list .section-title { margin-top: 0; }
.section-page__paths .card-grid { margin-top: var(--sp-4); }

/* Document pages: lesson, term, error, project, explainer, path ------------ */

.lesson__head, .term__head, .error-doc__head,
.project__head, .explainer__head { margin-bottom: var(--sp-5); }

.lesson__eyebrow, .term__eyebrow, .error-doc__eyebrow,
.project__eyebrow, .explainer__eyebrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  margin-bottom: var(--sp-3);
  color: var(--ink-faint);
}
.lesson__section, .term__eyebrow a, .error-doc__eyebrow a, .project__eyebrow a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--ink-faint);
  text-decoration: none;
}
.lesson__section:hover, .term__eyebrow a:hover,
.error-doc__eyebrow a:hover, .project__eyebrow a:hover { color: var(--accent); }

.lesson__title, .term__title, .error-doc__title,
.project__title, .explainer__title {
  font-size: var(--fs-h1);
  line-height: var(--lh-tight);
  letter-spacing: -.022em;
  font-weight: 700;
  text-wrap: balance;
}

.lesson__summary, .term__definition, .error-doc__summary,
.project__summary, .explainer__summary {
  margin-top: var(--sp-3);
  max-width: var(--measure);
  color: var(--ink-soft);
  font-size: 1.0625em;
}

.lesson__facts, .term__facts, .error-doc__facts,
.project__facts, .explainer__facts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-4);
  margin-top: var(--sp-4);
  list-style: none;
  padding: 0;
  font-size: var(--fs-sm);
  color: var(--ink-faint);
}
.lesson__fact, .project__fact, .explainer__fact,
.term__facts, .error-doc__facts { display: inline-flex; align-items: center; gap: var(--sp-2); }

.lesson__foot, .term__foot, .error-doc__foot,
.project__foot, .explainer__foot {
  margin-top: var(--sp-8);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--rule);
}

.lesson__tags, .term__tags, .error-doc__tags,
.project__tags, .explainer__tags { margin-bottom: var(--sp-5); }

.lesson__tags-title, .term__tags-title, .error-doc__tags-title,
.project__tags-title, .explainer__tags-title {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--sp-2);
}
.lesson__tag-list, .term__tag-list, .error-doc__tag-list,
.project__tag-list, .explainer__tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  list-style: none;
  padding: 0;
}

.term__back, .error-doc__back, .project__back, .path-page__back {
  margin-top: var(--sp-5);
  font-size: var(--fs-sm);
}
.term__back a, .error-doc__back a, .project__back a, .path-page__back a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  text-decoration: none;
  font-weight: 600;
}
.term__back a:hover, .error-doc__back a:hover,
.project__back a:hover, .path-page__back a:hover { text-decoration: underline; }

/* Glossary ---------------------------------------------------------------- */

.alphabet { margin-bottom: var(--sp-6); }
.alphabet__list { display: flex; flex-wrap: wrap; gap: var(--sp-1); list-style: none; padding: 0; }
.alphabet__link {
  display: grid;
  place-items: center;
  min-width: 2rem;
  padding: var(--sp-1);
  border-radius: var(--r-sm);
  font-weight: 700;
  font-size: var(--fs-sm);
  text-decoration: none;
}
a.alphabet__link:hover { background: var(--accent-soft); }
.alphabet__link--off { color: var(--rule-strong); }

.glossary-group { margin-bottom: var(--sp-7); scroll-margin-top: calc(var(--header-h) + 1rem); }
.glossary-group__letter {
  font-size: var(--fs-h2);
  font-weight: 800;
  color: var(--accent);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--rule);
  margin-bottom: var(--sp-3);
}
.glossary-list { list-style: none; padding: 0; }
.glossary-list__item { padding-block: var(--sp-3); border-bottom: 1px solid var(--rule); }
.glossary-list__link { font-weight: 700; text-decoration: none; }
.glossary-list__link:hover { text-decoration: underline; }
.glossary-list__summary { margin-top: var(--sp-1); font-size: var(--fs-sm); color: var(--ink-soft); }

.term__definition {
  display: block;
  padding: var(--sp-4);
  background: var(--accent-soft);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--r);
  color: var(--ink);
}
.term__definition-label {
  display: block;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--sp-1);
}
.term__definition-text { display: block; }

/* Error database ---------------------------------------------------------- */

.errors-group { margin-bottom: var(--sp-7); scroll-margin-top: calc(var(--header-h) + 1rem); }
.errors-list { list-style: none; padding: 0; }
.errors-list__item { padding-block: var(--sp-3); border-bottom: 1px solid var(--rule); }
.errors-list__link { text-decoration: none; }
.errors-list__code {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 600;
  background: var(--bg-sunken);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  padding: .15em .45em;
  overflow-wrap: anywhere;
}
.errors-list__link:hover .errors-list__code { border-color: var(--accent-line); color: var(--accent); }
.errors-list__summary { margin-top: var(--sp-2); font-size: var(--fs-sm); color: var(--ink-soft); }

.error-doc__library {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  background: var(--bg-sunken);
  border-radius: var(--r-pill);
  padding: 1px var(--sp-2);
}
.error-doc__title { font-family: var(--font-mono); font-size: var(--fs-h2); overflow-wrap: anywhere; }
.error-doc__message { margin-top: var(--sp-4); }
.error-doc__message-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--sp-2);
}

/* Learning paths ---------------------------------------------------------- */

.path-list { list-style: none; padding: 0; display: grid; gap: var(--sp-4); }
.path-card {
  padding: var(--sp-5);
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
}
.path-card:hover { border-color: var(--accent-line); box-shadow: var(--shadow); }
.path-card__head { display: flex; align-items: center; gap: var(--sp-3); }
.path-card__emoji { font-size: 1.6rem; line-height: 1; }
.path-card__title { font-size: var(--fs-h3); font-weight: 700; line-height: var(--lh-tight); }
.path-card__title a { color: var(--ink); text-decoration: none; }
.path-card__title a:hover { color: var(--accent); }
.path-card__summary { margin-top: var(--sp-3); color: var(--ink-soft); max-width: var(--measure); }
.path-card__preview {
  margin-top: var(--sp-3);
  padding-inline-start: 1.4em;
  font-size: var(--fs-sm);
  color: var(--ink-soft);
}
.path-card__step--more { list-style: none; margin-inline-start: -1.4em; color: var(--ink-faint); }
.path-card__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--rule);
  font-size: var(--fs-sm);
}
.path-card__count { color: var(--ink-faint); margin-inline-end: auto; }
.path-card__open, .path-card__start {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  font-weight: 600;
  text-decoration: none;
}
.path-card__open:hover, .path-card__start:hover { text-decoration: underline; }

.paths-index__note, .projects-index__note, .path-page__after {
  margin-top: var(--sp-7);
  padding: var(--sp-5);
  background: var(--bg-soft);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  max-width: var(--measure);
}
.paths-index__note .section-title,
.projects-index__note .section-title,
.path-page__after .section-title { margin-top: 0; }

.path-page__intro { margin-bottom: var(--sp-6); }
.path-steps__list { list-style: none; padding: 0; }
.path-steps__item {
  display: flex;
  gap: var(--sp-3);
  padding-block: var(--sp-4);
  border-bottom: 1px solid var(--rule);
}
.path-steps__item:first-child { border-top: 1px solid var(--rule); }
.path-steps__num {
  flex: none;
  font-family: var(--font-mono);
  font-weight: 700;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.path-steps__body { min-width: 0; }
.path-steps__section {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.path-steps__title { margin-top: var(--sp-1); font-size: var(--fs-h4); font-weight: 700; }
.path-steps__title a { color: var(--ink); text-decoration: none; }
.path-steps__title a:hover { color: var(--accent); }
.path-steps__summary { margin-top: var(--sp-1); font-size: var(--fs-sm); color: var(--ink-soft); }
.path-steps__soon {
  margin-inline-start: var(--sp-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-faint);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-pill);
  padding: 0 var(--sp-2);
  white-space: nowrap;
}
.path-steps__item--soon .path-steps__title { color: var(--ink-faint); font-weight: 600; }

/* Projects ---------------------------------------------------------------- */

.project__stack {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-4);
}
.project__stack-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Search results page ----------------------------------------------------- */

.search-results { list-style: none; padding: 0; display: grid; gap: var(--sp-3); }
.search-page__browse .card-grid { margin-top: var(--sp-4); }
.search-page__empty { max-width: var(--measure); }
.search-page__tips { padding-inline-start: 1.4em; color: var(--ink-soft); }
.search-page__tips li + li { margin-top: var(--sp-2); }
.search-page__suggestions { list-style: none; padding: 0; display: grid; gap: var(--sp-2); }
.search-page__suggestions a { font-weight: 600; }
.search-page__all { margin-top: var(--sp-5); font-size: var(--fs-sm); }

/* The LeetCode block under the results.

   1,055 solutions share the index with 96 lessons. app/Search.php ranks the
   lessons above them and hands the view the two sets apart; this rule is what
   makes the boundary visible, so a reader can see they have crossed out of the
   teaching material and into the archive rather than having to infer it from
   the titles. Set apart with a rule and quieter text — not hidden, just
   clearly secondary. */
.search-page__supporting {
  margin-top: var(--sp-7);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--rule);
}
.search-page__supporting-note {
  max-width: var(--measure);
  margin-top: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--ink-soft);
}
.search-results--supporting { margin-top: var(--sp-4); }

/* 404 --------------------------------------------------------------------- */

.notfound__path { margin-top: var(--sp-4); font-size: var(--fs-sm); color: var(--ink-faint); }
.notfound__path code {
  font-family: var(--font-mono);
  background: var(--bg-sunken);
  border-radius: var(--r-sm);
  padding: .1em .35em;
  overflow-wrap: anywhere;
}
.notfound__suggestion { margin-bottom: var(--sp-5); font-weight: 600; }
.notfound__search { max-width: 36rem; }
.notfound__browse { margin-top: var(--sp-6); }
.notfound__browse .card-grid { margin-top: var(--sp-4); }
.notfound__home { margin-top: var(--sp-6); }

/* ==========================================================================
   19. FOOTER
   ========================================================================== */

.site-footer {
  margin-top: var(--sp-8);
  background: var(--bg-soft);
  border-top: 1px solid var(--rule);
}
.site-footer__inner {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  gap: var(--sp-6);
  max-width: var(--max-w);
  margin-inline: auto;
  padding: var(--sp-7) var(--gutter);
}
.site-footer__brand { grid-column: 1 / -1; max-width: 26rem; }
@media (min-width: 900px) { .site-footer__brand { grid-column: auto; } }

.site-footer__name { font-weight: 700; font-size: 1.0625rem; }
.site-footer__tagline { color: var(--ink-soft); font-size: var(--fs-sm); margin-top: var(--sp-1); }
.site-footer__motto {
  margin-top: var(--sp-2);
  font-weight: 600;
  font-size: var(--fs-sm);
  color: var(--accent);
}
.site-footer__promise { margin-top: var(--sp-2); font-size: var(--fs-xs); color: var(--ink-faint); }

.site-footer__heading {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--sp-3);
}
.site-footer__list { list-style: none; padding: 0; display: grid; gap: var(--sp-2); font-size: var(--fs-sm); }
.site-footer__list a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  color: var(--ink-soft);
  text-decoration: none;
}
.site-footer__list a:hover { color: var(--accent); text-decoration: underline; }

.site-footer__bar {
  max-width: var(--max-w);
  margin-inline: auto;
  padding: var(--sp-4) var(--gutter) var(--sp-6);
  border-top: 1px solid var(--rule);
  font-size: var(--fs-xs);
  color: var(--ink-faint);
}
.site-footer__note { margin-top: var(--sp-1); }

/* ==========================================================================
   19b. STRUCTURAL ELEMENTS

   Wrappers and list items. Each rule here does one of two real jobs: cap the
   measure on a page that has no sidebar to constrain it, or set min-width:0 so
   a grid or flex child can shrink instead of pushing the page sideways. None
   of them is decoration.
   ========================================================================== */

/* A document page is bounded by the sidebar and the TOC rail. A listing page
   has neither, so on a wide monitor its text would run to 88rem and become
   unreadable. Cap the reading column instead of naming all fifteen roots. */
.shell:not(.shell--sidebar):not(.shell--toc) > .main {
  max-width: 76rem;
  margin-inline: auto;
  width: 100%;
}

/* Page roots. They carry no look of their own — the blocks inside them do —
   but they must not become a new min-content floor for the grid above. */
.home, .learn-index, .section-page, .lesson, .term, .glossary-index,
.errors-index, .error-doc, .paths-index, .path-page, .projects-index,
.project, .explainers-index, .explainer, .search-page, .notfound {
  min-width: 0;
}

/* Grid and flex children: without min-width:0 a long unbroken token (a URL, a
   Python identifier) sets the track width and the whole row overflows. */
.next-up__item, .recent-strip__item, .search-results__item,
.path-list__item, .card-grid > li, .area-grid > li, .section-rows > li { min-width: 0; }

.site-nav__item, .sidebar__link-item, .alphabet__item, .prereq__item { min-width: 0; }

.home-recent, .levels-intro, .path-steps { min-width: 0; }

.card__meta-item { white-space: nowrap; }
.level-panel__dot { flex: none; line-height: 1; }
.path-card__step { margin-top: var(--sp-1); }
.section-card__count { min-width: 0; }
.sidebar__toggle-text { white-space: nowrap; }
.site-footer__col { min-width: 0; }
.site-footer__copy { font-weight: 500; }

/* The previous link keeps the default reading direction; only --next flips.
   Stated explicitly so the pair reads as a deliberate choice. */
.pager__link--prev { text-align: start; }

/* ==========================================================================
   20. MOTION, PRINT, FORCED COLOURS
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

@media (forced-colors: active) {
  .card, .section-card, .area-card, .path-card, .callout, .code-block,
  .output-block, .level-tab, .chip, .btn, .code-block__copy {
    border: 1px solid ButtonBorder;
  }
  .level-tab[aria-selected="true"] { border-width: 3px; }
  /* The chosen area filter is a filled chip on screen; a forced palette drops
     the fill, so the state has to be carried by the border instead. */
  a.chip[aria-current], .chip.is-current { border-width: 3px; }
  .search__item[aria-selected="true"] { border: 2px solid Highlight; }
}

@media print {
  .site-header, .site-footer, .sidebar, .toc, .drawer-backdrop,
  .level-tabs, .nav-toggle, .theme-toggle, .skip-link, .search,
  .pager, .next-up, .code-block__copy, .heading-anchor,
  .areabar, .sectionbar, .share, .your-way {
    display: none !important;
  }
  /* Print every level, whatever the reader had open on screen. */
  :root[data-js] .level-panel,
  :root[data-js] .level-panel__label { display: block !important; }
  .shell,
  .shell--sidebar,
  .shell--toc,
  .shell--sidebar.shell--toc { grid-template-columns: minmax(0, 1fr); padding: 0; }
  .prose { max-width: none; }
  a { text-decoration: underline; }
  .prose a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .85em; word-break: break-all; }
  .code-block, .output-block, .callout, figure, table, .level-panel { break-inside: avoid; }
  .output-block { background: #eee !important; color: #000 !important; }
  .output-block__bar { background: #ddd !important; }
  .output-block__label, .output-block__body samp { color: #000 !important; }
}

/* ==========================================================================
   21. Authorship — byline on lessons, /about profile page
   ========================================================================== */

/* The byline sits in the same fact row as reading time and date, but the name
   is the one item in that row worth clicking, so it carries the link colour
   while the surrounding text stays faint. */
.lesson__fact--author { color: var(--ink-soft); }

.lesson__author {
  color: var(--accent);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid var(--accent-line);
}
.lesson__author:hover,
.lesson__author:focus-visible {
  color: var(--accent-hover);
  border-bottom-color: currentColor;
}

/* Collection pages — /learn/, a section index, /glossary/, /errors/, /paths/,
   /projects/, /how-it-works/, search results and 404 — list authored work
   rather than being a piece of it, so the attribution is one quiet sentence
   under the lede instead of a "By X" fact. The name inside still carries
   .lesson__author, so there is one link treatment for the author sitewide.
   One line only: the homepage hero already has .hero__byline and must never
   get this stacked on top of it. */
.page-head__byline {
  margin-top: var(--sp-3);
  max-width: var(--measure);
  font-size: var(--fs-sm);
  color: var(--ink-faint);
}

.about__bio {
  font-size: 1.0625em;
  color: var(--ink-soft);
  border-left: 3px solid var(--accent-line);
  padding-left: var(--sp-4);
  margin-block: var(--sp-5);
}

.about__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  list-style: none;
  padding: 0;
  margin-block: var(--sp-4);
}
.about__links li { margin: 0; }

.about__links a {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding: .4em .85em;
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  background: var(--bg-soft);
  color: var(--ink);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
  transition: border-color var(--speed), background var(--speed), color var(--speed);
}
.about__links a:hover,
.about__links a:focus-visible {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent-hover);
}

.about__stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-5);
  list-style: none;
  padding: 0;
  margin-block: var(--sp-4);
}
.about__stats li {
  margin: 0;
  color: var(--ink-soft);
  font-size: var(--fs-sm);
}
.about__stats strong {
  display: block;
  color: var(--ink);
  font-size: var(--fs-h3);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 480px) {
  .about__links a { font-size: var(--fs-xs); }
  .about__stats  { gap: var(--sp-3); }
}

/* ==========================================================================
   22. Authorship lists inside .prose — specificity fix
   ========================================================================== */

/* `.prose ul { list-style: disc }` is 0,1,1 and beat the plain `.about__links`
   rule at 0,1,0, so the pill rows on /about rendered with a disc marker between
   every pill. `.prose ul` also forces padding-inline-start: 1.4em, which pushed
   the whole row off the text margin. Both need a selector that outranks it —
   these are flex rows of buttons, not prose lists, and must carry no marker.
   Belt and braces: list-style on the <li> too, since ::marker is generated on
   the item, and list-style-type for browsers that ignore the shorthand reset. */
.prose .about__links,
.prose .about__stats {
  list-style: none;
  list-style-type: none;
  padding-inline-start: 0;
  padding: 0;
}

.prose .about__links > li,
.prose .about__stats > li {
  list-style: none;
  list-style-type: none;
  margin-top: 0;
}

/* .prose li + li adds a top margin; the flex `gap` already handles spacing. */
.prose .about__links > li + li,
.prose .about__stats > li + li { margin-top: 0; }

.prose .about__links > li::marker,
.prose .about__stats > li::marker { content: ""; }

/* ==========================================================================
   23. LEETCODE — /leetcode/ listing and /leetcode/{slug}
   views/pages/leetcode-index.php + views/pages/leetcode-detail.php

   1,055 rows is eleven times the lesson corpus, so this section is built for
   scanning, not for browsing: a fixed column rhythm, tabular numerals, one
   line per solution, and nothing that grows with the row count. There is no
   JavaScript behind any of it — the filters are links, the finder is a GET
   form — which is what keeps the CSP script-src at 'self' with no inline
   exception.

   Difficulty is carried by THREE channels at once: the word ("Easy"), the
   hue (--green/--amber/--red) and the dot's shape (circle / diamond /
   square). Colour is never the only one, so the page still reads in
   greyscale, in forced-colours mode, and with a colour deficiency.
   ========================================================================== */

/* Page roots: they must be able to shrink inside the shell grid, exactly like
   the fifteen roots already named in §19b. */
.leetcode-index, .lc-solution { min-width: 0; }

/* -- Filter bar ----------------------------------------------------------- */

.lc-filters {
  display: grid;
  gap: var(--sp-4);
  padding: var(--sp-4);
  margin-bottom: var(--sp-5);
  background: var(--bg-soft);
  border: 1px solid var(--rule);
  border-radius: var(--r);
}
.lc-filters__title,
.lc-facet__label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.lc-filters__title { margin: 0; }

/* Label beside its chips on a comfortable screen, above them on a phone. */
.lc-facet {
  display: grid;
  grid-template-columns: 5.5rem minmax(0, 1fr);
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
}
.lc-facet__label { margin: 0; padding-top: .3rem; }
.lc-facet__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  list-style: none;
  padding: 0;
  margin: 0;
  min-width: 0;
}
.lc-facet__list li { margin: 0; min-width: 0; }

@media (max-width: 560px) {
  .lc-facet { grid-template-columns: minmax(0, 1fr); }
  .lc-facet__label { padding-top: 0; }
}

/* A facet chip is a .chip with a state. The active one links to its own
   removal, so it shows an × and says so in its accessible name. */
.lc-chip { gap: var(--sp-2); }
.lc-chip__label { min-width: 0; }
.lc-chip__dot {
  width: .5rem;
  height: .5rem;
  flex: none;
  border-radius: 50%;
  background: var(--ink-faint);
}
.lc-chip--easy   .lc-chip__dot { background: var(--green); }
.lc-chip--medium .lc-chip__dot { background: var(--amber); border-radius: 1px; transform: rotate(45deg); }
.lc-chip--hard   .lc-chip__dot { background: var(--red); border-radius: 1px; }

a.lc-chip.is-on {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}
a.lc-chip.is-on .chip__count { color: inherit; }
.lc-chip__x { font-size: 1.05em; line-height: 1; }

/* The free-text finder. A plain GET form: it produces a real, shareable URL
   and it works with scripting switched off. */
.lc-find { display: grid; gap: var(--sp-2); }
.lc-find__label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.lc-find__row {
  display: flex;
  gap: var(--sp-2);
  max-width: 32rem;
}
.lc-find__input {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--edge);
  border-radius: var(--r-pill);
  transition: border-color var(--speed) ease, box-shadow var(--speed) ease;
}
.lc-find__input::placeholder { color: var(--ink-faint); }
.lc-find__input::-webkit-search-cancel-button { -webkit-appearance: none; }
.lc-find__input:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.lc-find__go {
  flex: none;
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
}

.lc-filters__clear { margin: 0; font-size: var(--fs-sm); }
.lc-filters__clear a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
}

/* -- "Showing X–Y of Z" --------------------------------------------------- */

.lc-count {
  margin-bottom: var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--ink-faint);
}
.lc-count strong {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* -- The list ------------------------------------------------------------
   A reference table drawn with grid rather than <table>, because at 360px a
   real table would either scroll sideways or crush its columns, and this one
   simply re-flows to two lines. overflow-x on the container is a guard, not a
   feature: nothing inside it is allowed to be wider than the container, so the
   PAGE never scrolls sideways even if a title arrives with no spaces in it.
   ----------------------------------------------------------------------- */

.lc-table {
  border: 1px solid var(--rule);
  border-radius: var(--r);
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.lc-table__head,
.lc-list__item {
  display: grid;
  grid-template-columns: 3.25rem minmax(0, 1fr) 6.75rem minmax(6rem, 13rem) 5rem;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
}

.lc-table__head {
  margin: 0;
  background: var(--bg-soft);
  border-bottom: 1px solid var(--rule);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.lc-table__h--num, .lc-table__h--time { text-align: end; }

.lc-list { list-style: none; padding: 0; margin: 0; }
.lc-list__item {
  position: relative;
  border-top: 1px solid var(--rule);
  min-width: 0;
}
.lc-list__item:first-child { border-top: 0; }
.lc-list__item:hover { background: var(--bg-hover); }

.lc-row__num {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
  text-align: end;
}
.lc-row__title { min-width: 0; }
.lc-row__link {
  color: var(--ink);
  font-weight: 600;
  font-size: var(--fs-sm);
  text-decoration: none;
  overflow-wrap: anywhere;
}
/* Stretched link: the whole row is the tap target, which matters far more on a
   phone than on a mouse. Same pattern as .card__link in §14. */
.lc-row__link::after { content: ""; position: absolute; inset: 0; }
.lc-list__item:hover .lc-row__link { color: var(--accent); }
.lc-row__link:focus-visible { outline: none; }
.lc-list__item:has(.lc-row__link:focus-visible) {
  outline: var(--ring);
  outline-offset: calc(var(--ring-off) * -1);
}

.lc-row__langs { display: flex; flex-wrap: wrap; gap: var(--sp-1); min-width: 0; }
.lc-row__time  { text-align: end; min-width: 0; }

/* Phone: number and title on the first line, everything else indented under
   the title on the second. No horizontal scroll, no hidden columns. */
@media (max-width: 860px) {
  .lc-table__head { display: none; }
  .lc-list__item {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: var(--sp-3);
    row-gap: var(--sp-2);
  }
  .lc-row__num   { flex: 0 0 2.5rem; text-align: start; }
  .lc-row__title { flex: 1 1 calc(100% - 2.5rem - var(--sp-3)); }
  .lc-row__diff  { margin-inline-start: calc(2.5rem + var(--sp-3)); }
  .lc-row__time  { text-align: start; }
}

/* -- Difficulty, language and runtime marks ------------------------------- */

.lc-diff {
  display: inline-flex;
  align-items: center;
  gap: .45em;
  padding: .15rem .6rem;
  border-radius: var(--r-pill);
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
  background: var(--bg-sunken);
  color: var(--ink-faint);
}
.lc-diff--easy   { background: var(--green-soft); color: var(--green); }
.lc-diff--medium { background: var(--amber-soft); color: var(--amber); }
.lc-diff--hard   { background: var(--red-soft);   color: var(--red); }

.lc-diff__dot {
  width: .45rem;
  height: .45rem;
  flex: none;
  background: currentColor;
  border-radius: 50%;
}
.lc-diff--medium  .lc-diff__dot { border-radius: 1px; transform: rotate(45deg); }
.lc-diff--hard    .lc-diff__dot { border-radius: 1px; }
.lc-diff--unknown .lc-diff__dot { background: none; box-shadow: inset 0 0 0 1.5px currentColor; }

.lc-lang {
  display: inline-block;
  padding: .1rem .45rem;
  border-radius: var(--r-sm);
  background: var(--bg-sunken);
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  white-space: nowrap;
}
.lc-lang--lg { padding: .25rem .6rem; font-size: var(--fs-sm); }

.lc-runtime {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
  white-space: nowrap;
}

.lc-empty { max-width: var(--measure); }
.lc-note {
  margin-top: var(--sp-6);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--rule);
  max-width: var(--measure);
  font-size: var(--fs-sm);
  color: var(--ink-faint);
}

/* -- Pagination ----------------------------------------------------------- */

.lc-pages {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  margin-top: var(--sp-6);
}
.lc-pages__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  list-style: none;
  padding: 0;
  margin: 0;
}
.lc-pages__list li { margin: 0; }

.lc-pages__num,
.lc-pages__step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-width: 2.5rem;
  min-height: 2.5rem;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-sm);
  color: var(--ink-soft);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  text-decoration: none;
}
a.lc-pages__num:hover,
a.lc-pages__step:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}
.lc-pages__num.is-current {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 700;
}
a.lc-pages__num.is-current:hover { background: var(--accent-hover); color: var(--on-accent); }
.lc-pages__step.is-off { border-style: dashed; color: var(--ink-faint); opacity: .5; }
.lc-pages__gap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  color: var(--ink-faint);
}
.lc-pages__where {
  margin-top: var(--sp-2);
  text-align: center;
  font-size: var(--fs-xs);
  color: var(--ink-faint);
}

/* Narrow phone: the arrows carry the meaning visually, but "Previous" and
   "Next" stay in the accessibility tree — clipped, never display:none. */
@media (max-width: 430px) {
  .lc-pages__step { padding-inline: var(--sp-2); }
  .lc-pages__step > span:not(.icon) {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

/* ==========================================================================
   23b. ONE SOLUTION — /leetcode/{slug}
   ========================================================================== */

.lc-solution__head { margin-bottom: var(--sp-5); }

.lc-solution__eyebrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-faint);
}
.lc-solution__eyebrow a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--ink-faint);
  text-decoration: none;
}
.lc-solution__eyebrow a:hover { color: var(--accent); }

.lc-solution__title {
  font-size: var(--fs-h1);
  line-height: var(--lh-tight);
  letter-spacing: -.022em;
  font-weight: 700;
  text-wrap: balance;
}
/* The number is part of the heading's text — splitting it out is styling, not
   content, so the <h1> still reads exactly the title in the frontmatter, which
   is the same string layout.php puts in the TechArticle headline. */
.lc-solution__num {
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.lc-solution__summary {
  margin-top: var(--sp-3);
  max-width: var(--measure);
  color: var(--ink-soft);
  font-size: 1.0625em;
}

.lc-solution__badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  list-style: none;
  padding: 0;
  margin-top: var(--sp-4);
}
.lc-solution__badges li { margin: 0; min-width: 0; }

.lc-metric {
  display: inline-flex;
  align-items: baseline;
  gap: .45em;
  padding: .2rem .65rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-pill);
  background: var(--bg);
  font-size: var(--fs-xs);
  white-space: nowrap;
}
.lc-metric__k {
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.lc-metric__v {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.lc-solution__facts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-4);
  list-style: none;
  padding: 0;
  margin-top: var(--sp-4);
  font-size: var(--fs-sm);
  color: var(--ink-faint);
}
.lc-solution__facts li { margin: 0; }

/* The two links that are the whole point of citing rather than copying: the
   problem stays on leetcode.com, the code stays on GitHub. white-space is
   normalised because .btn defaults to nowrap and "Read the problem on
   LeetCode" does not fit on a 360px screen in one line. */
.lc-solution__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-top: var(--sp-5);
}
.lc-solution__actions .btn {
  white-space: normal;
  text-align: center;
  justify-content: center;
}
@media (max-width: 480px) {
  .lc-solution__actions { flex-direction: column; align-items: stretch; }
}

.lc-solution__copyright {
  margin-top: var(--sp-3);
  max-width: var(--measure);
  font-size: var(--fs-xs);
  color: var(--ink-faint);
}

.lc-jump { margin-block: var(--sp-5); }
.lc-jump__title {
  margin-bottom: var(--sp-2);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.lc-jump__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  list-style: none;
  padding: 0;
  margin: 0;
}
.lc-jump__list li { margin: 0; min-width: 0; }

.lc-solution__body { min-width: 0; }

.lc-solution__foot {
  margin-top: var(--sp-8);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--rule);
}
.lc-solution__back {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-5);
  font-size: var(--fs-sm);
}
.lc-solution__back a { display: inline-flex; align-items: center; gap: var(--sp-1); }
.lc-solution__back-sep { color: var(--rule-strong); }

/* -- Forced colours and print -------------------------------------------- */

@media (forced-colors: active) {
  .lc-diff, .lc-lang, .lc-metric, .lc-pages__num, .lc-pages__step, .lc-table {
    border: 1px solid ButtonBorder;
  }
  .lc-pages__num.is-current { border: 3px solid Highlight; }
  .lc-chip.is-on { border: 3px solid Highlight; }
  /* The hue is gone in this mode, so the dot's shape is the surviving
     non-textual channel. It must not vanish with the background. */
  .lc-diff__dot, .lc-chip__dot { forced-color-adjust: none; }
}

@media print {
  .lc-filters, .lc-pages, .lc-pages__where, .lc-jump, .lc-note { display: none !important; }
  .lc-table { border-color: #999; }
  .lc-list__item { break-inside: avoid; }
}

/* -- The sixth nav item ---------------------------------------------------
   views/partials/header.php

   The header bar is one flex row: brand, six nav items, a 19rem search box, the
   cross-site pill and the theme toggle. Five items already filled it; measured
   at 960px a sixth pushed that row to 968px inside a 945px shell, and the page
   scrolled sideways — which is exactly what the min-width:0 rules in section
   19b exist to prevent.

   Between 901px (where the drawer hands the nav back) and 1180px the space is
   reclaimed three ways, none of which takes away anything a reader can use:

     1. The nav icons go. They are aria-hidden decoration, so a screen-reader
        or keyboard user loses nothing, and six words alone are unambiguous.
     2. The cross-site pill and the drawer's "Main site" entry swap places,
        which is the same trade the header already makes below 900px. One of
        the two is always present, so the link never disappears and a screen
        reader never meets both.
     3. The search box may shrink to an 11rem floor instead of holding a fixed
        19rem. Measured at 901px that leaves a 76px input: small, but real —
        the floor is what stops it collapsing to a stub.

   Above 1180px every one of those returns to full size.

   --aside marks the entry as supporting material rather than curriculum. It is
   a hairline and a shade of ink, never display:none — a section with no
   internal link anywhere is a section Google will not crawl.
   ------------------------------------------------------------------------ */

.site-nav__item--aside {
  padding-inline-start: var(--sp-2);
  margin-inline-start: var(--sp-1);
  border-inline-start: 1px solid var(--rule);
}
.site-nav__item--aside .site-nav__link { color: var(--ink-faint); }
.site-nav__item--aside .site-nav__link:hover { color: var(--ink); }
.site-nav__item--aside .site-nav__link.is-current { color: var(--accent); }

@media (min-width: 901px) and (max-width: 1180px) {
  .site-nav__icon { display: none; }
  .site-nav__link { padding-inline: var(--sp-2); }

  .site-header__search {
    width: min(15rem, 26vw);
    min-width: 11rem;
    flex: 0 1 auto;
  }

  /* Same swap as the max-width:900px block below, moved up a band. Still
     exactly one of the two, never both. */
  .mainsite--header { display: none; }
  .site-nav__item--external { display: block; }
  .site-nav__link--external { font-family: var(--font-mono); color: var(--ink-soft); }
}

/* Just above the band the icons, the pill and the 19rem search all come back at
   once, and between roughly 1181px and 1230px that is 34px more than the shell
   has. The search box could absorb it, but its min-width:auto floors it at its
   own min-content, so instead of shrinking it forced the row to overflow.
   Giving it a 13rem floor lets it give up the difference: measured at 1181px it
   settles at 208px with a 108px input, and above 1230px there is no pressure
   left, so it never shrinks at all. */
@media (min-width: 1181px) {
  .site-header__search { min-width: 13rem; flex: 0 1 auto; }
}

/* In the mobile drawer the list is vertical, so a vertical hairline would be a
   stray mark. Turn it into the horizontal rule the stacked list wants. */
@media (max-width: 900px) {
  .site-nav__item--aside {
    padding-inline-start: 0;
    margin-inline-start: 0;
    border-inline-start: 0;
    margin-block-start: var(--sp-2);
    padding-block-start: var(--sp-2);
    border-block-start: 1px solid var(--rule);
  }
}

/* ==========================================================================
   24. SHARE ROW, "LEARN THIS YOUR WAY", GLOSSARY AUTOLINKS
   views/partials/share.php · app/Autolink.php
   ========================================================================== */

/* -- Share row — sits under the byline facts on every doc detail page ------ */

.share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  margin-top: var(--sp-4);
}

.share__label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.share__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* "Copy link" is clipboard-only, so with no JavaScript it must not exist.
   Hidden at the <li>, not the button, or the empty item would still spend a
   flex gap. Same data-js flag the level tabs key off (assets/js/head.js). */
:root:not([data-js]) .share__item--copy { display: none; }

.chip--share { cursor: pointer; }
button.chip--share {
  transition: background var(--speed) ease, border-color var(--speed) ease,
              color var(--speed) ease;
}
button.chip--share:hover {
  border-color: var(--accent-line);
  background: var(--accent-soft);
  color: var(--accent);
}

/* Copy feedback, driven by site.js writing data-state for 1.8s. The colour
   pairs are the measured level tokens from §01 — AA in both themes. */
.share__copy[data-state="done"] {
  border-color: var(--green);
  background: var(--green-soft);
  color: var(--green);
}
.share__copy[data-state="fail"] {
  border-color: var(--red);
  background: var(--red-soft);
  color: var(--red);
}

/* -- "Learn this your way" — closes the lesson body ------------------------ */

.your-way {
  margin-top: var(--sp-7);
  padding: var(--sp-5);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-soft);
}

.your-way__title {
  font-size: var(--fs-h3);
  line-height: var(--lh-tight);
  font-weight: 700;
}

.your-way__note {
  margin-top: var(--sp-1);
  color: var(--ink-faint);
  font-size: var(--fs-sm);
}

.your-way__group { margin-top: var(--sp-4); }

.your-way__hint {
  margin-bottom: var(--sp-2);
  color: var(--ink-soft);
  font-size: var(--fs-sm);
}

/* .btn is white-space:nowrap for real buttons; this one is a sentence and
   must be allowed to wrap on a 320px phone. */
.your-way__ai {
  white-space: normal;
  text-align: left;
}
.your-way__ext { color: var(--ink-faint); }

.your-way__lang-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Native scripts (Devanagari, Kannada, Telugu, Tamil, Bengali) draw above and
   below the Latin x-height; a little extra padding keeps the pill from
   clipping their ascenders. */
.chip--lang {
  padding-block: calc(var(--sp-1) + 1px);
  font-weight: 600;
}

/* -- Glossary auto-links (app/Autolink.php) --------------------------------
   Same accent colour as every other link — the measured AA pairs in §01 —
   but a DOTTED underline, the convention <abbr> established for "this word
   has a definition". A reader can tell an automatic glossary hop from an
   editorial link before clicking, in both themes, without relying on colour.
   Hover and keyboard focus restore the solid line, same as ordinary links. */

.autolink {
  text-decoration-style: dotted;
  text-decoration-thickness: .09em;
  text-decoration-color: color-mix(in srgb, currentColor 65%, transparent);
}
.autolink:hover,
.autolink:focus-visible {
  text-decoration-style: solid;
  text-decoration-color: currentColor;
}

/* ==========================================================================
   24b. PRESENT MODE  (assets/js/present.js — loaded on first click)

   Injected markup, lesson pages only:
     .lesson__facts li.lesson__fact--present > button.present-btn (site.js)
     .present[role=dialog] > .present__slide > .present__slide-inner
                           + .present__hud   (present.js, while open)
   State: :root[data-present] while the overlay is up — the same
   attribute-on-<html> pattern as data-nav and data-drawer. The slide
   transition is a plain opacity/transform pair, so the global
   prefers-reduced-motion rule in §20 freezes it without help.
   ========================================================================== */

.present-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: .15rem var(--sp-3);
  border: 1px solid var(--edge);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--ink-soft);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--speed), color var(--speed), border-color var(--speed);
}
.present-btn::before { content: "▶"; font-size: .7em; translate: 0 -.05em; }
.present-btn:hover,
.present-btn:focus-visible {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.present-btn[data-state="loading"] { opacity: .6; cursor: progress; }
.present-btn:disabled { opacity: .5; cursor: default; }

:root[data-present] { overflow: hidden; }
:root[data-present] body { overflow: hidden; }

.present {
  position: fixed;
  inset: 0;
  z-index: 200;                     /* above the header, drawer and backdrop */
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--ink);
  animation: present-in var(--speed) ease-out;
}
@keyframes present-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.present__slide {
  flex: 1;
  min-height: 0;
  overflow-y: auto;                 /* a long section scrolls inside its slide */
  overscroll-behavior: contain;
  display: grid;
  cursor: default;
}
.present__slide-inner {
  width: min(62rem, 100%);
  margin-inline: auto;
  padding: clamp(var(--sp-5), 4vh, var(--sp-8)) clamp(var(--sp-4), 4vw, var(--sp-7));
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* -- title slide --------------------------------------------------------- */
.present__title-slide { text-align: center; }
.present__eyebrow {
  font-size: clamp(var(--fs-sm), 1.4vw, 1.1rem);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--sp-4);
}
.present__lesson-title {
  font-size: clamp(2rem, 5.5vw, 4rem);
  line-height: var(--lh-tight);
  font-weight: 750;
  letter-spacing: -.025em;
  text-wrap: balance;
  margin-bottom: var(--sp-4);
}
.present__summary {
  font-size: clamp(1.05rem, 2vw, 1.45rem);
  color: var(--ink-soft);
  max-width: 44ch;
  margin-inline: auto;
  margin-bottom: var(--sp-6);
  text-wrap: balance;
}
/* The teaching-attribution moment: the author's name, big, on slide one. */
.present__author {
  font-size: clamp(1.15rem, 2.4vw, 1.7rem);
  font-weight: 650;
  margin-bottom: var(--sp-2);
}
.present__site {
  font-size: clamp(var(--fs-sm), 1.4vw, 1.05rem);
  color: var(--ink-faint);
}

/* -- content slides ------------------------------------------------------ */
.present__heading {
  font-size: clamp(1.6rem, 3.6vw, 2.6rem);
  line-height: var(--lh-tight);
  font-weight: 720;
  letter-spacing: -.02em;
  text-wrap: balance;
  margin-bottom: clamp(var(--sp-4), 3vh, var(--sp-6));
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--rule);
}
/* Big type: the prose styles all still apply; only the base size grows. */
.present__body { font-size: clamp(1.02rem, 1.15vw + .55rem, 1.35rem); }
.present__body .code-block,
.present__body .output-block { font-size: .8em; }
.present__body img { max-width: 100%; height: auto; }

/* -- HUD ----------------------------------------------------------------- */
.present__hud {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  border-top: 1px solid var(--rule);
  background: var(--bg-soft);
}
.present__counter {
  margin-inline-end: auto;
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}
.present__hud button {
  inline-size: 2.25rem;
  block-size: 2.25rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--edge);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--ink-soft);
  font: inherit;
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--speed), color var(--speed);
}
.present__hud button:hover:not(:disabled),
.present__hud button:focus-visible { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.present__hud button:disabled { opacity: .35; cursor: default; }

@media (forced-colors: active) {
  .present { border: 1px solid ButtonBorder; }
  .present-btn, .present__hud button { border: 1px solid ButtonBorder; }
}
@media print {
  .present, .present-btn, .lesson__fact--present { display: none !important; }
}

/* ==========================================================================
   25. PRACTICE  — /practice (views/pages/practice.php)

   The snippets themselves are ordinary §11 code blocks rendered by
   app/Markdown.php and enhanced by playground.js; everything here is just
   the picker row and the rhythm between sections.
   ========================================================================== */

.practice__picker { margin-block: var(--sp-5) var(--sp-6); }
.practice__picker-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}
.practice__picker-list li { margin: 0; }

.practice__snippet { margin-block-start: var(--sp-7); }
.practice__snippet:first-of-type { margin-block-start: var(--sp-6); }
.practice__snippet .prose h2 {
  scroll-margin-top: calc(var(--header-h) + var(--sp-4));
}
.practice > noscript .callout { margin-block-start: var(--sp-5); }
