:root {
  --forest: #1a3a34;
  --green: #14532d;
  --stone: #292524;
  --muted: #404040;
  --border: #d6d3d1;
  --panel: #f5f2e9;
  --clay: #b5654a;
  --white: #ffffff;
  --paper: #faf8f2;
  --sans: "Helvetica Neue", Helvetica, "Heiti SC", "Arial Unicode MS", "Noto Sans CJK SC", "Source Han Sans SC", "Microsoft YaHei", sans-serif;
  --serif: "Songti SC", "STSong", "SimSun", Georgia, "Noto Serif CJK SC", "Source Han Serif SC", serif;
  --page: 1180px;
  --edge: clamp(22px, 5.5vw, 68px);
  --header-height: 64px;
}

/* The --serif stack already ends with "Source Han Serif SC"; ship a subset of it
   as a self-hosted web font so iOS Safari — which ignores system CJK serif names
   like Songti SC — and Android still render a 宋体. Desktop keeps Songti (named
   first in the stack); this only fills the slot where no system 宋体 is reachable,
   so the font stacks stay byte-identical everywhere (no drift). */
@font-face {
  font-family: "Source Han Serif SC";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("dg-serif-regular.woff2") format("woff2");
}
@font-face {
  font-family: "Source Han Serif SC";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("dg-serif-bold.woff2") format("woff2");
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--stone);
  font-family: var(--sans);
  font-size: 17px;
  font-weight: 350;
  line-height: 1.6;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

body.nav-open { overflow: hidden; }

a { color: inherit; }

button,
input { font: inherit; }

button { color: inherit; }

[hidden] { display: none !important; }

a:focus-visible,
button:focus-visible,
input:focus-visible,
summary:focus-visible {
  outline: 3px solid var(--clay);
  outline-offset: 3px;
}

.skip-link {
  position: fixed;
  left: 16px;
  top: 12px;
  z-index: 100;
  padding: 8px 12px;
  background: var(--forest);
  color: var(--white);
  transform: translateY(-180%);
}

.skip-link:focus { transform: translateY(0); }

.site-header {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--header-height);
  padding-inline: var(--edge);
  background: var(--paper);
  border-bottom: 1px solid var(--border);
}

.home-page .site-header,
.home-page main,
.home-page .site-footer {
  width: min(100%, var(--page));
  margin-inline: auto;
}

/* The .course-facts band closes with its own top rule and open bottom; the
   footer supplies the single divider below it. This gap keeps that divider off
   the stats row (they used to stack two 1px borders with no air between). */
.home-page .site-footer { margin-top: 44px; }

.wordmark {
  color: var(--forest);
  font-size: 14px;
  font-weight: 650;
  letter-spacing: .075em;
  line-height: 1.2;
  text-decoration: none;
  text-transform: uppercase;
}

.wordmark span {
  margin-left: 8px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 450;
  letter-spacing: .1em;
}

.site-header nav,
.course-header-actions {
  display: flex;
  align-items: center;
  gap: 25px;
}

.site-header nav a,
.course-header-actions > a {
  color: var(--muted);
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
}

.site-header nav a:hover,
.course-header-actions > a:hover { color: var(--green); }

/* Short homepage */

.home-page main { padding-inline: var(--edge); }

.hero {
  max-width: 930px;
  padding-block: clamp(34px, 3.7vw, 52px) clamp(28px, 3vw, 40px);
}

.eyebrow,
.licence-label,
.unit-code {
  margin: 0 0 17px;
  color: var(--clay);
  font-size: 13px;
  font-weight: 650;
  letter-spacing: .11em;
  line-height: 1.4;
  text-transform: uppercase;
}

.hero .eyebrow { margin-bottom: 12px; }

.hero h1 {
  margin: 0 0 16px;
  color: var(--forest);
  font-family: var(--serif);
  font-size: clamp(43px, 6.4vw, 76px);
  font-weight: 550;
  /* Chinese display type is evenly spaced; Latin negative tracking cramps it. */
  letter-spacing: 0;
  line-height: 1.1;
}

.lead {
  max-width: 790px;
  margin: 0;
  color: var(--muted);
  font-size: clamp(18px, 1.8vw, 22px);
  line-height: 1.58;
}

/* Who the course is for — a factual "does it fit me" line under the tagline,
   quieter than the lead so it reads as a qualifier, not a second headline. */
.hero-audience {
  max-width: 790px;
  margin: 14px 0 0;
  color: var(--muted);
  font-size: clamp(15px, 1.3vw, 16px);
  line-height: 1.55;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 13px;
  margin-top: 24px;
}

/* Presenter credit + course contact, directly under the CTA. Deliberately quiet
   (small labels, restrained sizes) but present — the name is the byline, the
   address is the course's own correction channel written out in full, not hidden
   behind a "反馈" link. */
.hero-credit {
  display: grid;
  gap: 8px;
  margin-top: 32px;
}

.hero-credit > div {
  display: grid;
  grid-template-columns: 4.6em minmax(0, 1fr);
  gap: 4px 14px;
  align-items: baseline;
}

.hero-credit dt {
  color: var(--muted);
  font-size: 13px;
  letter-spacing: .02em;
}

.hero-credit dd {
  margin: 0;
  color: var(--stone);
  font-size: 15px;
}

.hero-credit a {
  color: var(--forest);
  font-weight: 600;
  text-decoration-line: underline;
  text-decoration-color: var(--border);
  text-underline-offset: 3px;
}

.hero-credit a:hover {
  color: var(--green);
  text-decoration-color: var(--green);
}

.hero-credit .credit-mail { font-weight: 550; }

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 10px 21px;
  border: 1px solid var(--forest);
  font-size: 15px;
  font-weight: 620;
  text-decoration: none;
}

.button-primary {
  background: var(--forest);
  color: var(--white);
}

.button-primary:hover { background: var(--green); }

/* A quiet arrow marks the single primary CTA as forward motion (added in CSS so
   the button's HTML text stays exactly "开始学习"). */
.button-primary::after {
  content: "→";
  margin-left: 9px;
  font-weight: 400;
  transition: transform 150ms ease;
}

.button-primary:hover::after { transform: translateX(3px); }

@media (prefers-reduced-motion: reduce) {
  .button-primary::after { transition: none; }
}

.course-facts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.course-facts > div {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: 3px 7px;
  min-height: 92px;
  padding: 18px 22px;
  border-right: 1px solid var(--border);
}

.course-facts > div:first-child { padding-left: 0; }
.course-facts > div:last-child { border-right: 0; }

.course-facts strong {
  color: var(--forest);
  font-family: var(--serif);
  font-size: 37px;
  font-weight: 600;
  line-height: 1;
}

.course-facts span {
  color: var(--forest);
  font-weight: 600;
}

.course-facts small {
  grid-column: 1 / -1;
  color: var(--muted);
  font-size: 13px;
}

.site-footer {
  padding: 20px var(--edge) 24px;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 12px;
  line-height: 1.55;
}

.site-footer p { margin: 0 0 8px; }
.site-footer .licence-label { margin-bottom: 8px; }
.site-footer a { color: var(--green); }

/* Course browser */

.course-header {
  width: 100%;
  padding-inline: clamp(20px, 3vw, 44px);
}

.menu-toggle,
.drawer-close { display: none; }

/* App-shell: below the sticky header the browser fills exactly one viewport and
   never itself scrolls. Its two columns scroll INDEPENDENTLY (nav list on the
   left, lesson content on the right). This is what finally removes the whole
   class of page-scroll artefacts the sticky-sidebar model kept producing — a
   short lesson can no longer leave a blank band, the header can no longer hide a
   top padding that reappears on over-scroll, and the sidebar can no longer be
   dragged up under the header. The mobile block below reverts all of this to a
   single normally-flowing column. */
.course-browser {
  display: grid;
  grid-template-columns: 310px minmax(0, 1fr);
  height: calc(100vh - var(--header-height));
  max-width: 1480px;
  margin-inline: auto;
  overflow: hidden;
}

.course-sidebar {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  padding: 25px 18px 0;
  background: var(--panel);
  border-right: 1px solid var(--border);
}

.sidebar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 9px 18px;
}

.sidebar-head > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  width: 100%;
}

.sidebar-head span {
  color: var(--forest);
  font-family: var(--serif);
  font-size: 21px;
  font-weight: 600;
}

.sidebar-head strong {
  color: var(--muted);
  font-size: 12px;
  font-weight: 500;
}

.search-shell {
  display: none;
  padding: 0 8px 18px;
}

.js .search-shell { display: block; }

.search-shell label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.search-shell input {
  width: 100%;
  min-height: 40px;
  padding: 8px 11px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 0;
  color: var(--stone);
  font-size: 14px;
}

.search-shell input::placeholder { color: #6b6764; }

/* Head + search stay pinned; only the chapter list scrolls. */
.sidebar-head,
.search-shell { flex: 0 0 auto; }

.course-nav {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding-bottom: 35px;
  scrollbar-color: var(--border) transparent;
  scrollbar-width: thin;
}

.course-nav details { border-top: 1px solid var(--border); }
.course-nav details:last-of-type { border-bottom: 1px solid var(--border); }

.course-nav summary {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 7px;
  align-items: baseline;
  padding: 12px 9px;
  color: var(--stone);
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  list-style: none;
}

.course-nav summary::-webkit-details-marker { display: none; }

.course-nav summary::after {
  grid-column: 3;
  grid-row: 1;
  color: var(--muted);
  content: "+";
  font-size: 16px;
}

.course-nav details[open] summary::after { content: "−"; }

.course-nav summary > span {
  color: var(--forest);
  font-weight: 700;
}

.course-nav summary small {
  grid-column: 2;
  color: #696462;
  font-size: 11px;
  font-weight: 450;
}

.course-nav ol {
  margin: 0 0 10px;
  padding: 0;
  list-style: none;
}

.unit-nav-link {
  display: grid;
  grid-template-columns: 47px minmax(0, 1fr);
  gap: 3px;
  padding: 9px 9px 9px 12px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.4;
  text-decoration: none;
}

.unit-nav-link > span {
  color: #6b6764;
  font-size: 11px;
  line-height: 1.7;
}

.unit-nav-link:hover {
  background: #f0eeec;
  color: var(--forest);
}

.unit-nav-link.is-active,
.unit-nav-link[aria-current="page"] {
  background: var(--white);
  color: var(--forest);
  font-weight: 650;
  box-shadow: inset 3px 0 0 var(--clay);
}

.search-empty {
  margin: 18px 8px;
  color: var(--muted);
  font-size: 13px;
}

.nav-backdrop { display: none; }

.course-content {
  min-width: 0;
  height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: clamp(24px, 3vw, 36px) clamp(26px, 6vw, 88px) 0;
}

.js .unit-panel:not(.is-active),
.js .course-catalog:not(.is-active) { display: none; }

/* Two-state machine: catalog (sidebar folded away) ↔ browse (app-shell). goto() in
   course-browser.js flips the state class; the grid column and sidebar animate via
   the transitions below (desktop only — on mobile the sidebar is a drawer). The
   content crossfades through the JS-toggled .is-swapping class. .transitions-on is
   added after first paint so the initial state does not animate. Reduced motion is
   zeroed by the global rule at the end of this file. */
.course-content { transition: opacity 180ms ease; }
.course-content.is-swapping { opacity: 0; }

@media (min-width: 861px) {
  .course-browser.transitions-on {
    transition: grid-template-columns 460ms cubic-bezier(.4, 0, .2, 1);
  }

  .course-browser.is-catalog { grid-template-columns: 0 minmax(0, 1fr); }
  .course-browser.is-browsing { grid-template-columns: 310px minmax(0, 1fr); }

  .course-sidebar {
    transition: transform 460ms cubic-bezier(.4, 0, .2, 1), opacity 260ms ease;
  }

  .course-browser.is-catalog .course-sidebar {
    transform: translateX(-16px);
    opacity: 0;
    pointer-events: none;
  }
}

.unit-panel {
  max-width: 940px;
  margin-inline: auto;
  scroll-margin-top: calc(var(--header-height) + 18px);
}

/* Neutral catalog shown when no lecture is open, and the link back to it. */
.unit-back {
  display: inline-flex;
  align-items: center;
  margin-bottom: 20px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 550;
  text-decoration: none;
}

.unit-back:hover { color: var(--green); }

.course-catalog {
  max-width: 940px;
  margin-inline: auto;
}

.catalog-header {
  padding-bottom: 30px;
  border-bottom: 2px solid var(--forest);
}

.catalog-header h1 {
  margin: 0;
  color: var(--forest);
  font-family: var(--serif);
  font-size: clamp(36px, 5vw, 60px);
  font-weight: 600;
  letter-spacing: -.03em;
  line-height: 1.12;
}

/* Course-level welcome video, above the chapter cards. Rendered only when
   html-slides/course-intro.json names a url; absent by default. */
.course-intro {
  margin: 30px 0 0;
  padding: 22px 24px;
  background: var(--paper);
  border: 1px solid var(--border);
  border-left: 3px solid var(--forest);
}

.course-intro h2 {
  margin: 0;
  color: var(--forest);
  font-family: var(--serif);
  /* Sit at the chapter-card title scale (.cat-title is 21px) so this section
     heading harmonises with the chapter list below it instead of dominating it. */
  font-size: clamp(19px, 2vw, 22px);
  font-weight: 600;
  letter-spacing: -.01em;
}

.course-intro p {
  margin: 8px 0 0;
}

.course-intro-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin-top: 14px;
}

/* Chapter cards: a 2-column grid whose 1px gap shows the container's border colour,
   drawing hairline separators between paper-filled cards. */
.catalog-chapters {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  margin: 34px 0 0;
  padding: 0;
  list-style: none;
  background: var(--border);
  border: 1px solid var(--border);
}

.catalog-chapter {
  display: grid;
  gap: 5px;
  padding: 20px 22px;
  background: var(--paper);
  text-decoration: none;
}

.catalog-chapter:hover { background: #f0eeec; }

.catalog-chapter .cat-num {
  color: var(--clay);
  font-size: 12px;
  font-weight: 650;
  letter-spacing: .06em;
}

.catalog-chapter .cat-title {
  color: var(--forest);
  font-family: var(--serif);
  font-size: 21px;
  font-weight: 600;
  line-height: 1.25;
}

.catalog-chapter .cat-meta {
  color: var(--muted);
  font-size: 12px;
}

.unit-panel + .unit-panel {
  margin-top: 70px;
  padding-top: 65px;
  border-top: 1px solid var(--border);
}

/* The separator above only serves the no-JS fallback, where every unit panel is
   visible at once and needs a rule between them. With JS only .is-active renders,
   but the adjacent-sibling selector still matches through the display:none
   previous panel — so without this reset every panel except the first carried a
   stray 1px top rule and 135px of dead space (a divider and a big gap that made
   the second lecture look formatted differently from the first). */
.js .unit-panel + .unit-panel {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.unit-panel:focus { outline: none; }

.unit-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 35px;
  align-items: end;
  padding-bottom: 34px;
  border-bottom: 2px solid var(--forest);
}

.unit-code { margin-bottom: 12px; }

.unit-header h1 {
  margin: 0;
  color: var(--forest);
  font-family: var(--serif);
  font-size: clamp(35px, 4.4vw, 54px);
  font-weight: 600;
  letter-spacing: -.035em;
  line-height: 1.14;
}

.unit-context {
  margin: 14px 0 0;
  color: var(--muted);
  font-size: 14px;
}

.unit-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}

/* A quiet underlined text link, not a filled button — it sits lightly beside the
   title instead of competing with it, and collapses to a small line on mobile. */
.slides-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--green);
  font-size: 14px;
  font-weight: 600;
  text-decoration-line: underline;
  text-decoration-color: var(--border);
  text-underline-offset: 3px;
}

.slides-link:hover {
  color: var(--forest);
  text-decoration-color: var(--green);
}

/* External-link arrow drawn as SVG so it can never fall back to the iOS emoji ↗️. */
.ext-arrow { width: .8em; height: .8em; flex: none; }

/* A watch link carries a monochrome brand glyph (Bilibili / YouTube) that inherits
   the link's colour via currentColor, so the mark is single-colour and on-theme
   rather than the platform's multicolour logo. The label sits a touch smaller than
   the surrounding copy so the glyph reads first. Shared by the course-intro block
   and every module row; the underline affordance is on the label, on hover only. */
.watch-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--green);
  font-weight: 600;
  font-size: 13px;
  text-decoration: none;
}

.platform-icon { width: 15px; height: 15px; flex: none; }

.watch-link:hover { color: var(--forest); }

.watch-link:hover .watch-label {
  text-decoration: underline;
  text-decoration-color: var(--green);
  text-underline-offset: 3px;
}

.slides-pending,
.publish-count {
  color: var(--muted);
  font-size: 12px;
}

.module-section { padding-top: 28px; }

.module-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 8px;
}

.module-heading h2 {
  margin: 0;
  color: var(--forest);
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 600;
}

.module-list {
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--border);
  list-style: none;
}

.module {
  display: grid;
  grid-template-columns: 58px minmax(0, 1fr) auto;
  gap: 14px;
  align-items: center;
  min-height: 62px;
  padding: 11px 4px;
  border-bottom: 1px solid var(--border);
}

.module-number {
  color: var(--clay);
  font-size: 12px;
  font-weight: 650;
}

.module-title {
  color: var(--stone);
  font-size: 15px;
  line-height: 1.45;
}

.module-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  color: var(--muted);
  font-size: 12px;
  white-space: nowrap;
}

.module-actions a {
  color: var(--green);
  font-weight: 600;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

.module-pending { color: #6b6764; }

/* In-lecture prev/next. Besides the drawer, this is how a reader moves between
   lectures — and the primary way on a phone, where the drawer is hidden. Two
   cards on one row keep the left = back / right = forward metaphor; each names its
   destination (第X章第Y讲 · title) so a cross-chapter jump is legible. */
.lecture-nav {
  display: flex;
  gap: 12px;
  margin-top: 56px;
  padding-top: 26px;
  border-top: 1px solid var(--border);
}

.lecture-nav-link {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1 1 0;
  min-width: 0;
  padding: 14px 18px;
  background: var(--paper);
  border: 1px solid var(--border);
  text-decoration: none;
}

.lecture-nav-next { margin-left: auto; align-items: flex-end; text-align: right; }

.lecture-nav-link:hover { background: #f0eeec; }

.lecture-nav-dir {
  color: var(--clay);
  font-size: 12px;
  font-weight: 650;
  letter-spacing: .04em;
}

.lecture-nav-name {
  max-width: 100%;
  overflow: hidden;
  color: var(--forest);
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.3;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The footer is the last block inside the scrolling content column, so it aligns
   with the lesson panel (same max-width, centred) and closes the scroll with a
   top rule and comfortable bottom room. */
.course-footer {
  max-width: 940px;
  margin: 64px auto 0;
  padding: 22px 0 48px;
}

@media (max-width: 860px) {
  :root { --header-height: 58px; }

  body { font-size: 16px; }

  .site-header { padding-inline: 18px; }
  .wordmark { font-size: 12px; }
  .wordmark span { display: none; }
  .site-header nav { gap: 17px; }

  .home-page main { padding-inline: 22px; }
  .hero { padding-block: 30px 26px; }
  .hero h1 { font-size: clamp(40px, 13vw, 56px); }
  .lead { font-size: 17px; }
  .hero-actions { margin-top: 22px; }
  .button { flex: 1 1 155px; }

  .course-facts > div {
    min-height: 84px;
    padding: 14px 10px;
  }
  .course-facts > div:first-child { padding-left: 0; }
  .course-facts strong { font-size: 30px; }
  .course-facts small { font-size: 10px; line-height: 1.35; }

  .site-footer { padding-inline: 22px; }

  .course-header-actions { gap: 13px; }
  .course-header-actions > a { display: none; }

  /* On a phone the drawer is hidden and even the 反馈 link is dropped, so this is
     the header's only control — make it read as a solid, tappable button (filled
     accent, white text) rather than a faint white-on-paper chip nobody notices. */
  .js .menu-toggle {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    min-height: 40px;
    padding: 8px 16px;
    background: var(--forest);
    border: 1px solid var(--forest);
    color: var(--white);
    font-size: 14px;
    font-weight: 650;
  }

  /* Revert the desktop app-shell: one normally-flowing column, the page scrolls. */
  .course-browser {
    display: block;
    height: auto;
    overflow: visible;
  }

  .course-sidebar {
    display: block;
    position: static;
    width: 100%;
    height: auto;
    overflow: visible;
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }

  /* The drawer keeps the app-shell structure (pinned head/search, scrolling nav). */
  .js .course-sidebar {
    display: flex;
    flex-direction: column;
    position: fixed;
    z-index: 60;
    top: 0;
    left: 0;
    width: min(88vw, 350px);
    height: 100dvh;
    overflow: hidden;
    padding-top: 18px;
    border-right: 1px solid var(--border);
    border-bottom: 0;
    transform: translateX(-102%);
    transition: transform 180ms ease-out;
  }

  .js .course-sidebar.is-open { transform: translateX(0); }

  .js .drawer-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    padding: 0;
    background: transparent;
    border: 1px solid var(--border);
    font-size: 24px;
    line-height: 1;
  }

  .js .sidebar-head > div { width: auto; gap: 12px; }

  .js .nav-backdrop {
    position: fixed;
    z-index: 50;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    padding: 0;
    background: rgba(26, 58, 52, .34);
    border: 0;
    opacity: 0;
    pointer-events: none;
    transition: opacity 180ms ease-out;
  }

  .js .nav-backdrop.is-open {
    opacity: 1;
    pointer-events: auto;
  }

  .course-content {
    height: auto;
    overflow: visible;
    padding: 34px 22px 50px;
  }

  .unit-header {
    grid-template-columns: 1fr;
    gap: 16px;
    padding-bottom: 26px;
  }

  .unit-header h1 { font-size: clamp(34px, 10vw, 46px); }

  /* On its own row under the title, lay the link and the publish count side by side
     rather than stacking two lines — the small link no longer needs a column. */
  .unit-actions {
    flex-direction: row;
    align-items: baseline;
    gap: 16px;
    min-width: 0;
  }

  .module {
    grid-template-columns: 48px minmax(0, 1fr);
    gap: 4px 10px;
    padding-block: 13px;
  }

  .module-actions {
    grid-column: 2;
    justify-content: flex-start;
  }

  .catalog-chapters { grid-template-columns: minmax(0, 1fr); }

  .course-footer { padding: 23px 22px 30px; }
}

@media (max-width: 430px) {
  .hero h1 { font-size: 43px; }
  .course-facts strong { font-size: 29px; }
  .course-facts span { font-size: 14px; }
  .course-facts > div { padding-inline: 8px; }
  .course-facts > div:first-child { padding-left: 0; }
}

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