:root {
  --bg-dark: #000000;
  --text-primary: #f4f1ea;
  --text-muted: #9a958c;
  --accent-gold: #c9a962;
  --line: rgba(255, 255, 255, 0.1);
  --font-sans: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: 'IBM Plex Mono', 'SFMono-Regular', Menlo, Monaco, Consolas, monospace;
  --page-pad: clamp(1.25rem, 4vw, 2.5rem);
  --max: 1120px;
  /* One type scale for all page copy: display titles, section headers, body. */
  --type-display: clamp(1.85rem, 3.6vw + 0.5vh, 3.35rem);
  --type-heading: clamp(1.55rem, 2.8vw, 2.15rem);
  --type-body: clamp(0.95rem, 0.9vw + 0.45vh, 1.05rem);
  /* The art is drawn in Braille glyphs, which no mono webfont covers, so the
     browser substitutes a system font whose advance varies by platform (~0.74em
     on Chrome/Windows). 0.8 is the safe no-JS baseline; app.js measures the real
     advance and grows each block to fill its column exactly. */
  --ascii-advance: 0.8;
}

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

html { scroll-behavior: smooth; }

/* Site-wide slim scrollbar, matching the marketing pages (alignment.id). */
html {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
}

::-webkit-scrollbar {
  width: 4px;
  height: 4px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.2);
  border-radius: 0;
}

::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.35);
}

::-webkit-scrollbar-corner {
  background: transparent;
}

html::-webkit-scrollbar-button,
body::-webkit-scrollbar-button {
  display: none !important;
  width: 0 !important;
  height: 0 !important;
  min-width: 0 !important;
  min-height: 0 !important;
}

body {
  font-family: var(--font-sans);
  background: var(--bg-dark);
  color: var(--text-primary);
  min-height: 100vh;
  line-height: 1.7;
  overflow-x: hidden;
}

a { color: inherit; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

:focus-visible {
  outline: 2px solid var(--accent-gold);
  outline-offset: 3px;
}

.nav-shell {
  position: sticky;
  top: 0;
  z-index: 100;
  padding: 0.65rem var(--page-pad);
  background: rgba(0, 0, 0, 0.86);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}

.nav-bar {
  margin-inline: auto;
  max-width: var(--max);
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: 2.75rem;
}

.nav-logo { display: inline-flex; align-items: center; text-decoration: none; }
.nav-logo img { height: 24px; width: auto; display: block; }

.nav-primary {
  display: flex;
  align-items: center;
  margin-left: auto;
  gap: clamp(0.85rem, 2vw, 1.4rem);
}

.nav-links { display: flex; align-items: center; gap: clamp(0.75rem, 2vw, 1.15rem); }

.nav-separator {
  width: 1px;
  height: 1.15em;
  background: rgba(244, 241, 234, 0.22);
  flex: 0 0 auto;
}

.nav-link {
  position: relative;
  font-size: 0.78rem;
  color: rgba(244, 241, 234, 0.62);
  text-transform: uppercase;
  letter-spacing: 0.16em;
  text-decoration: none;
  padding: 0.35rem 0.1rem;
  transition: color 0.2s ease;
  font-family: var(--font-mono);
}

.nav-link:hover, .nav-link.is-active { color: #fff; }
.nav-link.is-active::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -0.2rem;
  height: 1px;
  background: var(--accent-gold);
}

.nav-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: rgba(244, 241, 234, 0.86);
  text-decoration: none;
  transition: color 0.2s ease, opacity 0.2s ease;
}

.nav-cta:hover { color: #fff; }

.nav-cta__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.2rem;
  height: 1.2rem;
}

.nav-cta--panel {
  gap: 0.65rem;
  padding: 0.55rem 1rem;
  margin-top: 0.4rem;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.04);
  justify-self: start;
}

.nav-menu-btn {
  display: none;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.45rem 0.7rem;
  cursor: pointer;
}

.nav-menu-panel[hidden] { display: none; }

.nav-menu-panel {
  max-width: var(--max);
  margin: 0.5rem auto 0;
  padding: 0 0 0.75rem;
  display: grid;
  gap: 0.5rem;
}

.nav-menu-panel .nav-link { font-size: 0.85rem; }

.hero {
  box-sizing: border-box;
  min-height: calc(100dvh - 4.05rem);
  height: calc(100dvh - 4.05rem);
  display: grid;
  align-items: center;
  padding: clamp(4.5rem, 8vh, 6rem) var(--page-pad);
  overflow: hidden;
}

.hero-grid {
  container-type: inline-size;
  max-width: var(--max);
  width: 100%;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
  align-items: center;
  gap: clamp(1.5rem, 4vw, 3.5rem);
}

.bird-column {
  display: grid;
  /* Without an explicit track the column is sized to the ASCII block's
     max-content, so the art escapes the hero column and hands fitAsciiArt a
     width it just inflated — each measure grows the bird a little more. */
  grid-template-columns: minmax(0, 1fr);
  justify-items: start;
  gap: 0.6rem;
  min-width: 0;
  margin-left: clamp(-1rem, -3vw, 0);
}

.bird-wrap {
  position: relative;
  align-self: start;
  width: 100%;
  transition: transform 0.45s ease;
  will-change: transform;
}

/* Hero bird: the feeding-at-flower frames are 95 columns wide. Weight,
   line-height and the blue glow below come from the source viewer; app.js
   repaints each glyph with its own brightness-scaled shade of the same hue, so
   this rule is what a visitor without JavaScript sees. */
.bird {
  --cols: 95;
  font-family: var(--font-mono);
  font-size: clamp(4.5px, 1.05vw, 10px);
  /* 0.44 of the hero grid is the bird column once the gap is taken out. */
  font-size: min(10px, calc(44cqw / (var(--cols) * var(--ascii-advance))));
  font-weight: 700;
  line-height: 0.96;
  white-space: pre;
  display: inline-block;
  margin: 0;
  transform: translateY(-2.9em);
  color: hsl(200, 95%, 62%);
  text-shadow: 0 0 0.24em currentColor;
}

.hero-text { max-width: 34rem; }

.tagline {
  font-size: var(--type-display);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.03em;
  margin-bottom: clamp(0.85rem, 2vh, 1.35rem);
}

.tagline::after {
  content: "";
  display: block;
  width: 3.2rem;
  height: 2px;
  margin-top: clamp(0.7rem, 1.5vh, 1rem);
  background: var(--accent-gold);
  transform-origin: left center;
}

/* Scroll / entrance reveals. Hidden only after JS marks <html class="js">
   so no-JS visitors still see everything. */
.js [data-reveal] {
  opacity: 0;
  transform: translate3d(0, 1.15rem, 0);
  transition:
    opacity 0.8s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.8s cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}

.js [data-reveal="left"] { transform: translate3d(-1.35rem, 0, 0); }
.js [data-reveal="right"] { transform: translate3d(1.35rem, 0, 0); }
.js [data-reveal="fade"] { transform: none; }

.js [data-reveal].is-in {
  opacity: 1;
  transform: none;
}

.js .hero-text:not(.is-in) .tagline::after { transform: scaleX(0); }
.js .hero-text.is-in .tagline::after {
  transform: scaleX(1);
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.35s;
}

.bio {
  font-size: var(--type-body);
  color: var(--text-muted);
  margin: 0 0 1.1rem;
  line-height: 1.55;
}

.section {
  max-width: var(--max);
  margin: 0 auto;
  padding: clamp(2.75rem, 6vh, 4.25rem) var(--page-pad);
}

.section h2 {
  font-size: var(--type-heading);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
  margin-bottom: 1.25rem;
  max-width: 22ch;
}

.section p {
  font-size: var(--type-body);
  color: var(--text-muted);
  max-width: 42rem;
  margin: 0 0 1.05rem;
  line-height: 1.55;
}

.section-wide { max-width: none; padding-inline: var(--page-pad); }
.section-wide > h2 { max-width: var(--max); margin-inline: auto; padding-inline: 0; }

/* Closing bloom: full-viewport statement — flower left, copy right.
   Not the top hero (that one is the hummingbird). */
.section-bloom {
  box-sizing: border-box;
  max-width: none;
  width: 100%;
  min-height: 100dvh;
  height: 100dvh;
  display: grid;
  align-items: center;
  margin: 0;
  padding: clamp(4.5rem, 10vh, 6rem) var(--page-pad) clamp(2rem, 5vh, 3.5rem);
  overflow: hidden;
}

/* Moiré concepts section — full-viewport field with copy overlaid. */
.moire-section {
  --kolibri-bg: var(--bg-dark);
  --kolibri-fg: var(--text-primary);
  --kolibri-muted: var(--text-muted);
  --kolibri-accent: var(--accent-gold);

  position: relative;
  max-width: none;
  width: 100%;
  min-height: 100dvh;
  height: 100dvh;
  display: grid;
  align-items: center;
  grid-template-columns: minmax(0, var(--max));
  justify-content: center;
  padding: clamp(4.5rem, 9vh, 6.5rem) var(--page-pad);
  overflow: hidden;
  isolation: isolate;
}

.moire-copy {
  position: relative;
  z-index: 1;
  max-width: min(36rem, 92vw);
  padding: clamp(0.85rem, 1.8vh, 1.35rem) clamp(0.9rem, 2vw, 1.5rem) clamp(0.85rem, 1.8vh, 1.35rem) 0;
}

.moire-eyebrow {
  margin: 0 0 clamp(0.7rem, 1.4vh, 1.1rem);
  color: var(--kolibri-accent);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  line-height: 1.4;
  text-transform: uppercase;
}

.moire-title {
  max-width: 22ch;
  margin: 0;
  color: var(--kolibri-fg);
  font-size: var(--type-display);
  font-weight: 500;
  letter-spacing: -0.045em;
  line-height: 1.02;
  text-wrap: balance;
}

.moire-body {
  display: grid;
  gap: clamp(0.65rem, 1.35vh, 1.1rem);
  max-width: 34rem;
  margin-top: clamp(1rem, 2.4vh, 1.85rem);
  color: var(--kolibri-muted);
  font-size: var(--type-body);
  line-height: 1.55;
}

.moire-body p { margin: 0; }

.moire-closing { color: color-mix(in srgb, var(--kolibri-fg) 78%, var(--kolibri-muted)); }

.moire-visual {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--kolibri-bg);
  border: 0;
  border-radius: 0;
  pointer-events: none;
}

/* Soft left→right dissolve — long ramp, no hard mid-screen wall. */
.moire-visual::after {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  content: "";
  background: linear-gradient(
    90deg,
    var(--kolibri-bg) 0%,
    color-mix(in srgb, var(--kolibri-bg) 92%, transparent) 22%,
    color-mix(in srgb, var(--kolibri-bg) 55%, transparent) 48%,
    color-mix(in srgb, var(--kolibri-bg) 18%, transparent) 72%,
    transparent 100%
  );
}

.moire-canvas {
  display: block;
  width: 100%;
  height: 100%;
  -webkit-mask-image: linear-gradient(
    90deg,
    transparent 0%,
    rgba(0, 0, 0, 0.15) 28%,
    rgba(0, 0, 0, 0.55) 52%,
    black 78%,
    black 100%
  );
  mask-image: linear-gradient(
    90deg,
    transparent 0%,
    rgba(0, 0, 0, 0.15) 28%,
    rgba(0, 0, 0, 0.55) 52%,
    black 78%,
    black 100%
  );
}

/* The painting is a child of this section and the section clips it, so the
   atmosphere can never bleed into the section that follows. */
#lessons {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

#lessons > h2,
.ide {
  position: relative;
  z-index: 1;
}

/* The lessons section fits one desktop viewport. It now holds a single IDE
   frame — lesson rail, workspace, AI chat — that the scripted demo drives. */
#lessons {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: calc(100dvh - 4.05rem);
  padding-top: clamp(1.75rem, 4vh, 3rem);
  padding-bottom: clamp(1.75rem, 4vh, 3rem);
}

#lessons > h2 { margin-bottom: 1rem; }

/* Section copy above the demo frame, aligned with the frame's width. The
   width is pinned (not just max-width): auto side margins would otherwise
   defeat flex stretch and size the block to its content. The heading sits
   centered above, so the copy centers to match. */
.lesson-intro {
  width: 100%;
  max-width: var(--max);
  margin: 0 auto 1.25rem;
  text-align: center;
}

/* Centered paragraphs: without this, .section p anchors each block to the
   left edge of the pinned intro. */
.lesson-intro p {
  margin-left: auto;
  margin-right: auto;
}

/* Same pin for the frame itself: with `margin: auto` in a flex column the
   .ide would size itself to fit its content (the longest code line, the
   tallest chat state), so the frame would grow and shrink as the demo
   animates. A definite width keeps it one constant size. */
.ide {
  width: 100%;
  max-width: var(--max);
  margin: 1.5rem auto 0;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 4px;
  overflow: hidden;
  background: rgba(10, 10, 9, 0.88);
  backdrop-filter: blur(16px) saturate(0.85);
  -webkit-backdrop-filter: blur(16px) saturate(0.85);
  display: flex;
  flex-direction: column;
}

@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .ide { background: #0a0a09; }
}

/* Stage tabs along the top of the frame. */
.ide-topbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
}

.ide-tab {
  border: 1px solid transparent;
  background: transparent;
  color: #8f8a82;
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  padding: 0.4rem 0.6rem;
  cursor: pointer;
  min-height: 44px;
}

.ide-tab span { color: #6f6a63; margin-right: 0.35rem; }
.ide-tab:hover { color: #ddd8cf; }
.ide-tab:focus-visible { outline: 2px solid var(--accent-gold); outline-offset: 2px; }

.ide-tab.is-active {
  color: #f4f1ea;
  border-color: rgba(201, 169, 98, 0.4);
  background: rgba(201, 169, 98, 0.06);
}
.ide-tab.is-active span { color: #d0bc7b; }

/* Three-pane body: rail / workspace / chat. The frame keeps one locked height
   per monitor — more text never grows it; the columns scroll internally. The
   rem line is the fallback: the vh clamp only varies with the viewport, so the
   demo can animate as much as it likes without ever expanding the frame. */
.ide-body {
  display: grid;
  grid-template-columns: 18fr 52fr 30fr;
  /* An fr row instead of the implicit auto row: auto would size to the
     panes' content (code + output + diff actions) and overflow the frame;
     minmax(0, 1fr) pins the row to the locked height, so the demo can only
     ever scroll inside its columns. */
  grid-template-rows: minmax(0, 1fr);
  height: 34rem;
  height: clamp(34rem, calc(100vh - 17rem), 52rem);
  min-height: 0;
}

.ide-mobile-tabs { display: none; }

/* Monet sits behind the lesson panel: a uniform scrim keeps the demo and
   text readable while the painting reads as atmosphere. */
.lesson-painting {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.lesson-painting::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(10, 10, 9, 0.72);
}

.lesson-painting img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
  filter: grayscale(0.25) sepia(0.35) saturate(0.85) contrast(0.9) brightness(0.72);
  opacity: 0.55;
}

/* Lesson rail: persistent structure only — stage, progress, files, scope. */
.ide-rail {
  min-width: 0;
  border-right: 1px solid var(--line);
  padding: 0.9rem 0.85rem;
  overflow: auto;
}

/* The `p` prefixes out-rank the section-wide body copy rule (.section p) so
   the IDE keeps its own sizes and colors instead of inheriting page copy. */
p.rail-heading {
  margin: 0 0 0.45rem;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #d0bc7b;
}

.rail-sub + .rail-heading,
.rail-list + .rail-heading { margin-top: 1.1rem; }

p.rail-sub {
  margin: 1.1rem 0 0.35rem;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #8f8a82;
}

.rail-list,
.scope-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.rail-list li,
.scope-list li {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font-size: 0.85rem;
  color: #9f9a91;
}

.rail-dot {
  flex: none;
  align-self: center;
  width: 0.6rem;
  height: 0.6rem;
  border: 1px solid #6f6a63;
  border-radius: 50%;
  transition: background 0.3s ease, border-color 0.3s ease;
}

.rail-list li.is-done { color: #ddd8cf; }
.rail-list li.is-done .rail-dot {
  background: var(--accent-gold);
  border-color: var(--accent-gold);
}

.scope-list--muted li { color: #6f6a63; }

.file-tree {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: #8f8a82;
}

.file-tree ul {
  list-style: none;
  margin: 0.35rem 0 0 0.9rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.file-folder { color: #6f6a63; }
.file-file.is-open { color: #f4f1ea; }

p.rail-progress {
  margin: 1rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: #9f9a91;
}
p.rail-progress strong { color: #d0bc7b; }

.ide-workspace {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

.workspace-file-tabs { display: flex; flex-wrap: wrap; gap: 0.5rem; }

/* Diff controls near the editor during a proposed change. Static in the demo:
   the script decides what happens next. */
.diff-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.6rem 0.75rem;
  border-top: 1px solid var(--line);
}
.diff-actions[hidden] { display: none; }

.diff-action {
  border: 1px solid rgba(201, 169, 98, 0.45);
  color: #d0bc7b;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  padding: 0.35rem 0.6rem;
}

.diff-action--reject {
  border-color: rgba(248, 113, 113, 0.4);
  color: #f87171;
}

.workspace-tabs {
  display: flex;
  gap: 0.5rem;
  padding: 0.55rem 0.75rem;
  border-bottom: 1px solid var(--line);
  background: rgba(14, 14, 12, 0.6);
}

.workspace-tab {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: #8f8a82;
  padding: 0.2rem 0.35rem;
}

.workspace-tab.is-active { color: #f4f1ea; }

/* JS badge: the workspace speaks JavaScript (real logo, local copy). */
.js-badge {
  display: inline-flex;
  align-items: center;
  align-self: center;
  width: 1.4rem;
  height: 1.4rem;
}
.js-badge img { display: block; width: 100%; height: 100%; }

/* Scripted selection and patch-diff highlighting inside the editor. */
.code-line--selected { background: rgba(201, 169, 98, 0.14); }
.code-line--add { background: rgba(208, 188, 123, 0.16); }
.code-line--del { background: rgba(248, 113, 113, 0.13); color: #e2b4b4; }

.output-panel {
  border-top: 1px solid var(--line);
  padding: 0.85rem;
  font-size: 0.88rem;
  color: #aaa59c;
  overflow: auto;
}

.expense-data,
.test-data {
  margin: 0.5rem 0 0.75rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: #c7c2b9;
  white-space: pre-wrap;
}

.check-result {
  margin-top: 0.65rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: #9f9a91;
}

.check-result[data-status="pass"] { color: #d0bc7b; }
.check-result[data-status="fail"] { color: #f87171; }

/* Transfer stage test verdicts match the example's check-result colors. */
.test-data[data-status="fail"] { color: #f87171; }
.test-data[data-status="pass"] { color: #d0bc7b; }

/* Autoplay check beat: preview rows settle in one at a time. */
.output-row {
  display: block;
  animation: demo-in 0.35s ease both;
}

@keyframes demo-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Rendered code replaces the old editable textarea. */
.code-render {
  margin: 0;
  min-height: 14rem;
  border: 0;
  background: rgba(7, 7, 6, 0.96);
  color: #e8e4dc;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  line-height: 1.55;
  padding: 0.85rem;
  overflow: auto;
  white-space: pre;
}

.code-line { display: block; }

.code-line--diff { animation: diff-flash 1.6s ease both; }
@keyframes diff-flash {
  from { background: rgba(201, 169, 98, 0.3); }
  to { background: transparent; }
}

/* AI chat column: the lesson lives here. */
.ide-chat {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border-left: 1px solid var(--line);
}

.chat-log {
  flex: 1;
  overflow: auto;
  padding: 0.85rem;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  min-height: 0;
}

.chat-msg {
  max-width: 94%;
  padding: 0.6rem 0.7rem;
  border-radius: 4px;
  font-size: 0.82rem;
  line-height: 1.5;
  color: #ddd8cf;
  background: rgba(255, 255, 255, 0.04);
  animation: demo-in 0.35s ease both;
}

/* The chat bubble carries its own size, color and measure; the section-wide
   body copy rule (.section p) must not override them. */
.chat-msg p {
  margin: 0 0 0.5rem;
  font-size: 1em;
  color: inherit;
  line-height: inherit;
  max-width: none;
}
.chat-msg p:last-child { margin-bottom: 0; }

.chat-msg--user {
  align-self: flex-end;
  background: rgba(201, 169, 98, 0.12);
  color: #f4f1ea;
}

p.chat-task {
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px dashed rgba(201, 169, 98, 0.35);
  color: #d0bc7b;
}

/* Brief "thinking" indicator before a message block lands. */
.chat-typing {
  display: flex;
  gap: 0.3rem;
  padding: 0.55rem 0.7rem;
  align-self: flex-start;
  background: rgba(255, 255, 255, 0.04);
  border-radius: 4px;
}
.chat-typing span {
  width: 0.35rem;
  height: 0.35rem;
  border-radius: 50%;
  background: #8f8a82;
  animation: typing-dot 1s ease-in-out infinite;
}
.chat-typing span:nth-child(2) { animation-delay: 0.15s; }
.chat-typing span:nth-child(3) { animation-delay: 0.3s; }
@keyframes typing-dot {
  0%, 60%, 100% { opacity: 0.35; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-2px); }
}

/* AI patch card in the chat. */
.patch-card {
  padding: 0.6rem 0.7rem;
  border: 1px solid rgba(201, 169, 98, 0.45);
  background: rgba(201, 169, 98, 0.06);
}
.patch-card strong {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  color: #d0bc7b;
  margin-bottom: 0.35rem;
}
.patch-card p { margin: 0 0 0.4rem; font-size: 0.78rem; color: #c7c2b9; }
.patch-preview {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: #9f9a91;
}

/* Suggested prompts under the first AI message. */
.chat-prompts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0 0.85rem 0.6rem;
}

.chat-chip {
  border: 1px solid rgba(201, 169, 98, 0.35);
  background: transparent;
  color: #c7c2b9;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  padding: 0.35rem 0.6rem;
  cursor: pointer;
  min-height: 44px;
}
.chat-chip:hover { color: #f4f1ea; border-color: rgba(201, 169, 98, 0.6); }
.chat-chip:focus-visible { outline: 2px solid var(--accent-gold); outline-offset: 2px; }

/* Input row: inert until the final-project stage asks for a problem; the
   lesson demo's other stages leave it disabled. */
.chat-input-row {
  border-top: 1px solid var(--line);
  padding: 0.7rem 0.85rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.attach-chip {
  align-self: flex-start;
  border: 1px solid rgba(201, 169, 98, 0.4);
  background: rgba(201, 169, 98, 0.08);
  color: #d0bc7b;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  padding: 0.3rem 0.55rem;
}

.chat-input {
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(0, 0, 0, 0.35);
  color: #8f8a82;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  padding: 0.55rem 0.7rem;
}

.chat-input:disabled { cursor: default; opacity: 0.5; }

.chat-input:focus {
  border-color: rgba(201, 169, 98, 0.6);
  color: var(--text-primary);
  outline: none;
}

.chat-send {
  border: none;
  background: none;
  padding: 0;
  align-self: flex-end;
  color: #d0bc7b;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  cursor: pointer;
}

.chat-send:disabled { cursor: default; opacity: 0.5; }
.chat-send:focus-visible { outline: 2px solid var(--accent-gold); outline-offset: 2px; }

/* Status bar along the bottom of the frame. */
.ide-statusbar {
  border-top: 1px solid var(--line);
  padding: 0.4rem 0.75rem;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: #6f6a63;
}

.caret {
  display: inline-block;
  width: 0.55em;
  height: 1.05em;
  vertical-align: text-bottom;
  background: var(--accent-gold);
  animation: caret-blink 1.1s steps(1) infinite;
}
@keyframes caret-blink { 50% { opacity: 0; } }

/* The code column holds one file at a time, plus output beneath. */
#demo-code { min-height: 0; flex: 1; }

.state-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(22rem, 1.15fr);
  gap: clamp(2.5rem, 6vw, 5rem);
  align-items: start;
}

.state-links {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 1.25rem;
}

/* Teaching-session carousel. Scroll-snap carries the swipe; the arrows and
   dots drive the same scroll. Autoplay lives in app.js and pauses on hover,
   focus, and touch; reduced motion snaps the transitions but keeps the timer. */
.teaching-gallery { min-width: 0; }

.teaching-stage {
  position: relative;
  overflow: hidden;
}

.teaching-track {
  display: flex;
  height: 100%;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  margin: 0;
  padding: 0;
  list-style: none;
}

.teaching-track::-webkit-scrollbar { display: none; }

.teaching-slide {
  position: relative;
  overflow: hidden;
  flex: 0 0 78vw;
  max-width: 420px;
  /* Tall portrait shots stay readable: cap the slide height and let cover
     crop the overflow rather than growing the carousel past a screen. */
  max-height: min(72vh, 560px);
  min-width: 0;
  /* Every card is one uniform 4:3 frame; the photo is cover-cropped into it,
     so landscape and portrait shots all share the same shape. */
  aspect-ratio: 4 / 3;
  /* Center-snap so the active slide sits mid-stage and its neighbours peek
     out on both sides, like the desktop coverflow. */
  scroll-snap-align: center;
}

.teaching-slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Anchor the crop to the photo's bottom: with a centered crop the caption
     fade lands over a portrait photo's middle band instead of its true
     bottom edge, which reads as a gradient floating mid-picture. */
  object-position: center bottom;
  user-select: none;
}

.teaching-slide--people img { object-position: center 30%; }

.teaching-caption {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  margin: 0;
  padding: 1.7rem 1rem 0.75rem;
  /* Keep the caption readable without turning the lower half of the photo
     into a detached black band. */
  background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.3) 45%, rgba(0, 0, 0, 0.82) 82%, rgba(0, 0, 0, 0.96) 100%);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.05em;
  color: rgba(244, 241, 234, 0.92);
  pointer-events: none;
}

.carousel-arrow {
  position: absolute;
  top: 50%;
  z-index: 2;
  transform: translateY(-50%);
  width: 2.6rem;
  height: 2.6rem;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(0, 0, 0, 0.55);
  color: var(--text-primary);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}

.carousel-arrow:hover { border-color: rgba(201, 169, 98, 0.6); background: rgba(0, 0, 0, 0.8); }
.carousel-arrow:focus-visible { outline: 2px solid var(--accent-gold); outline-offset: 2px; }
.carousel-arrow--prev { left: 0.75rem; }
.carousel-arrow--next { right: 0.75rem; }

.carousel-dots {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.85rem 0 0;
  padding: 0;
  list-style: none;
}

.carousel-dot {
  width: 0.55rem;
  height: 0.55rem;
  padding: 0;
  border: 1px solid rgba(244, 241, 234, 0.35);
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
}

.carousel-dot.is-active { background: var(--accent-gold); border-color: var(--accent-gold); }

/* The slide that takes the focus pops once — a short brighten/settle so each
   image animates as it becomes the active one. */
@keyframes coverflow-active-pop {
  from { filter: brightness(1.3) saturate(1.2); }
  to { filter: brightness(1) saturate(1); }
}

html.js .teaching-slide.is-active { animation: coverflow-active-pop 0.55s ease-out; }

/* Desktop coverflow: the active slide is front and centre, its neighbours
   peek out at the edges, and anything further out recedes to nearly
   invisible. app.js sets each slide's transform/opacity/z-index (and the
   data-offset it keys off); this block stages the space, masks the peeked
   previews into the page background, and hides captions on non-active
   slides. Gated on html.js so no-JS keeps the scroll-snap track above;
   below 901px the track is the whole carousel. */
@media (min-width: 901px) {
  html.js .teaching-stage {
    overflow: visible;
    perspective: 1100px;
    /* Bigger stage so the "What exists now" section shows more of each
       teaching session. */
    height: clamp(300px, 52vh, 520px);
  }

  html.js .teaching-track {
    position: relative;
    display: block;
    overflow: visible;
    scroll-snap-type: none;
    /* The previews extend past the stage; fade them into the page background
       at the gallery edges instead of cutting them off against the copy. */
    mask-image: linear-gradient(90deg, transparent 0, #000 16%, #000 84%, transparent 100%);
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 16%, #000 84%, transparent 100%);
  }

  html.js .teaching-slide {
    position: absolute;
    /* Centred on the stage: left: 50% with the JS transform adding the
       -50% recentre. Every card is one uniform 4:3 frame and the photo is
       cover-cropped into it, so no letterboxing or squashing — the crop is
       a CSS constant, not per-slide. */
    top: 0;
    bottom: 0;
    left: 50%;
    height: 100%;
    aspect-ratio: 4 / 3;
    max-width: 100%;
    flex: none;
    transition: transform 0.65s cubic-bezier(0.22, 0.61, 0.36, 1), opacity 0.65s ease;
    will-change: transform;
  }

  html.js .teaching-slide.is-clickable { cursor: pointer; }

  html.js .teaching-slide .teaching-caption { transition: opacity 0.25s ease; }
  html.js .teaching-slide:not([data-offset="0"]) .teaching-caption { opacity: 0; }
}

.portfolio-link {
  display: inline-flex;
  color: var(--accent-gold);
  text-decoration: none;
  border-bottom: 1px solid rgba(201, 169, 98, 0.35);
  padding-bottom: 0.1rem;
}

.portfolio-link:hover { color: #f4f1ea; border-color: rgba(244, 241, 234, 0.55); }

.waitlist-section {
  --cols: 121;
  --rows: 43;
  position: relative;
  box-sizing: border-box;
  max-width: var(--max);
  width: 100%;
  margin: 0 auto;
  /* Copy left, the great wave art right — the wave column gets 1.2x the
     copy's share so the art can render large. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  padding: clamp(4rem, 9vh, 7rem) var(--page-pad);
  display: grid;
  overflow: hidden;
  /* No divider between the bloom section and the wave. */
  border-top: none;
  /* The section must be tall enough to hold the whole art; the vertical fade
     hides any crop on small screens. */
  min-height: calc(30vw + clamp(8rem, 18vh, 14rem));
}

@keyframes waitlist-fade {
  from { opacity: 0.85; }
  to { opacity: 1; }
}

.waitlist-main {
  padding: 0;
  display: grid;
  justify-items: start;
  align-self: center;
  text-align: left;
}

.waitlist-main h2 { max-width: none; }

.waitlist-copy { max-width: 36rem; }

.waitlist {
  display: grid;
  gap: 0.45rem;
  margin-top: 1.25rem;
  max-width: 28rem;
  width: 100%;
}

.waitlist label {
  font-size: 0.82rem;
  color: #c7c2b9;
}

.waitlist-row { display: flex; gap: 0.65rem; justify-content: center; }

.waitlist input {
  flex: 1;
  min-width: 0;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 2px;
  padding: 0.75rem 0.95rem;
  font-family: var(--font-mono);
  font-size: 0.88rem;
  color: var(--text-primary);
  outline: none;
}

.waitlist input:focus { border-color: rgba(201, 169, 98, 0.7); }

.waitlist button {
  border: none;
  border-radius: 2px;
  background: #f4f1ea;
  color: #111;
  padding: 0.75rem 1.15rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}

.privacy {
  margin-top: 0.85rem;
  font-size: 0.88rem;
  color: rgba(244, 241, 234, 0.48);
}

.form-status {
  margin-top: 0.75rem;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: rgba(255, 255, 255, 0.55);
  min-height: 1.3em;
}

.form-status.ok { color: var(--accent-gold); }
.form-status.err { color: #f87171; }

/* The great wave: a full-strength art column beside the waitlist copy. The
   section grows to hold the art's height so the whole wave (121x43) shows —
   never cropped, no scrollbars, no scaleX squashing. */
.waitlist-wave {
  position: relative;
  /* Query container for the wave's own size (used by the font-size calc). */
  container-type: size;
  min-width: 0;
  width: 100%;
  /* Center the art both ways when the column is taller than the wave. */
  display: flex;
  align-items: center;
  justify-content: center;
  /* Fade the wave's top and bottom edges so it blends into the section
     background instead of ending in a hard line; the sides stay crisp. */
  overflow: hidden;
  mask-image:
    linear-gradient(180deg, transparent 0, #000 8%, #000 92%, transparent 100%);
  -webkit-mask-image:
    linear-gradient(180deg, transparent 0, #000 8%, #000 92%, transparent 100%);
  /* The art is the column's subject, not a dim backdrop: it sits beside the
     form at full strength and breathes softly. */
  opacity: 0.9;
  animation: waitlist-fade 7s ease-in-out infinite alternate;
  will-change: opacity;
}

.waitlist-wave-art {
  display: block;
  padding: 0;
  font-family: var(--font-mono);
  /* Keep the art large: fill ~96% of the column width at the real ~0.6em mono
     advance. The +0.08 counts the letter-spacing below; the 1.25 stretch also
     keeps the no-JS braille (~0.74em advance) inside the column. */
  font-size: max(
    4px,
    min(
      calc(100cqw / (var(--cols) * (var(--ascii-advance) + 0.08)) * 1.25),
      calc(100cqh / (var(--rows) * 1.3)),
      16px
    )
  );
  line-height: 1.3;
  white-space: pre;
  color: rgba(210, 202, 188, 0.72);
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  /* Horizontal-only spread between bits: glyphs keep full height. Keep in
     sync with the +0.08 in the font-size calc and the 480px override below. */
  letter-spacing: 0.08em;
}

/* The wave animates as bits: filled cells render a 1/0 glyph, empty cells stay
   blank. Bits flip on a timer and flash white — a cold glow that settles back
   to the warm rest color — with per-glyph halos, so the shimmer reads as lit
   digits. */
.bit {
  --bit-rest: rgba(220, 213, 200, 0.9);
  color: var(--bit-rest);
  /* Faint halo so the wave reads as lit glyphs even at rest. */
  text-shadow: 0 0 0.08em currentColor;
}

.bit--pulse {
  animation: bit-pulse 0.65s ease-out;
}

@keyframes bit-pulse {
  0% { color: #fff; text-shadow: 0 0 1.2em currentColor, 0 0 0.45em currentColor, 0 0 0.12em currentColor; }
  40% { color: hsl(0, 0%, 88%); text-shadow: 0 0 0.6em currentColor, 0 0 0.2em currentColor; }
  100% { color: var(--bit-rest); text-shadow: 0 0 0.08em currentColor; }
}

/* Match the site's motion convention (app.js): reduce, don't freeze. */
@media (prefers-reduced-motion: reduce) {
  .waitlist-wave { animation-duration: 30s; }
  /* Bits still flip, but the flash is a quick soft glow, not a bright pulse. */
  .bit--pulse { animation: bit-pulse-quiet 0.4s ease-out; }
  /* Chat typing indicator holds still; the script's pacing handles the rest. */
  .chat-typing span { animation: none; opacity: 0.5; }
  /* Coverflow swaps instantly instead of flying. */
  html.js .teaching-slide { transition: none; }
  html.js .teaching-slide.is-active { animation: none; }
}

@keyframes bit-pulse-quiet {
  0% { color: #fff; text-shadow: 0 0 0.4em currentColor; }
  100% { color: var(--bit-rest); text-shadow: 0 0 0.08em currentColor; }
}

.bloom-grid {
  container-type: inline-size;
  max-width: var(--max);
  width: 100%;
  margin: 0 auto;
  display: grid;
  /* Bloom left (larger), copy right — both tracks bounded so ASCII can't blow width. */
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}

.bloom-copy {
  min-width: 0;
  max-width: 34rem;
}

.bloom-copy h2 {
  font-size: var(--type-display);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.03em;
  margin-bottom: clamp(0.85rem, 2vh, 1.35rem);
  max-width: 18ch;
}

.bloom-copy p {
  font-size: var(--type-body);
  color: var(--text-muted);
  line-height: 1.55;
  max-width: 36rem;
  margin: 0;
}

.bloom-wrap {
  container-type: inline-size;
  min-width: 0;
  justify-self: start;
  width: 100%;
}

/* Closing bloom: flower frames are 90 cols. Blue glow matches the hero bird;
   sized to fill its column so the section reads as its own full screen. */
.bloom-art {
  --cols: 90;
  font-family: var(--font-mono);
  font-size: clamp(4.5px, 1.05vw, 11px);
  /* Fill the bloom column (≈ half the bloom grid after gap). */
  font-size: min(11px, calc(100cqw / (var(--cols) * var(--ascii-advance))));
  font-weight: 700;
  line-height: 0.96;
  white-space: pre;
  display: inline-block;
  color: hsl(200, 95%, 56%);
  text-shadow: 0 0 0.24em currentColor;
  margin: 0;
}

.site-footer {
  position: relative;
  margin-top: 0;
  min-height: clamp(640px, 88vh, 1080px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
  background: #000000;
  padding: clamp(3.6rem, 7vw, 5.6rem) var(--page-pad) clamp(0.5rem, 2vw, 1.1rem);
}

.site-footer__media {
  position: absolute;
  inset: 0;
}

.site-footer__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.35) 55%, rgba(0, 0, 0, 0.78) 100%);
  pointer-events: none;
}

.site-footer__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  transform: scale(1.15);
  transform-origin: top center;
  filter: saturate(110%);
  pointer-events: none;
  user-select: none;
}

.no-save,
.no-save * {
  -webkit-user-drag: none;
  user-select: none;
  pointer-events: none;
}

.site-footer__overlay {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--max);
  padding: clamp(4rem, 11vw, 6rem) clamp(1.5rem, 4vw, 3.5rem) clamp(1.6rem, 5vw, 2.6rem);
  color: rgba(255, 255, 255, 0.87);
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0.78));
  display: grid;
  gap: 1.6rem;
  text-shadow: 0 16px 36px rgba(0, 0, 0, 0.45);
}

.site-footer__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: clamp(1.2rem, 3vw, 2rem);
  margin-top: clamp(1rem, 2vw, 1.5rem);
}

.site-footer__grid--secondary {
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  margin-top: clamp(1.5rem, 3vw, 2rem);
}

.site-footer__column {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.site-footer__column-title {
  margin: 0 0 0.25rem;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.32em;
  font-weight: 600;
  color: #fff;
  font-family: var(--font-mono);
}

.site-footer__column-stack {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.site-footer__column-link {
  display: inline-flex;
  width: fit-content;
  color: rgba(255, 255, 255, 0.65);
  font-size: 0.95rem;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 0.1rem;
  transition: border-color 0.2s ease, color 0.2s ease;
}

.site-footer__column-link:hover,
.site-footer__column-link:focus-visible {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.4);
  outline: none;
}

.site-footer__column-note {
  color: rgba(255, 255, 255, 0.55);
  font-size: 0.95rem;
}

.site-footer__social-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
  margin-top: 0.5rem;
}

.site-footer__social-links {
  display: inline-flex;
  gap: 1rem;
  flex-wrap: wrap;
  align-items: center;
}

.site-footer__social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.85);
  padding: 0.2rem;
  transition: transform 0.2s ease, color 0.2s ease;
}

.site-footer__social-link:hover,
.site-footer__social-link:focus-visible {
  transform: translateY(-2px);
  color: #fff;
  outline: none;
}

.site-footer__social-icon {
  width: 22px;
  height: 22px;
}

.site-footer__meta {
  margin: 0;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-size: 0.7rem;
  color: rgba(255, 255, 255, 0.6);
  font-weight: 600;
  font-family: var(--font-mono);
}

.noscript-note {
  padding: 1rem var(--page-pad);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: #9f9a91;
}

@media (max-width: 900px) {
  .hero {
    height: auto;
    min-height: calc(100dvh - 4.05rem);
    padding: clamp(4.25rem, 6.5vh, 5.5rem) var(--page-pad);
  }
  .hero-grid,
  .state-grid,
  .bloom-grid {
    grid-template-columns: 1fr;
  }
  /* The IDE stacks: rail, then Code / Output / AI as switchable views. */
  .ide-body {
    display: flex;
    flex-direction: column;
    height: auto;
    min-height: 0;
  }
  .ide-mobile-tabs {
    display: flex;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--line);
    font-family: var(--font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.1em;
  }
  .ide-rail {
    border-right: 0;
    border-bottom: 1px solid var(--line);
    /* Locked like the workspace and chat below, so the stacked frame stays
       one height no matter which stage's rail pane is showing. */
    height: 28rem;
    min-height: 0;
  }
  .ide-chat {
    border-left: 0;
    height: 26rem;
  }
  /* The longest demo status text wraps to two lines at phone width; reserving
     the space keeps the frame one constant height through the whole demo. */
  .ide-statusbar {
    min-height: 3rem;
  }
  /* The stacked workspace gets a locked height too: the demo appends output
     rows and diff actions as it runs, and without a bound they would grow
     the workspace and push the section taller. */
  .ide-workspace {
    height: 28rem;
    min-height: 0;
  }
  .ide-body[data-mobile-view="output"] .code-render,
  .ide-body[data-mobile-view="output"] .workspace-tabs,
  .ide-body[data-mobile-view="code"] #demo-output,
  .ide-body[data-mobile-view="code"] .ide-chat,
  .ide-body[data-mobile-view="output"] .ide-chat,
  .ide-body[data-mobile-view="chat"] .ide-workspace { display: none; }
  .ide-body[data-mobile-view="chat"] .ide-chat { display: flex; }
  .section-bloom {
    height: auto;
    min-height: 100dvh;
    padding: clamp(4.25rem, 8vh, 5.5rem) var(--page-pad) clamp(2.5rem, 5vh, 3.5rem);
  }
  .moire-section {
    height: auto;
    min-height: 100dvh;
    padding: clamp(4rem, 8vh, 5.5rem) var(--page-pad);
  }
  .moire-copy { max-width: 44rem; }
  .moire-title { max-width: 24ch; }
  .bird-column { justify-items: center; margin-left: 0; order: -1; }
  .bird { font-size: min(9px, calc(96cqw / (var(--cols) * var(--ascii-advance)))); }
  .nav-primary { display: none; }
  .nav-menu-btn { display: inline-flex; }
  .bloom-wrap { justify-self: center; }
  .bloom-copy { max-width: 44rem; }
  .bloom-art {
    font-size: min(9px, calc(100cqw / (var(--cols) * var(--ascii-advance))));
  }
  .lesson-painting img { opacity: 0.2; }
  /* Waitlist stacks: copy first, the wave below it — never above. */
  .waitlist-section { grid-template-columns: minmax(0, 1fr); }
  .waitlist-main { justify-items: center; text-align: center; order: 1; }
  .waitlist-wave { order: 2; min-height: 10rem; }
}

/* Below 480px the wave column is ~320px wide and the 4px font-size floor
   binds: the letter-spacing would push the art past the edge, so drop it.
   Desktop/tablet values above stay untouched. */
@media (max-width: 480px) {
  .waitlist-wave-art { letter-spacing: 0; }
}

@media (max-width: 768px) {
  .site-footer { min-height: 520px; }
  .site-footer__overlay {
    gap: 1.2rem;
    text-align: left;
    padding: clamp(3.2rem, 12vw, 4.6rem) clamp(1.2rem, 6vw, 3rem) clamp(2rem, 7vw, 3rem);
  }
  .site-footer__grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
  .site-footer__social-row { flex-direction: column; align-items: flex-start; }
  .site-footer__social-links { gap: 0.75rem; }
}

@media (max-height: 720px) and (min-width: 769px) {
  .site-footer {
    min-height: clamp(460px, 78vh, 620px);
    align-items: flex-start;
  }
  .site-footer__overlay {
    padding: clamp(4rem, 10vh, 5rem) clamp(1.5rem, 5vw, 3.2rem) clamp(3rem, 8vh, 4rem);
    gap: 1.2rem;
  }
}

@media (max-width: 560px) {
  .waitlist-row { flex-direction: column; }
  .waitlist button { width: 100%; }
  .code-render { min-height: 12rem; }
  .moire-section { padding: clamp(3.75rem, 7vh, 4.5rem) var(--page-pad); }
  .moire-title { font-size: clamp(2rem, 9vw, 3.1rem); }
  .moire-body { font-size: 0.95rem; line-height: 1.55; }
}

@media (max-height: 720px) {
  .hero,
  .moire-section {
    height: auto;
    min-height: calc(100dvh - 4.05rem);
  }
}
