/* Isolated inside the companion shadow root; no academy selectors are changed. */
:host { all: initial; position: fixed; inset: 0; z-index: 65; pointer-events: none; color-scheme: light; font-family: "Segoe UI", Tahoma, Arial, sans-serif; color: #233044; }
/* The `hidden` attribute alone is not enough here, and this is the one place it
   isn't: the `all: initial` above is an author-origin declaration on the host, so
   it resets `display` over the user-agent rule that `hidden` relies on, and a
   hidden host keeps laying out and painting (measured: display:block, one client
   rect). Restating it as an attribute selector puts it back in the author origin
   where it can win. Anything that adds a `display` to :host must keep this below it. */
:host([hidden]) { display: none !important; }
* { box-sizing: border-box; }
[hidden] { display: none !important; }
button, textarea { font: inherit; }
button { cursor: pointer; }
button:focus-visible, textarea:focus-visible { outline: 3px solid #438fac; outline-offset: 3px; }
/* The statue. Anchored physically at 0,0 and moved by a transform, NOT by logical
   insets: transform is not direction-aware, the walk writes a pixel x every frame,
   and mixing the two would make the mascot travel backwards under dir="ltr". The
   box's own size is written by the script from petGeom(), so nothing here assumes
   a scale. Colours are literal because :host declares `all: initial` — the page's
   token variables do not cross into this shadow root. */
#pet { position: fixed; z-index: 20; top: 0; left: 0; pointer-events: none; user-select: none; transform: translate3d(var(--x, 0px), var(--y, 0px), 0); will-change: transform; }
/* Hidden until the script has measured the viewport and placed it. Without this
   the mascot paints for one frame in the top-left corner on every navigation,
   because --x/--y do not exist until boot() runs. */
#pet:not(.is-placed) { visibility: hidden; }
/* The levitation's ribbons sweep FAR wider than the figure. Measured on the
   levitate take: the man is about 326 of the frame's 1092 source pixels, but
   the golden streamers reach both edges, so at the companion's 224px box the
   frame's own 358px landed 67px outside on each side and the ribbons ended in
   a hard vertical cut. Every one of the three canvases is narrower than that
   frame -- the hero's 506 against 768, the loader's likewise -- so this belongs
   on all of them, not on the small one.
   A fade rather than a wider box: widening the companion's box would push the
   figure 67px short of each screen edge on its walk (clampPet works from the
   box, not the figure) and would stretch the drag grip with it. The mask is
   tied to the box in percentages so it scales with every breakpoint, and the
   outer 12% is empty on every clip -- the figure's own margin is never less
   than 43px of 224, or 138 of 506. The bottom is deliberately NOT faded: the
   feet and the shadow live there. */
#pet-canvas { display: block; -webkit-mask-image: linear-gradient(to right,transparent,#000 12%,#000 88%,transparent),linear-gradient(to bottom,transparent,#000 8%,#000); -webkit-mask-composite: source-in; mask-image: linear-gradient(to right,transparent,#000 12%,#000 88%,transparent),linear-gradient(to bottom,transparent,#000 8%,#000); mask-composite: intersect; }
.pet-shadow { position: absolute; left: 50%; bottom: 4%; transform: translateX(-50%); width: 42%; height: 12px; border-radius: 50%; background: radial-gradient(closest-side, rgba(0, 0, 0, .72), transparent 72%); filter: blur(4px); transition: transform .4s cubic-bezier(.22, 1, .36, 1), opacity .4s; }
/* He leaves the ground during the levitation, so the ground stops holding him.
   BOTH states, because the levitation is two clips: he rises and unwraps in
   'play' and he comes back down in 'return', and the touchdown is the last half
   second of the seven the return runs for. Lifting the shadow for 'play' alone
   snapped it back to full strength the instant the return began — a hard shadow
   under a mummy still in mid-air, held there for seven seconds. The state leaves
   'return' at the landing, so the .4s transition above is what puts the shadow
   back under his feet, arriving with him rather than ahead of him. */
#pet[data-state='play'] .pet-shadow,
#pet[data-state='return'] .pet-shadow { transform: translateX(-50%) scale(.6); opacity: .4; }
.pet-grip { position: absolute; left: 50%; top: 10%; transform: translateX(-50%); width: 44%; height: 84%; border-radius: 80px; border: 0; background: transparent; pointer-events: auto; cursor: grab; touch-action: none; }
#pet.is-dragging .pet-grip { cursor: grabbing; }
.pet-bubble { position: absolute; bottom: calc(100% - 6px); left: 50%; transform: translateX(-50%) translateY(0); min-width: 150px; max-width: 230px; padding: 11px 14px; border: 1px solid #8d6f18; border-radius: 14px 14px 14px 4px; background: linear-gradient(#1b1526, #120e1b); color: #efe4cd; font-size: 12.5px; line-height: 1.7; text-align: center; box-shadow: 0 18px 40px -18px #000; opacity: 1; pointer-events: none; transition: opacity .35s cubic-bezier(.22, 1, .36, 1), transform .45s cubic-bezier(.34, 1.56, .64, 1); }
/* The rest state, and the one the markup ships in: the bubble is silent until
   something the visitor did gives it a line. */
.pet-bubble.is-quiet { opacity: 0; transform: translateX(-50%) translateY(8px); }
.chat-panel { position: fixed; width: 366px; height: min(515px, calc(100dvh - 32px)); bottom: 24px; right: 24px; display: flex; flex-direction: column; z-index: 30; overflow: hidden; border: 1px solid #dce5ec; border-radius: 20px; background: #fff; box-shadow: 0 20px 70px #18374b20, 0 3px 9px #18374b08; }
.chat-header { display: flex; align-items: center; justify-content: space-between; padding: 20px 21px 16px; background: #f4f8fa; border-bottom: 1px solid #e9eff3; }
.chat-name { display: flex; align-items: center; gap: 8px; }
.chat-name h2 { margin: 0; color: #253e53; font-size: 18px; font-weight: 650; }
.online-dot { width: 6px; height: 6px; background: #50a78f; border-radius: 50%; }
/* Logical sides here and on the two bubbles below: the panel follows the page's
   locale (dir="ltr" in English), so an indent or a bubble's tail corner written
   as right/left would sit on the wrong side there. */
.chat-header p { margin: 6px 0 0; margin-inline-start: 14px; color: #7d8b99; font-size: 10px; }
.icon-button { position: relative; border: none; background: transparent; color: #81909d; font-size: 25px; width: 32px; height: 32px; line-height: 1; border-radius: 8px; }
/* (owner · phone) Measured 32x32 on a 375px phone — the control that closes
   this panel, twelve pixels under the 44px touch floor. The visible chip keeps
   its size because the header is 20px of padding around an 18px heading and a
   44px button would set that row's height; only the hit area grows, and its
   only neighbour is text, so there is nothing beside it for the extra six
   pixels to steal a tap from. */
.icon-button::after { content: ''; position: absolute; inset: -6px; }
.icon-button:hover { background: #e6eef2; }
.messages { flex: 1; min-height: 0; padding: 20px 18px 12px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; scrollbar-width: thin; }
.message { max-width: 92%; font-size: 13px; line-height: 1.85; color: #3c5266; }
.message p { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
/* Same-origin links companion.js builds inside a reply; same accent as .support-link. */
.message p a { color: #2f657e; text-decoration: underline; text-underline-offset: 2px; }
.message p a:focus-visible { outline: 3px solid #438fac; outline-offset: 2px; border-radius: 2px; }
.message.assistant { align-self: flex-start; padding: 12px 14px; border-radius: 14px; border-start-start-radius: 4px; background: #f1f6f8; }
.message.user { align-self: flex-end; padding: 11px 14px; border-radius: 14px; border-start-end-radius: 4px; background: #294b63; color: #fff; }
.message.pending { color: #8b9aa5; }
/* Suggested questions and starters. Nothing here is directional: the row is a
   wrapping flex line, which lays itself out from the panel's start edge (its dir
   follows the page locale), the chips align their text to `start`, and the only
   offsets are symmetric — so the same rules read correctly in the Arabic (RTL) and
   English (LTR) panel.
   The padding is 6px because the global focus ring is a 3px outline at 3px offset,
   and this row is a scroll container that would clip it; the margin is 18px minus
   that padding, so the row still lines up with the message log above it. The
   max-height is insurance, not layout: it clears three rows of chips (measured 112px
   of content — six starters wrap to three rows in the 366px panel, the ordinary case,
   which must never scroll), and exists only so a set that wraps to six rows on a
   320px phone cannot push the conversation itself out of the panel.
   (owner · phone) 132 -> 140 because the row gap below went 6 -> 14 to carry the
   chips' 44px hit areas, and this cap has to move with it or the fix would buy a
   touch target by making the ordinary six-starter set scroll. The arithmetic the
   132 came from, restated: three rows of 31.6px chips plus two 14px gaps is
   120.8px of content, and `box-sizing: border-box` means this cap also has to
   hold the row's own 12px of padding — 132.8 in total, which 132 did not. */
/* The shrink ORDER inside the panel column, which matters because .chat-panel is
   overflow:hidden and therefore CLIPS whatever does not fit rather than scrolling it.
   From most to least expendable: the log (flex:1, min-height:0 — it scrolls), then
   the chips (they shrink and scroll within their cap), and never the composer.
   `flex: none` here was the wrong end of that order: it made the chips immovable and
   left .chat-compose, on its default shrink factor, as the thing that gave way — so a
   short panel (landscape phone, or dvh collapsing when the keyboard opens) clipped
   the textarea and the send button off the bottom and the visitor could not type at
   all. The cap is viewport-relative for the same reason: 132px of chips is a
   reasonable third of a tall panel and most of a short one. */
.chat-chips { display: flex; flex: 0 1 auto; min-height: 0; flex-wrap: wrap; gap: 14px 6px; margin: 0 12px 10px; padding: 6px; max-height: min(140px, 22dvh); overflow-y: auto; scrollbar-width: thin; }
/* max-width, not a fixed one: a chip is as wide as its question and no wider, but a
   long question wraps inside the panel instead of widening it into a sideways scroll. */
.chat-chips button { position: relative; max-width: 100%; padding: 6px 11px; border: 1px solid #d5e2e9; border-radius: 999px; background: #f4f9fb; color: #37728a; font-size: 11px; line-height: 1.6; text-align: start; overflow-wrap: anywhere; transition: background 160ms, border-color 160ms; }
/* (owner · phone) A chip measured 31.6px tall. The BLOCK axis alone is grown,
   because that is the only one short of the floor — the narrowest chip on the
   page is 79px wide — and growing the inline axis too would push each chip's
   hit area 7px into its neighbour's across a 6px gap, which is the one failure
   worse than a small target. The row gap goes 6 -> 14 to pay for the same
   thing between rows, and the cap above moved with it so the row still fits:
   three rows now measure 3 x 31.6 + 2 x 14 = 120.8px of content inside a 140px
   border-box ceiling. Measured with six real starters: three rows, 134.8px,
   scrollHeight === clientHeight. */
.chat-chips button::after { content: ''; position: absolute; inset-block: -7px; inset-inline: 0; }
.chat-chips button:hover { background: #e6eff3; border-color: #b9d2dc; }
.chat-chips button:active { background: #dae8ee; }
/* The hand-off is the only thing in a reply the visitor can act on, so it is the
   only filled control in the log — the send button's teal, so it reads as this
   panel's own call to action rather than a link dropped into the text. inline-flex
   keeps the hit area to the label's width in either direction; the bubble's own
   block flow puts it on its own line under the reply. */
.support-link { display: inline-flex; align-items: center; margin: 10px 0 2px; padding: 8px 14px; border-radius: 11px; background: #2f657e; color: #fff; font-size: 12px; font-weight: 600; line-height: 1.5; text-decoration: none; transition: background 160ms; }
.support-link:hover { background: #26556b; }
/* The global focus rule covers button and textarea only; an anchor needs its own. */
.support-link:focus-visible { outline: 3px solid #438fac; outline-offset: 3px; }
/* Provenance, not answer: a reply the model wrote says so inside its own bubble.
   Muted and one step smaller than the reply it annotates so it reads as a footnote
   the eye can skip, and margin-top only because `.message p` zeroes both margins. */
.message.assistant .ai-note { margin-top: 8px; font-size: 10px; line-height: 1.7; color: #8e9aa5; }
/* An unmatched reply is warmed to the same family as .chat-error below — not an
   error, but not the confident answer the cool grey bubble promises either. */
.message.assistant[data-handoff] { background: #fdf6f0; }
.chat-error { margin: 0 18px 8px; padding: 9px 11px; background: #fff4ed; border-radius: 8px; color: #93603d; font-size: 11px; line-height: 1.7; }
.chat-options { display: flex; justify-content: space-between; margin: 3px 19px 10px; }
.chat-options button { position: relative; border: 0; background: none; color: #8695a2; font-size: 10px; padding: 3px; }
/* (owner · phone) 68x20 measured. Wide enough, 24px short in the block axis,
   so only that axis grows. The row is `justify-content: space-between` with one
   button at each end, so the two can never reach each other; above it the chips
   row ends 10px clear and below it the composer starts 10px clear, which is
   more than the 12px the hit box takes on each side of a 20px control. */
.chat-options button::after { content: ''; position: absolute; inset-block: -12px; inset-inline: 0; }
.chat-options button[aria-pressed='true'] { color: #3a8198; }
.chat-compose { flex: none; display: flex; align-items: center; gap: 8px; margin: 0 16px; border: 1px solid #dee7ed; border-radius: 13px; background: #fafcfd; padding: 6px 9px; }
.chat-compose:focus-within { border-color: #73a7b8; }
.chat-compose textarea { flex: 1; min-width: 0; min-height: 38px; max-height: 96px; padding: 9px 5px; background: transparent; color: #344e64; border: 0; font-size: 16px; line-height: 1.5; resize: none; }
.chat-compose textarea::placeholder { color: #93a0ab; font-size: 13px; }
.chat-compose button { position: relative; width: 33px; height: 33px; border: 0; border-radius: 10px; background: #2f657e; color: white; font-size: 22px; flex: none; }
/* (owner · phone) 33x33 measured — send, the one control that posts the
   message. Six pixels of hit area on every side takes it to 45, and the gap to
   the textarea beside it is 8, so the field it reaches toward keeps its own
   edge. The chip stays 33 because it is sized to the composer's 38px row. */
.chat-compose button::after { content: ''; position: absolute; inset: -6px; }
.chat-compose button:disabled { opacity: .4; cursor: wait; }
.chat-note { flex: none; margin: 9px 18px 13px; text-align: center; font-size: 9px; color: #95a0ab; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

.chat-panel { pointer-events: auto; }
.chat-panel { bottom: 50px; }
@media (max-width:700px) {
 .chat-panel { right: 12px; bottom: max(68px, env(safe-area-inset-bottom)); width: calc(100% - 24px); height: min(470px, calc(100dvh - 120px)); border-radius: 17px; }
}
@media (prefers-reduced-motion:reduce) { *, *::before, *::after { transition:none !important; } }
@media print { :host { display:none !important; } }
