/* Autonomous Control — BIST project page
   ======================================
   Hybrid of two references.

   From the MIT Senseable "Cooling Path" microsite: the page sits on ONE
   continuous gradient that runs the full document height, so scrolling the
   page is the story: blue while the problem is still standing, green once the
   electricity stops being spent — the same green the paper panels spend on
   energy not used. Not a straight line, though: at the 184 decisions that
   followed every rule and still made the room worse the colour drains toward
   slate, because a building on autonomous control does not only ever improve.
   Narrative type and line drawings float directly on it in white, with no
   boxes.

   From the Apple product page: wherever real numbers have to be read, the
   content moves onto a light panel with its own quiet typography. The MIT
   page carries about six numbers in total; this one carries a bar chart, a
   four-quadrant grid, 678 plotted episodes and a power curve, and white text
   on a saturated field is the wrong place for any of them.

   So: gradient for the argument, paper for the evidence. The gradient never
   passes through a pale zone, so white always clears contrast on it. */

:root {
  /* Paper — inside the light panels */
  --white:  #ffffff;
  --sand:   #f5f5f7;
  --ink:    #1d1d1f;
  --ink-2:  #424245;
  --gray:   #6e6e73;
  --gray-2: #86868b;
  --hair:   #d2d2d7;
  --hair-2: #e8e8ed;

  --blue:   #0066cc;
  --heat:   #d4581c;    /* energy spent */
  --heat-2: #f08a4b;
  --heat-bg: rgba(212, 88, 28, .10);
  --save:   #04795b;    /* energy NOT spent */
  --save-2: #35b394;
  --save-bg: rgba(4, 121, 91, .10);
  --warn:   #c0392b;
  --warn-bg: rgba(192, 57, 43, .10);

  /* Sky — on the gradient */
  --sky:    #ffffff;
  --sky-2:  rgba(255, 255, 255, .74);
  --sky-3:  rgba(255, 255, 255, .70);
  --sky-line: rgba(255, 255, 255, .40);
  /* A room being heated, seen from outside. On the old warm ground a pale
     peach read warm on its own; on this blue-to-green one it composites to
     neutral grey and the heating signal disappears, so the hue is stronger
     and the fills are carried further. */
  --glow:   #ff9d5c;

  --sans:  -apple-system, BlinkMacSystemFont, 'Inter', 'Apple SD Gothic Neo',
           'Noto Sans KR', 'Segoe UI', sans-serif;
  --serif: 'Instrument Serif', Georgia, serif;

  --r:    18px;
  --r-lg: 28px;
  --pad:  clamp(20px, 4vw, 32px);
  --ease: cubic-bezier(.28, .11, .32, 1);

  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; background: #22344d; }

body {
  margin: 0;
  min-height: 100%;
  color: var(--sky-2);
  font-family: var(--sans);
  font-size: 17px; font-weight: 400; line-height: 1.65;
  letter-spacing: -.005em;
  -webkit-font-smoothing: antialiased;
}
/* No overflow-x rule on the root or the body. `hidden` turns the element into
   a scroll container and `clip` changes what `position: sticky` inside it
   sticks to; both break the sticky nav and the pinned chart. Nothing on the
   page is wider than its column, so neither is needed. */

/* ── The sky ──────────────────────────────────────────────────
   The page sits on one gradient whose colours are keyed to the sections, so
   scrolling is the argument: the problem stated in blue, the saving turning it
   green, the colour draining where the agent got it wrong, then green again. It is
   a viewport-sized gradient on the root element whose two colours
   js/main.js re-samples from a palette as you scroll — not one gradient the
   height of the document, which on an 18,000px page is a single enormous
   raster, and not an extra fixed layer behind the content, which is one more
   thing for a compositor to get wrong. Without the script these defaults
   stand: the middle of the journey, statically, which still reads. */
html {
  background-image: linear-gradient(180deg, var(--c1, #245d5a), var(--c2, #215e4f));
  background-attachment: fixed;
}

/* ── Language ─────────────────────────────────────────────────
   Both languages sit in the markup and CSS hides one off a single attribute
   on <html>, so the page is right before any script runs. `display: none`
   works on SVG children too, which is how the drawings are bilingual. */
[data-lang="en"] .ko,
[data-lang="ko"] .en { display: none; }
[data-lang="ko"] body { word-break: keep-all; letter-spacing: -.01em; }
[data-lang="ko"] h1, [data-lang="ko"] h2 { font-weight: 700; letter-spacing: -.02em; }
/* Instrument Serif carries no Hangul, so Korean captions take the sans at a
   size tuned for it rather than falling back to an arbitrary face. */
[data-lang="ko"] .cap-serif { font-family: var(--sans); font-style: normal; font-size: 16px; }

a { color: inherit; }
strong, b { font-weight: 600; }
:focus-visible { outline: 2px solid var(--glow); outline-offset: 3px; border-radius: 4px; }

.skip { position: absolute; left: -9999px; }
.skip:focus { left: 12px; top: 62px; z-index: 100; background: #fff; color: #1d1d1f; padding: 10px 16px; border-radius: 8px; }

.wrap { max-width: 1080px; margin-inline: auto; padding-inline: var(--pad); }
.wrap.wide { max-width: 1280px; }
.text { max-width: 700px; }
.mid  { max-width: 720px; margin-inline: auto; text-align: center; }

/* ── Reveal ───────────────────────────────────────────────────
   The only motion vocabulary: fade and rise, once, on arrival. Elements are
   visible in the markup and hidden only here, so a browser that never runs
   the observer still shows a complete page. */
.rv { opacity: 0; transform: translateY(26px); transition: opacity .8s var(--ease), transform .8s var(--ease); }
.rv.in { opacity: 1; transform: none; }
.rv[data-d="1"] { transition-delay: .09s; }
.rv[data-d="2"] { transition-delay: .18s; }
.rv[data-d="3"] { transition-delay: .27s; }
.rv[data-d="4"] { transition-delay: .36s; }

/* ── Local nav ────────────────────────────────────────────────
   Dark and translucent, so the white lab mark is used as supplied — no
   filter, no second file. */
.localnav {
  position: sticky; top: 0; z-index: 90;
  background: rgba(30, 10, 4, 0);
  backdrop-filter: blur(0px);
  border-bottom: 1px solid transparent;
  transition: background .3s ease, border-color .3s ease, backdrop-filter .3s ease;
}
.localnav.stuck {
  background: rgba(24, 10, 6, .55);
  backdrop-filter: saturate(160%) blur(18px);
  -webkit-backdrop-filter: saturate(160%) blur(18px);
  border-bottom-color: rgba(255, 255, 255, .14);
}
.localnav .wrap { display: flex; align-items: center; gap: 16px; height: 56px; }
.nav-brand { display: flex; align-items: center; }
.bist-logo { display: block; height: 30px; width: auto; opacity: .9; }
.nav-title { font-size: 18px; font-weight: 600; color: var(--sky); letter-spacing: -.01em; white-space: nowrap; }
.nav-right { margin-left: auto; display: flex; align-items: center; gap: 18px; }
.lang-toggle {
  border: 0; background: none; cursor: pointer; padding: 4px 2px;
  font-family: inherit; font-size: 14px; color: var(--sky-2); letter-spacing: -.01em;
}
.lang-toggle:hover { color: var(--sky); text-decoration: underline; }
.btn {
  display: inline-block; border-radius: 980px;
  border: 1px solid rgba(255, 255, 255, .5); color: var(--sky);
  padding: 9px 20px; font-size: 15px; letter-spacing: -.01em;
  transition: background .2s ease, border-color .2s ease, color .2s ease;
}
.btn:hover { background: var(--sky); color: #2a1108; border-color: var(--sky); text-decoration: none; }
.btn.big { padding: 15px 34px; font-size: 18px; }
@media (max-width: 640px) {
  .nav-title { font-size: 15px; }
  .bist-logo { height: 24px; }
  .btn { padding: 8px 15px; font-size: 14px; }
}

/* ── On the gradient ──────────────────────────────────────── */
.band { padding-block: clamp(84px, 12vw, 168px); position: relative; }
.band.tight { padding-block: clamp(56px, 8vw, 100px); }

.eyebrow {
  margin: 0 0 22px; font-size: 14px; font-weight: 500;
  color: var(--sky-3); letter-spacing: .06em; text-transform: uppercase;
}
[data-lang="ko"] .eyebrow { letter-spacing: .02em; text-transform: none; font-size: 15px; }

h1 { margin: 0; font-weight: 600; font-size: clamp(38px, 6.4vw, 82px); line-height: 1.05; letter-spacing: -.02em; color: var(--sky); }
/* The Korean headline's second line, "건물 자율 제어 서비스", is one unit and
   has to stay one. At this weight it costs 8.99px of width per 1px of type,
   so 82px would need 737px and the hero column is 601px. These sizes are the
   largest that still fit — measured, not guessed — in both hero layouts. */
[data-lang="ko"] h1 { font-size: clamp(30px, calc(10.5vw - 5px), 64px); }
@media (min-width: 940px) {
  /* the hero splits into two columns here, so the headline's box shrinks */
  [data-lang="ko"] h1 { font-size: clamp(38px, 4.4vw, 64px); }
}
h2 { margin: 0 0 24px; font-weight: 600; font-size: clamp(30px, 4.8vw, 58px); line-height: 1.08; letter-spacing: -.018em; color: var(--sky); }
h3 { margin: 0 0 10px; font-weight: 600; font-size: 21px; line-height: 1.35; letter-spacing: -.01em; color: var(--sky); }
.band p { margin: 0 0 1.2em; color: var(--sky-2); }
.band p:last-child { margin-bottom: 0; }
.lede { font-size: clamp(18px, 2.1vw, 23px); line-height: 1.55; color: var(--sky); }

/* Editorial caption, borrowed straight from the reference: a serif italic
   line under a drawing, quieter than the body and clearly not part of it. */
.cap-serif { margin: 18px auto 0; max-width: 46rem; font-family: var(--serif); font-style: italic; font-size: 19px; line-height: 1.5; color: var(--sky-2); }
.note-sky { margin: 16px auto 0; max-width: 44rem; font-size: 13.5px; line-height: 1.6; color: var(--sky-3); }
.note-sky b { color: var(--sky-2); font-weight: 400; }
/* `.band p` below is one element more specific than `.cap-serif` / `.note-sky`
   and was overriding their `auto` side margins, so a caption meant to sit
   centred under a figure was left-aligned in the wrap instead. */
.band .cap-serif { margin: 18px auto 0; }
.band .note-sky  { margin: 16px auto 0; }

/* The one number allowed to be enormous. Thin, because at this size weight
   reads as shouting and the gradient is already doing the shouting. */
.figure-n {
  display: block; font-weight: 200; font-variant-numeric: tabular-nums;
  font-size: clamp(72px, 15vw, 190px); line-height: .92; letter-spacing: -.045em;
  color: var(--sky);
}
.figure-n .u { font-size: .42em; font-weight: 300; letter-spacing: -.02em; }
.figure-cap { margin: 4px 0 0; font-size: 15px; color: var(--sky-3); }

/* ── Hero ─────────────────────────────────────────────────── */
.hero { padding-block: clamp(70px, 10vw, 130px) clamp(50px, 7vw, 90px); }
.hero-grid { display: grid; gap: clamp(40px, 6vw, 72px); align-items: center; }
@media (min-width: 940px) { .hero-grid { grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); } }
.hero .intro { margin: 16px 0 0; max-width: 34rem; font-size: clamp(17px, 2vw, 21px); line-height: 1.55; color: var(--sky-2); }
/* An authored break, but only where the column is wide enough to hold the
   lines it makes. The longest of the three measures 511px at this size, and
   the hero column reaches that at about 1130px wide — below it the break would
   produce a four-line block with an orphan, so the text is left to wrap on its
   own, which reads normally. */
.intro br.wide { display: none; }
@media (min-width: 1130px) { .intro br.wide { display: inline; } }

.stat-line { display: flex; flex-wrap: wrap; gap: 16px 46px; margin: 40px 0 0; padding: 0; list-style: none; }
.stat-line li { font-size: 15px; color: var(--sky-3); }
.stat-line b { display: block; font-size: 40px; font-weight: 200; letter-spacing: -.03em; color: var(--sky); line-height: 1.1; font-variant-numeric: tabular-nums; }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 18px 26px; margin-top: 38px; }
.more { font-size: 16px; color: var(--sky-2); border-bottom: 1px solid var(--sky-line); padding-bottom: 2px; }
.more::after { content: " \203A"; }
.more:hover { color: var(--sky); border-bottom-color: var(--sky); }

/* ── Line drawings ────────────────────────────────────────────
   The reference draws buildings as thin white outlines on the gradient and
   nothing else. Both the hero axonometric and the floor plan below use the
   same pen, so they read as one hand — and as a preview of the BIM model
   that will replace the plan's geometry. */
.draw { width: 100%; height: auto; display: block; }
.draw .ln {
  fill: none; stroke: var(--sky-line); stroke-width: 1.1;
  stroke-linecap: round; stroke-linejoin: round;
  /* Every drawn stroke carries pathLength="100", so one dash covers the whole
     path however long it really is. Without that, a fixed dasharray shorter
     than the outer wall leaves the wall permanently broken. */
  stroke-dasharray: 100; stroke-dashoffset: 100;
  transition: stroke-dashoffset 2.2s var(--ease);
}
.draw .ln.hard { stroke: rgba(255, 255, 255, .62); stroke-width: 1.5; }
.in .draw .ln, .draw.in .ln { stroke-dashoffset: 0; }
.draw .ln.d1 { transition-delay: .15s; }
.draw .ln.d2 { transition-delay: .35s; }
.draw .ln.d3 { transition-delay: .55s; }
.draw .lbl { fill: rgba(255, 255, 255, .62); font-family: var(--sans); font-size: 11px; letter-spacing: .01em; }

/* A cell that is being conditioned. The fill breathes on its own clock, one
   room at a time, which is the whole service in one gesture. */
.draw .cell { fill: var(--glow); opacity: 0; animation: cellglow var(--dur, 14s) ease-in-out var(--del, 0s) infinite; }
/* Each cell keeps its own duration and delay, so the rooms never fall into
   step. That is not decoration: the agent does not work the floor in a rota,
   it decides for every room every half hour, and each room answers to whether
   anyone is in it. Unrelated clocks are the honest picture. */
@keyframes cellglow { 0%, 8% { opacity: 0; } 14%, 46% { opacity: .55; } 54%, 100% { opacity: 0; } }

.plan-legend { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px 26px; margin-top: 24px; font-size: 13.5px; color: var(--sky-3); }
.plan-legend span { display: inline-flex; align-items: center; gap: 9px; }
.plan-legend i { width: 15px; height: 15px; border-radius: 3px; border: 1px solid var(--sky-line); }
.plan-legend i.on { background: rgba(255, 157, 92, .55); border-color: rgba(255, 157, 92, .8); }

/* ── Two days ─────────────────────────────────────────────────
   Eighteen half-hour blocks across one operating day, drawn on the gradient
   in the same pen: an outline is a half hour the room stood idle, a filled
   block is a half hour it was heated. */
.days { display: grid; gap: 34px; max-width: 900px; margin-inline: auto; }
.d-lab { display: flex; align-items: baseline; gap: 12px; margin: 0 0 12px; font-size: 15px; color: var(--sky-3); }
.d-lab b { font-weight: 500; color: var(--sky); }
.d-lab .d-tot { margin-left: auto; font-variant-numeric: tabular-nums; }
.strip { display: flex; gap: 5px; }
.strip i { flex: 1 1 0; height: 54px; border-radius: 5px; border: 1px solid var(--sky-line); position: relative; overflow: hidden; }
.strip i::after { content: ""; position: absolute; inset: 0; opacity: 0; background: var(--glow); }
.in .strip i::after { animation: blocklit 6s linear var(--d, 0s) infinite; }
@keyframes blocklit { 0%, 3% { opacity: 0; } 9%, 94% { opacity: var(--o, .68); } 100% { opacity: 0; } }
.strip i.off { --o: 0; }
.d-axis { display: flex; justify-content: space-between; margin-top: 10px; font-size: 13px; color: var(--sky-3); font-variant-numeric: tabular-nums; }

/* ── Scroll-driven day ────────────────────────────────────────
   One chart, pinned, assembled in four steps, drawn on the gradient in the
   same pen as the plan. The reader builds the argument by scrolling instead
   of being handed a finished figure. */
.story { position: relative; }
.story-pin { position: sticky; top: 56px; height: calc(100vh - 56px); height: calc(100svh - 56px); display: flex; flex-direction: column; justify-content: center; }
/* A step is shorter than a screen. The panel is pinned the whole way, so the
   step height is not a layout box anyone sees — it is purely how much scroll
   buys one caption. A full screen each made the section 4.6 screens long. */
.story-step { height: 72vh; height: 72svh; }
/* A tail after the last step, so the finished figure is held on screen for a
   screenful before the panel unsticks. Without it, step 4 arrives and scrolls
   away in the same gesture and the reader never sees the result. */
.story-steps::after { content: ""; display: block; height: 44vh; height: 44svh; }

.chart { width: 100%; height: auto; max-height: 58vh; max-height: 58svh; display: block; }
.chart text { font-family: var(--sans); fill: var(--sky-3); font-size: 14px; }
.chart .axis { stroke: rgba(255, 255, 255, .28); stroke-width: 1; }

/* Every layer starts hidden and is switched on by a step class on the pinned
   container. Transitions, not keyframes, so scrolling back up takes the
   figure apart again instead of replaying it. */
.chart .layer { opacity: 0; transition: opacity .55s var(--ease); }
.chart .line {
  fill: none; stroke-width: 2.6; stroke-linejoin: round; stroke-linecap: round;
  stroke-dasharray: 100; stroke-dashoffset: 100;
  transition: stroke-dashoffset 1.5s var(--ease);
}
.chart .base { stroke: rgba(255, 255, 255, .45); }
.chart .act  { stroke: var(--glow); stroke-width: 3; }
.chart .gap  { fill: url(#savefill); opacity: 0; transition: opacity .7s var(--ease); }
.chart .occ-on  { fill: rgba(255, 157, 92, .55); }
.chart .occ-off { fill: rgba(255, 255, 255, .10); }
.chart .big { fill: #b9f0dd; font-size: 64px; font-weight: 200; letter-spacing: -.04em; }
.chart .big-sub { fill: var(--sky-2); font-size: 14px; }
.chart .lg-txt { fill: var(--sky-2); font-size: 14.5px; }

.story-pin.s1 .l-base, .story-pin.s2 .l-base, .story-pin.s3 .l-base, .story-pin.s4 .l-base { opacity: 1; }
.story-pin.s1 .base,   .story-pin.s2 .base,   .story-pin.s3 .base,   .story-pin.s4 .base   { stroke-dashoffset: 0; }
.story-pin.s2 .l-occ,  .story-pin.s3 .l-occ,  .story-pin.s4 .l-occ  { opacity: 1; }
.story-pin.s3 .l-act,  .story-pin.s4 .l-act  { opacity: 1; }
.story-pin.s3 .act,    .story-pin.s4 .act    { stroke-dashoffset: 0; }
.story-pin.s4 .gap, .story-pin.s4 .l-win { opacity: 1; }

.story-cap { margin: 24px auto 0; max-width: 660px; min-height: 3.4em; text-align: center; font-size: 17px; line-height: 1.55; color: var(--sky-2); }
.story-cap .sc { display: none; }
.story-pin.s1 .sc-1, .story-pin.s2 .sc-2, .story-pin.s3 .sc-3, .story-pin.s4 .sc-4 { display: block; }
.story-cap b { color: var(--sky); font-weight: 600; }
.story-dots { display: flex; gap: 8px; justify-content: center; margin-top: 20px; }
.story-dots i { width: 26px; height: 2px; border-radius: 2px; background: rgba(255, 255, 255, .25); transition: background .3s ease; }
.story-pin.s1 .story-dots i:nth-child(1),
.story-pin.s2 .story-dots i:nth-child(-n+2),
.story-pin.s3 .story-dots i:nth-child(-n+3),
.story-pin.s4 .story-dots i { background: var(--glow); }

/* ══ PAPER ════════════════════════════════════════════════════
   Everything below here lives inside a light panel: this is where numbers
   are read, and white type on a saturated gradient is the wrong place for a
   bar chart. */
.paper {
  background: var(--white); color: var(--ink-2);
  border-radius: var(--r-lg);
  padding: clamp(30px, 5vw, 72px) clamp(24px, 4vw, 64px);
  box-shadow: 0 30px 70px rgba(20, 6, 0, .22);
}
.paper-wrap { max-width: 1180px; margin-inline: auto; padding-inline: var(--pad); }
.paper h2 { color: var(--ink); }
.paper h3 { color: var(--ink); }
.paper p  { color: var(--ink-2); margin: 0 0 1.15em; }
.paper p:last-child { margin-bottom: 0; }
.paper .eyebrow { color: var(--heat); text-transform: none; letter-spacing: -.005em; font-size: 15px; }
.paper .lede { color: var(--gray); font-size: clamp(17px, 2vw, 22px); }
.paper .more { color: var(--blue); border-bottom-color: transparent; }
.note { margin: 22px 0 0; max-width: 760px; font-size: 13.5px; line-height: 1.6; color: var(--gray-2); }
.note b { color: var(--gray); font-weight: 500; }

/* ── Log ──────────────────────────────────────────────────────
   A control decision is abstract until you watch one arrive. Entries stack in
   on a timer, oldest dropping off the top, so the card reads as something
   running rather than something printed. */
.log { border: 1px solid var(--hair); border-radius: var(--r); overflow: hidden; }
.log-bar { display: flex; align-items: center; gap: 10px; padding: 16px clamp(16px, 2.4vw, 26px); border-bottom: 1px solid var(--hair-2); background: var(--sand); font-size: 14px; color: var(--gray); }
.log-bar .right { margin-left: auto; color: var(--gray-2); font-size: 13px; }
.dot-live { width: 8px; height: 8px; border-radius: 50%; background: var(--save-2); box-shadow: 0 0 0 0 rgba(53, 179, 148, .55); animation: live 2s ease-out infinite; }
@keyframes live { to { box-shadow: 0 0 0 10px rgba(53, 179, 148, 0); } }
.log-feed { height: 372px; overflow: hidden; display: flex; flex-direction: column; justify-content: flex-end; }
.log-row {
  display: grid; grid-template-columns: 4.6rem 9rem minmax(0, 1fr);
  gap: 2px 20px; padding: 16px clamp(16px, 2.4vw, 26px);
  border-top: 1px solid var(--hair-2);
  font-size: 15px; line-height: 1.5;
  animation: rowin .5s var(--ease) both;
}
@keyframes rowin { from { opacity: 0; transform: translateY(18px); } }
.lg-t { color: var(--gray-2); font-variant-numeric: tabular-nums; font-size: 14.5px; }
.lg-room { font-weight: 500; color: var(--ink); }
.lg-act { color: var(--heat); font-weight: 500; }
.lg-act.off { color: var(--save); }
.lg-obs { grid-column: 2 / -1; font-size: 13.5px; color: var(--gray-2); font-variant-numeric: tabular-nums; }
.lg-why { grid-column: 2 / -1; font-size: 14.5px; color: var(--gray); margin-top: 3px; }
@media (max-width: 720px) {
  /* Two columns: the time keeps the narrow one and everything else stacks in
     the wide one, or the action string is squeezed into 4rem. */
  .log-row { grid-template-columns: 4.2rem minmax(0, 1fr); gap: 2px 14px; }
  .lg-act, .lg-obs, .lg-why { grid-column: 2; }
  .log-feed { height: 430px; }
}

/* ── Stats and bars ───────────────────────────────────────── */
.stats { list-style: none; margin: 0; padding: 0; display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
/* The longest figure here is "32.01%", which needs about 3.5x its font size
   in width. At the old 34px padding and 58px type that is 204px inside a
   173px card, so it ran over the corner. Slightly less padding, slightly
   smaller cap: 183px inside 189px, with room left at every column count. */
.stats li { background: var(--sand); border-radius: var(--r); padding: clamp(20px, 2.2vw, 26px); }
.stats .n { display: block; font-size: clamp(34px, 4.2vw, 52px); font-weight: 500; line-height: 1.05; letter-spacing: -.03em; color: var(--ink); font-variant-numeric: tabular-nums; }
.stats .hot  .n { color: var(--heat); }
.stats .cold .n { color: var(--save); }
.stats .l { display: block; margin-top: 12px; font-size: 15px; color: var(--gray); line-height: 1.45; }
.stats .l small { display: block; margin-top: 5px; font-size: 13px; color: var(--gray-2); }

/* Width is the value, growing from zero when the bar arrives. Each fill
   carries its own number as --w, so nothing is computed in script. */
.bars { list-style: none; margin: 0; padding: 0; }
.bars li { display: grid; grid-template-columns: 13rem minmax(0, 1fr) 5rem; align-items: center; gap: 22px; padding: 14px 0; }
.b-lab { font-size: 16px; color: var(--gray); }
.b-track { height: 14px; background: var(--hair-2); border-radius: 7px; overflow: hidden; }
.b-fill { display: block; height: 100%; width: 0; border-radius: 7px; background: linear-gradient(90deg, var(--save-2), var(--save)); transition: width 1.3s var(--ease); }
.in .b-fill { width: var(--w); }
.b-val { text-align: right; font-size: 17px; font-weight: 500; color: var(--save); font-variant-numeric: tabular-nums; }
@media (max-width: 640px) {
  .bars li { grid-template-columns: minmax(0, 1fr) 5rem; gap: 8px 14px; }
  .b-lab { grid-column: 1 / -1; }
}

/* Before and after, both on one 0–100 scale so the rows compare directly. */
.shift { list-style: none; margin: 0; padding: 0; display: grid; gap: 40px; }
.s-lab { display: block; margin-bottom: 16px; font-size: 19px; font-weight: 500; color: var(--ink); letter-spacing: -.01em; }
.s-row { display: grid; grid-template-columns: 7rem minmax(0, 1fr) 4.5rem; align-items: center; gap: 18px; padding: 5px 0; }
.s-when { font-size: 14.5px; color: var(--gray-2); }
.s-track { height: 16px; background: var(--hair-2); border-radius: 8px; overflow: hidden; }
.s-fill { display: block; height: 100%; width: 0; border-radius: 8px; transition: width 1.3s var(--ease); }
.in .s-fill { width: var(--w); }
.s-fill.was  { background: #c7c7cc; }
.s-fill.warn { background: linear-gradient(90deg, #e2725f, var(--warn)); }
.s-fill.now  { background: linear-gradient(90deg, var(--heat-2), var(--heat)); }
.s-val { text-align: right; font-size: 17px; color: var(--gray); font-variant-numeric: tabular-nums; }
.s-row.after .s-val { color: var(--ink); font-weight: 500; }
.s-note { display: block; margin-top: 14px; font-size: 14px; line-height: 1.6; color: var(--gray-2); }
@media (max-width: 640px) { .s-row { grid-template-columns: 5rem minmax(0, 1fr) 4rem; gap: 12px; } }

/* ── 678 dots in four boxes ───────────────────────────────────
   One dot per control episode. The point of the figure is that the good box
   is not the only full one, and 184 dots of "did the correct thing, made the
   room worse" is an argument no bar chart makes as loudly. */
.quad-wrap { display: grid; grid-template-columns: 2.2rem minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) 2.2rem; gap: 12px; }
.quad { grid-area: 1 / 2; display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.q-cell { background: var(--sand); border-radius: var(--r); padding: clamp(20px, 2.6vw, 32px); display: flex; flex-direction: column; }
.q-cell.good { background: var(--heat-bg); }
.q-cell.bad  { background: var(--warn-bg); }
.q-n { font-size: clamp(32px, 3.8vw, 48px); font-weight: 500; line-height: 1; letter-spacing: -.03em; color: var(--gray); font-variant-numeric: tabular-nums; }
.q-cell.good .q-n { color: var(--heat); }
.q-cell.bad  .q-n { color: var(--warn); }
.q-name { margin-top: 12px; font-size: 17px; font-weight: 500; color: var(--ink); line-height: 1.35; letter-spacing: -.01em; }
.q-note { margin-top: 8px; font-size: 14px; line-height: 1.55; color: var(--gray); }
/* The dot field takes whatever height is left, so the four boxes stay level
   however long their labels run. */
.q-dots { margin-top: 18px; flex: 1 1 auto; min-height: 56px; display: flex; flex-wrap: wrap; align-content: flex-end; gap: 4px; }
.q-dots i {
  width: 5px; height: 5px; border-radius: 50%; background: #b6b6bc;
  opacity: 0; transform: scale(.2);
  transition: opacity .3s ease, transform .35s var(--ease); transition-delay: var(--dd, 0s);
}
.in .q-dots i { opacity: 1; transform: none; }
.q-cell.good .q-dots i { background: var(--heat); }
.q-cell.bad  .q-dots i { background: var(--warn); }
/* A y-axis label reads bottom-to-top. In Latin that needs the 180° flip on top
   of vertical-rl, which lays the glyphs on their side. Hangul in vertical-rl
   already stands upright and reads downward, so the same flip turns every
   glyph upside down — the rotation is for the Latin case only. */
.q-axis-y { grid-area: 1 / 1; display: flex; align-items: center; justify-content: center; writing-mode: vertical-rl; transform: rotate(180deg); }
[data-lang="ko"] .q-axis-y { transform: none; text-orientation: upright; }
.q-axis-x { grid-area: 2 / 2; display: flex; align-items: center; justify-content: center; }
.q-axis-y, .q-axis-x { font-size: 14px; color: var(--gray-2); }
@media (max-width: 720px) { .quad { grid-template-columns: 1fr; } .quad-wrap { grid-template-columns: 1.6rem minmax(0, 1fr); } }

/* ── Intervention split ───────────────────────────────────── */
.mix { list-style: none; margin: 0; padding: 0; display: grid; gap: 26px; }
.m-lab { display: block; margin-bottom: 12px; font-size: 16px; color: var(--gray); }
.m-bar { display: flex; height: 52px; border-radius: 12px; overflow: hidden; background: var(--hair-2); }
.m-seg {
  display: flex; align-items: center; justify-content: center;
  width: 0; transition: width 1.3s var(--ease);
  font-size: 15px; font-weight: 500; color: #fff;
  overflow: hidden; white-space: nowrap;   /* the smaller share gets too narrow for its own label */
}
.in .m-seg { width: var(--w); }
.m-s { background: #d8654f; }
.m-w { background: var(--save); }
.m-key { display: flex; flex-wrap: wrap; gap: 10px 26px; margin: 12px 0 0; font-size: 14.5px; color: var(--gray); }
.m-key span { display: inline-flex; align-items: center; gap: 9px; }
.m-key i { width: 14px; height: 14px; border-radius: 4px; }
.m-key i.m-s { background: #d8654f; }
.m-key i.m-w { background: var(--save); }

/* ── Model delta ──────────────────────────────────────────── */
.delta { margin: 0; background: var(--sand); border-radius: var(--r); overflow: hidden; }
.delta > div { display: grid; grid-template-columns: minmax(9rem, 15rem) minmax(0, 1fr); align-items: baseline; gap: 6px 24px; padding: 20px clamp(20px, 2.6vw, 30px); }
.delta > div + div { border-top: 1px solid var(--hair-2); }
.delta dt { font-size: 15px; color: var(--gray); }
.delta dd { margin: 0; display: flex; align-items: baseline; flex-wrap: wrap; gap: 14px; font-variant-numeric: tabular-nums; }
.d-was, .d-arw { color: var(--gray-2); }
.d-now { font-size: 20px; font-weight: 600; color: var(--ink); letter-spacing: -.02em; }
.d-chg { font-size: 14px; font-weight: 500; color: var(--save); }

/* ── Kit ──────────────────────────────────────────────────── */
.kit { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.kit li { background: var(--sand); border-radius: var(--r); padding: 24px 26px; }
.kit .k-sum { background: var(--heat-bg); }
.k-n { display: block; font-size: 27px; font-weight: 600; letter-spacing: -.02em; color: var(--ink); font-variant-numeric: tabular-nums; }
.kit .k-sum .k-n { color: var(--heat); }
.k-l { display: block; margin-top: 10px; font-size: 14.5px; line-height: 1.45; color: var(--gray); }

/* ── Lessons — back on the gradient, where prose belongs ──── */
.lessons { list-style: none; margin: 0; padding: 0; max-width: 860px; margin-inline: auto; }
.lessons li { display: grid; grid-template-columns: 3.6rem minmax(0, 1fr); gap: 0 20px; padding: clamp(26px, 3.2vw, 42px) 0; border-top: 1px solid rgba(255, 255, 255, .18); }
.lessons li:last-child { border-bottom: 1px solid rgba(255, 255, 255, .18); }
.lessons .n { font-size: 17px; font-weight: 500; color: var(--glow); font-variant-numeric: tabular-nums; padding-top: 3px; }
.lessons p { margin: 0; color: var(--sky-2); }
@media (max-width: 600px) { .lessons li { grid-template-columns: 1fr; gap: 8px; } }

/* ── Closing and footer ───────────────────────────────────── */
.cta { text-align: center; }
.cta h2 { max-width: 20ch; margin-inline: auto; }
.cta-note { margin: 22px 0 0; font-size: 14px; color: var(--sky-3); }

.foot { padding-block: 44px 64px; font-size: 13px; line-height: 1.7; color: var(--sky-3); border-top: 1px solid rgba(255, 255, 255, .14); }
.foot .credits { max-width: 780px; }
.foot p { margin: 0 0 .8em; }
.foot strong { color: var(--sky-2); font-weight: 600; }
.foot-links { display: flex; flex-wrap: wrap; gap: 10px 24px; margin-top: 24px; }
.foot-links a { color: var(--sky-2); border-bottom: 1px solid var(--sky-line); padding-bottom: 2px; }
.foot-links a:hover { color: var(--sky); }

/* ── Reduced motion ───────────────────────────────────────────
   Everything above is decoration on top of a page that is already complete.
   Here it all stops — final states, immediately. The scroll story still
   works, because its steps are classes rather than animations. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .rv { opacity: 1; transform: none; }
  .b-fill, .s-fill, .m-seg { width: var(--w); }
  .q-dots i { opacity: 1; transform: none; }
  .chart .line, .draw .ln { stroke-dashoffset: 0; }
  .draw .cell { opacity: .34; }
  .strip i::after { opacity: var(--o, .68); }
  .strip i.off::after { opacity: 0; }
}
