/* The Harvest Mix-Up / Bu Kimin? - kf-harvest.js styles.
 * Loaded AFTER kf-games.css. REDESIGNED per 08-nova-redesign-spec.md
 * (Deniz rejected the first layout, 2026-08-28): ONE wide scene + ONE
 * right rail that holds the Listen pill, ALL clue rows (each its own
 * sound button), and the tray. The left rail and the separate tray
 * panel are gone; Barnaby is a waist-up bust in the scene's lower left.
 *
 * Tokens only, NO new tokens. Buttons wear the V38 house pill dialect:
 * gold gradient primary + calm surface secondary on the 3px deep-purple
 * border with the hard 0 4px 0 shadow; the Listen pill is the house
 * gold-soft lantern pill.
 *
 * MOTION BUDGET: NOTHING loops. The spots are static dashed circles;
 * every glow is a fade to a STATIC state; lantern glows cap at opacity
 * 0.85 over >=520ms fades, Barnaby's lantern at 0.55 over >=320ms: a
 * flash is physically impossible. Reduced motion rests the rest. */

.kf-harvest {
  position: relative;
}

/* ---------- stage (Game Shell + the clue rail) ---------- */

.kfhv-stage {
  position: relative;
  max-width: 76rem;
  margin: 0 auto;
  box-sizing: border-box;
  background: var(--kf-lilac-soft);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: var(--space-2);
  display: grid;
}
/* LANDSCAPE (Nova 1.1): TWO columns only - wide scene + ONE right rail
   (Listen pill, all clue rows, tray). The rail track spans the scene and
   message rows; the message band sits under the scene at its width. */
.kfhv-stage[data-orient='l'] {
  --kfhv-rail-w: 15rem;
  grid-template-columns: minmax(0, 1fr) var(--kfhv-rail-w);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    'bar bar'
    'scene rail'
    'msg rail';
  gap: 8px 12px;
}
@media (min-width: 900px) {
  .kfhv-stage[data-orient='l'] { --kfhv-rail-w: 19rem; }
}
@media (min-width: 1440px) {
  .kfhv-stage[data-orient='l'] { --kfhv-rail-w: 21rem; }
}
/* PORTRAIT: scene, clue rows, band, tray, toolbar at the bottom (thumb
   zone). The clue rows are FULL-WIDTH vertical rows here too (the old
   one-active horizontal strip died with the sequential flow). */
.kfhv-stage[data-orient='p'] {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) auto auto auto auto;
  grid-template-areas:
    'scene'
    'rail'
    'msg'
    'tray'
    'bar';
  gap: 8px;
}

.kfhv-bar {
  grid-area: bar;
  display: flex;
  align-items: center;
  gap: 8px;
}
.kfhv-barbtn {
  min-width: 64px;
  min-height: 64px;
  box-sizing: border-box;
  display: grid;
  place-items: center;
  background: var(--color-surface);
  border: 2px solid var(--kf-lilac);
  border-radius: var(--radius-md);
  color: var(--kf-purple-deep);
  cursor: pointer;
  text-decoration: none;
  padding: 0;
}
.kfhv-barbtn:focus-visible { outline: 3px solid var(--kf-purple-deep); outline-offset: 3px; }
.kfhv-fs { margin-left: auto; }
/* the Notebook pill (starlight only; word + drawn icon) */
.kfhv-notebook {
  grid-auto-flow: column;
  gap: 6px;
  padding: 0 var(--space-3);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.95rem;
}

/* ---------- right rail: Listen pill + ALL clue rows + tray ----------
   Nova 2: one white panel. The grid TRACK spans the column; the panel
   itself wraps its content (5b.6, no dead white void) and scrolls inside
   only if it must. */

.kfhv-rail {
  grid-area: rail;
  box-sizing: border-box;
  background: var(--color-surface);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
  padding: var(--space-2);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-2);
  min-height: 0;
  min-width: 0;
  overflow-y: auto;
}
.kfhv-stage[data-orient='l'] .kfhv-rail {
  align-self: start;
  max-height: 100%;
}
/* the Listen pill: the house gold-soft lantern pill, >=64px; it replays
   Barnaby's how-to clip (the one that states the ONE-EACH rule) */
.kfhv-listen {
  min-height: 64px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: var(--radius-pill);
  border: 3px solid var(--kf-gold);
  background: linear-gradient(180deg, #fff6dc 0%, #ffe9a8 100%);
  box-shadow: var(--shadow-soft);
  color: var(--kf-purple-deep);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1rem;
  line-height: 1.2;
  padding: 0.4rem var(--space-3);
  cursor: pointer;
  flex: none;
}
.kfhv-listen svg { display: block; flex: 0 0 auto; }
.kfhv-listen:focus-visible { outline: 3px solid var(--kf-purple-deep); outline-offset: 3px; }

.kfhv-chips {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-height: 0;
  flex: none;
}
/* the clue ROW (Nova 2.2, the house card language): full-width card,
   cream ground, 18px radius, 2px lilac border, 72px tall. ALL rows are
   on screen from puzzle start; each row is ONE button that plays its own
   clip. Inside: speaker indicator, item thumb, ribbon, friend thumb
   (item first = the frozen sentence's own order, Pip V4 - kept over the
   spec's friend-first sketch, deviation logged in the build record). */
.kfhv-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 72px;
  box-sizing: border-box;
  background: var(--kf-cream);
  border: 2px solid var(--kf-lilac);
  border-radius: var(--radius-md);
  padding: 8px 10px;
  cursor: pointer;
}
.kfhv-chip:focus-visible { outline: 3px solid var(--kf-purple-deep); outline-offset: 2px; }
/* playing/active: the measured selection halo + white ground (colour of
   the border alone never carries the state) */
.kfhv-chip--active {
  background: #fff;
  box-shadow: var(--halo-select-strong, 0 0 0 5px rgba(212, 168, 67, 0.85));
}
/* resolved (its thing sits on the table): gold confirmation dot, still
   tappable and replayable */
.kfhv-chip--done { opacity: 0.85; }
.kfhv-chip--done::after {
  content: '';
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--kf-gold);
  box-shadow: inset 0 0 0 2px var(--kf-cream);
  margin-left: auto;
}
/* the drawn speaker indicator (28px, purple line family): one arc at
   rest, all arcs while its clip plays. An INDICATOR, not a nested button. */
.kfhv-chip-spk {
  flex: none;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  color: var(--kf-purple-deep);
}
.kfhv-chip-spk .kfhv-spk-arc { opacity: 0; }
.kfhv-chip--active .kfhv-chip-spk .kfhv-spk-arc { opacity: 1; }
.kfhv-chip-thumb {
  flex: none;
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
}
.kfhv-chip-thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.kfhv-chip-mid { flex: none; display: inline-flex; }
.kfhv-ribbon { display: block; }
.kfhv-ribbon--tied .kfhv-ribbon-line { stroke: var(--kf-gold); }
.kfhv-ribbon--tied .kfhv-ribbon-bow { fill: var(--kf-gold); stroke: var(--kf-purple-deep); }
.kfhv-ribbon--cut .kfhv-ribbon-half { stroke: #8b8598; stroke-dasharray: 5 3; }
/* the 2-miss scaffold: the relevant chip pulses (shows, never solves);
   reduced motion swaps it for a static gold mark */
.kfhv-chip--pulse { animation: kfhv-chippulse 1.3s ease-in-out 2; }
@keyframes kfhv-chippulse {
  0%, 100% { transform: scale(1); box-shadow: none; }
  50% { transform: scale(1.05); box-shadow: 0 0 0 4px rgba(212, 168, 67, 0.55); }
}
.kfhv-chip--mark { box-shadow: 0 0 0 4px rgba(212, 168, 67, 0.55); }
/* V3: a friend tapped with empty hands - the tray panel and the active
   clue's item thumb take ONE soft breath ("pick the thing first").
   Wordless by design; a single gentle cycle, never a flash. Reduced
   motion swaps it for a static gold ring for the class's lifetime. */
.kfhv-nudge { animation: kfhv-nudge 1s ease-in-out 1; }
@keyframes kfhv-nudge {
  0%, 100% { transform: scale(1); box-shadow: none; }
  40% { transform: scale(1.04); box-shadow: 0 0 0 4px rgba(212, 168, 67, 0.5); }
}

/* ---------- the scene ---------- */

.kfhv-boardwrap {
  grid-area: scene;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 0;
  min-width: 0;
}
.kfhv-scene {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-md);
  /* ?v=2: the V2 perspective-tabletop background (Deniz round 3) */
  background: url('/games/harvest/stage-bg.webp?v=2') center / cover no-repeat;
  box-shadow: var(--shadow-soft);
}

/* Barnaby at the left post, non-interactive; his lantern breathes light
   while a clip speaks (ceiling 0.55, >=300ms fades: never a flash) */
.kfhv-barnaby {
  position: absolute;
  pointer-events: none;
  user-select: none;
}
.kfhv-barnaby-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.kfhv-barnlamp {
  position: absolute;
  width: 55%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 224, 130, 0.9) 0%, rgba(212, 168, 67, 0.4) 45%, transparent 70%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 320ms ease;
}
.kfhv-barnlamp--on { opacity: 0.55; }
.kfhv-barnlamp--static { opacity: 0.55; transition: none; }

/* the nine painted lanterns' celebration glows: MEASURED spots, a fade
   to a static glow (520ms), ceiling 0.85; never a flash, never a loop */
.kfhv-lantern {
  position: absolute;
  width: 72px;
  height: 72px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 232, 150, 0.95) 0%, rgba(255, 210, 100, 0.45) 45%, transparent 72%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 520ms ease;
}
.kfhv-lantern--on { opacity: 0.85; }
.kfhv-lantern--static { opacity: 0.85; transition: none; }

.kfhv-cast {
  position: absolute;
  inset: 0;
}
.kfhv-friend {
  position: absolute;
  transform: translateX(-50%);
  pointer-events: none;
  user-select: none;
}
/* NO rim filter on the friends (Deniz round 2: the extra cream+navy
   glow on top of the art's own baked outline read as a pasted sticker).
   The baked cartoon outline carries the edge; grounding comes from the
   contact shadow below. */
.kfhv-friend-img {
  display: block;
  /* MEASURED BUG (Deniz round 3, "ratio bozuyorsun"): the RIGHTMOST
     friend rendered 148x253 out of a SQUARE canvas - with the inline
     height fixed and width:auto, a max-width cap (shrink-to-fit of the
     absolutely-positioned wrap against the scene's right edge) crushed
     the image horizontally. The canvas is square; nothing may cap it. */
  max-width: none;
}
/* the ground-contact shadow: a soft static ellipse under each friend's
   feet, engine-sized to the sprite's CONTENT width. This is what makes
   a cutout STAND on the wood instead of floating over it. */
.kfhv-friend-shadow {
  position: absolute;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(15, 10, 36, 0.32) 0%, rgba(15, 10, 36, 0.14) 52%, transparent 74%);
  pointer-events: none;
}
/* the negative clue's visible exclusion: the named friend steps back
   (transform-only, ~1.6s); reduced motion: a quiet dim instead */
.kfhv-friend--stepback { animation: kfhv-stepback 1.6s ease-in-out; }
@keyframes kfhv-stepback {
  0%, 100% { transform: translateX(-50%) translateY(0) scale(1); }
  25%, 75% { transform: translateX(-50%) translateY(-6px) scale(0.94); opacity: 0.75; }
}
.kfhv-friend--dim { opacity: 0.55; }
/* the feast's single hop (never repeats; reduced motion never adds it) */
.kfhv-friend--hop { animation: kfhv-hop 0.5s ease-out 1; }
@keyframes kfhv-hop {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  45% { transform: translateX(-50%) translateY(-10px); }
}

/* the whole column is the hit target: an invisible button, >=44px by
   construction (column width x friend+spots height), with a visible
   focus ring for keyboard players */
.kfhv-friendhit {
  position: absolute;
  background: transparent;
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  padding: 0;
}
.kfhv-friendhit:focus-visible { outline: 3px solid var(--kf-cream); outline-offset: -3px; }

/* table spots: dashed gold circles on the measured table band */
.kfhv-slot {
  position: absolute;
  box-sizing: border-box;
  border: 3px dashed var(--kf-gold);
  border-radius: 50%;
  background: rgba(253, 250, 244, 0.14);
  display: grid;
  place-items: center;
  pointer-events: none; /* the friend column takes the tap */
}
.kfhv-slot--full { border-style: solid; border-color: rgba(212, 168, 67, 0.55); background: none; }
/* the ONE-EACH rule drawn on the table (Nova 3): every empty spot shows
   its category as a NEUTRAL cream silhouette - the striped sprite used as
   a mask, single flat fill, so no pattern (and no answer) can leak
   (D-158 class). Cup is always the LEFT spot, scarf the RIGHT. The
   silhouette is reinforcement, not the sole carrier: the dashed ring,
   Barnaby's rule clip, and the slot order carry the rule too. */
.kfhv-slot-sil {
  width: 62%;
  height: 62%;
  background: var(--kf-cream);
  opacity: 0.5;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  pointer-events: none;
}
.kfhv-slot--full .kfhv-slot-sil { display: none; }
/* browsers without mask support: the dashed ring + voice carry the rule */
@supports not ((mask-image: url('')) or (-webkit-mask-image: url(''))) {
  .kfhv-slot-sil { display: none; }
}
.kfhv-placed {
  width: 92%;
  height: 92%;
  display: grid;
  place-items: center;
  background: none;
  border: none;
  padding: 0;
}
.kfhv-placed img,
.kfhv-placed .kfhv-fallback {
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: var(--rim-sprite);
}
/* starlight: a placed item is itself a button (tap = back to the tray) */
.kfhv-placed--movable { pointer-events: auto; cursor: pointer; }
.kfhv-placed--movable:focus-visible { outline: 3px solid var(--kf-cream); outline-offset: 2px; border-radius: 50%; }
/* givens sit calm and fixed: no ring, no cursor, slightly settled */
.kfhv-slot--full:has(.kfhv-placed--given) { border-color: rgba(212, 168, 67, 0.35); }

/* progress medallions, top-left (28px; 22px on phones) */
.kfhv-medals {
  position: absolute;
  top: 8px;
  left: 8px;
  display: flex;
  gap: 6px;
}
.kfhv-medal {
  width: 28px;
  height: 28px;
  box-sizing: border-box;
  border-radius: 50%;
  border: 3px solid var(--kf-cream);
  background: rgba(15, 10, 36, 0.35);
  box-shadow: var(--shadow-soft);
}
.kfhv-medal--full {
  background: var(--kf-gold);
  border-color: var(--kf-cream);
}
@media (max-width: 480px) {
  .kfhv-medal { width: 22px; height: 22px; border-width: 2px; }
}

/* ---------- message band: invisible at rest, zero CLS ---------- */

.kfhv-msg {
  grid-area: msg;
  min-height: 3.25rem;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: transparent;
  border-radius: var(--radius-md);
  box-shadow: none;
  transition: background-color 250ms ease, box-shadow 250ms ease;
  margin: 0;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-base);
  line-height: 1.35;
  color: var(--color-text);
}
.kfhv-msg--on {
  background: var(--color-surface);
  box-shadow: var(--shadow-soft);
}

/* ---------- tray: inside the rail on landscape, its own row on
   portrait; + starlight's Check pill ---------- */

.kfhv-traypanel {
  grid-area: tray;
  box-sizing: border-box;
  background: var(--color-surface);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  min-height: 0;
  min-width: 0;
}
/* landscape: the engine reparents the tray INTO the rail (Nova 2.4, one
   panel). Nested, it drops its own panel skin and wears the thin divider
   line above itself instead (Nova 2.3). */
.kfhv-rail .kfhv-traypanel {
  background: none;
  border-radius: 0;
  box-shadow: none;
  border-top: 1px solid var(--kf-lilac);
  padding: var(--space-2) 0 0;
  margin-top: var(--space-1, 4px);
  flex: none;
}
.kfhv-stage[data-orient='p'] .kfhv-traypanel {
  flex-direction: row;
  flex-wrap: wrap;
  padding: var(--space-2);
}
.kfhv-tray {
  display: grid;
  grid-template-columns: repeat(2, minmax(84px, 1fr));
  max-width: 232px;
  gap: var(--space-2);
  justify-items: center;
}
.kfhv-stage[data-orient='p'] .kfhv-tray {
  max-width: none;
  grid-template-columns: repeat(3, minmax(75px, 1fr));
}
/* PORTRAIT: the phone budget is real (a 667px stage must hold scene +
   rail + band + tray + bar), so the rail compacts - clue rows in a
   2-column grid at 48px, small thumbs - and the tray is ONE sideways-
   scrolling row of full-size (75px floor) items. Measured, not hoped. */
.kfhv-stage[data-orient='p'] .kfhv-listen { min-height: 44px; font-size: 0.9rem; }
.kfhv-stage[data-orient='p'] .kfhv-chips {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
}
.kfhv-stage[data-orient='p'] .kfhv-chip { min-height: 48px; padding: 4px 6px; gap: 4px; }
.kfhv-stage[data-orient='p'] .kfhv-chip-thumb { width: 32px; height: 32px; }
.kfhv-stage[data-orient='p'] .kfhv-chip-spk { width: 20px; height: 20px; }
.kfhv-stage[data-orient='p'] .kfhv-chip-spk svg { width: 18px; height: 18px; }
.kfhv-stage[data-orient='p'] .kfhv-ribbon { width: 26px; height: 12px; }
.kfhv-stage[data-orient='p'] .kfhv-chip--done::after { width: 14px; height: 14px; }
.kfhv-stage[data-orient='p'] .kfhv-msg { min-height: 2.5rem; }
.kfhv-stage[data-orient='p'] .kfhv-traypanel {
  flex-wrap: nowrap;
  overflow-x: auto;
  justify-content: flex-start;
}
.kfhv-stage[data-orient='p'] .kfhv-tray {
  display: flex;
  max-width: none;
  gap: 10px;
}
/* Sage r3 should-fix 3: 84px + 10px gaps leave the fourth item VISIBLY
   half-cut at 375px - the cut edge is the sideways-scroll signal a
   touch screen otherwise never shows */
.kfhv-stage[data-orient='p'] .kfhv-item { flex: none; width: 84px; height: 84px; }

/* the SNUG tier (rail track under 700px, Nova 2.4): rows 64px, thumbs
   48px, item chips 84px - the whole column fits without scrolling */
.kfhv-stage[data-dense='1'] .kfhv-chip { min-height: 64px; padding: 6px 8px; }
.kfhv-stage[data-dense='1'] .kfhv-chip-thumb { width: 48px; height: 48px; }
.kfhv-stage[data-dense='1'] .kfhv-item { width: 84px; height: 84px; }
.kfhv-stage[data-dense='1'] .kfhv-listen { min-height: 56px; }
/* Sage r3 should-fix 2: the snug budget never counted starlight's Check
   pill, and the rail overflowed 56px at 1366x768 fullscreen. In the
   snug tier, starlight's items rest at the 75px floor and the Check
   pill at 56px - measured column 675px against the 680px track. */
.kfhv-stage[data-dense='1'] .kfhv-checkbtn { min-height: 56px; }
.kfhv-stage[data-dense='1'][data-mode='starlight'] .kfhv-item { width: 75px; height: 75px; }
.kfhv-stage[data-dense='1'][data-mode='starlight'] .kfhv-chip { min-height: 60px; }
.kfhv-stage[data-dense='1'][data-mode='starlight'] .kfhv-chip-thumb { width: 44px; height: 44px; }
.kfhv-stage[data-dense='1'][data-mode='starlight'] .kfhv-traypanel { gap: var(--space-2); }
/* the item choice: circular calm pill, white ground, 3px deep purple
   border (the V38 dialect), sprite at 82%; >=75px everywhere */
.kfhv-item {
  width: 96px;
  height: 96px;
  min-width: var(--tap-play-min);
  min-height: var(--tap-play-min);
  box-sizing: border-box;
  border-radius: 50%;
  border: 3px solid var(--kf-purple-deep);
  background: #fff;
  box-shadow: 0 4px 0 rgba(45, 27, 78, 0.22), var(--shadow-soft);
  display: grid;
  place-items: center;
  cursor: pointer;
  padding: 0;
  transition: transform 0.15s ease, box-shadow 0.2s ease;
}
.kfhv-item img,
.kfhv-item .kfhv-fallback {
  width: 82%;
  height: 82%;
  object-fit: contain;
  pointer-events: none;
}
@media (hover: hover) {
  .kfhv-item:hover { transform: translateY(-2px); }
}
.kfhv-item:focus-visible {
  outline: 3px solid var(--kf-purple-deep);
  outline-offset: 3px;
}
/* picked up: the selection halo (strong, colour-blind-safe ring) */
.kfhv-item--sel {
  box-shadow: var(--halo-select-strong, 0 0 0 5px rgba(212, 168, 67, 0.85)), 0 4px 0 rgba(45, 27, 78, 0.22);
  border-color: var(--kf-gold);
}
/* the wrong pick's drift home: lift, drop back; no red, no shake */
.kfhv-item--drift { animation: kfhv-drift 420ms ease-out; }
@keyframes kfhv-drift {
  0% { transform: translateY(0); }
  30% { transform: translateY(-12px); }
  80% { transform: translateY(2px); }
  100% { transform: translateY(0); }
}
.kfhv-item--dim { opacity: 0.85; }
.kfhv-fallback {
  display: grid;
  place-items: center;
  text-align: center;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.7rem;
  color: var(--kf-purple-deep);
}
@media (max-width: 480px) {
  .kfhv-item { width: 80px; height: 80px; }
}

/* Check the Table (starlight): the house gold pill, >=64px; disabled is
   aria-disabled (focusable, explained), never a vanished control */
.kfhv-checkbtn {
  min-height: 64px;
  box-sizing: border-box;
  width: 100%;
  max-width: 232px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 3px solid var(--kf-purple-deep);
  border-radius: var(--radius-pill);
  background: linear-gradient(180deg, #ffd94d 0%, #f0b929 100%);
  box-shadow: 0 4px 0 rgba(45, 27, 78, 0.28), var(--shadow-soft);
  color: var(--kf-purple-deep);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1rem;
  padding: 0.4rem var(--space-3);
  cursor: pointer;
}
.kfhv-checkbtn[aria-disabled='true'] { opacity: 0.55; cursor: default; }
.kfhv-checkbtn:focus-visible { outline: 3px solid var(--kf-purple-deep); outline-offset: 3px; }

/* ---------- the flying clone ---------- */

.kfhv-fly {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 5;
  pointer-events: none;
  filter: var(--rim-sprite);
  max-width: none;
}

/* ---------- notebook (starlight only, pattern 6 modal) ---------- */

.kfhv-nb-overlay {
  position: absolute;
  inset: 0;
  background: var(--scrim-navy);
  display: grid;
  place-items: center;
  border-radius: var(--radius-lg);
  z-index: 7;
}
.kfhv-nb {
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lift);
  padding: var(--space-3);
  margin: var(--space-3);
  max-width: min(34rem, calc(100% - 2 * var(--space-3)));
  max-height: calc(100% - 2 * var(--space-3));
  overflow: auto;
  box-sizing: border-box;
}
.kfhv-nb-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}
.kfhv-nb-title {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--text-lg);
  color: var(--kf-purple-deep);
  margin: 0;
}
.kfhv-nb-close {
  min-height: 48px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 2px solid var(--kf-lilac);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  color: var(--kf-purple-deep);
  font-family: var(--font-heading);
  font-weight: 700;
  padding: 0 var(--space-3);
  cursor: pointer;
}
.kfhv-nb-close:focus-visible { outline: 3px solid var(--kf-purple-deep); outline-offset: 2px; }
.kfhv-nb-grid {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.kfhv-nb-row {
  display: grid;
  grid-template-columns: 44px repeat(var(--kfhv-nb-cols, 8), minmax(34px, 1fr));
  gap: 3px;
  align-items: center;
}
.kfhv-nb-corner { width: 44px; }
.kfhv-nb-colh,
.kfhv-nb-rowh {
  display: grid;
  place-items: center;
}
.kfhv-nb-colh img,
.kfhv-nb-rowh img {
  width: 30px;
  height: 30px;
  object-fit: contain;
  display: block;
}
.kfhv-nb-rowh img { width: 38px; height: 38px; }
/* a little air between the cup columns and the scarf columns */
.kfhv-nb-colh--cat2,
.kfhv-nb-cell--cat2 { margin-left: 6px; }
/* the cell: blank -> dim star ("maybe not") -> bright star ("this one!").
   Pure notes: nothing validates, nothing grades. 34px cells inside a
   pointer-precise modal the child opened on purpose. */
.kfhv-nb-cell {
  min-width: 34px;
  min-height: 34px;
  box-sizing: border-box;
  display: grid;
  place-items: center;
  background: var(--kf-cream);
  border: 2px solid var(--kf-lilac);
  border-radius: var(--radius-sm, 8px);
  cursor: pointer;
  padding: 0;
}
.kfhv-nb-cell:focus-visible { outline: 3px solid var(--kf-purple-deep); outline-offset: 1px; }
.kfhv-nb-cell svg path { fill: transparent; stroke: transparent; }
.kfhv-nb-cell[data-state='1'] svg path { fill: #cfc4e2; stroke: #8b8598; }
.kfhv-nb-cell[data-state='2'] svg path { fill: var(--kf-gold); stroke: var(--kf-purple-deep); }

/* ---------- start curtain (V1, the D-118 pattern) ---------- */

/* Above everything the game draws (notebook 7, celebration 6): nothing
   is playable until the one tap that also grants audio + fullscreen. */
.kfhv-gate {
  position: absolute;
  inset: 0;
  background: var(--scrim-navy);
  display: grid;
  place-items: center;
  border-radius: var(--radius-lg);
  z-index: 8;
}
/* ONE big gold pill: the house gold gradient on the 3px deep-purple
   border with the hard 0 4px 0 shadow (the V38 dialect) */
.kfhv-gate-btn {
  min-height: 76px;
  min-width: 14rem;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 3px solid var(--kf-purple-deep);
  border-radius: var(--radius-pill);
  background: linear-gradient(180deg, #ffd94d 0%, #f0b929 100%);
  box-shadow: 0 4px 0 rgba(45, 27, 78, 0.28), var(--shadow-soft);
  color: var(--kf-purple-deep);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1.35rem;
  padding: 0.5rem var(--space-5);
  cursor: pointer;
}
.kfhv-gate-btn:focus-visible { outline: 3px solid var(--kf-cream); outline-offset: 4px; }
@media (max-width: 480px) {
  .kfhv-gate-btn { min-width: 0; font-size: 1.15rem; padding: 0.5rem var(--space-4); }
}

/* ---------- celebration (house pattern 7) ---------- */

.kfhv-overlay {
  position: absolute;
  inset: 0;
  background: var(--scrim-navy);
  display: grid;
  place-items: center;
  border-radius: var(--radius-lg);
  z-index: 6;
}
.kfhv-wincard {
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lift);
  padding: var(--space-4);
  margin: var(--space-3);
  max-width: 30rem;
  text-align: center;
  max-height: calc(100% - 2 * var(--space-3));
  overflow-y: auto;
  box-sizing: border-box;
}
@media (max-width: 520px) {
  .kfhv-wincard { max-width: 90vw; }
}
.kfhv-winimg {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-md);
  margin: 0 0 var(--space-3);
}
.kfhv-wincard h2 {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--text-xl);
  color: var(--kf-purple-deep);
  margin: 0 0 var(--space-3);
}
.kfhv-pills {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-3);
}
.kfhv-pill {
  min-height: 64px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 3px solid var(--kf-purple-deep);
  border-radius: var(--radius-pill);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1.05rem;
  color: var(--kf-purple-deep);
  padding: 0 var(--space-4);
  cursor: pointer;
  text-decoration: none;
}
.kfhv-pill--gold {
  background: linear-gradient(180deg, #ffd94d 0%, #f0b929 100%);
  box-shadow: 0 4px 0 rgba(45, 27, 78, 0.28), var(--shadow-soft);
}
.kfhv-pill--calm {
  background: var(--color-surface);
  box-shadow: 0 4px 0 rgba(45, 27, 78, 0.22), var(--shadow-soft);
}
.kfhv-pill:focus-visible { outline: 3px solid var(--kf-purple-deep); outline-offset: 3px; }

/* ---------- full screen: grows, never recomposes ---------- */

.kfhv-stage.kfhv-full {
  max-width: none;
  width: 100%;
  height: 100%;
  border-radius: 0;
}

/* ---------- screen-reader only ---------- */

.kfhv-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- reduced motion: everything rests ---------- */

@media (prefers-reduced-motion: reduce) {
  .kfhv-item { transition: none; }
  .kfhv-item--drift { animation: none; }
  .kfhv-chip--pulse { animation: none; }
  .kfhv-nudge { animation: none; box-shadow: 0 0 0 4px rgba(212, 168, 67, 0.5); }
  .kfhv-friend--stepback { animation: none; opacity: 0.55; }
  .kfhv-friend--hop { animation: none; }
  .kfhv-barnlamp { transition: none; }
  .kfhv-lantern { transition: none; }
}
