/* KIMIA MIND — the stage.
   One full-viewport scene: the creature centre-stage, conversation flowing
   beneath her, capabilities orbiting when her head opens, work happening in
   floating glass windows. No page chrome, no dashboard. */

/* character palette hooks (theme-aware) */
:root, :root[data-theme="light"] {
  --k-face-hi: #fdf9ef; --k-face-lo: #f1e6cf;
  --k-dome-hi: rgba(255, 252, 244, .92); --k-dome-lo: rgba(238, 226, 198, .78);
  --k-line: #4a3b20;
  --k-eye: #fffdf8;
  --k-lid: #efe3c8;
  --k-facet: rgba(217, 165, 33, .25);
}
:root[data-theme="dark"] {
  --k-face-hi: #2a2617; --k-face-lo: #1c1910;
  --k-dome-hi: rgba(56, 50, 30, .92); --k-dome-lo: rgba(34, 30, 18, .82);
  --k-line: #f0d79b;
  --k-eye: #fbf4e2;
  --k-lid: #322c1a;
  --k-facet: rgba(233, 185, 73, .3);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --k-face-hi: #2a2617; --k-face-lo: #1c1910;
    --k-dome-hi: rgba(56, 50, 30, .92); --k-dome-lo: rgba(34, 30, 18, .82);
    --k-line: #f0d79b; --k-eye: #fbf4e2; --k-lid: #322c1a;
    --k-facet: rgba(233, 185, 73, .3);
  }
}

/* ------------------------------------------------------------------ stage -- */

#stage {
  position: absolute; inset: 0;
  display: grid;
  grid-template-rows: 1fr auto;
  background: var(--bg);
  overflow: hidden;
}

.scene {
  position: relative;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: var(--s4);
  min-height: 0;
  padding-block-start: calc(var(--k-page-pad) + 8px);
}

.being {
  position: relative;
  width: min(46vh, 82vw, 420px);
  aspect-ratio: 400 / 420;
  transition: width var(--dur-slow) var(--ease-out);
  touch-action: manipulation;
}
/* Her halo is part of her body, not stage scenery: it scales with her and
   travels with her, so a small کیمیا has a small glow — never a vast empty
   gradient owning half the display. */
.being::before {
  content: "";
  position: absolute; inset: -18%;
  border-radius: 50%;
  background: radial-gradient(closest-side, var(--halo), transparent 72%);
  pointer-events: none;
}
.kimia-svg { position: relative; width: 100%; height: 100%; display: block; overflow: visible; }

/* Conversing: the conversation owns the ENTIRE stage. کیمیا floats above it
   as a small being with nothing behind her but her own glow — no reserved
   row, no band, no section. Thoughts scroll straight underneath her body. */
:root { --k-small: min(18vh, 36vw, 150px); }
#stage.conversing { grid-template-rows: 1fr; }
#stage.conversing .scene {
  position: absolute;
  inset-inline: 0; top: calc(var(--k-page-pad) + 4px);
  height: auto; min-height: 0;
  z-index: 40;
  padding: 0;
  pointer-events: none;            /* the page scrolls through her row */
}
#stage.conversing .being {
  width: var(--k-small);
  margin-block-start: 2px;
  pointer-events: auto;            /* …but she herself stays touchable */
}
#stage.conversing .flow { max-height: none; }
#stage.conversing .thoughts {
  /* first bubbles clear her body; scrolled ones pass behind her */
  padding-block-start: calc(var(--k-small) * 1.1 + 30px);
  mask-image: linear-gradient(to bottom, transparent 6px, rgba(0,0,0,.55) calc(var(--k-small) * 0.9), black calc(var(--k-small) * 1.12));
  -webkit-mask-image: linear-gradient(to bottom, transparent 6px, rgba(0,0,0,.55) calc(var(--k-small) * 0.9), black calc(var(--k-small) * 1.12));
}

/* Opening her head is a focus moment: she returns to full presence, the
   conversation politely steps back, and her core exposes the conversations. */
#stage.core-open { grid-template-rows: 1fr auto; }
#stage.core-open:not(.conversing) .scene {
  position: relative; inset: auto; z-index: 40;
  pointer-events: auto;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: var(--s4);
}
/* Conversing + core-open: she must NEVER switch positioning modes — a jump
   from the absolute float to a flex row teleports her (and every orbit seat
   with her). The scene stays exactly the overlay conversing made it; she
   simply grows in place through one width transition. */
#stage.conversing.core-open { grid-template-rows: 1fr; }
#stage.core-open .being { width: min(40vh, 78vw, 380px); }
#stage.core-open .flow {
  opacity: .12;
  filter: blur(7px) saturate(.7);
  pointer-events: none;
  max-height: 14vh;
  overflow: hidden;
  transition: opacity var(--dur-med) var(--ease-out), filter var(--dur-med) var(--ease-out),
              max-height var(--dur-med) var(--ease-out);
}
.flow { transition: opacity var(--dur-med) var(--ease-out), filter var(--dur-med) var(--ease-out); }

/* The stage breathes at both ends: the top fade lives in the thoughts mask,
   and this is its bottom twin — a transparent gradient, never a surface. */
#stage::after {
  content: "";
  position: absolute;
  inset-inline: 0; bottom: 0;
  height: 110px;
  background: linear-gradient(to top, var(--bg) 8%, transparent);
  opacity: .82;
  pointer-events: none;
  z-index: 55;
}

/* the creature's spoken status, a whisper under her */
.k-status {
  position: absolute;
  inset-inline: 0;
  bottom: -6px;
  text-align: center;
  font-size: var(--text-sm);
  color: var(--ink-2);
  pointer-events: none;
  transition: opacity var(--dur-med) var(--ease-out);
}
.k-status .elapsed { font-family: var(--mono); font-size: var(--text-xs); color: var(--ink-3); }

/* ------------------------------------------------------------- orbit nodes -- */
/* Revealed when her head opens: capabilities arranged around the core. */

.orbit {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  pointer-events: none;
}
.orbit-node {
  position: absolute;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: var(--s3) var(--s5);
  border-radius: var(--r-lg);
  background: transparent;
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  backdrop-filter: blur(20px) saturate(1.4);
  border: 1px solid var(--hairline);
  color: var(--ink);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  /* Hidden nodes must be untouchable — an invisible button that still opens
     windows is a trap, not an affordance. */
  pointer-events: none;
  opacity: 0;
  /* Closed = collapsed INTO the head (the JS stamps each node's vector back
     to the head centre); open = flown out to its ring seat. One transition
     covers both directions — nothing vanishes, everything travels. */
  transform: translate(var(--to-head-x, 0px), var(--to-head-y, 0px)) scale(.05);
  /* ONE PATH: seats (left/top) and collapse vectors are stamped ONCE per
     open, from her FINAL grown geometry — never re-stamped mid-flight, so
     left/top must NOT transition (an easing seat mid-collapse is what made
     nodes glide onto each other). Only the reveal morph animates. */
  transition: opacity var(--dur-med) var(--ease-out) var(--stagger, 0ms),
              transform var(--dur-med) var(--ease-spring) var(--stagger, 0ms),
              box-shadow var(--dur-fast) var(--ease-out);
}
.orbit.open .orbit-node { opacity: 1; transform: none; pointer-events: auto; }
.orbit-node .ic { color: var(--accent); }
.orbit-node:hover { box-shadow: var(--glow); }
/* No :active transform: while the core closes, :active outranks the base
   collapse pose — the very node being tapped froze in place while its
   siblings flew into her head. Press feedback must never own `transform`. */
.orbit-node:active { filter: brightness(.92); }

/* stroke icons inherit text colour and align optically */
.ic { display: inline-grid; place-items: center; line-height: 0; flex: none; }
.orbit-node .count {
  position: absolute; top: -6px; inset-inline-end: -6px;
  min-width: 20px; height: 20px; padding-inline: 5px;
  display: grid; place-items: center;
  background: var(--gem-grad); color: #241a04;
  font-size: var(--text-xs); font-weight: 700;
  border-radius: var(--r-full);
  box-shadow: var(--shadow-1);
}

/* Conversations revealed under her open head — the first thing you reach. */
.core-convs {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  width: min(400px, calc(100vw - 48px));
  z-index: 45;
  /* Always mounted: zero layout when closed, one soft unified motion when
     open. Display toggles are what flash — so there are none. */
  height: 0; overflow: visible;
  opacity: 0;
  /* collapses up into the head, blooms back out of it */
  transform: translateY(-52px) scale(.12);
  transform-origin: top center;
  pointer-events: none;
  transition: opacity var(--dur-med) var(--ease-out),
              transform var(--dur-med) var(--ease-spring);
}
#stage.core-open .core-convs {
  height: auto; max-height: 34vh; overflow-y: auto;
  overscroll-behavior: contain;
  opacity: 1; transform: none;
  pointer-events: auto;
}
.core-conv {
  display: flex; align-items: center; gap: var(--s3);
  border: 1px solid var(--hairline);
  background: transparent;
  -webkit-backdrop-filter: blur(22px) saturate(1.4);
  backdrop-filter: blur(22px) saturate(1.4);
  border-radius: var(--r-full);
  padding: var(--s2) var(--s4);
  font: inherit; font-size: var(--text-sm); font-weight: 570;
  color: var(--ink);
  cursor: pointer; text-align: start;
}
.core-conv .ic { color: var(--accent); }
.core-conv:hover { border-color: var(--hairline-strong); }
.core-conv .t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.core-conv.new { font-weight: 700; }

/* ------------------------------------------------------------ conversation -- */

.flow {
  position: relative;
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  min-height: 0;
}
#stage:not(.conversing) .flow .thoughts { display: none; }

/* The stage's foot inset: the on-screen keyboard when it is up (--k-kb is
   the visual-viewport gap, stamped by JS), the home-bar inset when it isn't.
   max() — never both: the home bar lives UNDER an open keyboard. */
:root { --k-foot: max(env(safe-area-inset-bottom, 0px), var(--k-kb, 0px)); }

.thoughts {
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Clearance tracks the LIVE pill height (--k-composer-h, stamped on every
     grow) — messages keep their distance as the composer gains lines. */
  padding: var(--s5) clamp(var(--s4), 6vw, 12vw)
           calc(var(--k-composer-h, 51px) + 45px + var(--k-foot));
  /* THE ANCHOR LAW: plain BLOCK flow, never flex. WebKit resolves a flex
     item's cross-axis auto margins LOGICALLY under RTL — margin-left:auto
     and margin-right:auto BOTH collapse toward the inline start, which piled
     every bubble into the right column on iPhones while Chrome (physical
     resolution) looked correct. Block-flow auto margins have been physical
     on every engine since CSS1 — so the sides are picked here and nowhere
     else. */
  display: block;
  /* NO scroll-behavior:smooth here — it turned every programmatic pin into
     an animation: opening a conversation flew through the whole history and
     mid-flight scrollTop reads broke the keep-at-foot check. Instant pins by
     default; the one place that WANTS easing asks for it explicitly. */
  mask-image: linear-gradient(to bottom, transparent, black 28px);
  -webkit-mask-image: linear-gradient(to bottom, transparent, black 28px);
}

.bubble {
  width: -webkit-fit-content;          /* older WebKit spells it this way */
  width: fit-content;
  max-width: min(760px, 94%);
  animation: bubble-in var(--dur-med) var(--ease-out) both;
}
.bubble + .bubble { margin-block-start: var(--s5); }
@keyframes bubble-in { from { opacity: 0; transform: translateY(10px) scale(.98); } }

.bubble.you { margin-left: auto; margin-right: 0; }
.bubble.you .inner {
  background: var(--gem-grad);
  color: #241a04;
  font-weight: 550;
  border-radius: var(--r-lg) var(--r-lg) var(--r-md) var(--r-lg);
  padding: var(--s3) var(--s5);
  box-shadow: var(--shadow-1);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.bubble.her { margin-right: auto; margin-left: 0; width: 100%; display: flex; gap: var(--s3); align-items: flex-start; direction: ltr; }
[dir="rtl"] .bubble.her .inner { direction: rtl; }
[dir="ltr"] .bubble.her .inner { direction: ltr; }
.bubble.her .mini {
  flex: none; width: 34px; height: 34px; margin-top: 2px;
  border-radius: var(--r-full);
  background: var(--gem-grad);
  color: #241a04;
  display: grid; place-items: center;
  box-shadow: var(--shadow-1);
}
.bubble.her .inner {
  flex: 1; min-width: 0;
  background: var(--material);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  backdrop-filter: blur(20px) saturate(1.3);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md) var(--r-lg) var(--r-lg) var(--r-lg);
  padding: var(--s5) var(--s6);
  box-shadow: var(--shadow-1);
  overflow-wrap: anywhere;
}

/* the composer — her ear. iMessage anatomy: one translucent pill, one
   hairline, generous inline padding, a round send inside the end. Focus
   changes NOTHING but the caret — no rings, no gold, no second box. */
/* Floats over everything at the stage's foot; the wrapper is untouchable so
   bubbles beneath stay scrollable — only the pill itself takes the finger. */
.composer {
  position: absolute;
  inset-inline: 0; bottom: 0;
  z-index: 60;
  pointer-events: none;
  /* --k-foot pins the pill above the on-screen keyboard (visual viewport
     law) — the caret can never sit anywhere but inside the visible pill. */
  padding: var(--s3) var(--s4) calc(var(--s4) + var(--k-foot));
}
.composer-inner {
  pointer-events: auto;
  max-width: 640px;
  margin-inline: auto;
}
#stage.core-open .composer {
  opacity: .12;
  filter: blur(7px);
  transition: opacity var(--dur-med) var(--ease-out), filter var(--dur-med) var(--ease-out);
}
#stage.core-open .composer .composer-inner { pointer-events: none; }
.composer-inner {
  display: flex; align-items: flex-end; gap: var(--s2);
  background: transparent;
  /* Liquid glass: barely-there blur, a lift in saturation and light — the
     conversation stays readable straight through the pill. */
  -webkit-backdrop-filter: blur(12px) saturate(1.5) brightness(1.04);
  backdrop-filter: blur(12px) saturate(1.5) brightness(1.04);
  border: 1px solid var(--hairline);
  /* 42px: a full pill at one line (radius clamps to half-height), a soft
     rounded slab as lines grow — never ballooning toward a circle. */
  border-radius: 42px;
  padding: 5px 6px;
  padding-inline-start: var(--s5);
}
.composer-inner:focus-within { border-color: var(--hairline-strong); }
.composer textarea {
  flex: 1;
  background: none !important; border: 0 !important; box-shadow: none !important;
  border-radius: 0;
  resize: none;
  font: inherit; font-size: 14.5px; color: var(--ink);
  padding: 8px 2px;
  max-height: 160px; min-height: 24px;
  /* Hidden until the cap: the caret lives inside the pill at every height;
     past 160px the field scrolls internally (JS flips this to auto). */
  overflow-y: hidden;
  outline: none;
}
.composer textarea::placeholder { color: var(--ink-3); }
.send {
  flex: none; width: 34px; height: 34px;
  /* 34 + 2×2.5 = the single-line textarea's 39px: dead-centred at rest,
     bottom-anchored (with the same breath) once the pill grows lines. */
  margin-block: 2.5px;
  border: 0; border-radius: var(--r-full);
  background: var(--gem-grad); color: #241a04;
  font-size: 17px; cursor: pointer;
  display: grid; place-items: center;
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-fast);
}
.send:hover { box-shadow: var(--glow); }
.send:active { transform: scale(.9); }
.send:disabled { opacity: .45; }
/* iMessage law: the send arrow exists only once there is something to send. */
.send.is-empty {
  opacity: 0;
  transform: scale(.3);
  pointer-events: none;
}
.send { transition: transform var(--dur-fast) var(--ease-spring), opacity var(--dur-fast), box-shadow var(--dur-fast); }

/* steps inside her bubbles */
.k-steps { display: flex; flex-direction: column; gap: 5px; margin-block: var(--s2); }
.k-step { display: flex; align-items: baseline; gap: 8px; font-size: var(--text-sm); color: var(--ink-2); }
.k-step i {
  flex: none; width: 7px; height: 7px; border-radius: var(--r-full);
  background: var(--ink-3); position: relative; top: -1px;
}
.k-step.ok i { background: var(--ok); }
.k-step.fail i { background: var(--bad); }
.k-step.running i { background: var(--accent); animation: pulse 1.1s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: .3; transform: scale(.75); } }

/* ------------------------------------------------------------------- menu -- */

.menu-btn {
  position: absolute; z-index: 300;
  top: calc(var(--s4) + var(--k-page-pad));
  inset-inline-start: var(--s4);
  width: 44px; height: 44px;
  border: 1px solid var(--hairline); border-radius: var(--r-full);
  background: transparent;
  -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
  cursor: pointer;
  display: grid; place-items: center; gap: 0;
  transition: transform var(--dur-fast) var(--ease-spring);
}
.menu-btn:active { transform: scale(.9); }
.menu-btn .bars { display: flex; flex-direction: column; gap: 4px; }
.menu-btn .bars i {
  width: 16px; height: 2px; border-radius: 2px; background: var(--ink);
  transition: transform var(--dur-med) var(--ease-spring), opacity var(--dur-fast);
}
.menu-btn.open .bars i:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.menu-btn.open .bars i:nth-child(2) { opacity: 0; }
.menu-btn.open .bars i:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.menu {
  position: absolute; z-index: 290;
  top: calc(var(--s4) + var(--k-page-pad) + 54px);
  inset-inline-start: var(--s4);
  min-width: 248px;
  background: var(--material-thick);
  -webkit-backdrop-filter: blur(26px) saturate(1.5);
  backdrop-filter: blur(26px) saturate(1.5);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
  padding: var(--s2);
  transform-origin: top var(--origin-side, left);
  transform: scale(.7);
  opacity: 0; pointer-events: none;
  transition: transform var(--dur-med) var(--ease-spring), opacity var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .menu { --origin-side: right; }
.menu.open { transform: scale(1); opacity: 1; pointer-events: auto; }
.menu-item {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%; border: 0; background: none;
  padding: var(--s3) var(--s4); border-radius: var(--r-sm);
  font: inherit; font-weight: 570; color: var(--ink);
  cursor: pointer; text-align: start;
}
.menu-item:hover { background: var(--surface-2); }
.menu-item .ic { color: var(--ink-2); }
.menu-item:hover .ic { color: var(--accent); }
.menu-item .chev { margin-inline-start: auto; color: var(--ink-3); font-size: 11px;
                   transition: transform var(--dur-fast) var(--ease-out); }
.menu-sub {
  overflow: hidden; max-height: 0;
  transition: max-height var(--dur-med) var(--ease-out);
}
.menu-sub.open { max-height: 300px; }
.menu-sub .menu-item { padding-inline-start: calc(var(--s4) + 30px); font-weight: 480; font-size: var(--text-sm); }
.menu-sep { height: 1px; background: var(--hairline); margin: var(--s2) var(--s3); }

/* --------------------------------------------------------------- windows -- */

.win {
  position: absolute; z-index: 200;
  width: min(880px, calc(100vw - 24px));
  height: min(72vh, 720px);
  display: flex; flex-direction: column;
  background: var(--material-thick);
  -webkit-backdrop-filter: blur(30px) saturate(1.6);
  backdrop-filter: blur(30px) saturate(1.6);
  border: 1px solid var(--hairline);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-2);
  overflow: hidden;
  animation: win-in var(--dur-med) var(--ease-spring) both;
}
@keyframes win-in { from { opacity: 0; transform: scale(.92) translateY(16px); } }
.win.closing { animation: win-out var(--dur-fast) var(--ease-out) both; }
@keyframes win-out { to { opacity: 0; transform: scale(.95) translateY(10px); } }
.win.front { z-index: 240; }

.win-bar {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4);
  cursor: grab; user-select: none;
  flex: none;
}
.win-bar:active { cursor: grabbing; }
.win-title { font-weight: 650; font-size: var(--text-md); flex: 1; min-width: 0;
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.win-dot {
  width: 13px; height: 13px; border-radius: var(--r-full);
  border: 0; cursor: pointer; padding: 0;
  transition: transform var(--dur-fast) var(--ease-spring);
}
.win-dot:hover { transform: scale(1.25); }
.win-dot.close { background: #f26d60; }
.win-dot.mini { background: var(--gold-400); }
.win-body { flex: 1; overflow: auto; overscroll-behavior: contain; padding: var(--s6); }
.win-title .ic { color: var(--accent); vertical-align: -4px; margin-inline-end: 6px; }

/* ------------------------------------------------------------------- deck -- */
/* Minimised windows rest here as gems. */

.deck {
  position: absolute; z-index: 250;
  bottom: calc(var(--s2) + env(safe-area-inset-bottom));
  inset-inline: 0;
  display: flex; flex-direction: row; justify-content: center;
  gap: var(--s2);
  pointer-events: none;
}
.deck > * { pointer-events: auto; }
body.has-deck .composer {
  padding-block-end: calc(var(--s3) + 56px + var(--k-foot));
}
body.has-deck .thoughts {
  padding-block-end: calc(var(--k-composer-h, 51px) + 99px + var(--k-foot));
}
.deck-gem {
  width: 46px; height: 46px;
  color: var(--accent);
  border: 1px solid var(--hairline); border-radius: var(--r-lg);
  background: transparent;
  -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
  box-shadow: var(--shadow-1);
  display: grid; place-items: center;
  font-size: 19px; cursor: pointer;
  animation: win-in var(--dur-med) var(--ease-spring) both;
  transition: transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-fast);
}
.deck-gem:hover { transform: translateY(-3px); box-shadow: var(--glow); }
.deck-gem.is-min { opacity: .42; }
.deck-gem.is-min:hover { opacity: .8; }

/* ----------------------------------------------------------------- top bar -- */

.top-actions {
  position: absolute; z-index: 300;
  top: calc(var(--s4) + var(--k-page-pad));
  inset-inline-end: var(--s4);
  display: flex; gap: var(--s2);
}
.chip-btn {
  height: 40px; min-width: 40px; padding-inline: var(--s3);
  border: 1px solid var(--hairline); border-radius: var(--r-full);
  background: transparent;
  -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
  font: inherit; font-size: var(--text-sm); font-weight: 600; color: var(--ink);
  display: inline-flex; align-items: center; gap: 6px;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-fast);
}
.chip-btn:hover { box-shadow: var(--glow); }
.chip-btn:active { transform: scale(.93); }

/* ---------------------------------------------------------------- entry ---- */

.gate {
  position: absolute; inset: 0; z-index: 400;
  display: grid; place-items: center;
  background: var(--bg);
  transition: opacity var(--dur-slow) var(--ease-out);
}
.gate.hidden { opacity: 0; pointer-events: none; }
.gate-card {
  width: min(360px, calc(100vw - 40px));
  display: flex; flex-direction: column; gap: var(--s3);
  padding: var(--s6);
  background: var(--material-thick);
  -webkit-backdrop-filter: blur(26px); backdrop-filter: blur(26px);
  border: 1px solid var(--hairline);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-2);
}
.gate-card h1 { margin: 0; font-size: var(--text-xl); text-align: center; }
.gate-card p { margin: 0 0 var(--s2); color: var(--ink-2); text-align: center; font-size: var(--text-sm); }
.gate-card input {
  font: inherit; color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--hairline); border-radius: var(--r-md);
  padding: var(--s3) var(--s4);
  direction: ltr; text-align: left;
}
.gate-card input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.gate-card .go {
  border: 0; border-radius: var(--r-md);
  background: var(--gem-grad); color: #241a04;
  font: inherit; font-weight: 700; padding: var(--s3);
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-fast);
}
.gate-card .go:hover { box-shadow: var(--glow); }
.gate-card .go:active { transform: scale(.97); }
.gate-error { color: var(--bad); font-size: var(--text-sm); text-align: center; min-height: 1.4em; margin: 0; }

/* -------------------------------------------------------------- responsive -- */

@media (max-width: 720px) {
  /* Sheets have no traffic lights — gestures rule here. Content extends
     behind the status bar; the bar padding keeps titles readable under the
     clock (the OGA page-pad law: insets live inside the header). */
  .win-dot { display: none; }
  /* The grab handle lives at 8px+pad and is 4px tall — the title must START
     below it with real air (spacing law: text never touches a control).
     26px+pad puts 14px between the handle and the title's cap height. */
  .win-bar { cursor: default; padding-block-start: calc(26px + var(--k-page-pad)); }
  .win-body { padding-block-end: calc(var(--s6) + env(safe-area-inset-bottom)); }
  /* Symmetric chat: equal inline padding, no avatar column — and the sides
     must READ: hers hugs the left edge, yours hugs the right. A full-width
     bubble reads as neither. */
  .thoughts { padding-inline: var(--s4); }
  .being { width: min(38vh, 74vw, 340px); }
  #stage.conversing .being { width: min(15vh, 34vw, 130px); }
  /* Core-open must beat the compact conversing size on every breakpoint —
     the orbit can only breathe around a full-size head. */
  #stage.core-open .being { width: min(38vh, 74vw, 340px); }
  .flow { max-height: 68vh; }

  /* Windows own the phone entirely: true fullscreen, above every floating
     control — the menu and chips belong to the base page underneath. */
  .win {
    inset: 0 !important;
    width: 100%;
    height: 100%;          /* body carries the height law — never dvh here */
    border-radius: 0;
    border-color: transparent;   /* at rest the window IS the screen */
    z-index: 500;
    animation: sheet-in var(--dur-med) var(--ease-out) both;
  }
  .win.dragging { border-color: var(--hairline-strong); }
  .win.dragging .win-body { overflow: hidden !important; touch-action: none !important; }
  .win.dragging .win-body * { pointer-events: none !important; }
  /* Installed PWA: the window matches the device's own corner curve. */
  html.kimia-pwa .win { border-radius: 42px; }
  .win.front { z-index: 520; }
  @keyframes sheet-in { from { transform: translateY(60%); opacity: .4; } }
  .win-bar::before {
    content: ""; position: absolute; top: calc(8px + var(--k-page-pad)); inset-inline: 0;
    margin-inline: auto; width: 42px; height: 4px;
    border-radius: var(--r-full); background: var(--hairline-strong);
  }
}

/* MESSENGER ANATOMY — one dedicated block, one gate: every phone, EVERY
   orientation. Width alone missed landscape iPhones (736–932 CSS px), which
   silently fell back to the desktop avatar-row anatomy on rotation. */
@media (max-width: 720px), (pointer: coarse) and (max-height: 500px) {
  .bubble.her .mini { display: none; }
  /* The caps leave a REAL free lane on the far side — even a paragraph-long
     message visibly belongs to its edge. Caps close to 100% made every long
     bubble a full-width bar: one column, no sides. display:block — nothing
     in a bubble's anchoring path is a flex item (the anchor law). */
  .bubble.her {
    display: block;
    width: -webkit-fit-content;
    width: fit-content;
    max-width: 84%;
  }
  .bubble.you { max-width: 78%; }
  /* Interactive content needs ROOM, not shrink-wrap: percentage-wide inputs
     and 1fr chart tracks contribute zero intrinsic width, so a fit-content
     bubble starved question forms and charts into slivers. Bubbles carrying
     them fill the lane instead. */
  .bubble.her:has(.q-form, .chart, .table-scroll, .artifacts, .card, table) {
    width: auto;
    max-width: 92%;
  }
}

@media (min-width: 721px) and (max-width: 1100px) {
  .win { width: min(720px, calc(100vw - 32px)); }
}

/* Resize from ANY edge or corner — invisible hit zones, correct cursors. */
.win-edge { position: absolute; z-index: 5; }
.win-edge.n { top: -3px; inset-inline: 10px; height: 8px; cursor: ns-resize; }
.win-edge.s { bottom: -3px; inset-inline: 10px; height: 8px; cursor: ns-resize; }
.win-edge.e { right: -3px; inset-block: 10px; width: 8px; cursor: ew-resize; }
.win-edge.w { left: -3px; inset-block: 10px; width: 8px; cursor: ew-resize; }
.win-edge.ne { top: -4px; right: -4px; width: 14px; height: 14px; cursor: nesw-resize; }
.win-edge.sw { bottom: -4px; left: -4px; width: 14px; height: 14px; cursor: nesw-resize; }
.win-edge.nw { top: -4px; left: -4px; width: 14px; height: 14px; cursor: nwse-resize; }
.win-edge.se { bottom: -4px; right: -4px; width: 14px; height: 14px; cursor: nwse-resize; }
@media (max-width: 720px) { .win-edge { display: none; } }

.win-dot.full { background: #58c26a; }
.win.max {
  inset: 10px !important;
  width: auto !important;
  height: auto !important;
  z-index: 400;
}
.win.max.front { z-index: 410; }


/* ---------------------------------------------------------- genie + wheel -- */

/* Every show/hide is a morph toward or from the deck — never a toggle. */
.win { transition: transform 320ms var(--ease-out), opacity 320ms var(--ease-out); }
.win.genie-out {
  transform: translate(var(--gx, 0px), var(--gy, 60vh)) scale(.05) !important;
  opacity: .1;
  pointer-events: none;
  animation: none !important;
}
/* Pull-right dismiss: the window leaves the way the finger sent it — a clean
   slide off the right edge, no scale detour. */
.win.slide-out {
  transform: translateX(118vw) !important;
  opacity: .6;
  pointer-events: none;
  animation: none !important;
}

/* THE CARDS-WHEEL (OGA anatomy, ported verbatim): windows live in permanent
   slots inside ONE layer. Outside the wheel the slots are display:contents —
   zero layout effect, the windows behave as direct layer children. In wheel
   mode the LAYER becomes a native horizontal scroll row and each live window
   is scaled down (origin 0 0) inside its slot. No overlay scroller, no pose
   math per frame, no re-parenting, no duplicates. */
.win-layer { position: absolute; inset: 0; z-index: 200; pointer-events: none; }
.win-layer:has(.win.max) { z-index: 400; }
@media (max-width: 720px) { .win-layer { z-index: 500; } }
.win-slot { display: contents; }
.win-layer .win { pointer-events: auto; }

body.wheel .win-layer { z-index: 480; }
.win-layer.is-wheel {
  display: flex; align-items: center; justify-content: flex-start; gap: 22px;
  overflow-x: auto; overflow-y: hidden;
  touch-action: pan-x;
  padding: 24px;
  pointer-events: auto;
  /* ltr ONLY for the row's scrollLeft math — direction INHERITS, and letting
     it reach the windows re-laid-out every card's content LTR (the wheel
     RTL-flash bug). The slots restore the document's direction immediately,
     so the windows themselves never change AT ALL inside the wheel. */
  direction: ltr;
  background: rgba(4, 8, 16, .38);
  -webkit-backdrop-filter: blur(16px) saturate(1.1);
  backdrop-filter: blur(16px) saturate(1.1);
  scrollbar-width: none;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.win-layer.is-wheel::-webkit-scrollbar { display: none; }
.win-layer.is-wheel .win-slot {
  display: block; position: relative; flex: 0 0 auto; cursor: pointer;
  /* The document's direction re-enters HERE: the layer's ltr is row math
     only, and a carded window must be byte-identical to its live self —
     no re-layout, no text realignment, no flash. */
  direction: ltr;                 /* ltr documents */
  overflow: hidden; border-radius: 22px;
  border: 1px solid var(--hairline-strong);
  background: var(--bg);
  /* Slot owns the rounded corners; isolate + own layer so the transformed
     child is re-clipped cleanly by this radius (the OGA Safari seam fix).
     The slot owns card gestures via pointer capture — no native scroll can
     steal them mid-way. */
  isolation: isolate;
  transform: translateZ(0);
  touch-action: none;
}
[dir="rtl"] .win-layer.is-wheel .win-slot { direction: rtl; }
/* Carded windows are a PICKER: content inert, chrome hidden, no transitions
   (a transition would fight the FLIP), no hover effects at all. */
.win-layer.is-wheel .win,
.win-layer.is-wheel .win * {
  pointer-events: none !important;
  touch-action: none !important;
  -webkit-user-select: none !important; user-select: none !important;
}
.win-layer.is-wheel .win {
  transition: none !important;
  animation: none !important;
  border-radius: 0 !important;
  border-color: transparent !important;
  opacity: 1 !important;
  left: 0 !important; top: 0 !important;
  right: auto !important; bottom: auto !important;
  overflow: hidden;
}
.win-layer.is-wheel .win-bar { display: none !important; }
.win-layer.is-wheel .win-edge { display: none !important; }
/* The deck (with its dimmed gems) stays visible and tappable over the wheel —
   tapping a gem picks that window. */
body.wheel .deck { z-index: 497; }

/* Edge blocking: the page never rubber-bands or navigates on overscroll. */
html, body { overscroll-behavior: none; }
