/* Half a House / Yarım Ev - kf-mirror-house.js styles (internal code name).
 * Loaded AFTER kf-games.css. Tokens only, NO new tokens; engine constants
 * (plate, tray ground, wood band, piece colours) live in the engine and the
 * plan data (D-046 precedent). 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.
 *
 * MOTION BUDGET: the burning plate's inner yellow rim breathes
 * stroke-opacity 1..0.60 over 2.4s (0.417Hz, one sinus, no flash possible;
 * the fill is opaque and still, no halo; r15 EK 12 H24-H26, supersedes r9
 * EK 6 H14); the reflection shimmer is the
 * page's ONE ambient loop and starts only at bloom (3s, tiny amplitude);
 * the rung-3 look line is ONE 1.8s wave of stroke-width (0.55Hz, once).
 * Reduced motion rests all three and every twin below.
 *
 * Version rides MIRROR_HOUSE_V (src/config/assetVersions.ts). */

.kf-mirror-house {
  position: relative;
}

/* ---------- stage (Game Shell: toolbar, scene, band, tray rail) ----------
   DOM order is scene, band, tray, toolbar so the TAB order is
   cells -> exit -> bell -> restart (Nova 9.2); the grid places the
   toolbar visually on top in BOTH orientations. F-25 (review 08 EK 2, K11,
   a written deviation from the Game Shell's "toolbar at the bottom on
   phones"): here the thumb zone belongs to the PRIMARY target, the tray
   (104px cells); Pip measured the exit pill 4px under cell 0 and a low
   thumb leaving the game. The whole bar moves up (moving the exit alone
   would cost a 68px row and the scene its 379px floor), the tray takes
   the bottom, and the height budget is unchanged (rows swapped). Bak ve
   Hatırla's top row (D-146) is the precedent. */
.kfmh-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;
  --kfmh-cell: 150px;
}
.kfmh-stage[data-orient='l'] {
  --kfmh-rail-w: 15rem;
  grid-template-columns: minmax(0, 1fr) var(--kfmh-rail-w);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    'bar bar'
    'scene tray'
    'msg tray';
  gap: 8px 12px;
}
@media (min-width: 900px) {
  .kfmh-stage[data-orient='l'] { --kfmh-rail-w: 19rem; }
}
.kfmh-stage[data-orient='p'] {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  grid-template-areas:
    'bar'
    'scene'
    'msg'
    'tray';
  gap: 8px;
}

/* ---------- full screen (r17, D-118, the shared kf-fullscreen.js layer on the
   stage; Deniz 2026-08-17 "kolonlari ve yazilari iki yan kenara yapistiramazsin,
   ... sayfada ortali kalmali her sey"): the UA gives the :fullscreen element
   the whole screen (width/height 100%, max-width none, all !important), so the
   COLUMN is kept with padding: the grid inside never grows past the page's
   own 73rem (76rem shell minus 2 x 1.5rem page padding = 1241px at 17px root)
   and centres; the lilac surface fills the rest of the screen. The layer's
   faked mode (.kf-fs-fake, iPhone Safari / a refused request) gets the same.
   The bar's 24px of air at the top is the layer's own rule
   ([data-kf-fs-wide] > :first-child). ---------- */
.kfmh-stage:fullscreen,
.kfmh-stage.kf-fs-fake {
  padding-left: max(var(--space-2), calc((100% - 73rem) / 2));
  padding-right: max(var(--space-2), calc((100% - 73rem) / 2));
  border-radius: 0;
  box-shadow: none;
}

/* ---------- toolbar: exit (word + arrow), bell, restart. Nothing else. ---------- */
.kfmh-bar {
  grid-area: bar;
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.kfmh-barbtn {
  min-width: 64px;
  min-height: 64px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  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;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1rem;
}
.kfmh-barbtn:focus-visible { outline: 3px solid var(--color-link-strong); outline-offset: 2px; box-shadow: 0 0 0 2px #fff; }
.kfmh-exit { padding: 0 var(--space-3) 0 var(--space-2); }
.kfmh-exit-word { white-space: nowrap; }
.kfmh-mute { margin-left: auto; }

/* ---------- the scene: an SVG in scene units; nothing in it is a target ---------- */
.kfmh-scene {
  grid-area: scene;
  justify-self: center;
  align-self: center;
  overflow: hidden;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
  background: #d5e5b8;
  min-width: 0;
}
.kfmh-svg {
  display: block;
  width: 100%;
  height: 100%;
}
.kfmh-plate-glow {
  filter: blur(9px);
  transition: opacity 250ms ease;
}
/* H20 (review 08 EK 8, Pip r9 R9-2): the opaque wall-coloured backing the
   engine puts UNDER the glow and body of a plate that straddles the house
   axis (today: the door), so the wall seam never shows through the lit
   hollow. Not a target (the scene is not one either); the class exists for
   the gate's count and order check (back -> glow -> body -> rim -> clip);
   under the r15 opaque fill it cannot show, the order gate still counts it. */
.kfmh-plate-back { pointer-events: none; }
/* F-23 (review 08 EK 2, H7): a resting plate is a HOLLOW (the engine draws
   it lilac at .45 with a 2.5 screen-px dashed line). r15, F-67 (review 08
   EK 12 H24-H26; Deniz 33, the second "parlayan yer hiç belli değil": the
   r9 deep purple stayed inside the picture's lilac/purple family): exactly
   one plate is a CART plate, outside every colour family of the picture.
   H24 fill: opaque magenta (the engine's C.plateBurn #830051 through
   --kfmh-plate-burn; OKLCH 0.40 / 0.166 / 352, hue >= 45 deg from every
   scene colour, the most saturated magenta sRGB shows at this lightness),
   fill-opacity 1, NO breath (a breathing fill washes to lilac on cream,
   2,84:1, Deniz's very complaint), no line of its own. H25 rim: the plate
   group's own `.kfmh-plate-rim` path, cart yellow (C.plateRim #ffe600
   through --kfmh-plate-rim), a 10 px non-scaling stroke clipped to the
   fill so 5 px show INSIDE the magenta on every ground (a centred yellow
   line loses its outer half on the cream wall, 1,15:1), dashed 9 3 in
   screen px (step 12, gap/dash 0,33 against the resting plate's 16 / 0,60,
   EL-C82), butt caps (round caps eat the 3 px gap at 5 px), dashes NEVER
   march (no dashoffset animation, AC46); it breathes stroke-opacity
   1 <-> .60, 2.4 s ease-in-out (0.417 Hz, one sinus, no flash, EL-C83).
   H26: no halo (gold is the tray's word and sits 18 dE from the yellow).
   Paper (Nova, EK 12): fill/ground 9,1 cream / 4,7 roof / 3,7 water,
   deutan 3,22 water; rim/fill 7,95; the gate's F-38 (h) measures the
   colour family distance itself, because a 12:1 contrast never proved
   the spot visible. Three channels apart (fill colour, line weight +
   colour + dash step, motion), EL-C78/79/82. */
.kfmh-plate--burn .kfmh-plate-body {
  fill: var(--kfmh-plate-burn, #ff1493);
  fill-opacity: 1;
  stroke: none;
  animation: none;
}
.kfmh-plate-rim {
  opacity: 0;
  transition: stroke-width 400ms ease;
}
.kfmh-plate--burn .kfmh-plate-rim {
  opacity: 1;
  stroke: var(--kfmh-plate-rim, #ffe600);
  stroke-opacity: 1;
  animation: kfmh-stitch 2.4s ease-in-out infinite;
}
@keyframes kfmh-stitch {
  0%, 100% { stroke-opacity: 1; }
  50% { stroke-opacity: 0.6; }
}
/* r16 (Deniz, third time: "cart pembe ... kenarı kesik çizgili zıt cart renk"):
   the fill is BRIGHT cart pink (#ff1493, C.plateBurn), the inner stitch is
   7 px visible cart yellow (14 px clipped), and the glow path (drawn UNDER
   the body) becomes a 6 px solid dark-magenta line, so a 3 px dark edge
   shows OUTSIDE the plate: that edge is what separates bright pink from the
   bright water and the cream wall (fill/water luminance alone is ~1.3). */
.kfmh-plate--burn .kfmh-plate-glow {
  opacity: 1;
  filter: none;
  stroke: #5c0038;
  stroke-width: 6;
  vector-effect: non-scaling-stroke;
  animation: none;
}
/* the redirect answer to a scene tap: one 200ms extra pulse (rim to 1.0, breath paused; the fill never moves, H26) */
.kfmh-plate--pulse .kfmh-plate-rim { stroke-opacity: 1; animation: none; }
/* F-22 (EK 2, H6): ladder rung 3 says LOOK, not PRESS. The referenced
   outline (the plate on a shape round, the built twin on a mirror round,
   the ghost on a reflect round) thickens to 6 screen px in deep purple,
   dashes go solid, no filter, no gold (gold is the tray's word). The engine
   holds the class 1.4s; the 400ms transitions on the base elements make it
   one 1.8s wave (up 400, hold 1000, down 400), never repeated. */
.kfmh-plate-body {
  /* F-37: `fill 250ms` gives the lilac -> magenta onset one soft step when
     a plate starts to burn (the r15 fill is still after that; no animation) */
  transition: stroke-width 400ms ease, fill 250ms ease;
}
.kfmh-twin,
.kfmh-ghost path {
  transition: stroke-width 400ms ease;
}
/* .kfmh-svg prefix: same specificity as the burn rule above (a shape round's
   reference IS the burning plate) and later in the sheet, so 6 wins over 4 */
.kfmh-svg .kfmh-look3 {
  stroke: var(--kf-purple-deep);
  stroke-width: 6;
  stroke-dasharray: none;
  vector-effect: non-scaling-stroke;
}
/* r15 EK 12 H27: on the BURNING plate (a shape round's rung 3 target) the
   look wave is the inner yellow RIM: dashes go solid (the generic rule
   above) and the stroke goes 10 -> 16 px, i.e. 5 -> 8 px visible (+3,
   AC50); the body has no line of its own, and a deep purple 6 px line on
   the magenta fill would be invisible. Same 1.8 s wave, same class; the
   stitch breath keeps running through it. After the generic rule, same
   specificity as the burn rim rule plus one class, so 16 wins over 10. */
.kfmh-svg .kfmh-plate-rim.kfmh-look3 {
  stroke: var(--kfmh-plate-rim, #ffe600);
  stroke-width: 16;
}
/* the reflection shimmer: the ONE ambient loop, bloom only */
.kfmh-reflect {
  transform-box: fill-box;
  transform-origin: 50% 0;
}
.kfmh-shimmer {
  animation: kfmh-shimmer 3s ease-in-out infinite;
  will-change: transform;
}
@keyframes kfmh-shimmer {
  0%, 100% { transform: scaleY(1) translateY(0); }
  50% { transform: scaleY(0.985) translateY(2px); }
}
.kfmh-shimmer .kfmh-ghost,
.kfmh-shimmer .kfmh-reflsettled { animation: kfmh-shimmer-op 3s ease-in-out infinite; }
@keyframes kfmh-shimmer-op {
  0%, 100% { opacity: 0.50; }
  50% { opacity: 0.58; }
}
.kfmh-piece-set--water { opacity: 0.55; stroke-opacity: 0.8; }
/* F-18: under the painted reflection the engine fades the flat layers to 0
   (inline style); a running opacity animation would lift them back (an
   animation beats an inline style), so the pulse is off in that state. The
   group's transform shimmer still moves the painted reflection. */
.kfmh-shimmer.kfmh-reflect--painted .kfmh-ghost,
.kfmh-shimmer.kfmh-reflect--painted .kfmh-reflsettled { animation: none; }
.kfmh-painted-refl { pointer-events: none; }
.kfmh-friend { transform-box: fill-box; }

/* ---------- message band: invisible at rest, zero CLS (5b.6) ---------- */
.kfmh-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);
}
.kfmh-msg--on {
  background: var(--color-surface);
  box-shadow: var(--shadow-soft);
}

/* ---------- tray: the three cells, the only targets on the page ---------- */
.kfmh-tray {
  grid-area: tray;
  box-sizing: border-box;
  background: #faf3e2;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
  display: flex;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}
.kfmh-stage[data-orient='l'] .kfmh-tray {
  align-self: start;
  flex-direction: row;
}
.kfmh-stage[data-orient='p'] .kfmh-tray {
  flex-direction: column;
  justify-self: center;
}
/* one flat wood band on the tray's seam edge */
.kfmh-wood {
  flex: none;
  background: #c99a55;
  box-shadow: inset 0 0 0 2px rgba(45, 27, 78, 0.25);
}
.kfmh-stage[data-orient='l'] .kfmh-wood { width: 18px; }
.kfmh-stage[data-orient='p'] .kfmh-wood { height: 18px; }
.kfmh-cells {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: var(--space-3);
  transition: opacity 250ms ease;
  min-width: 0;
}
.kfmh-stage[data-orient='l'] .kfmh-cells { flex-direction: column; }
.kfmh-stage[data-orient='p'] .kfmh-cells { flex-direction: row; padding: var(--space-2) var(--space-3); }
/* the cells out: the 250ms redeal fade (burn) AND the finish (F-35, review 08
   EK 4: the tray empties on the bloom's first tick and stays empty until
   deal() repaints it). pointer-events none: a faded cell is not a target (a
   tap swallowed during the 250ms redeal is a choice, not a defect). Opacity
   only: the cells keep their boxes, the rail (.kfmh-tray) keeps its size,
   nothing in the layout moves. Reduced motion: .kfmh-cells has no
   transition below, so the cells go at once. */
.kfmh-cells--out { opacity: 0; pointer-events: none; }
.kfmh-cell {
  width: var(--kfmh-cell);
  height: var(--kfmh-cell);
  flex: none;
  box-sizing: border-box;
  display: grid;
  place-items: center;
  background: var(--color-surface);
  border: 3px solid var(--kf-lilac-deep);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
  padding: 0;
  cursor: pointer;
  transition: transform 100ms ease, box-shadow 200ms ease, opacity 300ms ease;
}
.kfmh-cell:active { transform: scale(0.97); }
@media (hover: hover) {
  .kfmh-cell:hover { box-shadow: var(--shadow-lift); }
}
.kfmh-cell:focus-visible { outline: 3px solid var(--color-link-strong); outline-offset: 2px; box-shadow: 0 0 0 2px #fff; }
.kfmh-cell-art { display: grid; place-items: center; }
.kfmh-cell-art svg { display: block; overflow: visible; }
/* the resting piece (rung 2): lies down, stays visible, is inert */
.kfmh-cell--rest {
  transform: rotate(8deg);
  opacity: 0.45;
  cursor: default;
}
.kfmh-cell--rest:active { transform: rotate(8deg); }
/* idle 40s: all three cells glow for 2s */
.kfmh-cell--hint { box-shadow: var(--glow-hint-soft), var(--shadow-soft); }

/* the flight clone, 1:1 with the tray piece */
.kfmh-flyer {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  display: grid;
  place-items: center;
  filter: drop-shadow(0 8px 14px rgba(45, 27, 78, 0.22));
}
.kfmh-flyer svg { width: 100%; height: 100%; display: block; overflow: visible; }

/* ---------- start gate (the D-118 pattern) ---------- */
.kfmh-gate {
  position: absolute;
  inset: 0;
  background: var(--scrim-navy);
  display: grid;
  place-items: center;
  border-radius: var(--radius-lg);
  z-index: 8;
  transition: opacity 250ms ease;
}
.kfmh-gate--out { opacity: 0; pointer-events: none; }
.kfmh-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;
}
.kfmh-gate-btn:focus-visible { outline: 3px solid var(--kf-cream); outline-offset: 4px; }
@media (max-width: 480px) {
  .kfmh-gate-btn { min-width: 0; font-size: 1.15rem; padding: 0.5rem var(--space-4); }
}

/* ---------- celebration popup + quit sheet (patterns 6 and 7) ---------- */
.kfmh-overlay {
  position: absolute;
  inset: 0;
  background: var(--scrim-navy);
  display: grid;
  place-items: center;
  border-radius: var(--radius-lg);
  z-index: 6;
}
/* F-21 (review 08 EK 2, H5; EK 15 H32 for the landscape arm): the
   CELEBRATION card steps off the reward. The engine paints the veil itself
   (C.doneVeil, rgba(15,10,36,.32); the quit sheet keeps --scrim-navy: that
   one is an interruption, this one is a reward). Landscape (F-68d, Deniz
   2026-09-06 "kenarda kalmamalı ortalanmalı"): the card is centred on the
   SCENE column (= the house axis, not the stage box: the stage's centre sits
   167.5 px right of the axis because of the tray rail) and sits at the
   stage's foot, so the house and the friend stay above it and only the
   lower reflection goes under; the old "lean to the tray rail, 476 px"
   rule (EK 2 H5 / EK 3 H10 (a)) is gone. Portrait keeps its own foot rule
   below. Both orientations share the tight recipe (64px portrait, text-lg
   heading, 1rem body). */
.kfmh-stage[data-orient='l'] .kfmh-overlay--done {
  /* the overlay carries the stage's own grid and padding, so column 1 IS the
     scene column at every width; in full screen the stage's side padding
     (339.5 at 1920) arrives through `inherit` */
  grid-template-columns: minmax(0, 1fr) var(--kfmh-rail-w);
  column-gap: 12px;
  padding: inherit;
  box-sizing: border-box;
  place-items: end center;
}
.kfmh-stage[data-orient='l'] .kfmh-overlay--done .kfmh-card {
  grid-column: 1;
  justify-self: center;
  align-self: end;
  margin: 0 0 var(--space-2);
  /* 36rem = 612 px at the 17 px root (EK 15 H32): the widest card whose EN
     water body stays ONE line at 1366, so the card is 269 tall and its top
     clears the ground line + 8 (measured +44.5 / +28.5 / +237.5 at
     1280 / 1366 / 1920 full screen); 34rem wrapped the body and lost that. */
  max-width: 36rem;
}
/* EK 15 H32: the three pills on ONE row, 8 px apart (EN 510.8 px in a 561 px
   inner, TR 468.6); wrap stays on so a fallback font drops the third pill
   under instead of overflowing (that case is a report line in the gate). */
.kfmh-stage[data-orient='l'] .kfmh-overlay--done .kfmh-pills { gap: 8px; }
.kfmh-stage[data-orient='p'] .kfmh-overlay--done .kfmh-card {
  align-self: end;
  padding: var(--space-3);
}
/* EK 3 H10 (b), ACCEPTED by Nova. Measured r5 at 375x667: with --space-3
   under the portrait and the body the card's top sat at y 271, 2px above
   the ground line + 8 (273.3), so the friend's feet touched it; both gaps
   are --space-2 and the card's top measured 288 (porch/roof/water) and 316
   (the retired r8 free layer), house y 95..265 and friend 205..265 uncovered; the reflection
   goes under the card on purpose (H5). The gate keeps: card top >= ground
   line + 8 (margin today 14.7px). EK 15 H32: the same recipe in BOTH
   orientations (the desktop card must fit the 305.7 px between the ground
   line + 8 and the stage's foot at 1280x800; the 96 px portrait did not). */
.kfmh-overlay--done .kfmh-card-ori { width: 64px; height: 64px; margin-bottom: var(--space-2); }
.kfmh-overlay--done .kfmh-card h2 { font-size: var(--text-lg); }
.kfmh-overlay--done .kfmh-card p { font-size: 1rem; margin-bottom: var(--space-2); }
/* F-68c (Nova EK 14, H31; Finn r16 F-21 finding: three stacked pills lifted
   the card 72 px onto the house and the friend at 375). Phone = the desktop
   2+1: next + again side by side at equal width (142 px, 8 px gap, 120 px for
   the text; both languages measured one line), back full width below; card
   top back at 288, ground line 265 clear. On the last house (no next) again
   fills the first row alone. */
.kfmh-stage[data-orient='p'] .kfmh-overlay--done .kfmh-pills { flex-direction: row; flex-wrap: wrap; gap: 8px; }
.kfmh-stage[data-orient='p'] .kfmh-overlay--done .kfmh-pill[data-act='next'],
.kfmh-stage[data-orient='p'] .kfmh-overlay--done .kfmh-pill[data-act='again'] { flex: 1 1 0; min-width: 0; padding: 0 8px; }
.kfmh-stage[data-orient='p'] .kfmh-overlay--done .kfmh-pill[data-act='back'] { flex: 0 0 100%; }
.kfmh-card {
  position: relative;
  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;
  width: calc(100% - 2 * var(--space-3));
  text-align: center;
  max-height: calc(100% - 2 * var(--space-3));
  overflow-y: auto;
  box-sizing: border-box;
}
@media (max-width: 520px) {
  .kfmh-card { max-width: 90vw; }
}
.kfmh-card-ori {
  display: block;
  width: 96px;
  height: 96px;
  object-fit: cover;
  object-position: 50% 20%;
  border-radius: 50%;
  border: 4px solid var(--kf-lilac-soft);
  margin: 0 auto var(--space-3);
}
.kfmh-card h2 {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--text-xl);
  color: var(--kf-purple-deep);
  margin: 0 0 var(--space-2);
}
.kfmh-card p {
  font-size: var(--text-base);
  color: var(--color-text);
  margin: 0 0 var(--space-3);
}
.kfmh-pills {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-3);
}
.kfmh-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;
}
.kfmh-pill--gold {
  background: linear-gradient(180deg, #ffd94d 0%, #f0b929 100%);
  box-shadow: 0 4px 0 rgba(45, 27, 78, 0.28), var(--shadow-soft);
}
.kfmh-pill--calm {
  background: var(--color-surface);
  box-shadow: 0 4px 0 rgba(45, 27, 78, 0.22), var(--shadow-soft);
}
.kfmh-pill:focus-visible { outline: 3px solid var(--color-link-strong); outline-offset: 3px; box-shadow: 0 0 0 2px #fff; }
/* the quit sheet: close 64px top-right, live mini of the scene */
.kfmh-card--sheet { padding-top: calc(var(--space-4) + 24px); }
.kfmh-close {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  width: 64px;
  height: 64px;
  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;
  padding: 0;
}
.kfmh-close:focus-visible { outline: 3px solid var(--color-link-strong); outline-offset: 2px; }
.kfmh-mini {
  width: 40%;
  max-width: 220px;
  margin: 0 auto var(--space-3);
  border-radius: var(--radius-sm);
  overflow: hidden;
  box-shadow: var(--shadow-soft);
}
.kfmh-mini svg { display: block; width: 100%; height: auto; }

/* ---------- loading skeleton + state panel (7.4) ---------- */
.kfmh-skeleton {
  max-width: 76rem;
  margin: 0 auto;
  min-height: 420px;
  border-radius: var(--radius-lg);
  background: linear-gradient(180deg, #f3ecdc 0%, #f3ecdc 52%, #e4efe9 52%, #e4efe9 100%);
  display: grid;
  place-items: center;
  animation: kfmh-pulse 1.8s ease-in-out infinite;
}
@keyframes kfmh-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.75; }
}
.kfmh-skeleton-txt {
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  color: var(--kf-purple-deep);
  margin: 0;
}
.kfmh-panel {
  max-width: 30rem;
  margin: 0 auto;
  background: var(--kf-lilac-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  text-align: center;
  display: grid;
  gap: var(--space-3);
  justify-items: center;
}
.kfmh-panel-ori {
  width: 120px;
  height: 120px;
  object-fit: cover;
  object-position: 50% 20%;
  border-radius: 50%;
}
.kfmh-panel p {
  margin: 0;
  font-size: var(--text-lg);
  color: var(--color-text);
}

/* ---------- screen-reader only (inside the one live region) ---------- */
.kfmh-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;
}

/* ---------- phones: the chrome tightens so the scene keeps its floor ----------
   (kept AFTER the tray rules above: same specificity, later wins) */
/* PHONES (measured 2026-09-03 at 375x667): the stage law gives 651px; the
   toolbar (64), a three-line band (90), the tray panel (140) and three gaps
   left the scene 352px tall, scale 0.352, door 45.8px, under the 48px
   readability floor. The chrome tightens here, not the cells: stage
   padding 4, gaps 4, band at 0.92rem, tray padding 2 and a 6px wood band.
   THE BAND RESERVES THREE LINES: the opener line of roof/water runs to
   three lines at this width, and a band that grows AFTER the scene was
   sized pushed the scene 9px out of the stage (measured: scene top 1px,
   under the scrolled-away header). A fixed reserve keeps the scene about
   379px, scale 0.379: door 49.3px, window 49.3, step (r 120) 45.5, all
   above their floors (48 / 44; the engine's build report carries the
   measured numbers). F-6 (review N5): the band's text is 1rem (16px), not
   0.92rem (15.6px, under the 17px floor); the reserve is three 1rem lines
   (74px) and the scene pays the 5px, which the step's r 115 -> 120 in the
   plan data gives back. The engine reads the real gap from the grid, never
   a constant, solves the cell/scene budget by iteration, and re-fits the
   scene if the band ever grows past its reserve. */
@media (max-width: 480px) {
  .kfmh-stage[data-orient='p'] { padding: 4px; gap: 4px; }
  .kfmh-stage[data-orient='p'] .kfmh-msg { font-size: 1rem; line-height: 1.3; padding: 4px 10px; min-height: calc(3 * 1.3 * 1rem + 8px); }
  .kfmh-stage[data-orient='p'] .kfmh-cells { padding: 2px 6px; gap: 10px; }
  .kfmh-stage[data-orient='p'] .kfmh-wood { height: 6px; }
}

/* ---------- reduced motion: everything rests ---------- */
@media (prefers-reduced-motion: reduce) {
  /* r15 EK 12 H26: the cart plate rests as it is; the fill was never
     animated, the rim's stitch stops at full 1.0 (the dash stays) */
  .kfmh-plate--burn .kfmh-plate-rim { animation: none; stroke-opacity: 1; }
  .kfmh-shimmer,
  .kfmh-shimmer .kfmh-ghost,
  .kfmh-shimmer .kfmh-reflsettled { animation: none; }
  .kfmh-skeleton { animation: none; }
  .kfmh-cells { transition: none; }
  .kfmh-cell { transition: none; }
  .kfmh-cell:active { transform: none; }
  .kfmh-gate { transition: none; }
  /* F-22: the look line is 6px (rim 16px) static for its 1.8s, no wave */
  .kfmh-plate-body, .kfmh-plate-rim, .kfmh-twin, .kfmh-ghost path { transition: none; }
}
