/* HANIYA, EVERYWHERE. The styles for the one shared assistant component (assets/js/haniya.js).
   ============================================================================================
   Owner, 2026-09-18: "Hania, the agent, we want her to be available on all pages of the website,
   not just the order."

   She used to live inside /order/index.html - markup, CSS and JS, all in one page. This file and
   haniya.js are that same assistant lifted out whole, so there is ONE definition of her and every
   page carries a script tag instead of a copy. /order/ loads these two files like every other page.

   WHY THIS IS A SEPARATE STYLESHEET AND NOT PART OF styles.css
   -----------------------------------------------------------
   styles.css is render-blocking on ninety-five SEO-critical pages. Nothing here is needed to paint
   the page - it dresses a launcher that mounts after load and a panel that does not exist until a
   guest opens it. So it ships on its own link, loaded at print priority and switched to `all` when
   it arrives (the same trick the pages already use for Google Fonts), and it is stamped by
   operator/tools/version_assets.py like every other versioned asset.

   THE THREE THINGS IT MUST NEVER DO - the same three account.js is held to
   -----------------------------------------------------------------------
   1. MOVE ANYTHING. Every element here is `position:fixed`. A fixed element is out of flow, so it
      cannot shift a single pixel of the page behind it. Contributed CLS is 0 by construction, not
      by measurement.
   2. BLOCK RENDERING. `media="print"` on the link, `defer` on the script.
   3. BREAK A PAGE WHEN THE SERVER IS DOWN. Nothing here waits on the network to draw, and the
      launcher looks identical whether the voice server answers or not.

   WHERE THE LAUNCHER SITS. `--wkh-bottom` is the gap above the bottom of the viewport. Pages that
   already own that corner raise her: the sticky `.mobile-bar` on phones (handled below), and
   /order/ which sets the variable itself because its cart bar is taller.
*/

:root { --wkh-bottom: 16px; --wkh-accent: #C9A227; --wkh-accent-deep: #8A6D14; }

/* ── the launcher ──────────────────────────────────────────────────────────────────────────── */
.wkh-fab {
  position: fixed; right: 16px; z-index: 180;
  bottom: calc(var(--wkh-bottom) + env(safe-area-inset-bottom, 0px));
  width: 56px; height: 56px; padding: 0; border: 0; border-radius: 50%;
  background: #fff; cursor: pointer; display: grid; place-items: center; overflow: visible;
  box-shadow: 0 10px 24px rgba(28, 26, 23, .24), 0 2px 6px rgba(28, 26, 23, .14);
  transition: transform .12s ease;
  -webkit-tap-highlight-color: transparent;
}
.wkh-fab:active { transform: scale(.94); }
.wkh-fab:focus-visible { outline: 3px solid var(--wkh-accent-deep); outline-offset: 3px; }
/* The one quiet nudge that she exists, then it gets out of the way. */
.wkh-fab::after {
  content: attr(data-peek); position: absolute; right: calc(100% + 10px); top: 50%;
  transform: translateY(-50%); background: #1C1A17; color: #fff; font-size: .78rem;
  font-weight: 600; font-family: var(--font-body, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
  padding: 6px 10px; border-radius: 999px; white-space: nowrap; opacity: 0;
  pointer-events: none; transition: opacity .25s ease;
}
/* THE GREETING IS A SENTENCE, SO IT HAS TO WRAP. `nowrap` is right for "Ask Haniya" and would run
   a welcome straight off the left edge of a phone - 320px of viewport does not hold one line of
   it. Width is capped against the viewport rather than set, so the bubble is as wide as the words
   need and never wider than the screen. */
.wkh-fab.wkh-greet::after {
  white-space: normal; width: max-content; max-width: min(64vw, 250px); text-align: left;
  line-height: 1.35; border-radius: 14px; padding: 9px 12px; font-weight: 500;
}
.wkh-fab.wkh-peek::after { opacity: 1; }
@media (hover: hover) { .wkh-fab:hover::after { opacity: 1; } }
/* The sticky mobile action bar owns the bottom of the screen below 760px on most pages. */
@media (max-width: 760px) { .wkh-fab.wkh-raised { bottom: calc(76px + env(safe-area-inset-bottom, 0px)); } }

/* ── Haniya herself: her own face ──────────────────────────────────────────────
   She used to be three drifting blobs of colour, because a DRAWN face at 46px lands in the
   uncanny valley. The owner supplied an illustrated portrait of her on 2026-09-18, which is not
   the same thing: a flat vector illustration survives being shrunk, where a rendered face does
   not. So she has a face now.

   WHAT WAS DONE TO THE SOURCE IMAGE. It arrived as a round badge - portrait, gold and green ring,
   the word HANIYA across the bottom. All that survives at 40px is the face, so the file shipped
   here (`img/haniya-avatar-192.webp`) is a square crop of the portrait alone, centred on her eyes,
   the wordmark and the outer ring cropped away. The disc below re-cuts it to a circle in CSS, so
   the crop stays a plain rectangle and the mask is one line. The whole badge is kept beside it as
   `haniya-badge.webp` for anywhere that has room for it.

   Two 96/192 sizes via image-set, the same pattern styles.css uses for the catering band: 192
   covers her at 3x on the panel header, 96 covers the launcher at 2x, and neither is fetched
   until the stylesheet is live - which is after load.

   THE STATE SIGNAL SURVIVED THE REDESIGN. It used to be carried by the blobs speeding up. It is
   now a ring that pulses around her while she is thinking, plus the live dot beside her, so the
   interface still shows her state without copy. The dot sits in an UNCLIPPED wrapper - inside the
   disc, overflow:hidden ate half of it. */
.wkh-orb { position: relative; flex: none; width: 46px; height: 46px; display: block; }
.wkh-orb i.wkh-in {
  position: absolute; inset: 0; border-radius: 50%; overflow: hidden; isolation: isolate;
  background: #FAF7F1 center / cover no-repeat;
  background-image: url("../img/haniya-avatar-192.webp");
  background-image: image-set(url("../img/haniya-avatar-96.webp") 1x, url("../img/haniya-avatar-192.webp") 2x);
  /* A hairline of the badge's own gold, so she does not dissolve into the white launcher. */
  box-shadow: 0 0 0 1.5px rgba(201, 162, 39, .85), 0 3px 10px rgba(26, 19, 12, .18);
}
.wkh-orb b.wkh-live {
  position: absolute; right: -2px; bottom: -1px; width: 12px; height: 12px; border-radius: 50%;
  background: #2E9E5B; border: 2.5px solid #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, .18);
}
/* "She is thinking." An expanding ring on a pseudo element of the WRAPPER, not of the disc: the
   disc clips its own overflow to stay round, so a halo drawn inside it would be cut off. */
.wkh-orb::after {
  content: ""; position: absolute; inset: -2px; border-radius: 50%; pointer-events: none;
  border: 2px solid var(--wkh-accent); opacity: 0;
}
.wkh-orb.wkh-busy::after { animation: wkh-think 1.5s ease-out infinite; }
.wkh-orb.wkh-sm { width: 44px; height: 44px; }
.wkh-orb.wkh-sm b.wkh-live { width: 11px; height: 11px; right: -3px; }
@keyframes wkh-think {
  0%   { transform: scale(.94); opacity: .75; }
  100% { transform: scale(1.22); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .wkh-orb.wkh-busy::after { animation: none; opacity: .6; } }

/* ── the panel ─────────────────────────────────────────────────────────────────────────────── */
/* THE ENTRANCE IS AN ANIMATION, NOT A TRANSITION ON A CLASS THE SCRIPT ADDS ONE FRAME LATER.
   A transition needs two states, so the script has to paint the panel offset and then add a class
   next frame - and it used requestAnimationFrame to do it. A browser that throttles rAF (a
   backgrounded tab, an occluded frame) never delivers that frame, and the panel sits 14px below
   where it belongs for as long as it is open. Measured, not theorised. An animation has no second
   state to wait for: revealing the element runs it, and hiding it rearms it. */
.wkh-scrim {
  position: fixed; inset: 0; z-index: 320; display: flex; align-items: flex-end;
  justify-content: center; background: rgba(20, 15, 11, .52);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  /* NO fill mode. `both` holds the FROM state whenever animations do not run - a throttled frame,
     an engine that has paused them - and the from state of the panel below is 16px off. With no
     fill the element's own styles are the resting state and the animation is only the entrance, so
     "the animation did not play" degrades to "it appeared", never to "it sits in the wrong place". */
  animation: wkh-fade .18s ease;
}
.wkh-scrim[hidden] { display: none !important; }
.wkh-panel {
  position: relative; display: flex; flex-direction: column; width: 100%; max-width: 560px;
  max-height: 86vh; max-height: 86dvh; background: #fff; color: #1C1A17;
  border-radius: 20px 20px 0 0; box-shadow: 0 -18px 50px rgba(20, 15, 11, .28);
  font-family: var(--font-body, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
  font-size: 17px; line-height: 1.5;
  animation: wkh-rise .22s cubic-bezier(.2, .7, .2, 1);
}
@keyframes wkh-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes wkh-rise { from { transform: translateY(16px); } to { transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .wkh-scrim, .wkh-panel { animation: none; }
}
@media (min-width: 761px) {
  .wkh-scrim { align-items: flex-end; justify-content: flex-end; padding: 0 20px 20px; }
  .wkh-panel { width: 400px; max-height: min(640px, 82vh); border-radius: 20px; }
}

.wkh-x {
  position: absolute; top: 10px; right: 10px; width: 36px; height: 36px; border: 0; padding: 0;
  border-radius: 50%; background: #F3ECDF; color: #6B6258; font-size: 1.3rem; line-height: 1;
  cursor: pointer; display: grid; place-items: center;
}
.wkh-x:hover { background: #E7E0D4; color: #1C1A17; }
.wkh-x:focus-visible { outline: 2px solid var(--wkh-accent-deep); outline-offset: 2px; }

.wkh-head { display: flex; align-items: center; gap: 12px; padding: 18px 56px 12px 16px; border-bottom: 1px solid #E7E0D4; }
.wkh-who { flex: 1; min-width: 0; }
.wkh-who b { display: block; font-size: 1.02rem; line-height: 1.2; font-weight: 700; }
/* The green is one step darker than the dot beside it. The dot is decoration and may be bright;
   the word is 12px text on white, and #2E9E5B reads 3.4:1 there - under the 4.5:1 floor. #1F7042
   is 6.0:1 and looks like the same green. */
.wkh-who small { display: inline-flex; align-items: center; gap: 5px; color: #1F7042; font-size: .76rem; font-weight: 600; }
.wkh-who small::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: #2E9E5B; }
.wkh-call {
  flex: none; display: inline-flex; align-items: center; gap: 6px; padding: 8px 13px;
  border-radius: 999px; border: 1px solid #E7E0D4; background: #fff; color: #B23A1E;
  font-size: .82rem; font-weight: 600; text-decoration: none; white-space: nowrap; min-height: 38px;
}
.wkh-call:active { background: #FBEDE8; }
.wkh-call:focus-visible { outline: 2px solid #B23A1E; outline-offset: 2px; }

.wkh-log { flex: 1; min-height: 120px; overflow-y: auto; overscroll-behavior: contain; padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }
.wkh-msg { max-width: 84%; padding: 10px 13px; border-radius: 14px; font-size: .92rem; line-height: 1.45; white-space: pre-wrap; }
.wkh-msg.her { background: #F4EFE7; color: #1C1A17; border-bottom-left-radius: 4px; align-self: flex-start; }
.wkh-msg.you { background: #B23A1E; color: #fff; border-bottom-right-radius: 4px; align-self: flex-end; }
.wkh-typing { display: inline-flex; gap: 4px; align-items: center; }
.wkh-typing i { width: 6px; height: 6px; border-radius: 50%; background: #B0A79A; animation: wkh-blink 1.1s ease-in-out infinite; }
.wkh-typing i:nth-child(2) { animation-delay: .15s; }
.wkh-typing i:nth-child(3) { animation-delay: .3s; }
@keyframes wkh-blink { 0%, 80%, 100% { opacity: .3; } 40% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .wkh-typing i { animation: none; opacity: .6; } }

.wkh-sugs { display: flex; gap: 6px; overflow-x: auto; padding: 0 16px 10px; scrollbar-width: none; }
.wkh-sugs::-webkit-scrollbar { display: none; }
.wkh-sugs button {
  flex: none; font: inherit; font-size: .82rem; padding: 7px 12px; border-radius: 999px;
  border: 1px solid #E7E0D4; background: #fff; color: #4A443C; cursor: pointer; white-space: nowrap;
}
.wkh-sugs button:hover { border-color: var(--wkh-accent); color: #1C1A17; }
.wkh-sugs button:focus-visible { outline: 2px solid var(--wkh-accent-deep); outline-offset: 2px; }

.wkh-bar { display: flex; gap: 8px; padding: 10px 16px; border-top: 1px solid #E7E0D4; margin: 0; }
.wkh-field {
  flex: 1; display: flex; align-items: center; gap: 6px; background: #fff; border: 1px solid #E7E0D4;
  border-radius: 26px; padding: 4px 4px 4px 16px; transition: border-color .15s ease, box-shadow .15s ease;
}
.wkh-field:focus-within { border-color: #B23A1E; box-shadow: 0 0 0 3px rgba(178, 58, 30, .10); }
/* 16px or iOS zooms the whole page when the field takes focus, which reads as a layout bug. */
.wkh-field input { flex: 1; border: 0; background: none; font: inherit; font-size: 16px; padding: 11px 0; color: #1C1A17; outline: none; min-width: 0; }
.wkh-field button {
  flex: none; width: 38px; height: 38px; border: 0; border-radius: 50%; background: #B23A1E; color: #fff;
  display: grid; place-items: center; cursor: pointer; transition: transform .12s ease, opacity .15s ease;
}
.wkh-field button:active { transform: scale(.93); }
.wkh-field button:disabled { opacity: .35; cursor: default; }
.wkh-field button:focus-visible { outline: 2px solid #B23A1E; outline-offset: 2px; }

.wkh-foot { display: block; text-align: center; margin: 0; padding: 0 16px 16px; font-size: .82rem; color: #6B6258; }

/* Haniya's own scroll lock, applied ONLY when the host page has not already locked the body.
   Two locks fighting over `top` is how a page ends up jumping to the wrong scroll position. */
.wkh-locked { position: fixed; left: 0; right: 0; width: 100%; overflow: hidden; }

.wkh-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
