/* ─── Ask Sunny · airship communicator skin (layers-v1 assembly) ───
 *
 * Pendant: the closed communicator hangs behind the airship gondola's tail.
 * Open state: Codex's layered machine on a 1586x992 design grid -- Sunny's
 * call feed BEHIND the mechanical frame (the brass rim masks her), all text
 * as real DOM per HANDOFF-TO-CC.md. Decorative PNGs never take pointer
 * events; Close and Send stay real buttons with >=44px hit areas.
 */

.ask-sunny-overlay {
  position: absolute;
  inset: 0;
  z-index: 40;
  pointer-events: none;
}
.ask-sunny-overlay.is-transition .ask-sunny-bubble,
.ask-sunny-overlay.is-anchor-lost .ask-sunny-bubble { opacity: 0; pointer-events: none; }

.ask-sunny-bubble {
  position: absolute;
  transform: translate(-50%, 0);
  /* Base size; storymode.js overrides per frame from the ship's on-screen
   * size, so Start's smaller airship wears a smaller device. */
  width: 116px;
  height: 116px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  pointer-events: auto;
  opacity: .96;
  filter: drop-shadow(0 6px 14px rgba(8, 65, 78, .3));
  transition: opacity .3s ease;
  /* No motion of its own: the pendant rides the ship rigidly, like the
   * flags. Any life it shows is rotation in place (see unread below). */
}
/* Portrait art: canopy, lines and device in one piece. Contain keeps the
 * whole silhouette inside the square the ship-scale sizing gives us, and
 * bottom alignment pins the device rather than the canopy. */
.ask-sunny-bubble img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center bottom;
}
.ask-sunny-bubble:hover { opacity: 1; }
/* The pendant stays on the ship while the machine is open, dimmed under the
 * scrim exactly like the About and Map flags. */

/* Hover tag so the pendant reads as interactive -- above the device. */
.ask-sunny-tip {
  position: absolute;
  left: 50%;
  top: -8px;
  transform: translate(-50%, -100%) scale(.94);
  padding: 5px 11px;
  border: 1px solid rgba(255, 255, 255, .82);
  border-radius: 999px;
  background: rgba(244, 252, 246, .92);
  box-shadow: 0 6px 16px rgba(8, 65, 78, .22), inset 0 1px 0 #fff;
  color: #174e5a;
  font-family: var(--round);
  font-size: 12.5px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
}
.ask-sunny-bubble:hover .ask-sunny-tip,
.ask-sunny-bubble:focus-visible .ask-sunny-tip {
  opacity: 1;
  transform: translate(-50%, -100%) scale(1);
}

/* Unread: the About flag's ready recipe -- a glowing duplicate of the very
 * artwork breathing over itself -- plus an occasional shake and a dot in the
 * loading screen's teal. */
.ask-sunny-bubble.is-unread { animation: ask-sunny-shake 3.4s ease-in-out infinite; }
.ask-sunny-bubble.is-unread::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--pendant-image) center / contain no-repeat;
  filter:
    drop-shadow(0 0 3px rgba(255, 255, 255, 1))
    drop-shadow(0 0 8px rgba(255, 255, 255, .94))
    drop-shadow(0 0 17px rgba(213, 244, 255, .76));
  mix-blend-mode: screen;
  animation: ask-sunny-unread-glow 1.85s ease-in-out infinite;
  pointer-events: none;
}
@keyframes ask-sunny-unread-glow {
  0%, 100% { opacity: .48; }
  50% { opacity: .92; }
}
/* Pure rotation in place: xy stays welded to the ship. */
@keyframes ask-sunny-shake {
  0%, 24%, 100% { transform: translate(-50%, 0) rotate(0deg); }
  4% { transform: translate(-50%, 0) rotate(-9deg); }
  8% { transform: translate(-50%, 0) rotate(8deg); }
  12% { transform: translate(-50%, 0) rotate(-6deg); }
  16% { transform: translate(-50%, 0) rotate(4deg); }
  20% { transform: translate(-50%, 0) rotate(-2deg); }
}
@media (prefers-reduced-motion: reduce) {
  .ask-sunny-bubble.is-unread { animation: none; }
  .ask-sunny-bubble.is-unread::before { animation: none; opacity: .88; }
}

/* Comic speech bubble over Sunny's head. Dressed like the crossing-wall
 * hint (frosted glass, mono type), but the silhouette is a single svg path
 * drawn in JS from the rendered box -- body and tail traced in one stroke,
 * so fill, hairline and blur share one outline with nothing to seam. */
.ask-sunny-speech[hidden] { display: none; }
.ask-sunny-speech {
  /* Fixed on body: above the vinyl room's fixed plate (z 30), below the
   * Map/About/chat overlays (86+). */
  position: fixed;
  transform: translate(-50%, calc(-100% - 30px));
  width: max-content;
  /* Wide enough that a one-breath mood line stays on one line in mono; the
     box still shrink-wraps, so shorter worlds do not get a wider bubble. */
  max-width: min(46vw, 30em);
  /* Bottom padding carries the tail's height so the text never sits on it. */
  padding: 10px 15px 20px;
  color: #174f58;
  font: 600 13px/1.45 var(--mono, "IBM Plex Mono", ui-monospace, monospace);
  letter-spacing: .03em;
  pointer-events: auto;
  cursor: pointer;
  opacity: 0;
  transition: opacity .3s ease, margin-top .3s ease;
  margin-top: 6px;
  z-index: 45;
  filter: drop-shadow(0 6px 20px rgba(7, 75, 80, .13)) drop-shadow(0 0 22px rgba(255, 255, 255, .5));
}
.ask-sunny-speech-text { position: relative; z-index: 1; }
/* The one path: filled and stroked together, so the edge is the silhouette. */
.ask-sunny-speech-skin {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.ask-sunny-speech-skin path {
  fill: rgba(247, 255, 250, .74);
  stroke: rgba(255, 255, 255, .8);
  stroke-width: 1;
}
/* The frosted blur, clipped to that same path. */
.ask-sunny-speech::before {
  content: "";
  position: absolute;
  inset: 0;
  backdrop-filter: blur(7px) saturate(1.05);
  -webkit-backdrop-filter: blur(7px) saturate(1.05);
  clip-path: var(--speech-clip, none);
  -webkit-clip-path: var(--speech-clip, none);
}
.ask-sunny-speech.is-visible { opacity: 1; margin-top: 0; }
.ask-sunny-speech.is-anchor-lost { opacity: 0; pointer-events: none; }
/* Start's welcome sits beside Sunny on her left; the tail swings to the
 * right edge (the path generator mirrors it). */
.ask-sunny-speech.is-beside {
  transform: translate(calc(-100% - 26px), -60%);
  padding: 10px 26px 10px 15px;
}

/* Pendant edit mode panel (?pendant-edit=1). Parked in the top-left
 * corner: the open machine is centered and now wide enough that a
 * vertically centered panel sat on top of its left-hand device. */
.ask-edit-panel {
  position: fixed;
  left: 12px;
  top: 12px;
  z-index: 2147483646;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(10, 40, 50, .88);
  color: #d5f4ff;
  font: 12px/1.6 var(--mono);
  max-width: 340px;
}
.ask-edit-panel textarea {
  display: block;
  width: 100%;
  margin: 6px 0;
  font: 11px/1.4 var(--mono);
  background: rgba(255, 255, 255, .08);
  color: inherit;
  border: 1px solid rgba(255, 255, 255, .25);
  border-radius: 6px;
}
.ask-edit-panel button {
  margin-right: 8px;
  padding: 4px 10px;
  border: 0;
  border-radius: 6px;
  background: #55c8da;
  color: #06323e;
  font: inherit;
  cursor: pointer;
}

/* ── Lightbox room, same behavior as About and Map ── */
html.is-ask-sunny-open, body.is-ask-sunny-open { overflow: hidden; }
.ask-sunny-lightbox[hidden] { display: none; }
.ask-sunny-lightbox {
  position: fixed;
  inset: 0;
  /* Same layer neighborhood as the Map (86): the chat sits at 87 and the
   * journey header rises above it, exactly like Map and About. */
  z-index: 87;
  display: grid;
  place-items: center;
  opacity: 0;
  transition: opacity .3s ease;
}
.ask-sunny-lightbox.is-open,
/* Waking counts as on-screen: the scrim and the note are the whole point of
 * this state, and a transparent lightbox would hide them along with the
 * machine they are standing in for. */
.ask-sunny-lightbox.is-waking { opacity: 1; }
html {
  --scene-scrim-a: rgba(94, 187, 181, .52);
  --scene-scrim-b: rgba(62, 147, 142, .60);
}
.ask-sunny-scrim {
  position: absolute;
  inset: 0;
  /* Tinted glass in the world's own key -- the per-skin colours below
   * override this. One fixed wash could not work: the same pale blue that
   * suits the glass sea washes out Airbnb's blue-hour Beijing and fights
   * the AI Lab's pastel cream.
   *
   * The blur is real, and affordable here for one specific reason: the
   * journey's render loop early-returns while a lightbox is open
   * (journeyCovered() in storymode.js counts is-ask-sunny-open), so the
   * canvas behind is a frozen still. The house rule against a live
   * backdrop-filter is about blurring a stage that is still drawing every
   * frame; this one is blurred once and then sits there. Tint alpha drops
   * accordingly, so the world's own blurred shapes read through it rather
   * than being painted over. */
  -webkit-backdrop-filter: blur(8px) saturate(1.12);
  backdrop-filter: blur(8px) saturate(1.12);
  background: linear-gradient(145deg, var(--scene-scrim-a), var(--scene-scrim-b));
}
html.is-ask-sunny-open .journey-header { z-index: 89; }

/* ── The machine · 1586x992 design grid, children in percent ── */
.ask-sunny-machine {
  position: relative;
  aspect-ratio: 1586 / 992;
  /* One notch bigger than the first pass (1180px), so the busier world
   * skins have room for their controls. The gutters shrink with it, or a
   * mid-size window would stay pinned to the viewport term and never reach
   * the larger cap. */
  width: min(1239px, calc(100vw - 24px), calc((100vh - 40px) * 1586 / 992));
  transform: scale(.9);
  transition: transform .45s cubic-bezier(.18, .82, .2, 1);
  color: #174e5a;
  font-family: var(--round);
}
.ask-sunny-lightbox.is-open .ask-sunny-machine { transform: none; }
.ask-sunny-machine > img { position: absolute; pointer-events: none; }

/* z1 · Sunny's circular call feed, masked by the frame above it */
.ask-sunny-feed {
  /* Slightly larger than the aperture so the art covers the whole circular
   * hole with no sliver of backdrop at the rim. */
  left: 12.35%;
  top: 20.15%;
  width: 27.4%;
  z-index: 1;
}
/* z2 · the connected mechanical frame (full canvas) */
.ask-sunny-frame {
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
}
/* z3-4 · terminal status card: skin + real copy */
.ask-sunny-intro {
  position: absolute;
  /* Hand-tuned in pendant-edit mode, 2026-08-11. */
  left: 12.51%;
  top: 64.65%;
  width: 27.62%;
  z-index: 3;
  pointer-events: none;
}
.ask-sunny-intro-skin { display: block; width: 100%; }
/* Three facts, left-aligned, evenly spaced, centred in the card's blank
 * area. One rule for all eight machines: the cards differ in shape, not in
 * how a list of three should read. */
.ask-sunny-intro-copy {
  position: absolute;
  inset: 16% 9% 14% 13%;
  z-index: 4;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: .3em;
}
.ask-sunny-intro-copy p {
  margin: 0;
  width: 100%;
  white-space: nowrap;
  display: flex;
  align-items: center;
}
.ask-sunny-intro-who {
  font: 600 clamp(10.5px, .98vw, 14px)/1.1 var(--round);
  letter-spacing: .07em;
  text-transform: uppercase;
  color: #7a5a2c;
}
.ask-sunny-intro-from,
.ask-sunny-intro-star {
  font-size: clamp(10px, .9vw, 12.5px);
  line-height: 1.25;
  color: rgba(23, 78, 90, .85);
}
/* z6 · title accessory over the top rail */
.ask-sunny-title-skin {
  left: 57.38%;
  /* Centered on the top brass rail's axis. */
  top: 3.4%;
  width: 24.91%;
  z-index: 6;
}
/* z7 · real close button wearing the brass X */
.ask-sunny-close {
  position: absolute;
  left: 91.2%;
  top: 3.2%;
  width: 5.2%;
  min-width: 44px;
  min-height: 44px;
  aspect-ratio: 1;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  z-index: 7;
  transition: transform 180ms cubic-bezier(.2, .8, .2, 1);
}
.ask-sunny-close img { display: block; width: 100%; height: 100%; object-fit: contain; }
.ask-sunny-close:hover, .ask-sunny-close:focus-visible { transform: rotate(90deg); }

/* z4 · the chat region inside the right panel */
.ask-sunny-chat {
  position: absolute;
  left: 47.9%;
  right: 8.5%;
  top: 10.5%;
  bottom: 17%;
  display: flex;
  flex-direction: column;
  z-index: 4;
}
.ask-sunny-messages {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  /* Room to tell one message from the next: several are long, and at the
     old spacing two paragraphs of hers ran together as one wall. */
  gap: 18px;
  padding: 18px 6px 4px;
  scrollbar-width: thin;
}
/* Call-log opening line: quiet mono timestamp centered over the first turn. */
/* The stamp floats straight on each world's console -- black vinyl, navy
 * origami, knitted cream -- so a single ink colour was legible on some and
 * invisible on others. It carries its own small pill instead, which reads
 * on any of them and needs no per-world guess. */
.ask-sunny-stamp {
  flex: 0 0 auto;
  /* Room either side: it heads the conversation, and crowding it against
     the first message made it read as a divider between two of them. */
  margin: 6px 0 16px;
  align-self: center;
  padding: 3px 11px;
  border-radius: 999px;
  border: 1px solid var(--ask-edge, rgba(164, 128, 61, .26));
  background: var(--ask-stamp-bg, rgba(255, 252, 244, .74));
  font: 11.5px/1 var(--mono);
  letter-spacing: .08em;
  color: var(--ask-stamp-ink, rgba(23, 78, 90, .62));
}
/* Mechanical transmissions: ivory for Sunny, pale aqua for the player,
 * both with a thin brass border. Real text, intrinsic height. */
.ask-sunny-message {
  margin: 0;
  /* The log is a flex column, so a message would otherwise be squashed to
     fit rather than overflow -- the container reported no scroll height at
     all while a tall card sat clipped inside it. */
  flex: 0 0 auto;
  padding: 9px 14px;
  max-width: 82%;
  font-size: clamp(12px, 1.05vw, 15px);
  line-height: 1.45;
  color: #174e5a;
  white-space: pre-wrap;
  /* Let the browser avoid orphans rather than hand-tuning every line to a
     panel width: the machine scales proportionally, so a fix here holds at
     every desktop size, and it covers streamed answers too, which no copy
     pass could reach. */
  text-wrap: pretty;
  border: 1px solid var(--ask-edge, rgba(164, 128, 61, .55));
  position: relative;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5);
}
.ask-sunny-message.is-me {
  align-self: flex-end;
  border-radius: 14px 14px 5px 14px;
  border-color: var(--ask-me-edge, var(--ask-edge, rgba(164, 128, 61, .55)));
  background: var(--ask-me-bubble, rgba(199, 233, 226, .82));
}
.ask-sunny-message.is-sunny {
  align-self: flex-start;
  border-radius: 14px 14px 14px 5px;
  border-color: var(--ask-sunny-edge, var(--ask-edge, rgba(164, 128, 61, .55)));
  background: var(--ask-sunny-bubble, rgba(252, 246, 227, .88));
}
/* Tiny origin tag on Sunny's messages -- keeps mixed-scene logs readable. */
.ask-sunny-from-tag {
  display: block;
  margin-top: 7px;
  text-align: left;
  font: 10px/1 var(--mono);
  letter-spacing: .06em;
  color: rgba(122, 90, 44, .72);
}
/* Waiting for her to answer: "Thinking", then her butterfly crossing three
   lamps below it. The label and both sets of numbers come from ask-me -- the
   flutter curve and the dots' breathing were tuned over there -- and only the
   butterfly's path is the journey's own, which is the one thing here that
   should sound like a road.

   The first attempt at that path replaced the dots' easing with linear and
   spread them .52s apart, and it read as dead: three separate stopwatches
   instead of one wave. What makes the original feel like rise and fall is
   not height at all -- it is scale on ease-in-out with the neighbours only
   .2s behind, close enough to run together. Those values are back untouched;
   the butterfly now flies over the wave rather than replacing it. */
.ask-sunny-message.is-pending {
  /* Not a flex row. "Thinking ..." is one line of type, so it is laid out as
     one: an inline label followed by an inline lane, which puts them on a
     shared baseline by the same rules that put a full stop after a word.
     Flex with align-items:baseline should have done the same and floated the
     dots 5px high instead -- the type has a baseline, the boxes had their
     own idea about it. */
  display: block;
  opacity: .85;
  font-style: italic;
}
.ask-sunny-pending-verb {
  font-weight: 300;
  letter-spacing: .02em;
}
/* The lane the butterfly crosses, sitting in the text flow right after the
   label -- the dots are its only content, so its baseline is their floor. */
.ask-sunny-pending-lane {
  position: relative;
  display: inline-block;
  margin-left: 7px;
  padding-right: 6px;
}
.ask-sunny-pending-dots {
  display: inline-block;
  white-space: nowrap;
}
.ask-sunny-pending-dots span {
  display: inline-block;
  vertical-align: baseline;
  width: 3px;
  height: 3px;
  margin-left: 4px;
  border-radius: 50%;
  background: currentColor;
  opacity: .3;
  transform-origin: center bottom;
  animation: ask-sunny-pending-dot 1.5s ease-in-out infinite;
}
.ask-sunny-pending-dots span:first-child { margin-left: 0; }
.ask-sunny-pending-dots span:nth-child(2) { animation-delay: .2s; }
.ask-sunny-pending-dots span:nth-child(3) { animation-delay: .4s; }
@keyframes ask-sunny-pending-dot {
  0%, 100% { opacity: .3;  transform: scale(0.85); }
  50%      { opacity: .95; transform: scale(1.25); }
}
/* Two elements, because one cannot carry two transforms: the outer glides
   across, the inner keeps ask-me's flutter exactly as written. The crossing
   is 1.5 wingbeats long (2.4s against 1.6s): the flutter lifts twice per
   beat, so a two-beat crossing spent four rises getting over three dots and
   read as a long haul. Three rises is one per lamp, and the lane no longer
   has to be padded out to hold the extra distance. */
/* Above the bubble, not inside it. Padding the bubble to make room for the
   flight made the bubble taller than it had any reason to be, and pushed
   "Thinking ..." off its centre -- and the line sitting right is what the
   reader actually looks at. The butterfly is a visitor; it crosses over the
   roof and is allowed past the edge. */
.ask-sunny-pending-flight {
  position: absolute;
  left: 0;
  bottom: 6px;
  animation: ask-sunny-pending-cruise 2.4s ease-in-out infinite;
}
.ask-sunny-pending-butterfly {
  display: inline-block;
  font-size: 1.05em;
  line-height: 1;
  animation: ask-sunny-pending-flutter 1.6s ease-in-out infinite;
  transform-origin: center bottom;
}
/* ask-me's flutter, value for value. */
@keyframes ask-sunny-pending-flutter {
  0%   { transform: translateY(0) rotate(-3deg) scale(1); }
  25%  { transform: translateY(-4px) rotate(2deg) scale(1.06); }
  50%  { transform: translateY(-2px) rotate(-2deg) scale(1.02); }
  75%  { transform: translateY(-5px) rotate(3deg) scale(1.08); }
  100% { transform: translateY(0) rotate(-3deg) scale(1); }
}
/* The crossing: in from the left, over the three lamps, out to the right,
   then a beat of empty lane before coming round again -- a butterfly that
   never leaves is a spinner. Height is the flutter's job, not this one's. */
/* The x stops are measured, not guessed: the three dots sit at 0, 7 and 14,
   and the glyph is ~14px wide, so its centre rides a lamp when the element's
   left edge is about 5px short of it. */
@keyframes ask-sunny-pending-cruise {
  0%   { transform: translate(-10px, -1px); opacity: 0; }
  14%  { transform: translate(-4px, -3px);  opacity: 1; }  /* over lamp 1 */
  44%  { transform: translate(3px, -5px);   opacity: 1; }  /* over lamp 2 */
  74%  { transform: translate(10px, -3px);  opacity: 1; }  /* over lamp 3 */
  90%  { transform: translate(13px, -2px);  opacity: .7; }
  97%  { transform: translate(15px, 0);     opacity: 0; }
  100% { transform: translate(-10px, -1px); opacity: 0; }
}
  50%      { opacity: .95; transform: scale(1.25); }
}
@media (prefers-reduced-motion: reduce) {
  .ask-sunny-pending-flight { animation: none; transform: translate(3px, 0); opacity: .9; }
  .ask-sunny-pending-butterfly { animation: none; }
  .ask-sunny-pending-dots span { animation: none; opacity: .5; }
}
/* One row that scrolls sideways, like ask-me's chip rail: a second line
 * would push the input row down and shift the whole machine's layout. */
.ask-sunny-pills {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
  padding: 8px 6px 0;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* The last chip fades out at the edge so the rail reads as continuing. */
  mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}
.ask-sunny-pills::-webkit-scrollbar { display: none; }
.ask-sunny-pills[hidden] { display: none; }
.ask-sunny-pill {
  flex: 0 0 auto;
  white-space: nowrap;
  border: 1px solid var(--ask-edge, rgba(164, 128, 61, .55));
  border-radius: 15px;
  padding: 7px 13px;
  background: var(--ask-chip, rgba(199, 233, 226, .82));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5);
  font: inherit;
  font-size: clamp(11px, .95vw, 13.5px);
  color: #174e5a;
  cursor: pointer;
  transition: background-color 160ms ease;
}
.ask-sunny-pill:hover, .ask-sunny-pill:focus-visible { background: var(--ask-chip-hover, rgba(178, 226, 216, .95)); }

/* z5 · real input row seated in the machine's input slot */
/* Mic, input and Send share one axis, all full row height, the row itself
 * centered in the frame's bottom band. */
.ask-sunny-form {
  position: absolute;
  /* Hand-tuned in pendant-edit mode, 2026-08-11. */
  left: 48.25%;
  top: 84.69%;
  width: 42.7%;
  height: 6.08%;
  display: flex;
  align-items: center;
  gap: .8%;
  z-index: 5;
}
.ask-sunny-speaker {
  height: 100%;
  aspect-ratio: 1;
  pointer-events: none;
}
/* The input pill holds the emoji button inside its right edge, the way
 * bubble universe's ask box does. */
.ask-sunny-input-slot {
  position: relative;
  overflow: visible;
  flex: 1;
  min-width: 0;
  height: 88%;
  align-self: center;
  display: flex;
  align-items: center;
  /* The hairline belongs to the shell's well as well: nothing about the
     resting composer is drawn by the DOM. */
  border: 1px solid transparent;
  /* One radius for the well and for the field, in both states. CSS clamps
     a radius to half the shorter side, so a single value reads as a pill
     while the box is one line high and as a rounded panel once it has
     grown -- which is what lets the width and bottom tuned on the
     invisible box carry straight over to the grown one. */
  border-radius: var(--ask-well-radius, 26px);
  /* Resting, the composer is the shell's own well and nothing else: every
     machine draws one, and a DOM surface on top of it was always a second
     frame over a good one. A background appears only once the field has
     outgrown that well. */
  background: transparent;
  box-shadow: none;
}
/* Focus reads as the slot warming up, not as a rectangle: an outline here
 * is inherited by the borderless input and drawn square around it. */
/* Focus shows in the text, not in a ring: the well is artwork, and a ring
   drawn over it read as a second frame. */
.ask-sunny-input-slot:focus-within { box-shadow: none; }
.ask-sunny-input {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
  /* Auto height, centred by the slot: a textarea cannot centre its own
     text, so it is sized to its content and the row does the centring. */
  height: auto;
  padding: 4px 54px 4px 16px;
  border: 1px solid transparent;
  border-radius: var(--ask-well-radius, 26px);
  background: transparent;
  font: inherit;
  font-size: clamp(12px, 1.05vw, 15px);
  line-height: 1.4;
  color: var(--ask-ink, #174e5a);
  resize: none;
  overflow-y: hidden;
}
/* One line sits in the shell's own well. Past that the field has to carry
   its own surface, or the overflow reads straight on the machine. */
.ask-sunny-input-slot.is-multiline { position: relative; }
/* Only once the text is taller than the shell's own well does the field
   need a surface of its own; below that the well is the background. */
[data-skin] .ask-sunny-input-slot.is-multiline .ask-sunny-input,
.ask-sunny-input-slot.is-multiline .ask-sunny-input {
  z-index: 6;
  padding: 9px 54px 9px 16px;
  border-color: var(--ask-edge, rgba(164, 128, 61, .55));
  background: var(--ask-field-bg, rgba(255, 253, 246, .96));
  box-shadow: 0 -6px 18px rgba(12, 40, 48, .16);
}
/* The grown field covers the slot, so the emoji has to be placed against
   the same corner rather than left to the row it is no longer part of. */
/* Grown, the field is tall: the button sits at its foot rather than
   halfway up it. The tilt is untouched. */
.ask-sunny-input-slot.is-multiline .ask-sunny-emoji { top: auto; bottom: 11px; margin: 0; }

.ask-sunny-input:focus, .ask-sunny-input:focus-visible { outline: none; }
.ask-sunny-input::placeholder { color: rgba(23, 78, 90, .45); }
/* Emoji exchange, seated inside the input pill at its right edge. Costs
 * no quota, so it can sit right where a chat app puts its sticker key. */
/* Always in the same corner of the field, in both states: seated in the
   row it would jump sideways the moment the field became a layer over it,
   and the jump read as a bug. Only the vertical anchor follows the field
   as it grows. */
.ask-sunny-emoji {
  position: absolute;
  right: 16px;
  /* Centred by auto margins between two edges, never by transform: the
     hover tilt is a transform, and the two cannot share the property. */
  top: 0;
  bottom: 0;
  margin: auto 0;
  z-index: 7;
  flex: 0 0 auto;
  width: 2em;
  height: 2em;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: rgba(122, 90, 44, .72);
  cursor: pointer;
  transition: color .25s cubic-bezier(.175, .885, .32, 1.275),
    background-color .25s ease,
    transform .25s cubic-bezier(.175, .885, .32, 1.275);
}
.ask-sunny-emoji svg {
  width: 68%;
  height: 68%;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ask-sunny-emoji:focus { outline: none; }
.ask-sunny-emoji:hover, .ask-sunny-emoji:focus-visible {
  background: rgba(164, 128, 61, .14);
  color: #a4803d;
  transform: scale(1.12) rotate(14deg);
  outline: none;
}
.ask-sunny-emoji:active { transform: scale(.95) rotate(-6deg); }

/* Emoji rising off a bubble. Lives on the chat column so the message
 * bubbles' own clipping can't cut it short. */
.ask-sunny-burst {
  position: absolute;
  height: 0;
  pointer-events: none;
  overflow: visible;
  z-index: 6;
}
.ask-sunny-burst span {
  position: absolute;
  top: 0;
  display: inline-block;
  line-height: 1;
  filter: drop-shadow(0 2px 6px rgba(7, 75, 80, .18));
  animation: ask-sunny-emoji-rise 2.4s cubic-bezier(.22, 1, .36, 1) forwards;
  will-change: transform, opacity;
}
@keyframes ask-sunny-emoji-rise {
  0% { transform: translate(0, 0) rotate(0deg) scale(.4); opacity: 0; }
  15% { opacity: 1; transform: translate(0, -10px) rotate(0deg) scale(1.05); }
  60% { opacity: .95; }
  100% {
    transform: translate(var(--drift, 0), var(--rise, -160px)) rotate(var(--rot, 0deg)) scale(1);
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .ask-sunny-burst { display: none; }
}

.ask-sunny-send {
  position: relative;
  height: 88%;
  align-self: center;
  aspect-ratio: 1426 / 573;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transition: filter 160ms ease;
}
.ask-sunny-send img { display: block; width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
/* Invisible hit-area extension keeps the 44px accessible target while the
 * visible capsule matches the input's height exactly. */
.ask-sunny-send::before { content: ""; position: absolute; inset: -4px -6px; }
.ask-sunny-send:hover, .ask-sunny-send:focus-visible { filter: brightness(1.08); }

/* Narrow rooms: the machine ratio would crush text; stack instead. */
@media (max-width: 760px) {
  .ask-sunny-machine {
    aspect-ratio: auto;
    width: calc(100vw - 24px);
    height: min(640px, calc(100vh - 32px));
    background: rgba(244, 252, 246, .92);
    border-radius: 20px;
  }
  .ask-sunny-feed, .ask-sunny-frame, .ask-sunny-intro, .ask-sunny-title-skin, .ask-sunny-speaker { display: none; }
  .ask-sunny-close { left: auto; right: 8px; top: 8px; width: 44px; }
  .ask-sunny-chat { left: 12px; right: 12px; top: 56px; bottom: 74px; }
  .ask-sunny-form { left: 12px; width: auto; right: 12px; top: auto; bottom: 12px; height: 48px; }
}

/* ── Per-world machine skins ──────────────────────────────────────────────
 * Every world answers on its own device. The DOM assembly is identical for
 * all of them; only the artwork (swapped in JS) and these percent anchors
 * differ. All coordinates are of the shared 1586x992 design canvas, so the
 * whole machine still scales as one unit.
 *
 * Trulia / Google / Airbnb come straight from Codex's baseline tables in
 * each kit's HANDOFF-TO-CC.md. Art & Jewelry and AI Lab ship no table, so
 * theirs are read off the approved concept renders and are a first pass
 * meant to be dragged into place in ?pendant-edit=1.
 *
 * The call feed is deliberately a little larger than its aperture: the
 * frame above masks it, and a snug fit leaves a sliver of backdrop at the
 * rim (the same reason the default skin overfills). */

/* ── Trulia · multicolor modern flip phone ── */
[data-skin="trulia"] .ask-sunny-feed { left: 21%; top: 12.4%; width: 22.2%; }
[data-skin="trulia"] .ask-sunny-intro { left: 21.63%; top: 66.13%; width: 21.06%; }
[data-skin="trulia"] .ask-sunny-title-skin { left: 63.05%; top: 3.83%; width: 22.38%; }
[data-skin="trulia"] .ask-sunny-close { left: 91.43%; top: 5.85%; width: 4.29%; }
[data-skin="trulia"] .ask-sunny-chat { left: 55.5%; right: 8.5%; top: 16%; bottom: 18%; }
[data-skin="trulia"] .ask-sunny-form { left: 51.2%; top: 84.5%; width: 42.12%; height: 6.2%; }

/* ── Google · crochet phone (approved v2) ──
 * This kit is drawn on 1570x1001, so the machine takes that ratio here.
 * Coordinates come straight from the kit's own baseline table. */
[data-skin="google"] .ask-sunny-machine { aspect-ratio: 1570 / 1001; }
[data-skin="google"] .ask-sunny-feed { left: 22.4%; top: 20.6%; width: 19.8%; }
[data-skin="google"] .ask-sunny-intro { left: 22%; top: 71%; width: 20.3%; }
[data-skin="google"] .ask-sunny-title-skin { left: 63%; top: 9.99%; width: 20.83%; }
[data-skin="google"] .ask-sunny-close { left: 91.02%; top: 14.39%; width: 5.22%; }
[data-skin="google"] .ask-sunny-chat { left: 53.5%; right: 7%; top: 19%; bottom: 18%; }
[data-skin="google"] .ask-sunny-form { left: 54.65%; top: 83.3%; width: 40.12%; height: 8.29%; }

/* ── Airbnb · origami pager ── */
[data-skin="airbnb"] .ask-sunny-feed { left: 8%; top: 20.6%; width: 24.8%; }
[data-skin="airbnb"] .ask-sunny-intro { left: 8.32%; top: 59.27%; width: 24.27%; }
[data-skin="airbnb"] .ask-sunny-title-skin { left: 56.75%; top: 4.54%; width: 25.85%; }
[data-skin="airbnb"] .ask-sunny-close { left: 88.27%; top: 5.85%; width: 5.17%; }
[data-skin="airbnb"] .ask-sunny-chat { left: 46.03%; right: 9.2%; top: 15.63%; bottom: 21.37%; }
[data-skin="airbnb"] .ask-sunny-form { left: 44.26%; top: 81.4%; width: 49.06%; height: 7.9%; }

/* ── Art & Jewelry · underwater glass communicator ── */
[data-skin="art-jewelry"] .ask-sunny-feed { left: 9.2%; top: 12.2%; width: 25.6%; }
[data-skin="art-jewelry"] .ask-sunny-intro { left: 8%; top: 61%; width: 26%; }
[data-skin="art-jewelry"] .ask-sunny-title-skin { left: 59.3%; top: 3.8%; width: 21.5%; }
[data-skin="art-jewelry"] .ask-sunny-close { left: 91.3%; top: 7.3%; width: 4%; }
[data-skin="art-jewelry"] .ask-sunny-chat { left: 43.7%; right: 7%; top: 11%; bottom: 24%; }
[data-skin="art-jewelry"] .ask-sunny-form { left: 41.2%; top: 79%; width: 53.4%; height: 11%; }

/* ── AI Lab · soft-clay egg pod ── */
[data-skin="ai-lab"] .ask-sunny-feed { left: 8.4%; top: 18.3%; width: 23.5%; }
[data-skin="ai-lab"] .ask-sunny-intro { left: 8.2%; top: 66.5%; width: 24%; }
[data-skin="ai-lab"] .ask-sunny-title-skin { left: 62%; top: 6.8%; width: 19.3%; }
[data-skin="ai-lab"] .ask-sunny-close { left: 90.5%; top: 9.7%; width: 5.2%; }
[data-skin="ai-lab"] .ask-sunny-chat { left: 44%; right: 5.5%; top: 16%; bottom: 24.5%; }
[data-skin="ai-lab"] .ask-sunny-form { left: 42.2%; top: 77.8%; width: 53%; height: 11.2%; }

/* The pod kits draw their Send as a round button, not a capsule. */
[data-skin="art-jewelry"] .ask-sunny-send,
[data-skin="ai-lab"] .ask-sunny-send { aspect-ratio: 1; height: 100%; }


/* ── Thoughts · thin-paper phone ──
 * These two kits ship no coordinate table, but their layer 01 is a full
 * 1586x992 canvas whose apertures are genuine holes in the alpha, so the
 * boxes below are measured off those holes rather than guessed. Title and
 * Close sit on the frame rather than in a hole, so those two are by eye. */
[data-skin="thoughts"] .ask-sunny-feed { left: 11.3%; top: 12.2%; width: 21.6%; }
[data-skin="thoughts"] .ask-sunny-intro { left: 11%; top: 60.3%; width: 22.4%; }
[data-skin="thoughts"] .ask-sunny-title-skin { left: 55%; top: 3.4%; width: 22%; }
[data-skin="thoughts"] .ask-sunny-close { left: 87.5%; top: 2.6%; width: 5.5%; }
[data-skin="thoughts"] .ask-sunny-chat { left: 46.09%; right: 7.13%; top: 13%; bottom: 25.61%; }
[data-skin="thoughts"] .ask-sunny-form { left: 43.88%; top: 77.2%; width: 49.18%; height: 11.6%; }

/* ── Reflection · vinyl communication desk ── */
[data-skin="reflection"] .ask-sunny-feed { left: 11.2%; top: 17.8%; width: 18%; }
[data-skin="reflection"] .ask-sunny-intro { left: 5.7%; top: 60.3%; width: 29.5%; }
[data-skin="reflection"] .ask-sunny-title-skin { left: 59%; top: 9.5%; width: 20%; }
[data-skin="reflection"] .ask-sunny-close { left: 89%; top: 6%; width: 5%; }
[data-skin="reflection"] .ask-sunny-chat { left: 44.64%; right: 7.57%; top: 16.53%; bottom: 29.34%; }
[data-skin="reflection"] .ask-sunny-form { left: 42.62%; top: 74.6%; width: 49.5%; height: 9.8%; }

[data-skin="thoughts"] .ask-sunny-send,
[data-skin="reflection"] .ask-sunny-send { aspect-ratio: 1; height: 100%; }

/* ── Per-world scrim ──────────────────────────────────────────────────────
 * The colour behind the open machine is the world's own. Each pair below is
 * the average of that world's background plate, sampled with PIL, with its
 * saturation nudged up and its lightness pulled part-way toward mid: a
 * bright world stays bright and a night world stays dark, but neither
 * extreme leaves the machine without anything to sit on. Two stops on the
 * content panel's 145deg axis, so a chat and a project read as one room. */
html[data-scene-skin="trulia"] {
  --scene-scrim-a: rgba(117, 161, 174, .52);
  --scene-scrim-b: rgba(81, 125, 138, .60);
}
html[data-scene-skin="google"] {
  --scene-scrim-a: rgba(130, 168, 165, .52);
  --scene-scrim-b: rgba(93, 134, 131, .60);
}
html[data-scene-skin="airbnb"] {
  --scene-scrim-a: rgba(49, 69, 133, .52);
  --scene-scrim-b: rgba(30, 42, 80, .60);
}
html[data-scene-skin="art-jewelry"] {
  --scene-scrim-a: rgba(101, 177, 198, .52);
  --scene-scrim-b: rgba(62, 143, 167, .60);
}
html[data-scene-skin="ai-lab"] {
  --scene-scrim-a: rgba(206, 185, 185, .52);
  --scene-scrim-b: rgba(177, 144, 143, .60);
}
html[data-scene-skin="thoughts"] {
  --scene-scrim-a: rgba(214, 182, 154, .52);
  --scene-scrim-b: rgba(194, 145, 104, .60);
}
html[data-scene-skin="reflection"] {
  --scene-scrim-a: rgba(87, 85, 123, .52);
  --scene-scrim-b: rgba(57, 56, 81, .60);
}

/* ── Composer row · one height, one gap ───────────────────────────────────
 * The three pieces are a set: speaker, field, Send. They used to be sized
 * independently (buttons at 88%, speaker at 100%), so the row read as three
 * loose objects and the Send capsule was wider than its own artwork, which
 * is where the extra space on the right came from. Now everything is the
 * row's full height and each button carries its art's true aspect ratio, so
 * the visible edge is the art's edge and the gaps either side match. */
.ask-sunny-input-slot { height: 100%; }
.ask-sunny-speaker,
.ask-sunny-send { height: 100%; align-self: center; }
.ask-sunny-speaker { aspect-ratio: 1; }
.ask-sunny-send { aspect-ratio: 980 / 989; }
[data-skin="trulia"] .ask-sunny-speaker { aspect-ratio: 960 / 991; }
[data-skin="trulia"] .ask-sunny-send { aspect-ratio: 1112 / 1126; }
[data-skin="google"] .ask-sunny-speaker { aspect-ratio: 1521 / 267; }
[data-skin="google"] .ask-sunny-send { aspect-ratio: 1040 / 1047; }
[data-skin="airbnb"] .ask-sunny-speaker { aspect-ratio: 845 / 865; }
[data-skin="airbnb"] .ask-sunny-send { aspect-ratio: 1; }
[data-skin="art-jewelry"] .ask-sunny-speaker { aspect-ratio: 227 / 249; }
[data-skin="art-jewelry"] .ask-sunny-send { aspect-ratio: 1027 / 1046; }
[data-skin="ai-lab"] .ask-sunny-speaker { aspect-ratio: 194 / 195; }
[data-skin="ai-lab"] .ask-sunny-send { aspect-ratio: 1084 / 1100; }
[data-skin="thoughts"] .ask-sunny-speaker { aspect-ratio: 219 / 223; }
[data-skin="thoughts"] .ask-sunny-send { aspect-ratio: 1051 / 1077; }
[data-skin="reflection"] .ask-sunny-speaker { aspect-ratio: 1; }
[data-skin="reflection"] .ask-sunny-send { aspect-ratio: 912 / 917; }

/* A tuned height means the card was deliberately stretched, so let the skin
 * fill it instead of keeping its own intrinsic height. */
.ask-sunny-intro[style*="height"] .ask-sunny-intro-skin { height: 100%; object-fit: fill; }

/* The vinyl room's status card is warm tan leather; the shared teal copy
 * colour all but vanished on it. Ink the card in its own browns instead. */
[data-skin="reflection"] .ask-sunny-intro-who { color: #4a3521; }
[data-skin="reflection"] .ask-sunny-intro-from,
[data-skin="reflection"] .ask-sunny-intro-star { color: rgba(62, 44, 27, .92); }


/* ── Focus lives in the text ──────────────────────────────────────────────
 * Every shell draws its own well, so a ring over it was a second frame on
 * a good one. Focus shows the way the emoji button shows it instead: the
 * placeholder sits back, and what you type comes forward. */
.ask-sunny-input::placeholder {
  color: var(--ask-placeholder, rgba(23, 78, 90, .42));
  transition: color 160ms ease;
}
.ask-sunny-input {
  color: var(--ask-ink, #174e5a);
  transition: color 160ms ease;
}
.ask-sunny-input:focus { color: var(--ask-ink-strong, #0d3a45); }
.ask-sunny-input:focus::placeholder {
  color: var(--ask-placeholder-focus, rgba(23, 78, 90, .62));
}
/* The vinyl room's well is near-black walnut: its ink has to invert. */
[data-skin="reflection"] {
  --ask-ink: #e8dcc4;
  --ask-ink-strong: #fbf3e0;
  --ask-placeholder: rgba(232, 220, 196, .45);
  --ask-placeholder-focus: rgba(251, 243, 224, .78);
}
[data-skin="reflection"] .ask-sunny-emoji { color: rgba(232, 220, 196, .6); }
[data-skin="reflection"] .ask-sunny-emoji:hover { color: #fbf3e0; }

/* The status card's first row is a name, not a sentence: let it shrink to
 * hold one line rather than break a world's title in half. */
.ask-sunny-intro-who {
  white-space: nowrap;
  font-size: clamp(9px, .92vw, 13px);
}

/* ── Each console's own palette ───────────────────────────────────────────
 * The log used to be one set of teal-and-brass surfaces floating on eight
 * very different consoles. These pull the timestamp, the two bubbles, the
 * suggestion chips and every hairline into the colours of the machine they
 * are sitting inside, taken from each kit's own artwork. */
[data-skin="trulia"] {
  --ask-edge: rgba(32, 78, 150, .42);
  --ask-sunny-bubble: rgba(253, 247, 235, .92);
  --ask-me-bubble: rgba(198, 224, 248, .86);
  --ask-chip: rgba(198, 224, 248, .86);
  --ask-chip-hover: rgba(172, 208, 244, .96);
  --ask-stamp-bg: rgba(253, 247, 235, .8);
  --ask-stamp-ink: rgba(24, 62, 116, .66);
}
[data-skin="google"] {
  --ask-edge: rgba(184, 142, 60, .48);
  --ask-sunny-bubble: rgba(253, 248, 233, .92);
  --ask-me-bubble: rgba(186, 224, 216, .86);
  --ask-chip: rgba(186, 224, 216, .86);
  --ask-chip-hover: rgba(160, 212, 202, .96);
  --ask-stamp-bg: rgba(253, 248, 233, .84);
  --ask-stamp-ink: rgba(38, 82, 78, .68);
}
[data-skin="airbnb"] {
  --ask-edge: rgba(186, 148, 84, .5);
  --ask-sunny-bubble: rgba(250, 243, 228, .93);
  --ask-me-bubble: rgba(170, 190, 178, .82);
  --ask-chip: rgba(206, 220, 210, .88);
  --ask-chip-hover: rgba(226, 236, 228, .96);
  --ask-stamp-bg: rgba(250, 243, 228, .82);
  --ask-stamp-ink: rgba(38, 46, 78, .7);
}
[data-skin="art-jewelry"] {
  --ask-edge: rgba(196, 168, 96, .46);
  --ask-sunny-bubble: rgba(250, 252, 251, .9);
  --ask-me-bubble: rgba(196, 232, 234, .8);
  --ask-chip: rgba(196, 232, 234, .82);
  --ask-chip-hover: rgba(172, 222, 226, .94);
  --ask-stamp-bg: rgba(250, 252, 251, .78);
  --ask-stamp-ink: rgba(26, 92, 116, .68);
}
[data-skin="ai-lab"] {
  --ask-edge: rgba(178, 156, 118, .42);
  --ask-sunny-bubble: rgba(253, 247, 231, .93);
  --ask-me-bubble: rgba(212, 202, 232, .82);
  --ask-chip: rgba(196, 224, 208, .86);
  --ask-chip-hover: rgba(174, 214, 194, .96);
  --ask-stamp-bg: rgba(253, 247, 231, .84);
  --ask-stamp-ink: rgba(70, 84, 96, .7);
}
[data-skin="thoughts"] {
  --ask-edge: rgba(186, 150, 104, .44);
  --ask-sunny-bubble: rgba(250, 242, 224, .93);
  --ask-me-bubble: rgba(200, 206, 178, .82);
  --ask-chip: rgba(206, 226, 218, .86);
  --ask-chip-hover: rgba(184, 214, 204, .96);
  --ask-stamp-bg: rgba(250, 242, 224, .84);
  --ask-stamp-ink: rgba(92, 76, 54, .7);
}
[data-skin="reflection"] {
  --ask-edge: rgba(178, 140, 74, .55);
  --ask-sunny-bubble: rgba(244, 233, 209, .93);
  --ask-me-bubble: rgba(120, 104, 78, .8);
  --ask-chip: rgba(78, 66, 50, .78);
  --ask-chip-hover: rgba(104, 88, 64, .92);
  --ask-stamp-bg: rgba(46, 38, 30, .74);
  --ask-stamp-ink: rgba(232, 220, 196, .78);
}
/* The vinyl room is the one dark console, so its reader-side surfaces need
 * light type where every other world reads dark on light. */
[data-skin="reflection"] .ask-sunny-message.is-me,
[data-skin="reflection"] .ask-sunny-pill { color: #f4e9d1; }

/* ── Bubble palettes, traced from each kit's own concept render ───────────
 * The renders drew these bubbles with real colour decisions in them -- a
 * cobalt hairline on Trulia's cream, a teal knit outline on Google's, brass
 * on Airbnb's parchment, pearl and seafoam under the glass sea. Rebuilding
 * those in CSS keeps the conversation looking like it belongs to the
 * machine instead of a chat widget parked inside one.
 *
 * Art & Jewelry, AI Lab and Thoughts are sampled from the render; Trulia,
 * Google, Airbnb and Reflection are read off it by eye, since their bubbles
 * sit on backgrounds too close in value to sample cleanly. */
[data-skin="trulia"] {
  --ask-sunny-bubble: #f3e6d2;
  --ask-me-bubble: #a9d2ce;
  --ask-sunny-edge: rgba(31, 111, 184, .85);
  --ask-me-edge: rgba(31, 111, 184, .85);
}
[data-skin="google"] {
  --ask-sunny-bubble: #efe0c2;
  --ask-me-bubble: #7fb3ad;
  --ask-sunny-edge: rgba(60, 138, 134, .8);
  --ask-me-edge: rgba(60, 138, 134, .85);
}
[data-skin="airbnb"] {
  --ask-sunny-bubble: #f6dcaa;
  --ask-me-bubble: #a9a075;
  --ask-sunny-edge: rgba(185, 143, 79, .7);
  --ask-me-edge: rgba(185, 143, 79, .6);
}
[data-skin="art-jewelry"] {
  --ask-sunny-bubble: rgba(236, 238, 235, .9);
  --ask-me-bubble: rgba(174, 204, 192, .85);
  --ask-sunny-edge: rgba(198, 154, 100, .75);
  --ask-me-edge: rgba(198, 154, 100, .7);
}
[data-skin="ai-lab"] {
  --ask-sunny-bubble: #f5e2ca;
  --ask-me-bubble: #e0cfda;
  --ask-sunny-edge: rgba(181, 181, 136, .7);
  --ask-me-edge: rgba(198, 176, 205, .9);
}
[data-skin="thoughts"] {
  --ask-sunny-bubble: #e8d0b4;
  --ask-me-bubble: #b3a789;
  --ask-sunny-edge: rgba(193, 153, 97, .8);
  --ask-me-edge: rgba(130, 111, 76, .8);
}
[data-skin="reflection"] {
  --ask-sunny-bubble: #e4d3ae;
  --ask-me-bubble: #b9724f;
  --ask-sunny-edge: rgba(156, 122, 69, .85);
  --ask-me-edge: rgba(196, 150, 106, .7);
}
[data-skin="reflection"] .ask-sunny-message.is-sunny { color: #3b2c19; }
[data-skin="reflection"] .ask-sunny-message.is-me { color: #fbf1e0; }
[data-skin="airbnb"] .ask-sunny-message.is-me,
[data-skin="thoughts"] .ask-sunny-message.is-me { color: #2c2a1c; }


/* ── Suggestion chips are the reader's own voice ──────────────────────────
 * A chip becomes the reader's message the moment it is clicked, so it is
 * that message pre-written: the same colour, lighter, warming to the real
 * thing on hover. Every world follows the rule; only the hue changes.
 *
 * These also correct the first pass against the renders: Thoughts read too
 * dark, Google's and Art's reader blue too saturated to hold text, and
 * Airbnb's green too olive for the matcha in its artwork. */
html { --ask-chip: rgba(224, 244, 240, .72); }
[data-skin="trulia"] {
  --ask-me-bubble: #bcdedb;
  --ask-chip: rgba(214, 238, 236, .78);
  --ask-chip-hover: #bcdedb;
}
[data-skin="google"] {
  --ask-me-bubble: #b3d5d0;
  --ask-chip: rgba(212, 234, 230, .78);
  --ask-chip-hover: #b3d5d0;
}
[data-skin="airbnb"] {
  --ask-me-bubble: #c3d1a4;
  --ask-chip: rgba(216, 228, 194, .78);
  --ask-chip-hover: #c3d1a4;
}
[data-skin="art-jewelry"] {
  --ask-me-bubble: rgba(206, 238, 240, .88);
  --ask-chip: rgba(228, 247, 248, .7);
  --ask-chip-hover: rgba(206, 238, 240, .96);
}
[data-skin="ai-lab"] {
  --ask-me-bubble: #e6d8ea;
  --ask-chip: rgba(241, 233, 245, .8);
  --ask-chip-hover: #e6d8ea;
}
[data-skin="thoughts"] {
  --ask-sunny-bubble: #f4e8d3;
  --ask-me-bubble: #cfcfb2;
  --ask-sunny-edge: rgba(199, 166, 116, .7);
  --ask-me-edge: rgba(154, 143, 108, .7);
  --ask-chip: rgba(228, 231, 211, .78);
  --ask-chip-hover: #cfcfb2;
}
/* The one dark console: a paler tint would glow, so its chip is the same
   terracotta held back instead. */
[data-skin="reflection"] {
  --ask-chip: rgba(185, 114, 79, .4);
  --ask-chip-hover: rgba(185, 114, 79, .82);
}
[data-skin="reflection"] .ask-sunny-pill { color: #f6e7d6; }
[data-skin="airbnb"] .ask-sunny-pill,
[data-skin="thoughts"] .ask-sunny-pill { color: #2c2a1c; }

/* ── Desktop only ─────────────────────────────────────────────────────────
 * The machines are 1586x992 assemblies with a call screen, a status card
 * and a scrolling log side by side. There is no honest way to fold that
 * onto a phone, and a half-folded version would read worse than not
 * offering it at all, so the whole feature stays away: the pendant on the
 * airship, the mood bubble over her head, and the lightbox itself. */
html.is-mobile-storymode .ask-sunny-overlay,
html.is-mobile-storymode .ask-sunny-bubble,
html.is-mobile-storymode .ask-sunny-speech,
html.is-mobile-storymode .ask-sunny-lightbox {
  display: none !important;
}

/* ── Edit mode · show the boxes that have no artwork ──────────────────────
 * The well, the conversation and the composer row are invisible by design:
 * the shell draws them. That makes them the hardest parts to place, so
 * ?pendant-edit=1 outlines them. The well's outline is the one that
 * matters most -- its box is what the grown field snaps to. */
.ask-sunny-lightbox.is-edit .ask-sunny-input-slot,
.ask-sunny-lightbox.is-edit .ask-sunny-chat,
.ask-sunny-lightbox.is-edit .ask-sunny-form {
  outline: 1px dashed rgba(85, 200, 218, .85);
  outline-offset: -1px;
}
.ask-sunny-lightbox.is-edit .ask-sunny-input-slot {
  outline-color: rgba(255, 176, 90, .95);
  outline-width: 2px;
}
.ask-sunny-lightbox.is-edit .ask-sunny-intro,
.ask-sunny-lightbox.is-edit .ask-sunny-feed,
.ask-sunny-lightbox.is-edit .ask-sunny-title-skin,
.ask-sunny-lightbox.is-edit .ask-sunny-close,
.ask-sunny-lightbox.is-edit .ask-sunny-speaker,
.ask-sunny-lightbox.is-edit .ask-sunny-send {
  outline: 1px dashed rgba(85, 200, 218, .5);
  outline-offset: -1px;
}

/* The grown field's surface is each world's own paper rather than a
 * generic near-white sheet, so a question in progress still looks like it
 * was written on the machine it is being typed into. */
[data-skin="trulia"] { --ask-field-bg: rgba(250, 243, 231, .96); }
[data-skin="google"] { --ask-field-bg: rgba(250, 243, 226, .96); }
[data-skin="airbnb"] { --ask-field-bg: rgba(248, 238, 218, .96); }
[data-skin="art-jewelry"] { --ask-field-bg: rgba(246, 251, 251, .94); }
[data-skin="ai-lab"] { --ask-field-bg: rgba(250, 243, 228, .96); }
[data-skin="thoughts"] { --ask-field-bg: rgba(246, 236, 217, .97); }
[data-skin="reflection"] { --ask-field-bg: rgba(38, 31, 24, .94); }

/* ── The input well ───────────────────────────────────────────────────────
 * Some kits draw the surround for the input as its own piece rather than
 * baking it into the frame. Google's footer accessory shipped the mic and
 * the well welded into one strip, which meant placing either one moved the
 * other; split, each sits where it belongs. It is decoration only: the real
 * field floats above it. */
.ask-sunny-well {
  z-index: 3;
  pointer-events: none;
}
.ask-sunny-well[hidden] { display: none; }
[data-skin="google"] .ask-sunny-well { left: 60.7%; top: 82.6%; width: 27.4%; }
[data-skin="google"] .ask-sunny-speaker { aspect-ratio: 269 / 267; }


/* The three status rows are a list: one left edge, one gap, and short
 * enough that the box can actually centre them. At the old spacing the
 * stack was taller than its box, so justify-content had no slack to work
 * with and the rows read as scattered rather than as a group. */
.ask-sunny-intro-copy {
  align-items: flex-start;
  gap: .3em;
}
.ask-sunny-intro-copy p { margin-left: 0; text-align: left; }

/* A world's intro keeps the ordinary card for now. The cover slot comes
 * back with the art, and with the copy wrapped in an element of its own:
 * the message's text is a bare text node, so padding the card pushed the
 * cover around and left the words against its edges. */
/* Shown where it already is, rather than moved to the bottom: a message
 * that teleports is worse than one you have to be pointed at. */
.ask-sunny-message.is-found {
  animation: ask-sunny-found 1.6s ease-in-out infinite;
}
@keyframes ask-sunny-found {
  0%, 100% {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5),
      0 0 0 4px var(--ask-chip, rgba(199, 233, 226, .6)),
      0 0 22px 6px var(--ask-chip-hover, rgba(178, 226, 216, .55));
  }
  50% {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5),
      0 0 0 7px var(--ask-chip-hover, rgba(178, 226, 216, .95)),
      0 0 34px 12px var(--ask-chip-hover, rgba(178, 226, 216, .75));
  }
}
@media (prefers-reduced-motion: reduce) {
  .ask-sunny-message.is-found { animation: none; outline: 3px solid var(--ask-chip-hover, rgba(178, 226, 216, .95)); }
}

/* ── Two kinds of composer ───────────────────────────────────────────────
 * Some shells draw an input well into their artwork and some do not, and
 * the difference decides everything about this control.
 *
 * Where the artwork has a well -- Google, Art & Jewelry, the AI Lab,
 * Thoughts, the vinyl room -- the DOM draws nothing at rest, because a
 * second surface on top of a good one is just a second frame. A surface
 * appears only when the text outgrows that well.
 *
 * Where it does not -- the airship communicator, Trulia, Airbnb -- the
 * field is the artwork. It is visible at rest, and growing is the same
 * pill getting taller: same paper, same hairline, same radius, so there is
 * no threshold to see.
 */
[data-skin="default"] .ask-sunny-input-slot,
[data-skin="trulia"] .ask-sunny-input-slot,
[data-skin="airbnb"] .ask-sunny-input-slot {
  border-color: var(--ask-edge, rgba(164, 128, 61, .5));
  background: var(--ask-field-bg, rgba(255, 253, 246, .96));
  box-shadow: inset 0 1px 3px rgba(122, 90, 44, .16);
}
[data-skin="default"] .ask-sunny-input-slot:focus-within,
[data-skin="trulia"] .ask-sunny-input-slot:focus-within,
[data-skin="airbnb"] .ask-sunny-input-slot:focus-within {
  border-color: var(--ask-edge, rgba(164, 128, 61, .5));
  box-shadow: inset 0 1px 3px rgba(122, 90, 44, .16), 0 0 0 3px rgba(164, 128, 61, .16);
}

/* ── A world's intro is a postcard ────────────────────────────────────────
 * Several long intros stacked in one log is what made the conversation
 * hard to read. With a cover it can be scanned and located at a glance,
 * and the world it came from is legible before a word is read. */
.ask-sunny-message.is-scene-intro {
  max-width: 92%;
  padding: 0 0 9px;
  overflow: hidden;
}
.ask-sunny-cover {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 1;
  object-fit: cover;
  margin-bottom: 9px;
  border-bottom: 1px solid var(--ask-edge, rgba(164, 128, 61, .4));
}
.ask-sunny-message.is-scene-intro .ask-sunny-body { display: block; padding: 0 14px; }
.ask-sunny-message.is-scene-intro .ask-sunny-from-tag { margin-left: 14px; }

/* The cover occupies its space before the pixels arrive, and says so: an
 * empty box of the right size reads as a broken image, a moving one reads
 * as loading. A cover that fails removes itself rather than leaving a
 * shimmer running forever. */
.ask-sunny-cover.is-loading {
  background:
    linear-gradient(100deg,
      transparent 20%,
      rgba(255, 255, 255, .55) 45%,
      transparent 70%)
      var(--ask-me-bubble, rgba(199, 233, 226, .82));
  background-size: 220% 100%;
  background-repeat: no-repeat;
  animation: ask-sunny-shimmer 1.5s ease-in-out infinite;
}
@keyframes ask-sunny-shimmer {
  from { background-position: 140% 0; }
  to { background-position: -40% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .ask-sunny-cover.is-loading { animation: none; }
}

/* Waking: the machine is laid out so its images can fetch and its rows can
 * measure, but it stays invisible until every piece has landed. The scrim
 * comes in on time, so the world dims the instant the handle is pulled and
 * the reader knows the door heard them.
 *
 * Same furniture as About's overlay on purpose -- one note, one hairline
 * bar, asides that speak up only if the wait gets long. A reader who has
 * waited for the book once should recognise this immediately. */
/* Copied from About's loader, not re-derived: same box, same corner, same
 * gradient, same 1.25s sweep. A reader who has waited for the book should
 * meet the exact same object here, so the only things that differ are the
 * words and the scrim -- the scrim stays the world's own tint, which is
 * the rule this panel has followed since the per-world scrim went in.
 *
 * The machine goes fully transparent behind it but keeps its box: the
 * images still need somewhere live to fetch into, and the intro rows still
 * need a real width to measure against while nobody is looking. */
.ask-sunny-lightbox {
  --ask-shimmer-width: min(72vw, 1180px);
  --ask-shimmer-height: min(76vh, 760px);
}
.ask-sunny-lightbox.is-waking .ask-sunny-machine { opacity: 0; pointer-events: none; }
.ask-sunny-lightbox.is-waking::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  width: var(--ask-shimmer-width);
  height: var(--ask-shimmer-height);
  border-radius: 10px 16px 12px 18px / 14px 10px 18px 12px;
  background: linear-gradient(100deg,
    rgba(247, 241, 226, .10) 0 38%, rgba(247, 241, 226, .22) 50%, rgba(247, 241, 226, .10) 62% 100%);
  background-size: 260% 100%;
  animation: ask-sunny-panel-shimmer 1.25s ease-in-out infinite;
  pointer-events: none;
}
@keyframes ask-sunny-panel-shimmer {
  0% { background-position: 130% 0; }
  100% { background-position: -30% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .ask-sunny-lightbox.is-waking::after { animation: none; }
}
/* Hung on the panel's top-right corner, off the same two values the panel
 * is drawn from -- About does exactly this while its book is on the wire.
 * A door the reader cannot leave is worse than a slow one. */
.ask-sunny-wake-close.story-map__close {
  /* Keeps the light pair it inherits from .story-map__close rather than the
   * project panels' teal-on-mint. This button stands on the scrim, not on
   * panel glass, and the scrim is a different colour in every world -- the
   * dark mark that reads perfectly on a project panel's pale mint would be
   * nearly gone over Airbnb's blue hour. Shape, size and motion still match
   * the panel close exactly; only the two colours follow the surface. */
  display: none;
  z-index: 4;
  right: auto;
  top: calc(50% - (var(--ask-shimmer-height) / 2) + 22px);
  left: calc(50% + (var(--ask-shimmer-width) / 2) - 22px);
  translate: -50% -50%;
}
.ask-sunny-lightbox.is-waking .ask-sunny-wake-close.story-map__close { display: block; }
.ask-sunny-bubble.is-waking { animation: ask-sunny-waking 1s ease-in-out infinite; }
@keyframes ask-sunny-waking {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.22); }
}
@media (prefers-reduced-motion: reduce) {
  .ask-sunny-bubble.is-waking { animation: none; }
}

.ask-sunny-note {
  position: absolute;
  z-index: 3;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  display: grid;
  gap: 14px;
  justify-items: center;
  margin: 0;
  color: #fff;
  /* About's cream at .86 works because About's scrim is near-black. This
   * one stands on the world's own tint, which is pale in most of them, so
   * the same value simply vanishes. Full white plus a dark halo carries on
   * both ends of that range without tinting the panel or overriding the
   * per-world scrim. */
  /* Tight and light. A wide soft halo reads as fog around the letters and
   * costs them their edges; a short offset plus a small close glow keeps the
   * type crisp and still separates it from a bright world underneath. */
  text-shadow: 0 1px 1px rgba(6, 34, 38, .34), 0 0 5px rgba(6, 34, 38, .26);
  font: 400 clamp(15px, 1.5vw, 19px)/1.5 var(--storybook, serif);
  letter-spacing: .02em;
  text-align: center;
  white-space: pre-line;
  opacity: 0;
  pointer-events: none;
  transition: opacity 400ms ease;
}
/* Only after a beat. A warmed machine opens in about a frame, and a machine
 * that arrives at once should never have said a word. */
.ask-sunny-lightbox.is-waking .ask-sunny-note {
  opacity: 1;
  transition-delay: 550ms;
}
.ask-sunny-bar {
  display: block;
  width: min(38vw, 260px);
  height: 2px;
  overflow: hidden;
  border-radius: 2px;
  background: rgba(6, 34, 38, .28);
}
.ask-sunny-bar i {
  display: block;
  width: 0%;
  height: 100%;
  border-radius: inherit;
  background: rgba(255, 255, 255, .9);
  /* Eased, so a batch of parts landing together reads as filling rather
   * than as a jump. */
  transition: width 420ms cubic-bezier(.22, .61, .36, 1);
}
.ask-sunny-aside {
  /* Wide enough that the written line breaks fall where they were written,
   * rather than the box inventing its own and stranding a word. */
  max-width: 40ch;
  font-size: .82em;
  line-height: 1.55;
  white-space: pre-line;
  color: rgba(255, 255, 255, .92);
  opacity: 0;
  transition: opacity 260ms ease;
}
.ask-sunny-aside.is-visible { opacity: 1; }

/* The unread mark on a world's card. Small on purpose: it marks a message,
 * it does not compete with it. Inside the card's top-right, inset by the
 * same 8px on both edges so it reads as belonging to the card rather than
 * floating beside it. Site cyan, the same colour the panels use to say
 * "look here". */
.ask-sunny-message.is-unseen { position: relative; }
.ask-sunny-message.is-unseen::before {
  content: "";
  position: absolute;
  right: 8px;
  top: 8px;
  z-index: 2;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--ask-unseen-dot, #62d6dc);
  box-shadow: 0 0 0 3px rgba(98, 214, 220, .22);
  pointer-events: none;
  /* Fades rather than vanishing: the reader is looking straight at the card
   * when it releases, and a mark that blinks out draws more attention on the
   * way out than it ever did sitting there. */
  transition: opacity 320ms ease;
}
@media (prefers-reduced-motion: reduce) {
  .ask-sunny-message.is-unseen::before { transition: none; }
}
