/* ===================================================
   Hornbill Academy — Production CSS
   POST-BRAND, 28 Aug 2026. Built on the Hornbill brand kit.

   Dark/Light theme via [data-theme] on <html>:
     dark  -> the kit's dark-red scheme   (ig-red)
     light -> the kit's paper-yellow      (ig-yellow)
   Textured/solid ground via [data-ground] on <html>.

   The site keeps its own token NAMES on purpose. 61 inline style=""
   attributes across 7 pages and 10 lines of js/app.js reference
   var(--text), var(--card-border), var(--mono) and friends directly.
   There is no build step and no linter, and a renamed custom property
   fails silently to nothing -- the element renders unstyled rather than
   erroring. So the names stay and the VALUES are re-expressed in terms
   of the kit. See hornbill-brand/programs/website/PROGRAM.md.
   =================================================== */
@import url('hb-tokens.css');
@import url('https://fonts.googleapis.com/css2?family=Source+Serif+4:ital,opsz,wght@0,8..60,300;0,8..60,400;0,8..60,600;0,8..60,700;1,8..60,300;1,8..60,400;1,8..60,600&family=Inter:wght@300;400;500;600;700;800;900&display=swap');

/* --- Minion Variable Concept, self-hosted ---------------------------
   RECORDED DECISION, 6 Sep 2026. hornbill-brand/README.md: Minion "is
   licensed for use by a CC subscriber; self-hosting it for the web goes
   beyond Adobe's embedding terms." That was raised and the client chose to
   ship it. Used by the hero eyebrow ONLY -- body, excerpts and the pullout
   stay on Source Serif 4 via --serif.

   Roman only, 292KB, unsubset: fonttools/brotli are not installed here, so
   there is no subsetting or WOFF2 step. font-display: swap keeps it off the
   critical path.

   TO REVERSE: delete this block. --hb-font-text falls through to Source
   Serif 4, already loaded above at weight 600, and nothing else changes. */
@font-face {
  font-family: "Minion Variable Concept";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/adobe/MinionVariableConcept-Roman.otf") format("opentype");
}

/* --- SCHEME ---------------------------------------------------------
   The kit's four scheme variables, selected by the site's existing
   data-theme attribute. The accent swaps with the ground: red on dark,
   yellow on paper. Each page still carries exactly ONE accent, which is
   what tokens.json pairing.rule requires. */
:root, [data-theme="dark"] {
  --hb-ground: var(--hb-dark);
  --hb-ink: var(--hb-white);
  --hb-accent: var(--hb-red);
  --hb-ink-on-accent: var(--hb-white);
  --hb-wash: 0.07;          /* texture presence on this ground */
  --hb-highlight-alpha: 26%;
}
[data-theme="light"] {
  --hb-ground: var(--hb-paper);
  --hb-ink: var(--hb-dark);
  --hb-accent: var(--hb-yellow);
  --hb-ink-on-accent: var(--hb-dark);
  --hb-wash: 0.11;          /* yellow on paper needs more to register */
  --hb-highlight-alpha: 42%;
}

/* --- THE BRIDGE -----------------------------------------------------
   The site's 15 tokens, re-expressed in terms of the kit's four.

   The selector list matters. A custom property substitutes its var()s at
   computed-value time ON THE ELEMENT WHERE IT IS DECLARED -- so --text
   declared here resolves against :root's --hb-ink and descendants
   inherit that finished colour. Redefining --hb-ink deeper does NOT
   reach it. Any scope that needs to re-ground itself must therefore
   re-declare this whole block, which is why .site-footer is in the
   selector below rather than just overriding --hb-ground. */
:root, .site-footer {
  --bg: var(--hb-ground);
  --text: var(--hb-ink);
  --highlight: var(--hb-accent);
  --bg-footer: var(--hb-dark);

  /* Derived inks. The kit has NO tint ramp -- every artefact built on it
     so far is flat, so tokens.json never needed one. That is a real gap
     (raised in PROGRAM.md), and per the kit's own doctrine a gap is to be
     named, not filled with invented hexes. So these are mixed from the
     two tokens actually in play and no literal colour enters this file. */
  --text-secondary: color-mix(in srgb, var(--hb-ink) 70%, var(--hb-ground));
  --text-muted:     color-mix(in srgb, var(--hb-ink) 45%, var(--hb-ground));
  --card-border:    color-mix(in srgb, var(--hb-ink) 22%, var(--hb-ground));
  --card-hover:     color-mix(in srgb, var(--hb-ink) 40%, var(--hb-ground));
  --card-bg:        color-mix(in srgb, var(--hb-ink)  4%, var(--hb-ground));
  --input-bg:       color-mix(in srgb, var(--hb-ink)  4%, var(--hb-ground));
  --placeholder-bg: color-mix(in srgb, var(--hb-ink)  7%, var(--hb-ground));
  --placeholder-border: color-mix(in srgb, var(--hb-ink) 18%, var(--hb-ground));
  --placeholder-text:   color-mix(in srgb, var(--hb-ink) 38%, var(--hb-ground));

  /* Alpha, not hue, is what had to be retuned here: the old gold sat at
     22% in both themes, but red at 22% on #011E23 and yellow at 22% on
     #F4F4F4 land nowhere near each other. */
  --highlight-bg: color-mix(in srgb, var(--hb-accent) var(--hb-highlight-alpha), transparent);

  /* The accent used as TYPE, which is not the same thing as the accent.
     Red on #011E23 holds; yellow on #F4F4F4 does not, and tokens.json's
     freeWithin says "dark ink on the light accents" -- they are fills, not
     text colours. So on paper this falls back to ink and the accent is
     carried by fills, borders and chips instead. Set per theme below. */
  --accent-text: var(--hb-accent);
}

/* AFTER the bridge on purpose: [data-theme="light"] and :root are both
   specificity (0,1,0), so source order decides and this has to come second.
   Yellow as type on paper runs about 1.4:1. Note this targets <html> only --
   the footer is dark-grounded, so its accent-text stays yellow, which is
   legible there. */
[data-theme="light"] { --accent-text: var(--hb-ink); }

/* --- FONTS ---
   --mono keeps its NAME (8 inline uses depend on it) but is no longer
   monospace: the kit gives interface text to Inter via the ui/uiSmall
   roles, which is exactly what these selectors are. Space Mono is gone
   from the @import above. Inter is narrower, so the uppercase pills that
   used it get a touch more tracking below. */
:root {
  --serif: 'Source Serif 4', Georgia, serif;
  --sans: var(--hb-font-display);
  --mono: var(--hb-font-display);
  --max-w: 1340px;
}

/* --- The inversion ---------------------------------------------------
   BRAND.md: "Grotesk display over serif body. Body copy is Minion, not
   Inter. That inversion is what makes the system look like a publisher
   rather than a startup, and it is the single easiest thing to get wrong."

   It was wrong here. body was Inter and --hb-font-text was never used, so
   the serif carried one line on the entire site. Body, excerpts, bios and
   pullouts are the serif; every heading and all UI stay Inter. */
.post-page-body, .post-page-body p, .about-bio, .course-desc, .events-intro,
.faq-intro, .faq-answer p, .pedagogy-card p, .event-desc, .topic-blurb,
.about-intro, .cta-note, .empty-state,
.course-team-info p, .hero-pullout, .footer-brand p {
  font-family: var(--serif);
}
/* Inter carries the argument; the serif is the reading surface. Every
   heading, every label, every control. */
h1, h2, h3, h4, h5, h6,
.nav-inner, .site-header, .btn, .pill, .card-toolbar,
.card-label-hl, .cat-filter-btn, .fact-label,
.topic-week, .post-page-cat, .about-role, .team-role, .event-series,
.footer-col a, .footer-col h4, .footer-bottom, .ticker-track, .img-slot,
.form-group label, .form-group input, .form-group textarea, .theme-toggle {
  font-family: var(--sans);
}

/* --- RESET --- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  /* The serif is the reading surface; Inter carries headings and UI. This is
     set HERE rather than in the type block above, which came earlier in the
     file and lost on source order. */
  font-family: var(--serif); font-size: 16px; line-height: 1.65;
  color: var(--text); background: var(--bg);
  -webkit-font-smoothing: antialiased; overflow-x: hidden;
  transition: background 0.3s, color 0.3s;
}
a { color: inherit; text-decoration: none; }
button { cursor: pointer; font-family: inherit; border: none; background: none; }
input, textarea { font-family: inherit; font-size: inherit; }
ul, ol { list-style: none; }

/* --- UTILITIES --- */
.container { max-width: var(--max-w); margin: 0 auto; padding: 0 32px; }
@media (max-width: 600px) { .container { padding: 0 16px; } }

/* =================== NAV =================== */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: var(--bg); border-bottom: 1px solid var(--card-border);
  transition: background 0.3s;
}
.nav-inner {
  max-width: var(--max-w); margin: 0 auto; padding: 0 32px;
  height: 60px; display: flex; align-items: center; justify-content: space-between;
}
.nav-logo {
  display: flex; align-items: center;
}
.nav-logo img { height: 58px; width: auto; display: block; }
.footer-brand .nav-logo img { height: 64px; }
/* Dark theme: show dark logo (white text), hide bright */
/* Light theme: show bright logo (dark text), hide dark */
/* Footer always dark bg, always show dark logo (white text) */
.nav-right { display: flex; align-items: center; gap: 24px; }
.nav-right a {
  font-size: 13px; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-secondary); transition: color 0.2s;
}
.nav-right a:hover, .nav-right a.active { color: var(--text); }
.nav-subscribe-btn {
  font-size: 12px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text);
  border: 1.5px solid var(--text); padding: 7px 16px; transition: background 0.2s;
}
.nav-subscribe-btn:hover { background: var(--highlight-bg); }
/* The button says what it does; the hint says what you get. Serif, because it
   is a sentence to read rather than a control -- the same reading surface as
   body copy. :focus-within as well as :hover so it is reachable by keyboard.
   Absolutely positioned, so revealing it never reflows the header. */
.nav-subscribe-wrap { position: relative; display: inline-flex; }
.nav-subscribe-hint {
  position: absolute; top: calc(100% + 10px); right: 0;
  width: max-content; max-width: 260px;
  padding: 10px 14px;
  background: var(--card-bg); border: 1px solid var(--card-border);
  font-family: var(--serif); font-size: 13px; line-height: 1.45;
  letter-spacing: 0; text-transform: none; color: var(--text-secondary);
  opacity: 0; visibility: hidden; transition: opacity 0.2s, visibility 0.2s;
  pointer-events: none; z-index: 1;
}
.nav-subscribe-wrap:hover .nav-subscribe-hint,
.nav-subscribe-wrap:focus-within .nav-subscribe-hint { opacity: 1; visibility: visible; }
/* Below 850px the nav is a tap-only panel, where hover means nothing. */
@media (max-width: 850px) { .nav-subscribe-hint { display: none; } }
.theme-toggle {
  font-size: 18px; padding: 4px 8px; color: var(--text);
  cursor: pointer; transition: transform 0.3s;
}
.theme-toggle:hover { transform: scale(1.15); }
.nav-toggle { display: none; flex-direction: column; gap: 5px; padding: 4px; }
.nav-toggle span { display: block; width: 22px; height: 2px; background: var(--text); }
@media (max-width: 850px) {
  .nav-toggle { display: flex; }
  .nav-right {
    display: none; position: absolute; top: 60px; left: 0; right: 0;
    background: var(--card-bg); border-bottom: 1px solid var(--card-border);
    flex-direction: column; padding: 24px 32px; gap: 16px; align-items: flex-start;
  }
  .nav-right.open { display: flex; }
}

/* =================== GIANT HERO TYPE =================== */
.hero-type { padding: 40px 0 0; overflow: hidden; }
/* =================== CATEGORY TICKER =================== */
.category-ticker {
  border-top: 1px solid var(--card-border);
  border-bottom: 1px solid var(--card-border);
  padding: 18px 0; overflow: hidden; margin-top: 40px;
}
.ticker-track {
  display: flex; align-items: center; gap: 0;
  animation: ticker 30s linear infinite; width: max-content;
}
.ticker-item {
  font-size: 13px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-secondary);
  white-space: nowrap; padding: 0 28px;
}
.ticker-sep { color: var(--text-muted); font-size: 16px; }
@keyframes ticker { 0%{transform:translateX(0)} 100%{transform:translateX(-50%)} }

/* The hero's own layout lives in the composition block further down. The
   .hero-content / .hero-left grid that used to sit here belonged to the
   pre-p11 hero and no page has referenced it since that rebuild; the dashed
   subscribe box went with the lede rewrite on 6 Sep 2026. */

/* =================== BROWSER CARD FRAME =================== */
.card-frame {
  border: 1px solid var(--card-border); background: var(--card-bg);
  transition: border-color 0.25s, background 0.3s;
}
.card-frame:hover { border-color: var(--card-hover); }
/* The three squares, the dashed rules and the [NO. 001] / [EP. 001] numbers
   came off every card on 6 Sep 2026. The STRIP stays: without a floor, a card
   carrying no label would lose the 38px it has today and every blog and
   recent-post card would change proportion. Measured before the change at
   38.1px with a number and 36.5px with a label alone. */
.card-toolbar {
  /* Every box label sits left, the course card included. */
  display: flex; align-items: center; justify-content: flex-start;
  padding: 10px 16px; min-height: 38px; font-family: var(--mono);
}
.card-label-hl { color: var(--accent-text); letter-spacing: 0.1em; font-size: 10px; margin-right: 8px; }

/* =================== IMAGE PLACEHOLDER =================== */
.img-slot {
  background: var(--placeholder-bg); display: flex; flex-direction: column;
  align-items: center; justify-content: center; overflow: hidden;
  border: 1px dashed var(--placeholder-border); position: relative;
  transition: background 0.3s;
}
.img-slot img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  position: absolute; top: 0; left: 0;
}
.img-slot .ph-label {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--placeholder-text); text-align: center;
  line-height: 1.5; padding: 8px; z-index: 1;
}
.img-slot .ph-icon { font-size: 28px; color: var(--placeholder-text); margin-bottom: 8px; z-index: 1; }

/* =================== POST CARD =================== */
.post-card-img {
  aspect-ratio: 16/10; margin: 0 16px;
}
.post-card-meta-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 16px 0; font-size: 13px; flex-wrap: wrap; gap: 4px;
}
.post-card-meta-row .cat { font-weight: 500; }
.post-card-meta-row .right { color: var(--text-secondary); }
.post-card-title {
  font-family: var(--sans); font-size: 20px; font-weight: 700;
  line-height: 1.3; padding: 8px 16px 16px;
}
/* The marker on a featured title. Solid accent with the scheme's own
   ink-on-accent, not a translucent wash: at 26% the red went muddy brown
   over the dark ground, and the system is flat -- there are no tints in
   the guide's own applications. */
.hl {
  background: var(--highlight); color: var(--hb-ink-on-accent);
  padding: 2px 6px;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}

/* =================== SECTION =================== */
.section-header {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 70px 0 12px;
}
.section-heading {
  font-family: var(--sans); font-size: clamp(28px, 4vw, 48px);
  font-weight: 700; font-style: normal;
}
.section-divider { border: none; border-top: 1px solid var(--card-border); margin: 0; }
.view-all-btn {
  font-size: 12px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text);
  border: 1.5px solid var(--text); padding: 8px 18px;
  transition: background 0.2s; white-space: nowrap;
}
.view-all-btn:hover { background: var(--highlight-bg); }

/* =================== POST GRID =================== */
.post-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; padding-top: 20px;
}
@media (max-width: 900px) { .post-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .post-grid { grid-template-columns: 1fr; } }

/* =================== FEATURED CARD =================== */
.featured-inner { display: grid; grid-template-columns: 1fr 1fr; }
@media (max-width: 768px) { .featured-inner { grid-template-columns: 1fr; } }
.featured-img { aspect-ratio: 4/3; margin: 0 0 0 16px; }
@media (max-width: 768px) { .featured-img { margin: 0 16px; } }
.featured-text { padding: 20px 24px 24px; display: flex; flex-direction: column; justify-content: flex-end; }
.featured-text .cat { font-size: 14px; margin-bottom: 4px; }
.featured-text .meta { font-size: 13px; color: var(--text-secondary); margin-bottom: 8px; }
.featured-text .title {
  font-family: var(--sans); font-size: clamp(22px, 2.5vw, 32px);
  font-weight: 700; line-height: 1.25;
}

/* =================== COURSES =================== */
.course-inner { padding: 24px 32px 32px; }
.course-title { font-family: var(--sans); font-size: clamp(24px, 3vw, 36px); font-weight: 700; margin-bottom: 16px; }
.course-desc { font-size: 15px; color: var(--text-secondary); line-height: 1.7; max-width: 680px; margin-bottom: 24px; }
/* Quick facts on the home course card. Same shape as the block courses.html
   already renders, so the two pages read as one course. */
.course-quick-facts { display: flex; gap: 32px; flex-wrap: wrap; font-size: 14px; margin-bottom: 24px; }
.course-quick-facts .fact-label { font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); display: block; margin-bottom: 4px; }
.course-quick-facts .fact-value { color: var(--text); font-weight: 500; }
/* The whole card is already an <a>, so this cannot be another one. */
.course-more { font-family: var(--sans); font-size: 13px; font-weight: 700; letter-spacing: 0.04em; color: var(--accent-text); }
.card-frame:hover .course-more { text-decoration: underline; }

/* =================== PODCAST =================== */
.podcast-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding-top: 20px;
}
@media (max-width: 768px) { .podcast-grid { grid-template-columns: 1fr; } }
.podcast-inner { display: grid; grid-template-columns: 220px 1fr; gap: 0; }
@media (max-width: 600px) { .podcast-inner { grid-template-columns: 1fr; } }
.podcast-art { aspect-ratio: 1/1; margin: 0 0 0 16px; }
@media (max-width: 600px) { .podcast-art { margin: 0 16px; } }
.podcast-info { padding: 16px 20px 20px; }
.podcast-info .title { font-family: var(--sans); font-size: 20px; font-weight: 700; line-height: 1.3; margin-bottom: 8px; }
.podcast-info .meta { font-size: 13px; color: var(--text-secondary); margin-bottom: 16px; }
.play-btn {
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text);
  border: 1.5px solid var(--text); padding: 7px 16px;
  display: inline-block; margin-bottom: 20px; transition: background 0.2s;
}
.play-btn:hover { background: var(--highlight-bg); }
.listen-on { font-size: 12px; color: var(--text-muted); display: flex; align-items: center; gap: 10px; }
/* .ic sizes itself at 1em, so font-size IS the icon size here. At 10px in a
   22px box the marks were 10px of muted grey -- present but unreadable.
   30px box, 16px mark, and --text-secondary rather than --text-muted. */
.platform-icon {
  width: 30px; height: 30px;  border: 1px solid var(--text-muted);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 16px; color: var(--text-secondary); transition: border-color 0.2s, color 0.2s;
}
.platform-icon:hover { border-color: var(--highlight); color: var(--accent-text); }

/* =================== PAGE HEADER =================== */
.page-header { padding: 80px 0 12px; }
/* COMPOSITION.md §6: "Enormous shape and enormous headline against very
   small UI text. The jump is extreme and deliberate." The kit's own ratio is
   heading 1.0 to uiSmall 0.07 = 14.3x. This was 6x -- 60px against 10px --
   which is a normal editorial hierarchy, not this system's. Raised at the
   display end; the UI text is already at 10px and cannot go lower. */
.page-header h1 {
  font-family: var(--sans); font-size: clamp(40px, 9vw, 128px);
  font-weight: 700; line-height: 0.95; letter-spacing: -0.025em;
}

/* =================== CATEGORY FILTERS =================== */
.category-filters { display: flex; gap: 10px; flex-wrap: wrap; margin: 24px 0 40px; }
.cat-filter-btn {
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em;
  text-transform: uppercase; padding: 8px 18px; border: 1px solid var(--card-border);
  color: var(--text-secondary); background: transparent; transition: all 0.2s;
}
.cat-filter-btn:hover, .cat-filter-btn.active {
  color: var(--accent-text); border-color: var(--highlight); background: var(--highlight-bg);
}

/* =================== SINGLE POST =================== */
.post-page { padding: 80px 0; }
/* COMPOSITION.md §7: "Nothing is centred. Nothing is balanced." Text is
   left-weighted throughout. */
/* COMPOSITION.md §7: "Nothing is centred. Nothing is balanced." Text is
   left-weighted throughout. */
.post-page-header { max-width: 760px; margin: 0 0 48px; text-align: left; }
.post-page-cat {
  font-family: var(--sans); font-size: 13px; font-weight: 700;
  letter-spacing: 0; text-transform: none; color: var(--accent-text);
  margin-bottom: 16px; display: inline-block;
}
.post-page-title {
  font-family: var(--sans); font-size: clamp(28px, 4vw, 42px);
  font-weight: 700; line-height: 1.25; margin-bottom: 20px;
}
.post-page-meta { font-size: 14px; color: var(--text-muted); }
.post-page-meta span { margin: 0 10px; }
.post-page-body {
  max-width: 720px; margin: 0 auto; font-size: 17px;
  line-height: 1.85; color: var(--text-secondary);
}
.post-page-body p { margin-bottom: 24px; }
.back-link {
  font-size: 14px; color: var(--text-secondary); display: inline-flex;
  align-items: center; gap: 6px; margin-bottom: 32px; transition: color 0.2s;
}
.back-link:hover { color: var(--accent-text); }
/* Icons are drawn, never a typographic glyph standing in for one. Sized by
   height off the type beside them, in currentColor, so they follow the scheme. */
.ic { display: inline-block; height: 1em; width: auto; vertical-align: -0.125em; }
.ic-slot { display: inline-flex; margin-left: 6px; }
.theme-toggle .ic { height: 18px; }
.ph-icon .ic { height: 28px; }

/* =================== ABOUT PAGE =================== */
.about-hero { padding: 80px 0 40px; text-align: left; }
.about-hero h1 {
  font-family: var(--sans); font-size: clamp(32px, 5vw, 52px);
  font-weight: 700; margin-bottom: 24px;
}
.about-intro { font-size: 17px; color: var(--text-secondary); max-width: 640px; margin: 0; line-height: 1.75; }
.about-founder-grid { display: grid; grid-template-columns: 280px 1fr; gap: 48px; align-items: center; padding: 60px 0; }
@media (max-width: 768px) { .about-founder-grid { grid-template-columns: 1fr; text-align: center; } }
/* Circular portraits, by request 6 Sep 2026. A 1:1 box plus the kit's own
   pill radius (9999px) renders an exact circle, so no literal radius enters
   the stylesheet -- BRAND.md sanctions the full pill. See PROGRAM.md. */
.about-avatar { width: 100%; aspect-ratio: 1; border-radius: var(--hb-radius-pill); }
@media (max-width: 768px) { .about-avatar { max-width: 200px; margin: 0 auto; } }
.about-name { font-family: var(--sans); font-size: 26px; font-weight: 700; margin-bottom: 4px; }
/* voice.json rules.eyebrowText (settled, Veecheet 2026-08-28): the eyebrow
   carries short section or chapter indicators only -- "never a series name,
   never a sentence, never a description. If the text names something, it is a
   heading." These NAME things (a series, a job, a category), so they drop the
   tracked-uppercase eyebrow styling and become small Inter headings. Only
   [NO. 001] and Week 01 remain legitimate markers. */
.about-role { font-family: var(--sans); font-size: 13px; font-weight: 700; color: var(--text-secondary); letter-spacing: 0; text-transform: none; margin-bottom: 16px; }
.about-bio { font-size: 15px; color: var(--text-secondary); line-height: 1.7; }
/* The reset zeroes every margin, so consecutive bio paragraphs would run
   together with no gap. */
.about-bio + .about-bio { margin-top: 1em; }
/* Contact line under each bio. Inter, not the bio serif -- it is a label and
   an address, not reading matter. Deliberately a plain mailto rather than a
   spelled-out "name dot surname at" address: the pattern here is
   firstname.lastname@hornbill.academy and all four names are printed on this
   same page as headings, so anything that can read the names can build the
   addresses. Obfuscation would buy nothing and cost the tap-to-mail that
   matters most on a phone. */
.about-contact {
  font-family: var(--sans);
  font-size: 13px; letter-spacing: 0.02em;
  margin-top: 18px; color: var(--text-muted);
}
.about-contact a {
  color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px;
  transition: color 0.2s;
}
.about-contact a:hover { color: var(--text); }

/* Portrait framing. `object-fit: cover` into a square keeps the middle of a
   tall photo, and both of these were shot with the head high in the frame, so
   the default centred crop cut it: Sudeesh lost 56px of head, Jayaraj 21px.
   Keyed to the file rather than to a class so the framing travels with the
   photo -- jayaraj-profile.jpg is used on the Faculty grid as well.

   Measured on the source files: Sudeesh 678x1017, head top at y=114, so 12%
   puts the crop window at y=41 and leaves 73px of headroom. Jayaraj 1190x1322
   with head top at y=45 has almost no headroom to give, so it is simply
   top-aligned; that photo wants re-cropping if it is ever to sit well in a
   circle. */
img[src$="sudeesh-profile.jpg"] { object-position: center 12%; }
img[src$="jayaraj-profile.jpg"] { object-position: center 0%; }
/* Lekha 1122x1402, hair top at y=118: a centred crop clips 22px of it,
   so top-aligned, which leaves 10.5% of the circle as headroom. */
img[src$="lekha-profile.jpg"]   { object-position: center 0%; }

/* Contact form */
.contact-form { max-width: 560px; }
.form-group { margin-bottom: 20px; }
.form-group label { display: block; font-size: 13px; font-weight: 500; color: var(--text-secondary); margin-bottom: 6px; }
.form-group input, .form-group textarea {
  width: 100%; padding: 12px 16px; background: var(--input-bg);
  border: 1px solid var(--card-border); color: var(--text); outline: none;
  transition: border-color 0.2s;
}
.form-group input:focus, .form-group textarea:focus { border-color: var(--highlight); }
.form-group textarea { resize: vertical; min-height: 120px; }
.form-submit {
  padding: 12px 32px; background: var(--text); color: var(--bg);
  border-radius: var(--hb-radius-pill);
  font-weight: 600; font-size: 14px; letter-spacing: 0.04em; text-transform: uppercase;
  transition: background 0.2s, color 0.2s;
}
.form-submit:hover { background: var(--highlight); color: var(--hb-ink-on-accent); border-color: var(--highlight); }

/* =================== SUBSCRIBE PAGE =================== */
.subscribe-page {
  min-height: 70vh; display: flex; align-items: flex-start; justify-content: center;
  text-align: left; padding: 80px 24px;
}
.subscribe-page h1 {
  font-family: var(--sans); font-size: clamp(28px, 4vw, 44px);
  font-weight: 700; margin-bottom: 16px;
}
.subscribe-page p { font-size: 16px; color: var(--text-secondary); margin-bottom: 32px; max-width: 480px; margin-left: auto; margin-right: auto; line-height: 1.7; }

/* =================== FOOTER =================== */
.site-footer {
  margin-top: 100px; background: var(--bg-footer);
  border-top: 1px solid var(--card-border); padding: 60px 0 32px;
  transition: background 0.3s;
}
.footer-top { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: 48px; margin-bottom: 48px; }
@media (max-width: 768px) { .footer-top { grid-template-columns: 1fr 1fr; gap: 32px; } }
/* Pages holds 6 links, Categories 9, so as single columns the second ran well
   past the first while the Socials cell -- five icons on one row -- sat mostly
   empty. Widening the Categories cell and running it two-up puts the two lists
   within a line of each other and takes the footer from 511px to 417px.

   column-span keeps the heading across both sub-columns instead of letting it
   flow into the first. Floored at 769px so the mobile block (max-width: 480px,
   where these same links become inline-block and wrap) is untouched, and so is
   the plain two-column grid in the 481-768 range. */
@media (min-width: 769px) {
  .footer-top { grid-template-columns: 1.3fr 0.9fr 1.7fr 1fr; }
  .footer-categories { column-count: 2; column-gap: 28px; }
  .footer-categories h4 { column-span: all; }
}
.footer-brand .nav-logo { margin-bottom: 20px; }
.footer-brand h4 { font-family: var(--sans); font-size: 18px; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 16px; }
.footer-col h4 { font-size: 15px; font-weight: 600; margin-bottom: 16px; }
.footer-col span, .footer-col a {
  display: block; font-size: 13px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-secondary); margin-bottom: 10px; cursor: pointer; transition: color 0.2s;
}
.footer-col span:hover, .footer-col a:hover { color: var(--text); }
/* Socials are marks, not a list of words. The column rule above makes every
   footer link display:block with a bottom margin and uppercase tracking --
   all wrong for a row of icons -- so .social-icon overrides it rather than
   inheriting and fighting it. */
.footer-socials { display: flex; flex-wrap: wrap; gap: 10px; }
.footer-col a.social-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; margin-bottom: 0;
  border: 1px solid var(--card-border);
  font-size: 18px;               /* .ic is 1em, so this sizes the mark */
  letter-spacing: 0; text-transform: none;
  color: var(--text-secondary);
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}
.footer-col a.social-icon:hover {
  color: var(--hb-ink-on-accent); background: var(--highlight); border-color: var(--highlight);
}

/* Below 480px the grid is one column, but the LINK LISTS flow rather than
   stack: each link used to sit alone on a 358px row with the right-hand side
   empty, which ran the footer to 1143px. Inline-block plus wrapping fills the
   width and brings it to ~796px.

   THIS BLOCK MUST STAY BELOW `.footer-col span, .footer-col a` ABOVE. That
   rule sets display:block and both selectors are specificity (0,2,0), so
   source order is the only thing deciding the winner. Moved up the file this
   silently stops working -- the links stack again and nothing errors.

   .footer-col a.social-icon is (0,3,0), so the icon row outranks this and
   keeps inline-flex at every width. */
@media (max-width: 480px) {
  .footer-top { grid-template-columns: 1fr; gap: 26px; }
  .footer-pages a,
  .footer-categories a { display: inline-block; margin: 0 16px 10px 0; }
}
.footer-bottom {
  /* Three children and space-between puts them left / centre / right with no
     extra rules; they stack left-aligned once the row wraps on mobile. */
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px;
  border-top: 1px solid var(--card-border); padding-top: 24px; font-size: 13px; color: var(--text-muted);
}
/* The bar is --text-muted, which is too quiet to read as a link, so the
   credit lifts to --text-secondary and carries an underline. */
.footer-credit a {
  color: var(--text-secondary); text-decoration: underline; text-underline-offset: 2px;
  transition: color 0.2s;
}
.footer-credit a:hover { color: var(--text); }

/* =================== EMPTY STATE =================== */
.empty-state { text-align: center; padding: 60px 24px; color: var(--text-muted); font-size: 15px; }

/* --- Nav Dropdown --- */
.nav-dropdown {
  position: relative;
}
.nav-dropdown-trigger {
  font-size: 13px; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-secondary);
  transition: color 0.2s; cursor: pointer;
}
.nav-dropdown-trigger:hover,
.nav-dropdown-trigger.active { color: var(--text); }
.nav-dropdown-menu {
  display: none; position: absolute; top: 100%; left: 50%;
  transform: translateX(-50%); padding-top: 12px; margin-top: 0;
  background: var(--card-bg); border: 1px solid var(--card-border);
  padding: 8px 0; min-width: 140px; z-index: 200;
}
.nav-dropdown:hover .nav-dropdown-menu { display: block; }
.nav-dropdown-menu a {
  display: block; padding: 10px 20px; font-size: 13px;
  font-weight: 500; color: var(--text-secondary);
  transition: color 0.2s, background 0.2s;
  text-transform: none; letter-spacing: 0;
}
.nav-dropdown-menu a:hover {
  color: var(--text); background: var(--highlight-bg);
}
@media (max-width: 850px) {
  .nav-dropdown-menu {
    position: static; transform: none; margin-top: 0;
    border: none; background: transparent; padding: 0 0 0 16px;
    display: block;
  }
  .nav-dropdown-trigger::after { content: none; }
}
.pedagogy-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding-top: 20px; }
@media (max-width: 768px) { .pedagogy-grid { grid-template-columns: 1fr; } }
.pedagogy-card { border: 1px solid var(--card-border); padding: 32px; background: var(--card-bg); transition: border-color 0.2s; }
.pedagogy-card:hover { border-color: var(--card-hover); }
.pedagogy-card h4 { font-family: var(--sans); font-size: 20px; font-weight: 700; margin-bottom: 12px; }
.pedagogy-card p { font-size: 14px; color: var(--text-secondary); line-height: 1.7; }

/* ===================================================
   BRAND LAYER — added 28 Aug 2026
   Everything below comes off the kit. No literal colour appears here;
   sizes are the website programme's own layout numbers and each one is
   justified in hornbill-brand/programs/website/PROGRAM.md.
   =================================================== */

/* --- The footer re-grounds itself ----------------------------------
   Dark ground on every page, in both themes -- it is the band that
   closes the page. Only --hb-ground and --hb-ink are overridden;
   --hb-accent and --hb-ink-on-accent deliberately INHERIT from the page,
   which is what makes the footer carry the page's own accent: red in
   dark mode, yellow in light. Both pairings are legal on a dark ground
   (dark-red and dark-yellow are kit schemes), so the page never ends up
   showing two accents.

   Because --bg, --text, --card-border and the rest hold unresolved
   var(--hb-*) streams, this one rule re-resolves the entire subtree and
   replaces the 16 hardcoded values the old light-footer block carried. */
.site-footer {
  --hb-ground: var(--hb-dark);
  --hb-ink: var(--hb-white);
  /* The bridge block above lists .site-footer in its selector, so every
     site token re-resolves here against these two. `color` additionally
     has to be re-declared: it inherits from body as an already-resolved
     colour, which no amount of token redefinition would change. */
  color: var(--text);
  background: var(--bg-footer);
}

/* --- Construction lines --------------------------------------------
   layout.constructionLines: 3 hairlines between 3% and 8.3% of the
   frame, 1px, drawn in the accent. These are on every slide and post in
   the guide -- BRAND.md calls them the "showing the working" motif and
   CLAUDE.md says dropping them is the fastest way to make something stop
   looking like Hornbill. Fixed, behind everything, inert to the pointer. */
.hb-rules {
  position: fixed; inset: 0; z-index: 0;
  pointer-events: none;
  color: var(--hb-accent);
}
.hb-rules svg { display: block; width: 100%; height: 100%; }
/* Below the site's existing nav breakpoint, 3%-8.3% collapses to roughly
   12-33px and the lines would sit under the content rather than beside
   it. The motif needs a margin to live in, so it withdraws instead. */
@media (max-width: 850px) { .hb-rules { display: none; } }

/* --- Texture ground ------------------------------------------------
   A subtle wash, alternating page to page so each page reads as
   separated. ONE greyscale plate, tinted by the accent through a mask --
   the same trick the lockup plays with currentColor, so the texture
   follows the theme instead of shipping a red copy and a yellow copy.
   Background only: the kit's plates never fill an image slot or a
   thumbnail here. */
[data-ground="texture"] body::before {
  content: "";
  position: fixed; inset: 0; z-index: 0;
  pointer-events: none;
  background: var(--hb-accent);
  opacity: var(--hb-wash);
  -webkit-mask-image: url("../img/texture/lichen-mask.png");
          mask-image: url("../img/texture/lichen-mask.png");
  -webkit-mask-size: cover; mask-size: cover;
  -webkit-mask-position: center; mask-position: center;
}
/* Content sits above both fixed layers. */
.container, .hero-type, .category-ticker, .site-footer,
.post-page, .subscribe-page { position: relative; z-index: 1; }
/* .site-header is deliberately NOT in that list. It needs to clear the two
   fixed layers as well, but `position: relative` here silently overrode the
   sticky nav declared at the top of this file -- later rule, same
   specificity, so it won, and the header scrolled away. Its stacking is
   already handled: z-index 100 up there, raised to 200 by the override at
   the end of this file, both far above the fixed layers' 0. */
/* Cards stay opaque so body text never sits directly on grain. */
[data-ground="texture"] .card-frame,
[data-ground="texture"] .pedagogy-card,
[data-ground="texture"] .nav-dropdown-menu { background: var(--card-bg); }

/* --- The mark ------------------------------------------------------
   marks/lockup-h.svg, inlined so fill="currentColor" can follow the
   scheme. Sized by HEIGHT, never width -- MARKS.md is explicit that
   width-first sizing is how the conventions drifted apart. All three
   sizes clear the 24px screen minimum in mark.minSize. */
.hb-lockup { display: block; width: auto; color: currentColor; }
.nav-logo .hb-lockup { height: 40px; }
.footer-brand .nav-logo .hb-lockup { height: 48px; }
.hero-type .hb-lockup { height: 132px; }
@media (max-width: 600px) { .hero-type .hb-lockup { height: 84px; } }

/* --- One button ----------------------------------------------------
   Replaces four inline variants that had drifted to three paddings and
   two font sizes while all meaning the same button. */
.btn {
  display: inline-block; text-align: center;
  padding: 13px 32px;
  border-radius: var(--hb-radius-pill);   /* BRAND.md Layout: buttons are full pills */
  font-family: var(--sans);
  font-size: 12px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  text-decoration: none; cursor: pointer;
  border: 1.5px solid transparent;
  transition: background 0.2s, color 0.2s, border-color 0.2s;
}
.btn-solid   { background: var(--text); color: var(--bg); border-color: var(--text); }
.btn-outline { background: transparent; color: var(--text); border-color: var(--text); }
.btn-muted   { background: transparent; color: var(--text-muted); border-color: var(--text-muted); cursor: default; }
.btn-solid:hover   { background: var(--highlight); color: var(--hb-ink-on-accent); border-color: var(--highlight); }
.btn-outline:hover { background: var(--text); color: var(--bg); }

/* Inter is narrower than the monospace it replaces, so the uppercase
   pills that used --mono get their tracking opened up to hold shape. */
.cat-filter-btn, .post-page-cat, .about-role,
.img-slot .ph-label { letter-spacing: 0.1em; }

/* --- Section openers ------------------------------------------------
   §4 Layering: "Nothing sits in its own untouched lane." The heading sits
   ON the band rather than in a clear strip above it. Contrast holds in both
   schemes -- white on red, dark on yellow; white on yellow never occurs. */
.section-header { position: relative; }
.section-header.on-band {
  padding: 54px 28px 26px;
  margin-top: 40px;
  background: var(--highlight);
  color: var(--hb-ink-on-accent);
  /* Full-bleed: cropped by both side edges rather than sitting inside a
     margin. §1 -- a shape fully inside the frame is "the least Hornbill
     thing available". */
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  padding-left: max(28px, calc(50vw - 50% + 0px));
  padding-right: max(28px, calc(50vw - 50%));
}
.section-header.on-band .section-heading,
.section-header.on-band .view-all-btn { color: var(--hb-ink-on-accent); }
.section-header.on-band .view-all-btn { border-color: var(--hb-ink-on-accent); }
.section-header.on-band + .section-divider { display: none; }


/* ===================================================
   LANDING HERO — rebuilt from guide p11, 4 Sep 2026

   p11 is the designer's own website preview, the only web artefact in the
   deck. Measured at 150dpi rather than eyeballed: panel 2413px, all figures
   below scaled to a 1440 viewport.

   What this replaces: a .hb-band mechanism that put an over-scaled chevron in
   a short overflow:hidden box. That produced a horizontal SLICE -- a
   parallelogram cut through the middle of the shape, the notch and
   terminations discarded. MARKS.md: "Nothing here may be distorted." Crop in
   COMPOSITION.md §1 means the shape is scaled past the FRAME and cut by the
   frame's edges, still recognisably itself. That is what p11 does.
   =================================================== */

/* One solid accent plus one screened. BRAND.md blesses this exact artefact:
   "the website hero on p11 lays a red halftone over a yellow band, which is
   fine because one of them is screened rather than solid." Light is p11 as
   drawn; dark inverts the pair. One solid accent plus one screened in both. */
[data-theme="light"] { --hero-solid: var(--hb-yellow); --hero-screen: var(--hb-red); }
[data-theme="dark"]  { --hero-solid: var(--hb-red);    --hero-screen: var(--hb-yellow); }

.hero-type {
  position: relative;
  overflow: hidden;              /* the FRAME edge -- this is what crops */
  padding: 0;
}
.hero-type .container {
  position: relative; z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 52%) minmax(0, 48%);
  align-items: start;
  /* p11's text column starts at 4.5% of the panel. The site's 9.5% gutter
     exists to clear the construction lines, which the hero does not have. */
  padding: 40px 0 56px;
  min-height: clamp(430px, 37vw, 560px);
}
.hero-lede { grid-column: 1; padding-left: 4.5%; }

/* p11: ~72px at 1440, two lines, ~1.0 leading. NOT the 124px the previous
   pass set from COMPOSITION.md §6's 14.3x -- that ratio is off the POSTER
   system, and the designer's own web page runs about 5x. */
.hero-heading {
  font-family: var(--sans);
  /* Sized by p11's measured LINE WIDTH, which is unambiguous, rather than by
     ink height: line 1 "Critical thinking for" spans 41.3% of the viewport
     there, and that string measures ~10.8em in Inter Bold -- so ~56px at 1440.
     Sizing it at 72px from the ink height pushed the break to after
     "thinking" and did not match the guide. */
  font-size: clamp(30px, 4.6vw, 66px);
  font-weight: 700;
  line-height: 1.0;
  letter-spacing: -0.02em;
  margin: 0;
}

/* The lede: eyebrow over the heading, a pullout under it, then the two
   actions where p11 puts "Explore courses" -- about 405px down a ~533px hero.

   RECORDED DIVERGENCE: voice.json marks the eyebrow role settled -- short
   section or chapter markers only, "never a series name, never a sentence,
   never a description." This one is a description. Taken deliberately, on
   the client's call; see PROGRAM.md.

   The TYPE is now the role as tokens.json actually defines it: family "text"
   (Minion, not Inter), weight 600, 0.18em tracking, uppercase. opsz 36 is
   not optional -- BRAND.md: "Display Semibold" is a named instance at
   wght 600, opsz 36, and the eyebrow is meant to take the display cut.
   Without the axis this renders the text cut, a different face. */
.hero-eyebrow {
  display: block;
  font-family: var(--hb-font-text);
  /* ONE LINE at every width, which is what caps the size rather than taste.
     Measured: the string is 461.8px at 16px, so width = 28.86 * size.

     DESKTOP the lede column is 52% minus a 4.5% indent = 47.5vw, so the fit
     condition is size <= 0.475vw / 28.86 = 1.646vw. 18px therefore only
     survives above a ~1094px viewport; below that the column, not the
     viewport, is the binding constraint. 1.62vw carries a small margin.

     The mobile override below has its own, looser bound -- see it there. */
  font-size: min(18px, 1.62vw); font-weight: 600;
  font-variation-settings: "wght" 600, "opsz" 36;
  letter-spacing: 0.18em; text-transform: uppercase;
  line-height: 1.2;
  color: var(--text);
  margin-bottom: 16px;
}
/* The eyebrow's key words take the screened half of the hero's solid/screened
   accent pair -- yellow on dark, red on light, the inverse of the page accent.
   Already a sanctioned second colour inside this one composition. */
.hero-accent { color: var(--hero-screen); }
/* The heading's "for" is red in BOTH themes, by request -- so it cannot use
   the theme-swapping pair above and names the kit's red token directly. In
   dark that matches the page accent; in light it is the same red the eyebrow
   words carry, so the light hero stays two-coloured. */
.hero-accent-red { color: var(--hb-red); }
/* Serif comes from the reading-surface selector at the top of this file, so
   only the italic is set here -- the grotesk-over-serif inversion again. */
.hero-pullout {
  font-style: italic;
  font-size: clamp(16px, 1.35vw, 19px);
  line-height: 1.55;
  color: var(--text-secondary);
  max-width: 30em;
  margin-top: clamp(18px, 2.2vw, 28px);
}
.hero-actions {
  display: flex; flex-wrap: wrap; gap: 12px;
  margin-top: clamp(20px, 2.4vw, 30px);
}

/* --- The shape cluster ---------------------------------------------
   Three whole shapes, each at its own true aspect, cropped only by the
   hero's right edge. Every box below is aspect-ratio 1000/879.012 =
   1.1376 -- the clip paths are objectBoundingBox, so a box at any other
   aspect would stretch the geometry. That is the rule the old code broke. */
.hero-figure { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.hero-figure > * { position: absolute; aspect-ratio: 1000 / 879.012; }

/* Positions are p11's own, as % of the panel width, measured at 150dpi:
   yellow arrow 59.9%->89.0%, photo 40.9%->99.8% and cut by the frame edge.
   z-order is p11's too -- screened arrow behind, solid arrow over it, photo
   ON TOP occluding the solid arrow's lower arm. That occlusion is why the
   yellow's visible box measures 1.56 aspect while the shape itself is
   1.1376: part of it is behind the photograph, not distorted. */
.hero-arrow-screen {
  z-index: 1;
  top: auto; bottom: 4.95vw; left: 49.24%; width: 33.02%;
  background: var(--hero-screen);
  clip-path: url(#hbArrowClip);
  -webkit-mask-image: url("../img/texture/lichen-mask.png");
          mask-image: url("../img/texture/lichen-mask.png");
  -webkit-mask-size: 560px auto; mask-size: 560px auto;
}
.hero-arrow-solid {
  z-index: 2;
  top: auto; bottom: 2.48vw; left: 53.43%; width: 33.02%;
  background: var(--hero-solid);
  clip-path: url(#hbArrowClip);
}
/* The wedge: wide edge right, tapering to a short flat left face. Its right
   edge meets the frame exactly (57.62 + 42.38 = 100.00) and its lower-right
   corner falls 5.43vw past the hero's bottom edge, where the hero's own
   overflow:hidden cuts it against the ticker.

   It is the one mark set to a different size from the arrows, and only its
   WIDTH is given -- height comes from the aspect-ratio above, so this is a
   uniform scale and the clip geometry is untouched.

   What steps evenly across the three is the LEFT-TOP VERTEX, the (0, 0.33855)
   corner all three clips share, not the box. The arrows are the same size, so
   for them box step and vertex step coincide; the photo is larger, so its box
   sits 0.33855 * (h_photo - h_arrow) = 2.79vw lower to put its vertex on the
   same diagonal. Measured at 1440: the vertices step (60.3, 35.5) then
   (60.3, 35.6) px. */
.hero-photo {
  z-index: 3;
  top: auto; bottom: -5.43vw; left: 57.62%; width: 42.38%;
  clip-path: url(#hbBoxClip);
  overflow: hidden;
}
.hero-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The landing page carried no construction lines (p11 shows none). That
   suppression is gone because the lines are now off on EVERY page -- see the
   commented-out renderRules() call in app.js. The .hb-rules block above is
   kept intact so uncommenting that one line restores the motif site-wide. */

/* Mobile: the cluster cannot hold p11's side-by-side arrangement at 390px, so
   the hero stacks -- heading, then the marks and photo, then the subscribe box.

   `display: contents` on .container and .hero-lede dissolves those wrappers on
   mobile so the heading, the figure and the subscribe box all become flex
   children of .hero-type and can be ordered independently. Order alone cannot
   do it otherwise: the figure is a sibling of .container, so it could only go
   wholly above or wholly below the type block, never between the heading and
   the box. The dissolved wrappers' padding is re-applied to the children. */
@media (max-width: 900px) {
  .hero-type { display: flex; flex-direction: column; }
  .hero-type .container,
  .hero-type .hero-lede { display: contents; }

  .hero-heading {
    order: 1;
    font-size: clamp(30px, 9.7vw, 56px);
    padding: 0 5%;
    /* Forces the break after "for" at ANY size or width, instead of relying on
       the container happening to be the right width. "Critical thinking for"
       measures 8.95em with the -0.02em tracking and "...for social" 12.05em, so
       a cap between the two puts "social justice" on line 2 deterministically.
       It never causes overflow: width stays auto and is merely capped, and the
       longest line (8.95em) always fits inside it. */
    max-width: 11.2em;
  }
  .hero-figure {
    order: 2;
    position: relative; inset: auto;
    /* Sized to the tallest element in the stack: the photo bottoms out at
       16.565 + 38.678 = 55.2vw. No max-height -- capping it would break the
       vw-based placement below, which is what keeps the shapes locked
       together at every width. */
    height: 92vw;
    margin: 18px 0 0;
    overflow: hidden;
  }
  /* Mobile gives the eyebrow the full 90vw rather than a 47.5vw column, so
     the bound loosens to 0.90vw / 28.86 = 3.118vw; 3.05vw keeps a margin.
     Holding one line is what costs the size here: at 390px a 40-character
     tracked uppercase line cannot exceed ~11.9px, below even the 12px it
     started at. Geometry, not preference. */
  .hero-eyebrow { order: 0; padding: 28px 5% 0; margin-bottom: 10px; font-size: min(18px, 3.05vw); }
  .hero-pullout {
    order: 3;
    max-width: none;
    margin: 22px 5% 0;
    font-size: 18px;
  }
  .hero-actions { order: 4; margin: 22px 5% 34px; }
  .hero-actions .btn { font-size: 14px; padding: 15px 28px; }

  /* Tops are in vw, not %, so they scale on the same basis as the widths and
     the three shapes stay locked together however tall the figure is.

     All three marks are the SAME SIZE (44vw), and both contacts are solved
     rather than nudged.

     PHOTO -> YELLOW: the box clip's top-left vertex is at (0, 0.33855) of its
     box and the arrow's inner notch apex at (0.252, 0.500) of its own. Setting
     those equal gives left 12 + 0.252x44 = 23.09%, top 16 + 0.16145x38.678.

     RED -> YELLOW: the red's NOTCH APEX (0.252, 0.500) lands on the yellow's
     UPPER-LEFT outer vertex (0, 0.33855). That is the pair either side of the
     white wedge, so making them coincide is what closes it -- and because the
     two shapes' edges diverge to the right of that point, everything right of
     it overlaps rather than gapping.

     The three are then treated as ONE GROUP and scaled uniformly about the
     group's top-left by k = 100 / 66.18 = 1.5110, so the red's left edge lands
     on 0% and the photo's right edge on 100% -- edge to edge. Uniform scaling
     about a common origin preserves every relative distance, so both solved
     contacts survive it exactly. */
  /* `bottom: auto` is not decoration. The desktop rules anchor these by
     `bottom`, and a rule here that sets only `top` would leave BOTH in force
     -- an over-constrained box, whose height is then solved from the two
     offsets instead of from aspect-ratio, which stretches the mark. Resetting
     bottom hands the height back to the 1000/879.012 aspect. */
  .hero-arrow-screen { top: 14.75vw; bottom: auto; left: 0%;     width: 66.49%; }
  .hero-arrow-solid  { top: 24.18vw; bottom: auto; left: 16.76%; width: 66.49%; }
  .hero-photo        { top: 33.61vw; bottom: auto; left: 33.51%; width: 66.49%; }
}

/* --- p11's dark banner --------------------------------------------
   In p11 a full-bleed dark band sits immediately below the hero, measured
   ~144px tall at 1440. The category ticker takes that slot and that
   treatment, instead of the thin bordered strip it was. */
/* The photo's lower-right corner is meant to be cut by this band, and the
   hero's overflow:hidden crops at its own bottom edge -- so the two can only
   meet once the ticker's 40px top margin closes. Scoped to the hero, so no
   other page's ticker spacing moves. */
.hero-type + .category-ticker { margin-top: 0; }
.category-ticker {
  background: var(--ticker-bg);
  color: var(--ticker-ink);
  border: 0;
  min-height: 144px;
  display: flex; align-items: center;
  /* The marquee track is ~10900px wide. Without this the whole document
     overflows horizontally -- the original rule had it and my rewrite dropped
     it, which made every section look clipped on mobile. */
  overflow: hidden;
}
.category-ticker .ticker-item,
.category-ticker a { color: var(--ticker-ink); }

/* The band is the opposite of the page ground: dark on the light page, as p11
   draws it, and light on the dark page.

   RECORDED DIVERGENCE: the light band is neutral --hb-white, not --hb-paper.
   tokens.json pairing.forbidden lists "pure white as a composition ground" and
   colour.white carries the role "reversed text" -- a type colour here, not a
   ground. This was raised and white was chosen deliberately. It is an existing
   token, so no literal enters the stylesheet. See PROGRAM.md. */
[data-theme="light"] { --ticker-bg: var(--hb-dark);  --ticker-ink: var(--hb-white); }
[data-theme="dark"]  { --ticker-bg: var(--hb-white); --ticker-ink: var(--hb-dark); }
@media (max-width: 900px) { .category-ticker { min-height: 96px; } }

.post-teaser {
  position: relative;
  max-height: 1200px;
  overflow: hidden;
}
.post-teaser::after {
  content: '';
  position: absolute;
  bottom: 0; left: 0; right: 0;
  height: 200px;
  background: linear-gradient(transparent, var(--bg));
  pointer-events: none;
}
@media (max-width: 900px) {
  div[style*="grid-template-columns:1fr 1fr"] { grid-template-columns: 1fr !important; }
}
.nav-dropdown-menu {
  padding-top: 16px !important;
  margin-top: 0 !important;
}
.nav-dropdown-menu a {
  color: var(--text) !important;
}
[data-theme="light"] .nav-dropdown-menu {
  background: #fff !important;
  border-color: #ddd !important;
}
[data-theme="dark"] .nav-dropdown-menu {
  background: #111 !important;
  border-color: #2a2a2a !important;
}
.nav-dropdown-menu {
  z-index: 300 !important;
}
.site-header {
  z-index: 200 !important;
}