/* Kangaroo demo. Layered on style.css, which supplies the tokens, the
   self-hosted fonts, the button classes and the footer. Only the parts that do
   not exist on the main page live here, so the app's stylesheet stays untouched
   and the demo cannot drag its cache around. */

.kg {
  /* Widened from 860px for the two-column layout: at 860 the narrative column
     would be ~450px, too narrow for a 58px display headline. 1120px matches
     .body on the main page, so the header and footer rules line up with the
     content the way they do there. */
  max-width: 1120px;
  margin: 0 auto;
  padding: 38px 28px 40px;

  /* Deeper variants of the semantic pair, for small text. --pass and --fail are
     tuned for fills and large type. Over their own 10-12% tints on --bg, --pass
     measures 4.12:1 and fails AA; --fail measures 4.74:1 and passes, so only the
     first was a real defect. Both are moved anyway, because a pass/fail pair
     that derives its two colours by different rules is the kind of thing that
     drifts. The inks measure 5.27:1 and 5.75:1. Same bright-fill versus
     deep-text split style.css already makes for --accent/--accent-ink. Scoped
     here rather than added to :root so the demo does not force a cache VERSION
     bump; promote them when style.css is next touched. */
  --pass-ink: #125c31;
  --fail-ink: #94152d;
}

/* Narrative left, instrument right. Two columns so the control sits beside the
   opening argument instead of below it, which is what kept it off the fold.
   align-items: start below is load-bearing: without it the panel stretches to
   the narrative's full height and becomes a 1,500px box with content at the top.

   Deliberately NOT sticky. To be accurate about why, since an earlier version of
   this comment was not: sticky on its own does not cause a second scrollbar. A
   sticky panel taller than the viewport just scrolls with the page until its
   bottom edge arrives and then pins, which is fine. The second scrollbar came
   from a max-height plus overflow-y that was added to "fix" that non-problem.

   The real reason is attention. The narrative beside this panel is about 1,480px
   of prose, so pinning would park the instrument in peripheral vision for the
   whole read, which is a large claim for something you touch twice. Two columns
   already solved the fold problem, which was the actual complaint.

   The cost, knowingly accepted: during a long walk (about eleven minutes at
   2^60) the counters leave the viewport once the reader reaches the honesty
   section. If that turns out to matter, add `position: sticky; top: 20px` here
   and add nothing else. */
.kg2 {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(320px, 380px);
  gap: 0 28px;
  align-items: start;
}
/* Placed explicitly rather than reordered. The rig is second in the DOM so
   keyboard order reaches it before the narrative and before the off-site bridge
   link, and so the mobile stack is already correct without an `order` reversal,
   which would have made visual and focus order disagree. */
.kg-intro     { grid-column: 1; grid-row: 1; }
.kg-narrative { grid-column: 1; grid-row: 2; }
.rig          { grid-column: 2; grid-row: 1 / span 2; }
/* The tail breaks out under both columns. The rig is only ~700px against ~1,950px
   of narrative, so without this the right third of the screen is blank for most
   of the scroll, and blankest beside the honesty section, which is the longest
   and most important prose on the page. */
.kg-fig-herds { grid-column: 1 / -1; grid-row: 3; }
.kg-honest    { grid-column: 1 / -1; grid-row: 4; }
.kg-bridge    { grid-column: 1 / -1; grid-row: 5; }

.rig {
  background: var(--panel);
  border: 1.5px solid var(--line);
  border-radius: 11px;
  padding: 16px;
  display: flex; flex-direction: column; gap: 16px;
}
/* wrap, because at 200% text zoom the heading and the tag together exceed the
   fixed track and the tag cannot shrink. */
.rig-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 12px; }
.rig-head h2 { font: 800 17px/1.2 var(--font-head); letter-spacing: -0.01em; margin: 0; }
/* 10px, not 9px: everything else in both stylesheets floors at 10px, and this
   was the smallest text on the site. Uppercased in CSS like every other label,
   so the accessible name is not shouted. */
.rig-tag {
  font: 700 10px/1 var(--font-mono); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--soft); white-space: nowrap;
}
.rig-block { display: flex; flex-direction: column; gap: 10px; }
/* Margins do not collapse in a flex column, so UA paragraph margins were adding
   ~25px where the block declares 10px. */
.rig-block > p { margin: 0; }
.rig-block + .rig-block { border-top: 1.5px solid var(--border); padding-top: 16px; }
.rig-k {
  font: 700 10px/1 var(--font-mono); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--soft); margin: 0;
}
/* Inside the panel the facts sit on the panel surface, not a nested card. */
.rig .kg-facts {
  grid-template-columns: fit-content(38%) minmax(0, 1fr);
  gap: 6px 12px; padding: 0; margin: 0;
  background: transparent; border: 0; border-radius: 0;
}
.rig .kg-live { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 8px; margin: 0; }
.rig .kg-metric { padding: 12px; }

.rig .verdict { padding: 12px; }
.rig .btn-primary, .rig .btn-ghost { flex: 0 0 auto; }
/* aria-disabled rather than the disabled property, so focus is not dropped off
   a button the user is standing on. Needs its own styling because :disabled
   will not match. */
button[aria-disabled="true"] { cursor: not-allowed; filter: none; opacity: 0.75; }

.kg-intro h1 {
  font-family: var(--font-display);
  font-size: clamp(34px, 6vw, 58px);
  line-height: 1.02;
  color: var(--accent);
  margin: 0 0 18px;
  text-wrap: balance;
}
.kg-lede { font-size: 17px; line-height: 1.6; margin: 0 0 14px; }
.kg-sub { font-size: 15px; line-height: 1.6; color: var(--soft); margin: 0 0 8px; }

.kg-step, .kg-honest, .kg-bridge {
  margin-top: 42px;
  padding-top: 26px;
  border-top: 1.5px solid var(--line);
}
.kg-step h2, .kg-honest h2 {
  font: 800 24px/1.2 var(--font-head);
  letter-spacing: -0.01em;
  margin: 0 0 10px;
  display: flex; align-items: baseline; gap: 12px;
}
.kg-n {
  font: 700 12px/1 var(--font-mono);
  color: var(--accent-ink);
  letter-spacing: 0.1em;
}
.kg-step p, .kg-honest p { font-size: 15px; line-height: 1.62; margin: 0 0 14px; }

/* Range picker */
.range-choices { display: flex; flex-wrap: wrap; gap: 10px; margin: 0; }
.range-choice {
  position: relative;
  display: flex; flex-direction: column; gap: 2px;
  padding: 12px 16px; border-radius: 11px;
  background: var(--panel2); cursor: pointer;
  border: 1.5px solid transparent;
}
.range-choice:has(input:checked) { border-color: var(--accent-ink); }
.range-choice:has(input:checked) .rc-label { font-weight: 800; }
.range-choice:focus-within,
.range-choice:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.range-choice input { position: absolute; opacity: 0; pointer-events: none; }
.rc-label { font: 700 15px/1.2 var(--font-mono); color: var(--text); }
.rc-note { font-size: 11px; color: var(--soft); }
/* Dimmed with tokens rather than opacity. A blanket 0.5 took the "no WebGPU
   here" note to 2.40:1, and disabled controls being exempt from AA does not
   make an unreadable explanation useful. */
.range-choice.blocked { background: transparent; border-color: var(--border); cursor: not-allowed; }
.range-choice.blocked .rc-label { color: var(--muted); }

/* Checked and hover both work the border, not the surface. --panel2 is the
   darkest surface in the palette, so inside a --panel rig any surface change
   moves the chip toward its background and weakens it: the earlier
   surface-based hover made an unchecked card dissolve into the panel and look
   selected at the same time. Checked carries border colour plus label weight;
   hover carries a neutral border. Both add a channel rather than trading one. */
.range-choice:hover:not(.blocked) { border-color: var(--muted); }
.range-choice:active:not(.blocked) { transform: translateY(1px); }

/* Facts list. Both instances live inside the rig, so this is only the grid;
   the surface comes from .rig. The recovered key renders --accent-ink on
   --panel, which measures 5.41:1, better than the 4.85:1 it got on --bg. */
.kg-facts { display: grid; margin: 0; }
.kg-facts dt { font: 700 10px/1.6 var(--font-mono); letter-spacing: 0.1em; color: var(--soft); text-transform: uppercase; }
.kg-facts dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.kg .mono { font-size: 12.5px; line-height: 1.5; }
.kg-facts dd.accent { color: var(--accent-ink); font-weight: 700; }
/* Declared after .accent so it wins at equal specificity: placeholder text is
   prose, not data, and must not inherit the recovered-key treatment. */
.kg-facts dd.empty { color: var(--muted); font-weight: 400; font-family: var(--font-body); font-size: 13px; font-style: italic; }
/* The leading zeros of a 64-character key carry no information and stop the eye
   doing the comparison the page invites. Dimmed to --soft, not --muted: these
   are key characters rather than decoration, so they have to clear AA. */
.hex-pad { color: var(--soft); font-weight: 400; }

/* Benchmark, step 03. All three states of this slot sit on the same surface at
   the same padding so the section does not jump as one replaces another. The
   min-height holds the tallest of them. */
.bench-start, .bench-running, .kg-compare > .kg-card { min-height: 84px; }
.bench-start {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px;
  background: var(--panel2); border-radius: 11px; padding: 16px;
  margin: 0 0 12px;
}
.bench-start .kg-note { flex: 1 1 260px; margin: 0; }
.bench-running {
  display: flex; flex-direction: column; gap: 4px;
  background: var(--panel2); border-radius: 11px; padding: 16px;
  margin: 0 0 12px;
}
.kg-card-unit { font-size: 15px; font-weight: 400; color: var(--soft); }

/* Brute-force comparison cards */
.kg-compare { display: flex; flex-wrap: wrap; gap: 12px; margin: 4px 0 12px; }
.kg-card {
  flex: 1 1 240px; display: flex; flex-direction: column; gap: 4px;
  background: var(--panel2); border-radius: 11px; padding: 16px;
}
.kg-card-k { font: 700 10px/1 var(--font-mono); letter-spacing: 0.1em; color: var(--soft); text-transform: uppercase; }
.kg-card-v { font: 700 22px/1.2 var(--font-head); color: var(--text); overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.kg-card-n { font-size: 12px; color: var(--soft); }

.kg .kg-note { font-size: 13px; color: var(--soft); line-height: 1.55; }

/* Live counters */
.kg-live {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px; margin-top: 18px;
}
.kg-metric {
  background: var(--panel2); border-radius: 11px; padding: 16px;
  display: flex; flex-direction: column; gap: 4px;
}
.kg-metric .label { font: 700 10px/1 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--soft); }
.kg-metric .val { font: 700 22px/1.2 var(--font-head); color: var(--text); min-width: 0; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.kg-metric .val.small { font-size: 14px; font-family: var(--font-mono); font-weight: 400; }

/* Result. Inside the rig, so the emphasis comes from a heavier divider than the
   other blocks rather than from a section rule. */
.rig-block.kg-result { border-top-width: 3px; border-top-color: var(--accent); }
.kg-result:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.verdict {
  font-weight: 700; font-size: 17px;
  padding: 16px; border-radius: 11px;
  background: var(--panel2); border: 1.5px solid var(--border); color: var(--soft);
}
.verdict.ok { background: rgba(23, 110, 59, 0.12); border-color: var(--pass); color: var(--pass-ink); }
.verdict.bad { background: rgba(168, 31, 56, 0.1); border-color: var(--fail); color: var(--fail-ink); }

/* Two across, mirroring .faq-grid on the main page. Full width alone would put
   15px prose on a ~145 character measure; three columns would put it on ~40,
   which is newspaper-narrow for 60-word paragraphs. Two gives ~63, which is the
   measure the main page's FAQ already uses. The third item sitting alone on row
   two is the same behaviour .faq-grid has with an odd count. */
.kg-honest-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px 34px; margin-bottom: 14px; }
.kg-honest-item h3 { margin-top: 0; }
.kg-honest-item p:last-child { margin-bottom: 0; }
@media (max-width: 720px)  { .kg-honest-grid { grid-template-columns: 1fr; gap: 0; } }

/* The honesty block carries the weight of the page, so it reads as content
   rather than as a disclaimer buried at the bottom. */
.kg-honest h3 {
  font: 700 16px/1.3 var(--font-head);
  margin: 20px 0 6px; color: var(--text);
}
.kg-honest sup { font-size: 0.7em; }

/* Links in the narrative prose. Nothing in style.css reaches this far, so
   without it an inline link falls back to browser-default blue. Same treatment
   as .footer a and .faq-item a on the main page. */
.kg-step a, .kg-honest a {
  color: var(--accent-ink);
  text-decoration: underline;
  text-underline-offset: 2px;
  font-weight: 500;
  transition: filter 0.15s ease;
}
.kg-step a:hover, .kg-honest a:hover { filter: brightness(1.1); }

/* Platform-specific WebGPU fix steps. Same treatment as #gpu-info on the main
   page: monospace and pre-wrap, so the numbered steps keep their line breaks. */
.gpu-fix {
  border: 1px solid var(--border); border-radius: 9px; background: var(--panel2);
  padding: 11px 12px; margin-top: 10px;
  font: 500 11.5px/1.55 var(--font-mono); color: var(--soft);
  white-space: pre-wrap; overflow-wrap: anywhere;
}

.kg-bridge p { font-size: 15px; margin: 0; }
.kg-bridge a { color: var(--accent-ink); font-weight: 700; text-decoration: underline; text-underline-offset: 2px; transition: filter 0.15s ease; }
.kg-bridge a:hover { filter: brightness(1.1); }

/* Explanatory figures. Each SVG carries its own type and colour from the same
   tokens as the page, so the only job here is spacing and a caption that reads
   as a caption. They must stay inline: as an <img src> or a background the
   var() fills stop resolving and every shape renders black. */
.kg-fig { margin: 22px 0 0; }
.kg-fig svg { display: block; width: 100%; height: auto; }
.kg-fig figcaption { margin-top: 10px; }
.kg-fig-herds { margin-top: 30px; }
.kg-honest .kg-fig { margin-top: 30px; }
.kg-fig-scroll:focus-visible { outline: 3px solid var(--accent-ink); outline-offset: 3px; }

/* The type inside these figures is real SVG text, so it scales with the box
   instead of reflowing. Below about 700px the 13.5-unit body copy would render
   under 10px, so the figure gets a floor and scrolls sideways within its own
   container rather than shrinking to unreadable. The container is focusable so
   a keyboard can reach that scroll. */
@media (max-width: 700px) {
  .kg-fig-scroll { overflow-x: auto; overscroll-behavior-x: contain; }
  .kg-fig-scroll svg { min-width: 700px; }
}
/* One column. The rig moves ABOVE the narrative so a phone opens on the control
   rather than on three paragraphs. */
/* 1000px, not 900: at a 901px viewport the narrative column is 437px, narrower
   than the 450px that got the container widened in the first place. */
@media (max-width: 1000px) {
  .kg2 { grid-template-columns: minmax(0, 1fr); }
  /* Every placed child returns to auto, not just the three columns. Leaving the
     tail pinned to explicit rows while the rest auto-placed pushed the narrative
     past the honesty section and the bridge link, because auto-placement skips
     cells that explicit placement already owns. */
  .kg-intro, .kg-narrative, .rig,
  .kg-fig-herds, .kg-honest, .kg-bridge { grid-column: 1; grid-row: auto; }
}

@media (max-width: 640px) {
  .kg { padding: 26px 18px 32px; }
  .rig .kg-facts { grid-template-columns: 1fr; gap: 2px 0; }
  .rig .kg-facts dd { margin-bottom: 8px; }
}
