/* ===== [1] TOKENS ===== */
/* Theme A — the briefing: ink on paper. Theme B — the child's world: chalk on swept
   earth at dawn, the kolam's own inversion of the document. The seal-red is reserved
   for the Kural thread alone, the way Kural numbers are printed red in Tamil editions. */
:root {
  --paper: #FBFAF6;
  --ink: #191A1F;
  --ink-soft: #5A5B60;
  --rule: #DAD7CD;
  --seal: #A3271C;

  --earth: #241C16;
  --earth-warm: #33261A;
  --dawn: #232838;
  --chalk: #F4EDDD;
  --chalk-soft: #C9C0AC;
  --turmeric: #E0A33C;
  --lamp: #FFB566;

  --ta-display: 'Anek Tamil', 'Hind Madurai', sans-serif;
  --body: 'Hind Madurai', 'Anek Tamil', sans-serif;

  --wrap: 34rem;
  --pad: clamp(1.35rem, 5vw, 2rem);
}

/* ===== [2] BASE ===== */
* { margin: 0; padding: 0; box-sizing: border-box; }
/* [1] overflow-x lives on <html>, NOT on body. Putting `overflow-x: hidden` on body
   forces its overflow-y to `auto`, which silently turns body into a scroll container —
   two nested scrollers, one of which the browser is not driving. On a phone that is how
   you get "swiping does nothing". The document scroller must stay the document. */
html {
  scroll-behavior: auto;
  overflow-x: hidden;
}
body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: 1.05rem;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
::selection { background: var(--ink); color: var(--paper); }
a { color: inherit; text-decoration-color: var(--rule); text-underline-offset: 3px; }
a:focus-visible, button:focus-visible { outline: 2px solid var(--seal); outline-offset: 3px; }
[lang="ta"] { font-family: var(--ta-display); }
img { max-width: 100%; height: auto; display: block; }

.wrap { max-width: var(--wrap); margin-inline: auto; padding-inline: var(--pad); width: 100%; }
.wrap--center { text-align: center; }

/* ===== [3] MASTHEAD ===== */
.masthead { padding: 1.1rem var(--pad) 0; text-align: center; }
.masthead__line {
  font-family: var(--ta-display);
  font-size: 0.92rem;
  font-weight: 560;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
}
.masthead__en { font-weight: 420; }

/* ===== [4] SCREENS — THE SCROLL CONTRACT ===== */
/* [1] NO PINS. NO SCROLL RUNWAYS. A screen is an ordinary section, one viewport tall,
   with its content composed inside it. The reader swipes; the next screen arrives whole
   and then animates itself on its own clock (see js/main.js). Nothing is ever gated
   behind a scroll position, so a single swipe can never land him "halfway" through a
   screen's content. This replaces the sticky+scrubbed architecture, which is what made
   one screen cost several swipes. */
.scene { position: relative; }
.scene__screen {
  /* [1] `vh` first as the fallback for browsers without the small-viewport unit; `svh`
     is deliberately the SMALL viewport (address bar showing), so the box does NOT
     resize when a phone's URL bar collapses. Nothing in this page measures geometry,
     so a viewport change is now cosmetic rather than fatal. */
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: clamp(2rem, 6vh, 3.5rem);
}
/* [2] Screens whose content is honestly taller than a phone viewport — a five-year
   ladder, the threshold, the ask. They read as documents: top-aligned, free-flowing,
   never centred (which would hang content off both edges). */
.scene__screen--tall {
  justify-content: flex-start;
  padding-block: clamp(2.6rem, 8vh, 4.5rem);
}

/* [3] Crossing into the child's world: the page darkens like the hour before light.
   The band at each edge is the threshold — deliberate, legible, felt. */
.scene--earth {
  background:
    linear-gradient(180deg, var(--dawn) 0%, var(--earth) 9%, var(--earth) 91%, var(--dawn) 100%);
  color: var(--chalk);
}
.scene--earth ::selection { background: var(--chalk); color: var(--earth); }

/* ===== [5] ONE SWIPE, ONE SCREEN ===== */
/* [1] NO SCROLL SNAPPING. It was tried and it is gone: on a real phone it fought the
   scroller and swiping did nothing. The one-swipe-one-screen contract is kept by the
   only means that cannot backfire — every screen is exactly one viewport tall and its
   content FITS inside that viewport (see [20]). The reader's own thumb does the rest,
   and the page never takes the scroll away from him. */

/* ===== [6] EYEBROWS, RULES ===== */
.eyebrow {
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  border-top: 1px solid var(--rule);
  padding-top: 0.85rem;
  margin-bottom: clamp(1.4rem, 4vh, 2.2rem);
}
.eyebrow [lang="ta"] { letter-spacing: 0.02em; font-size: 0.9rem; font-weight: 500; }
.eyebrow__sep { color: var(--rule); padding-inline: 0.35em; }
.eyebrow--earth { color: var(--chalk-soft); border-top-color: rgba(244, 237, 221, 0.22); margin-bottom: clamp(1.1rem, 3.5vh, 1.8rem); }

/* ===== [7] S1 — அ / A. The first screen: two letters, nothing to argue with. ===== */
.twin { display: flex; align-items: center; justify-content: center; gap: clamp(0.8rem, 4vw, 1.4rem); }
.twin__letter {
  font-family: var(--ta-display);
  font-size: clamp(5.2rem, 28vw, 8.5rem);
  font-weight: 620;
  line-height: 1;
}
.twin__link { width: clamp(2.6rem, 10vw, 3.8rem); color: var(--ink-soft); flex: none; }
.twin__caption { margin-top: 1.6rem; font-size: 0.95rem; color: var(--ink-soft); }
.twin__caption [lang="ta"] { display: block; color: var(--ink); font-weight: 560; font-size: 1.05rem; }
.twin__same { margin-top: 1.4rem; font-family: var(--ta-display); font-size: 1.18rem; font-weight: 600; }
.twin__same-en { display: block; font-family: var(--body); font-weight: 400; color: var(--ink-soft); font-size: 0.95rem; margin-top: 0.25rem; }
.twin__years { margin-top: 1.2rem; font-family: var(--ta-display); font-size: 1.05rem; font-weight: 520; }

/* ===== [8] S2 — THE TWO TEACHERS ===== */
.clock {
  font-family: var(--ta-display);
  font-size: clamp(1.5rem, 7vw, 2.1rem);
  font-weight: 620;
  line-height: 1.6;
  margin-bottom: 1.2rem;
}
.claim { margin-top: clamp(1.6rem, 5vh, 3rem); border-left: 2px solid var(--ink); padding-left: 1.1rem; }
.claim [lang="ta"] { font-family: var(--ta-display); font-size: 1.22rem; font-weight: 600; line-height: 1.85; }
.claim__en { color: var(--ink-soft); font-size: 0.98rem; margin-top: 0.6rem; }

/* ===== [9] STATEMENTS — the load-bearing lines ===== */
.statement { margin-top: clamp(1.2rem, 3.5vh, 2rem); }
.statement [lang="ta"] {
  font-size: 1.13rem;
  font-weight: 560;
  line-height: 1.85;
}
.statement__en { color: var(--ink-soft); font-size: 0.97rem; margin-top: 0.55rem; }
.statement--tight { margin-top: 1rem; }
.statement--earth { margin-top: 1rem; }
.statement--earth [lang="ta"] { color: var(--chalk); font-size: 1.02rem; line-height: 1.7; }
.statement--earth .statement__en { color: var(--chalk-soft); font-size: 0.92rem; }
.statement--after { padding-block: clamp(2rem, 7vh, 3.5rem) 0; }

/* ===== [10] THE KURAL THREAD — three beats (S3 opens, S4 teaches, S8 resolves).
   Seal-red is the thread's colour and nothing else on the page is red. ===== */
.kural {
  font-family: var(--ta-display);
  color: var(--seal);
  line-height: 2;
}
.kural--open { font-size: clamp(1.2rem, 5vw, 1.45rem); font-weight: 560; }
.kural--resolved {
  font-size: clamp(1.05rem, 4.4vw, 1.45rem);
  font-weight: 560;
  text-align: center;
  margin-top: clamp(2rem, 6vh, 3.2rem);
}
/* [1] The underline is a pseudo-element sitting at its full width by default, so its
   "drawn" state needs no script. The animation (below) only replays it from zero. */
.kural__word { font-weight: 720; position: relative; }
.kural__word::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: -2px;
  height: 2px; background: currentColor;
  transform: scaleX(1);
  transform-origin: left center;
}

.meanings { margin-top: clamp(1.4rem, 4vh, 2.2rem); border-top: 1px solid var(--rule); padding-top: 1.4rem; }
.meanings__ta { font-family: var(--ta-display); font-weight: 540; font-size: 1.06rem; line-height: 1.95; }
.meanings__en { color: var(--ink-soft); font-size: 0.97rem; margin-top: 0.9rem; }
.attrib { margin-top: 1.5rem; font-size: 0.82rem; color: var(--ink-soft); }
.attrib [lang="ta"] { display: block; font-size: 0.88rem; }
.attrib__en { display: block; margin-top: 0.15rem; }

/* ===== [11] S4 — THE LESSON (Theme B) ===== */
.kolam { width: min(38vw, 165px); margin: 0 auto; color: var(--chalk); display: block; }
.kolam__dots circle { fill: var(--chalk); }
.lesson { margin-top: clamp(0.6rem, 2vh, 1rem); }
/* [1] Her first lesson was never in a school — it was at the doorway, at dawn. */
.lesson__open { margin-bottom: 0.8rem; }
.lesson__open [lang="ta"] { display: block; font-family: var(--ta-display); font-size: 1.12rem; font-weight: 600; color: var(--chalk); line-height: 1.5; }
.lesson__open-en { display: block; font-size: 0.82rem; line-height: 1.45; color: var(--chalk-soft); margin-top: 0.3rem; }
/* [2] The positive fact — the verb Tamil actually uses. Not a denial of one it doesn't. */
.lesson__note { margin-top: 0.9rem; font-size: 0.86rem; color: var(--chalk-soft); }
.lesson__note [lang="ta"] { display: block; font-size: 1.1rem; font-weight: 600; color: var(--chalk); }
.lesson__note-en { display: block; margin-top: 0.1rem; }
/* [3] THE TURN — the couplet closes S4. The sentence turns on itself, and the turn IS
   the idea. Give it a rule above and air around it: it is the screen's last word. */
.turn {
  margin-top: clamp(0.9rem, 3vh, 1.4rem);
  padding-top: clamp(0.8rem, 2.5vh, 1.2rem);
  border-top: 1px solid rgba(244, 237, 221, 0.28);
}
.turn__ta {
  font-family: var(--ta-display);
  font-size: 1.06rem;
  font-weight: 620;
  line-height: 1.7;
  color: var(--chalk);
}
.turn__en { font-size: 0.84rem; line-height: 1.5; color: var(--chalk-soft); margin-top: 0.4rem; }
.word {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  gap: 0.35rem 0.9rem;
  margin-top: clamp(0.9rem, 3vh, 1.4rem);
}
.word__ta { font-family: var(--ta-display); font-size: clamp(2.2rem, 11vw, 3rem); font-weight: 640; color: var(--chalk); line-height: 1.15; }
.word__sound { font-size: 0.95rem; color: var(--turmeric); letter-spacing: 0.04em; }
.word__link { width: 2.6rem; color: var(--turmeric); flex: none; }
.word__en { font-size: 1.12rem; font-weight: 600; color: var(--chalk); }

/* ===== [12] S5 — THE LADDER ===== */
.ladder { list-style: none; position: relative; padding-left: 2.6rem; }
.ladder::before {
  content: '';
  position: absolute; left: 0.78rem; top: 0.4rem; bottom: 0.4rem;
  width: 2px; background: var(--ink);
  transform-origin: top center;
  /* [1] Driven by GSAP via --spine-scale on .ladder; falls back to fully drawn without JS */
  transform: scaleY(var(--spine-scale, 1));
}
.rung { position: relative; padding-bottom: clamp(1.3rem, 4vh, 2rem); }
.rung:last-child { padding-bottom: 0; }
.rung__grade {
  position: absolute; left: -2.6rem; top: 0.05rem;
  width: 1.7rem; height: 1.7rem;
  display: grid; place-items: center;
  background: var(--paper);
  border: 1.5px solid var(--ink);
  border-radius: 50%;
  font-family: var(--ta-display);
  font-weight: 640;
  font-size: 0.9rem;
}
.rung__body [lang="ta"] { font-weight: 560; line-height: 1.8; font-size: 1.03rem; }
.rung__en { color: var(--ink-soft); font-size: 0.92rem; margin-top: 0.25rem; line-height: 1.6; }
.rung--goal .rung__grade { background: var(--ink); color: var(--paper); }
.rung--goal .rung__body [lang="ta"] { font-weight: 620; }

/* ===== [12b] THE TREE — the child at the end. Theme A: sober, restrained, his desk. ===== */
.tree__head { margin-bottom: clamp(0.8rem, 2.5vh, 1.3rem); }
.tree__head [lang="ta"] {
  display: block;
  font-family: var(--ta-display);
  font-size: clamp(1.3rem, 6vw, 1.7rem);
  font-weight: 640;
  line-height: 1.4;
}
.tree__head-en { display: block; font-size: 1rem; color: var(--ink-soft); margin-top: 0.1rem; }

/* [1] The tree stands BESIDE the text — not behind it, and nothing is labelled on it.
   [2] `stretch` lets it stand the FULL height of the text column, instead of floating as
   a short motif in the middle of a tall block of text. With the tall viewBox, the drawing
   is bound by the column's HEIGHT and simply grows to match it. The tree therefore adds
   no height of its own to the screen — the list still sets the row, so the one-viewport
   rule is untouched.
   [3] COMPOSITION: one hairline runs the FULL height between the tree and the list — the
   same rule device the page already uses at .claim and .prose--anchor. Previously each
   bullet carried its own little border, which broke that line into seven stubs and made
   the two halves read as two things placed next to each other. One continuous rule binds
   them into a single composed unit. */
.tree__grid {
  display: grid;
  grid-template-columns: minmax(104px, 36%) 1fr;
  gap: clamp(0.9rem, 4vw, 1.5rem);
  align-items: stretch;
}
.tree__svg { width: 100%; height: 100%; display: block; }

.tree__list {
  list-style: disc;
  border-left: 1px solid var(--rule);
  padding-left: 1.75rem;
  margin-left: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;   /* the bullets breathe evenly against the tree's height */
}
.tree__list li {
  font-size: 0.88rem;
  line-height: 1.4;
  color: var(--ink-soft);
  padding: 0.1rem 0;
}
.tree__list li::marker { color: var(--rule); font-size: 0.85em; }
/* [4] Everything above it is skill. THIS one is what the state wants and cannot buy — so
   it carries more weight. It is still a bullet in the same list. */
.tree__pride { color: var(--ink); font-weight: 600; }
.tree__pride::marker { color: var(--ink); }

/* ===== [13] S6 — PROSE ===== */
.prose { margin-bottom: 1.4rem; }
.prose [lang="ta"] { font-weight: 540; }
.prose--anchor {
  border-left: 2px solid var(--ink);
  padding-left: 1.1rem;
  font-size: 0.98rem;
}

/* ===== [14] S7 — THE THRESHOLD (3D scene owned by js/kolam3d.js) ===== */
.scene--threshold .scene__screen { padding-block: clamp(2rem, 6vh, 3.4rem); }
.kicker { text-align: center; margin-bottom: 1.2rem; padding-inline: var(--pad); }
.kicker [lang="ta"] { display: block; font-family: var(--ta-display); font-size: 1.12rem; font-weight: 560; color: var(--chalk); }
.kicker__en { font-size: 0.88rem; color: var(--chalk-soft); }
.threshold {
  position: relative;
  width: min(100%, 640px);
  margin-inline: auto;
  aspect-ratio: 4 / 5;
  max-height: 66svh;
  overflow: hidden;
  touch-action: manipulation;
}
@media (min-width: 700px) {
  .threshold { aspect-ratio: 4 / 3; max-height: 60svh; }
}
.threshold__still { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.threshold--live .threshold__still { display: none; }
.threshold canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; z-index: 1; }
.threshold__labels { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
/* [1] The taught word: it ARRIVES — Tamil first, spoken — and the English lands onto
   the same block. A lesson happening, not a tooltip firing.

   [2] TAMIL AND ENGLISH ARE THE SAME SIZE AND THE SAME WEIGHT. This is not typography,
   it is the argument: the whole claim is that the two languages are equal and grow on
   the same object at the same moment. A large Tamil word with a small English caption
   under it would say the opposite — Tamil is the word, English is the gloss. Both are
   the word. (600 is the one weight both families carry, so they can be truly equal.) */
.teach {
  position: absolute;
  transform: translate(-50%, -100%);
  text-align: center;
  line-height: 1.2;
  text-shadow: 0 2px 16px rgba(12, 9, 6, 0.9);
  white-space: nowrap;
  pointer-events: none;
}
.teach__ta, .teach__en {
  display: block;
  font-size: clamp(1.85rem, 8.4vw, 2.4rem);
  font-weight: 600;
  color: var(--chalk);
}
.teach__ta { font-family: var(--ta-display); }
.teach__en { font-family: var(--body); }
/* [3] The transliteration is a WHISPER — not a third line. The Tamil official's eye
   skips it entirely; the colleague he forwards this to, who cannot read the script,
   finds it exactly when he needs it. Without it, that reader learns nothing: he sees a
   shape and a translation, and cannot say the word. */
.teach__sound {
  display: block;
  font-family: var(--body);
  font-size: 0.62rem;
  font-weight: 400;
  letter-spacing: 0.16em;
  color: rgba(244, 237, 221, 0.42);
  margin: 0.18rem 0 0.14rem;
}
.threshold__words {
  text-align: center;
  margin-top: 1.1rem;
  font-size: 0.95rem;
  color: var(--chalk-soft);
}
.threshold__words [lang="ta"] { font-weight: 600; color: var(--chalk); font-size: 1.05rem; }

/* [1] THE END CARD — the punch line lands inside the scene, over the finished kolam.
   The paragraph is MOVED here from below the scene by js/kolam3d.js; it is never
   duplicated and never hidden. Its default state here is fully visible — the
   `is-entering` class only plays it in, and its keyframe ends where it already sits.
   If that class never lands, he sees the line. If the scene never runs, the paragraph
   never moves and he reads it under the frame, as plain text. */
.endcard {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 0.9rem 1rem 0.85rem;
  text-align: center;
  pointer-events: none;
  /* [2] The scrim only owns the bottom of the frame. He must still SEE the kolam he
     just wrote — burying it under the line defeats the whole point of the screen. */
  background: linear-gradient(to top,
    rgba(16, 11, 7, 0.9) 0%, rgba(16, 11, 7, 0.78) 26%, rgba(16, 11, 7, 0.36) 46%, rgba(16, 11, 7, 0) 62%);
}
.endcard .statement--after { padding: 0; margin: 0; }
.endcard .statement--after [lang="ta"] { font-size: 0.88rem; font-weight: 560; line-height: 1.55; color: var(--chalk); }
.endcard .statement--after .statement__en { font-size: 0.78rem; line-height: 1.45; margin-top: 0.3rem; color: var(--chalk-soft); }
@media (prefers-reduced-motion: no-preference) {
  @keyframes tns-endcard { from { opacity: 0; transform: translateY(14px); } }
  .endcard.is-entering { animation: tns-endcard 1.4s cubic-bezier(.2,.7,.3,1) both; }
}

/* ===== [15] S8 — THE ASK. The presentation ends here. ===== */
.ask { font-family: var(--ta-display); font-size: clamp(1.6rem, 7.4vw, 2.2rem); font-weight: 640; line-height: 1.5; margin-bottom: 1.4rem; }
.ask__en { font-family: var(--body); font-size: 1rem; font-weight: 400; color: var(--ink-soft); display: block; margin-top: 0.3rem; line-height: 1.65; }
/* [1] CONTACT — the block is deliberately empty (see index.html). These rules stay so a
   name and number can be dropped in with markup alone, when there is one to give. */
.contact {
  margin-top: clamp(2rem, 6vh, 3rem);
  border-top: 1px solid var(--rule);
  padding-top: 1.3rem;
  font-family: var(--ta-display);
  font-weight: 600;
  font-size: 1.15rem;
  line-height: 1.9;
}
.contact a { font-family: var(--body); font-weight: 400; font-size: 0.98rem; }
.finale { text-align: center; margin-top: clamp(2rem, 6vh, 3.4rem); }
.finale [lang="ta"] { font-family: var(--ta-display); font-size: 1.22rem; font-weight: 620; line-height: 1.9; }
.finale__en { color: var(--ink-soft); font-size: 0.98rem; margin-top: 0.7rem; }

/* ===== [16] THE APPENDIX — evidence, not argument.
   The register break is the whole job: a heavy rule, the word itself, tighter and
   quieter type. He must see at a glance that the argument is over. ===== */
.appendix {
  position: relative;
  background: #F4F2EC;
  border-top: 3px solid var(--ink);
  padding-block: clamp(2.6rem, 8vh, 4rem) clamp(3rem, 9vh, 4.5rem);
}
.appendix__label {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  font-family: var(--ta-display);
  font-weight: 640;
  font-size: 1.35rem;
  letter-spacing: 0.02em;
}
.appendix__label-en {
  font-family: var(--body);
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.appendix__note {
  margin-top: 0.5rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
  max-width: 30rem;
}
.appendix__note [lang="ta"] { display: block; font-size: 0.95rem; font-weight: 540; color: var(--ink); }
.appendix__note-en { display: block; margin-top: 0.15rem; }
.ap-block { margin-top: clamp(2.2rem, 7vh, 3.2rem); }
.appendix .eyebrow { border-top-color: #C9C5B8; }
.appendix .statement [lang="ta"] { font-size: 1.02rem; line-height: 1.75; }
.appendix .statement__en { font-size: 0.92rem; }
.appendix .bar__track { background: #E6E3DA; }

/* ===== [17] FIGURES (appendix) ===== */
.figure { margin-bottom: clamp(1.3rem, 4vh, 2rem); }
.figure__label {
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--ink-soft);
  margin-bottom: 0.2rem;
}
.figure__label [lang="ta"] { display: block; font-weight: 560; color: var(--ink); font-size: 1.02rem; }
.figure__label-en { display: block; }
.figure__value {
  font-family: var(--ta-display);
  font-weight: 640;
  font-size: clamp(2.8rem, 15vw, 4.2rem);
  line-height: 1.05;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}
.figure__unit { font-size: 0.98rem; font-weight: 480; color: var(--ink-soft); letter-spacing: 0; }
.figure__dir { font-family: var(--body); padding-left: 0.1em; }
.figure__range {
  font-size: 0.85rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 0.9rem;
}

/* ===== [18] BARS (appendix) ===== */
.bars { margin-block: clamp(1rem, 3vh, 1.6rem) 0; }
.bar { display: grid; grid-template-columns: 6.4rem 1fr 3.2rem; align-items: center; gap: 0.8rem; margin-bottom: 1.15rem; }
.bar__label { font-size: 0.92rem; color: var(--ink-soft); line-height: 1.35; }
.bar__label [lang="ta"] { display: block; color: var(--ink); font-weight: 560; font-size: 1rem; }
.bar__track { background: #EFEDE5; height: 16px; overflow: hidden; }
.bar__fill { background: var(--ink); height: 100%; transform-origin: left center; }
.bar__fill[data-pct="76"] { width: 76%; }
.bar__fill[data-pct="51"] { width: 51%; }
.bar__fill--low { background: var(--ink-soft); }
.bar__pct { font-family: var(--ta-display); font-weight: 620; font-variant-numeric: tabular-nums; text-align: right; }

/* ===== [19] FOOTER ===== */
.foot { padding: 2.2rem var(--pad) calc(2.2rem + env(safe-area-inset-bottom)); text-align: center; }
.foot p { font-size: 0.78rem; color: var(--ink-soft); letter-spacing: 0.04em; }

/* ===== [20] FITTING EACH SCREEN TO ONE VIEWPORT ===== */
/* [1] The scroll contract is only kept if a screen's content actually FITS the screen.
   A screen that overflows by even a hundred pixels forces a second swipe to read its
   last line — which is the original complaint in miniature. These are the per-screen
   compressions that bring S5–S8 inside one phone viewport, measured on a 390×844 device.
   The appendix is exempt: it is reference material, read like a document. */

/* S4 — the lesson now carries the opening, the fact, the word AND the closing couplet.
   That is a lot of screen; every value below is what makes it sit in one viewport. */
#s4 .scene__screen { padding-block: clamp(1rem, 3vh, 1.8rem); }
#s4 .eyebrow { margin-bottom: 0.7rem; }
#s4 .kolam { width: min(31vw, 128px); }
#s4 .lesson { margin-top: 0.4rem; }
#s4 .lesson__open { margin-bottom: 0.55rem; }
#s4 .lesson__open [lang="ta"] { font-size: 1.02rem; line-height: 1.45; }
#s4 .lesson__open-en { font-size: 0.78rem; line-height: 1.4; margin-top: 0.22rem; }
#s4 .statement--earth { margin-top: 0.7rem; }
#s4 .statement--earth [lang="ta"] { font-size: 0.95rem; line-height: 1.55; }
#s4 .statement--earth .statement__en { font-size: 0.79rem; line-height: 1.4; margin-top: 0.3rem; }
#s4 .lesson__note { margin-top: 0.7rem; }
#s4 .lesson__note [lang="ta"] { font-size: 1.02rem; }
#s4 .lesson__note-en { font-size: 0.79rem; }
#s4 .word { margin-top: 0.55rem; gap: 0.25rem 0.7rem; }
#s4 .word__ta { font-size: clamp(1.65rem, 8vw, 2.1rem); }
#s4 .word__en { font-size: 0.98rem; }
#s4 .word__sound { font-size: 0.82rem; }
#s4 .turn { margin-top: 0.75rem; padding-top: 0.7rem; }
#s4 .turn__ta { font-size: 1rem; line-height: 1.6; }
#s4 .turn__en { font-size: 0.79rem; line-height: 1.42; margin-top: 0.28rem; }

/* S5 — the ladder: five years must sit on one screen */
#s5 .eyebrow { margin-bottom: 1rem; }
#s5 .rung { padding-bottom: 0.65rem; }
#s5 .rung__body [lang="ta"] { font-size: 0.98rem; line-height: 1.55; }
#s5 .rung__en { font-size: 0.87rem; line-height: 1.4; margin-top: 0.12rem; }
#s5 .rung__grade { width: 1.55rem; height: 1.55rem; font-size: 0.85rem; }
#s5 .ladder { padding-left: 2.4rem; }
#s5 .rung__grade { left: -2.4rem; }

/* S6 — where it sits */
#s6 .prose { margin-bottom: 1rem; font-size: 1rem; line-height: 1.68; }
#s6 .eyebrow { margin-bottom: 1.2rem; }

/* S7 — the threshold: the frame plus its line, on one screen */
#s7 .threshold { max-height: 52svh; }
#s7 .kicker { margin-bottom: 0.9rem; }
#s7 .threshold__words { margin-top: 0.9rem; }
#s7 .statement--after { padding-block: clamp(1.4rem, 4vh, 2.2rem) 0; }

/* S8 — the ask: the last line of the page must never fall below the fold */
#s8 .ask { font-size: clamp(1.45rem, 6.6vw, 1.95rem); margin-bottom: 1rem; }
#s8 .prose { font-size: 0.98rem; line-height: 1.65; margin-bottom: 0; }
#s8 .kural--resolved { margin-top: 1.7rem; }
#s8 .finale { margin-top: 1.6rem; }
#s8 .finale [lang="ta"] { font-size: 1.12rem; line-height: 1.75; }
#s8 .finale__en { font-size: 0.94rem; margin-top: 0.5rem; }

/* [2] Shorter phones (≈360×800 and below): the same screens, compressed harder, so the
   one-swipe contract survives the smaller viewport. */
@media (max-height: 830px) {
  .scene__screen { padding-block: clamp(1.4rem, 4vh, 2.4rem); }
  .scene__screen--tall { padding-block: clamp(1.6rem, 5vh, 3rem); }
  #s5 .eyebrow { margin-bottom: 0.7rem; }
  #s5 .rung { padding-bottom: 0.45rem; }
  #s5 .rung__body [lang="ta"] { font-size: 0.92rem; line-height: 1.45; }
  #s5 .rung__en { font-size: 0.81rem; line-height: 1.32; }
  #s5 .rung__grade { width: 1.4rem; height: 1.4rem; font-size: 0.8rem; }
  #s6 .prose { font-size: 0.94rem; line-height: 1.6; margin-bottom: 0.8rem; }
  #s6 .eyebrow { margin-bottom: 0.9rem; }
  #s7 .threshold { max-height: 48svh; }
  #s7 .kicker { margin-bottom: 0.7rem; }
  #s7 .threshold__words { margin-top: 0.7rem; }
  /* S4 carries the most copy on the page; on a shorter phone it needs the last of it */
  #s4 .kolam { width: min(26vw, 104px); }
  #s4 .lesson__open [lang="ta"] { font-size: 0.96rem; }
  #s4 .lesson__open-en { font-size: 0.74rem; line-height: 1.36; }
  #s4 .statement--earth [lang="ta"] { font-size: 0.9rem; line-height: 1.5; }
  #s4 .statement--earth .statement__en { font-size: 0.75rem; }
  #s4 .lesson__note [lang="ta"] { font-size: 0.96rem; }
  #s4 .word__ta { font-size: clamp(1.5rem, 7.2vw, 1.9rem); }
  #s4 .turn__ta { font-size: 0.95rem; line-height: 1.55; }
  #s4 .turn__en { font-size: 0.75rem; }
  #tree .tree__list li { font-size: 0.83rem; padding-block: 0.26rem; }
}

/* ===== [21] SHORT VIEWPORTS — every screen must still compose inside one viewport ===== */
@media (max-height: 760px) {
  .twin__letter { font-size: clamp(4rem, 22vw, 6.5rem); }
  .twin__caption { margin-top: 1.1rem; }
  .twin__same { margin-top: 1rem; }
  .twin__years { margin-top: 0.9rem; }
  .clock { font-size: clamp(1.35rem, 6.4vw, 1.9rem); margin-bottom: 0.9rem; }
  .claim [lang="ta"] { font-size: 1.12rem; line-height: 1.75; }
  .statement [lang="ta"] { font-size: 1.05rem; line-height: 1.7; }
  .meanings { padding-top: 1rem; margin-top: 1.2rem; }
  .meanings__ta { font-size: 0.98rem; line-height: 1.8; }
  .kolam { width: min(34vw, 145px); }
  .eyebrow { margin-bottom: 1.1rem; }
  .figure { margin-bottom: 1.1rem; }
  .figure__value { font-size: clamp(2.5rem, 13vw, 3.6rem); }
  .figure__range { padding-bottom: 0.55rem; }
  .rung { padding-bottom: 1rem; }
  /* [1] S4 and S8 need the last of the slack on the smallest phones */
  #s4 .lesson { margin-top: 0.6rem; }
  #s4 .word { margin-top: 0.7rem; }
  #s4 .word__ta { font-size: clamp(1.9rem, 9.5vw, 2.5rem); }
  #s4 .statement--earth [lang="ta"] { font-size: 0.96rem; line-height: 1.6; }
  #s4 .statement--earth .statement__en { font-size: 0.87rem; }
  #s8 .ask { font-size: clamp(1.3rem, 6vw, 1.7rem); margin-bottom: 0.8rem; }
  #s8 .kural--resolved { margin-top: 1.2rem; }
  #s8 .finale { margin-top: 1.2rem; }
}

/* ===== [22] THE ANIMATION LAYER — ENTIRELY OPTIONAL, BY CONSTRUCTION =====
   Read this before changing anything in it.

   Every rule below is additive. Each keyframe STARTS from a hidden/undrawn state and
   ENDS exactly where the element already sits without any of this. So:
     · JS off, or main.js deleted        -> no `in` class -> no animation -> finished page
     · observer never fires (URL-bar resize, in-app browser, anything) -> finished page
     · animations disabled / reduced motion -> finished page
     · an animation interrupted halfway  -> falls back to the element's base state
   Nothing here can leave a screen half-composed, because nothing here ever hides
   anything by default. That failure mode is now structurally impossible, not merely
   guarded against. Delete this whole block and the page still reads, top to bottom.

   The one animation that needs no class at all is S1's, because S1 is always on screen
   at load — so it runs on its own, with no trigger of any kind. ===== */
@media (prefers-reduced-motion: no-preference) {

  /* --- S1: the two letters grow. No class, no observer, no JS. --- */
  @keyframes tns-letter { from { opacity: 0.25; font-weight: 100; } }
  @keyframes tns-link   { from { transform: scaleX(0); } }
  @keyframes tns-rise   { from { opacity: 0; transform: translateY(14px); } }
  #s1 .twin__letter { animation: tns-letter 1.4s cubic-bezier(.2,.7,.3,1) both; }
  #s1 .twin__link { transform-origin: center; animation: tns-link .6s ease .9s both; }
  #s1 .twin__caption { animation: tns-rise .7s ease 1.25s both; }
  #s1 .twin__same    { animation: tns-rise .7s ease 1.6s both; }
  #s1 .twin__years   { animation: tns-rise .7s ease 1.9s both; }

  /* --- Screens that arrive later: the `in` class releases the same kind of entrance. --- */
  #s2.in .clock      { animation: tns-rise .7s ease both; }
  #s2.in .statement  { animation: tns-rise .7s ease .35s both; }
  #s2.in .claim      { animation: tns-rise .8s ease .7s both; }

  #s3.in .kural--open { animation: tns-rise .8s ease both; }
  #s3.in .meanings    { animation: tns-rise .8s ease .45s both; }
  #s3.in .attrib      { animation: tns-rise .7s ease 1s both; }

  /* --- S4: the kolam writes itself. Base state is FULLY DRAWN (dashoffset 0). --- */
  .kolam__line { stroke-dasharray: 832; stroke-dashoffset: 0; }
  @keyframes tns-draw { from { stroke-dashoffset: 832; } }
  @keyframes tns-dot  { from { transform: scale(0); } }
  .kolam__dots circle { transform-box: fill-box; transform-origin: center; }
  #s4.in .kolam__dots circle { animation: tns-dot .45s cubic-bezier(.2,1.4,.4,1) both; }
  #s4.in .kolam__dots circle:nth-child(1) { animation-delay: .05s; }
  #s4.in .kolam__dots circle:nth-child(2) { animation-delay: .13s; }
  #s4.in .kolam__dots circle:nth-child(3) { animation-delay: .21s; }
  #s4.in .kolam__dots circle:nth-child(4) { animation-delay: .29s; }
  #s4.in .kolam__dots circle:nth-child(5) { animation-delay: .37s; }
  #s4.in .kolam__line { animation: tns-draw 2s ease-in-out .5s both; }
  #s4.in .lesson__note { animation: tns-rise .7s ease 2.2s both; }
  #s4.in .word         { animation: tns-rise .7s ease 2.6s both; }
  #s4.in .statement    { animation: tns-rise .8s ease 3s both; }

  /* --- S5: the spine draws down beside the rungs. Base --spine-scale is 1. --- */
  @keyframes tns-spine { from { transform: scaleY(0); } }
  #s5.in .ladder::before { animation: tns-spine 2s ease-in-out both; }
  @keyframes tns-rung { from { opacity: 0; transform: translateX(-12px); } }
  #s5.in .rung { animation: tns-rung .6s ease both; }
  #s5.in .rung:nth-child(1) { animation-delay: .1s; }
  #s5.in .rung:nth-child(2) { animation-delay: .38s; }
  #s5.in .rung:nth-child(3) { animation-delay: .66s; }
  #s5.in .rung:nth-child(4) { animation-delay: .94s; }
  #s5.in .rung:nth-child(5) { animation-delay: 1.22s; }

  #s6.in .prose { animation: tns-rise .8s ease both; }

  /* --- S4's closing couplet --- */
  #s4.in .lesson__note { animation: tns-rise .7s ease 3.3s both; }
  #s4.in .turn { animation: tns-rise .9s ease 3.7s both; }

  /* --- THE TREE: roots and branches grow AT THE SAME TIME, from one trunk.
     Every path is `pathLength="100"`, so one keyframe draws them all regardless of their
     real lengths. The `.tree__down` and `.tree__up` groups share IDENTICAL timings — that
     simultaneity is the product claim, and it is why the roots are not drawn first.
     Base state (no `in` class) is stroke-dashoffset 0 — THE FINISHED TREE. --- */
  /* [1] Only the BRANCH paths draw by dash — every one carries pathLength="100", so one
     keyframe serves them all whatever their true length. */
  #tree .tree__svg .tk,
  #tree .tree__svg .mn,
  #tree .tree__svg .sc,
  #tree .tree__svg .tw { stroke-dasharray: 100; stroke-dashoffset: 0; }
  @keyframes tns-grow { from { stroke-dashoffset: 100; } }
  @keyframes tns-leaf { from { opacity: 0; } }
  /* [2] `.tree__up` and `.tree__down` share these class names and therefore these exact
     timings. The roots CANNOT draw before the branches, or after them — they grow at the
     same instant, from one trunk. That simultaneity is the product claim. */
  #tree.in .tree__svg .tk { animation: tns-grow 1s ease-out both; }
  #tree.in .tree__svg .mn { animation: tns-grow 1.4s ease-out 0.7s both; }
  #tree.in .tree__svg .sc { animation: tns-grow 1.2s ease-out 1.5s both; }
  #tree.in .tree__svg .tw { animation: tns-grow 1s ease-out 2.1s both; }
  /* [3] The leaves open and the fine roots feather out together — same keyframe, same
     delay, same duration. The crown does not come into leaf while the roots wait. */
  #tree.in .tree__svg .lf,
  #tree.in .tree__svg .rt { animation: tns-leaf 0.9s ease-out 2.7s both; }
  #tree.in .tree__head { animation: tns-rise .7s ease both; }
  #tree.in .tree__list li { animation: tns-rise .7s ease 0.5s both; }

  #s7.in .kicker          { animation: tns-rise .7s ease both; }
  #s7.in .statement--after { animation: tns-rise .8s ease .3s both; }

  #s8.in .ask            { animation: tns-rise .8s ease both; }
  #s8.in .prose          { animation: tns-rise .8s ease .3s both; }
  #s8.in .kural--resolved { animation: tns-rise .9s ease .8s both; }
  @keyframes tns-underline { from { transform: scaleX(0); } }
  #s8.in .kural__word::after { animation: tns-underline 1s ease 1.9s both; }
  #s8.in .finale         { animation: tns-rise .9s ease 2.5s both; }

  /* --- Appendix: the bars grow. Base scaleX is 1. --- */
  @keyframes tns-bar { from { transform: scaleX(0); } }
  #appendix.in .appendix__label { animation: tns-rise .6s ease both; }
  #appendix.in .appendix__note  { animation: tns-rise .6s ease .15s both; }
  #appendix.in .bar__fill { animation: tns-bar 1.3s ease-in-out .3s both; }
  #appendix.in .bar:nth-child(2) .bar__fill { animation-delay: .55s; }
  #appendix.in .statement { animation: tns-rise .7s ease .8s both; }
}

/* ===== [23] REDUCED MOTION — belt and braces; the layer above is already opt-in ===== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
