/* ============================================================
   INTELLYFT — chat widget ("Cube")
   Built on the same tokens as style.css (--sky/--blue/--ink,
   --font-display/--font-body, --r-lg/--r-md/--r-sm). Cube is the assistant
   identity from /cube-avatar: the brand mark's own geometry with two
   skewed eyes baked in and deliberately no mouth — see cube-avatar/README.txt
   for the full rationale. One face (#cube-avatar, in index.html's shared
   defs) is reused everywhere in the widget — launcher, header, message
   avatars, teaser — so it reads as a single character. Motion is restrained
   (an idle blink, a "thinking" seam-glow while waiting on a reply) and
   every animation has a prefers-reduced-motion fallback.
   ============================================================ */

.chat-widget {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 200;
  font-family: var(--font-body);
}

/* ---------- Cube, the chat assistant's face (#cube-avatar) ----------
   A dedicated symbol, separate from the brand mark (#cube/#cube-sm): "Cube
   has eyes, the Intellyft logo never does." Eyes are baked into the SVG,
   skewed 30° to sit on the isometric faces rather than float over them, and
   there's no mouth — see the identity kit for why. `.blink` drives the
   idle blink, `.thinking` lights the hidden seam sky-blue while a reply is
   generating, in place of a bolted-on spinner. Every avatar in the widget
   (launcher, header, message rows, teaser) shares this one face. ---------- */
.cube-avatar-mark { width: 100%; height: 100%; display: block; }
/* `.cube-avatar-eye`/`.cube-avatar-glow` live inside the <symbol> and only
   ever reach the page through <use href="#cube-avatar">. A <use> clones that
   content into a shadow instance tree, and while inherited CSS properties
   (custom properties included) flow into it from `.blink`/`.thinking` on the
   real ancestor, a descendant *selector* like `.blink .cube-avatar-eye`
   cannot cross that boundary — it silently matches nothing, which is why
   Cube never blinked. Route the toggle through inherited custom properties
   instead, which do cross into <use> content. */
.cube-avatar-glow { opacity: var(--cube-glow-opacity, 0); animation: var(--cube-glow-anim, none); }
/* `--cube-eye-x`/`--cube-eye-y` carry the cursor-tracking offset (set on
   `:root` by js/cube-cursor.js so it inherits into every `<use>` clone, same
   trick as `--cube-blink-anim` below). They're baked into the *base*
   transform and into every keyframe stop of both animations below, rather
   than left as a plain `.style.transform` a script sets directly — a
   running CSS animation replaces `transform` outright for as long as it's
   active, so a bare inline transform on this element would be silently
   discarded the instant `.blink` (which runs 5.6s infinite, i.e. always) is
   present. `--cube-blink-anim`/`--cube-eye-anim` are two separate slots
   rather than one shared `animation` value so `.happy` can suspend blinking
   for its duration instead of the two fighting over `transform`. */
.cube-avatar-eye {
  animation: var(--cube-blink-anim, none), var(--cube-eye-anim, none);
  transform-origin: center;
  transform: translate(var(--cube-eye-x, 0px), var(--cube-eye-y, 0px));
}
.blink { --cube-blink-anim: cubeBlink 5.6s infinite; }
.thinking { --cube-glow-anim: cubeSeamGlow 1.8s ease-in-out infinite; }
/* Triggered on chat open (see js/chat.js's triggerHappy()). Suspends the
   blink slot for the duration so the two animations never contend for
   `transform` on the same element. */
.happy { --cube-blink-anim: none; --cube-eye-anim: cubeHappy .6s var(--ease) forwards; }
@keyframes cubeBlink {
  0%, 92%, 100% { transform: translate(var(--cube-eye-x, 0px), var(--cube-eye-y, 0px)) scaleY(1); }
  95%           { transform: translate(var(--cube-eye-x, 0px), var(--cube-eye-y, 0px)) scaleY(.12); }
}
@keyframes cubeHappy {
  0%   { transform: translate(var(--cube-eye-x, 0px), var(--cube-eye-y, 0px)) scaleY(1); }
  50%  { transform: translate(var(--cube-eye-x, 0px), var(--cube-eye-y, 0px)) scaleY(.1) scaleX(1.2); }
  100% { transform: translate(var(--cube-eye-x, 0px), var(--cube-eye-y, 0px)) scaleY(1); }
}
@keyframes cubeSeamGlow { 0%, 100% { opacity: 0; } 50% { opacity: .6; } }
@media (prefers-reduced-motion: reduce) {
  .blink { --cube-blink-anim: none; }
  .thinking { --cube-glow-anim: none; --cube-glow-opacity: .6; }
  .happy { --cube-eye-anim: none; }
}

/* ---------- Flat mascot avatar chip (header, messages, teaser) ---------- */
.cube-avatar {
  flex-shrink: 0;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--ink);
}
.cube-avatar-sm { width: 26px; height: 26px; padding: 3px; }
.cube-avatar-md { width: 34px; height: 34px; padding: 4px; }
.cube-avatar-lg { width: 38px; height: 38px; padding: 5px; }

/* ---------- Floating action button ----------
   Per the identity kit: a plain 60px circle, ink ground — Cube's own face
   (blink included) is the only thing doing the talking, so it reads as the
   same character as the header and message avatars, not a separate prop. */
.chat-fab {
  position: relative;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  border: none;
  background: var(--ink);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 10px 26px rgba(11, 18, 32, .28), 0 3px 10px rgba(11, 18, 32, .16);
  transition: transform .18s var(--ease), background .25s var(--ease), color .25s var(--ease), box-shadow .25s var(--ease);
}
/* The FAB's default ink circle is the same --ink as the hero, the stats
   band, the mv-cards and the footer (see css/style.css), so the button's
   own circle disappears into any of those. js/chat.js samples the actual
   color behind the fixed widget as the page scrolls (rather than hardcoding
   "which sections are dark") and toggles this on the widget when it's dark
   under there — the circle flips to white and the icon gets a richer,
   more-saturated palette (the pale default reads fine on dark ink but is
   too washed out against white). */
.chat-widget--on-dark .chat-fab {
  background: #fff;
  color: var(--ink);
  box-shadow: 0 10px 26px rgba(11, 18, 32, .18), 0 3px 10px rgba(11, 18, 32, .1);
  /* Set here, on the button, rather than scoped to .cube-avatar-mark —
     these need to reach both the flat glyph's <use> content (inherited
     custom properties cross that boundary, ordinary selectors can't, see
     the .cube-avatar-eye comment further up) *and* .cube3d-face below,
     which is a sibling of the glyph, not a descendant of it. Set once
     here, both pick it up through plain inheritance. */
  --ca-top: #60A5FA;
  --ca-left: #1E3A8A;
  --ca-right: #2563EB;
  /* The seam is a knockout line, not a stroke color — it's meant to match
     whatever ground the mark sits on so the background shows through as the
     gap between faces (same idea as the plain mark's --seam across its
     .m-full/.m-rev/.m-onblue palettes). #0B1220 matches the *default* ink
     circle; on the white circle here it has to flip to white too, or the
     seam stays dark and stops reading as a gap. */
  --ca-seam: #fff;
}
.chat-fab:hover {
  transform: translateY(-2px);
  box-shadow: 0 14px 34px rgba(11, 18, 32, .34), 0 3px 10px rgba(11, 18, 32, .18);
}
/* One-time "look here" spin shortly after the page loads (see js/chat.js's
   ATTENTION_DELAY_MS), so the widget catches the eye before the teaser
   bubble opens on its own (TEASER_DELAY_MS, 3.2s later).

   A flat glyph rotated in 3D — even correctly, even as two backface-hidden
   layers swapping front/back — still reads as a card flipping, because
   there's nothing to see once it's edge-on: no thickness, no side. So this
   swaps in an actual 6-face box (`.cube3d`, markup in index.html) for the
   duration of the spin: real geometry, each face `translateZ`'d out to a
   side of a real cube, so turning it shows genuine edges and shifting
   faces rather than a flat plane foreshortening.

   For it to read as *this* logo spinning rather than a generic box that
   happens to be nearby, its resting pose is set to the same true-isometric
   angle the flat mark is drawn in — rotateX(-35.264deg) (arctan(1/√2), the
   standard isometric tilt) rotateY(45deg) puts three faces on screen as
   equal rhombi, top/front/right, matching the mark's top/left/right
   silhouette almost exactly (check the brand symbol's own numbers: its top
   face is 120 wide by 70 tall, a 1.71:1 ratio — true isometric's is
   √3:1 ≈ 1.73:1). `perspective: 900px` against a 22px box is ~40x its
   size, weak enough that edges stay close to parallel rather than visibly
   converging — real isometric drawing has no vanishing point at all, so
   this is the closest CSS's perspective-based 3D gets to matching it.
   Faces are colored with the mark's own --ca-top/-left/-right tokens
   (inherited from the same custom properties css/chat.css's on-dark rule
   already sets — see `.chat-widget--on-dark .chat-fab`), not invented
   hex values, so a color change to the brand palette updates both
   automatically. (Front/right's assignment below assumes the standard
   result of this rotation combo puts front on-screen-left and right
   on-screen-right, to match dark-on-left/mid-on-right like the flat mark —
   if it renders mirrored, swap the two color values.)

   The branded glyph (with Cube's eyes) fades out as the box fades in, and
   back in once the box settles — eyes only belong on the calm, resting
   icon, not a tumbling block, the same reasoning as the hero's "assemble"
   sequence not showing a face until the pieces have landed. The spin holds
   the isometric tilt constant and only sweeps rotateY, landing back on a
   multiple of 360° past its start — so the box's last frame before it
   fades out is pixel-for-pixel the same pose the flat glyph fades back in
   at, and the handoff between the two reads as continuous rather than a
   swap. All three animations below share one 1.3s duration so they land in
   sync; js/chat.js clears `.attention` on a matching timeout rather than
   `animationend`, since three separate per-element animation-end events
   racing to bubble first would risk cutting one of them off a frame
   early. */
.chat-fab.attention .icon-chat { animation: fabGlyphOut 1.3s var(--ease); }
@keyframes fabGlyphOut {
  0%, 100% { opacity: 1; transform: scale(1); }
  14%, 86% { opacity: 0; transform: scale(.7); }
}

.chat-fab-icon.icon-cube3d { opacity: 0; perspective: 900px; }
.chat-fab.attention .icon-cube3d { animation: fabCubeFade 1.3s var(--ease); }
@keyframes fabCubeFade {
  0%, 100% { opacity: 0; }
  16%, 84% { opacity: 1; }
}

.cube3d {
  position: relative;
  width: 22px;
  height: 22px;
  transform-style: preserve-3d;
  transform: rotateX(-35.264deg) rotateY(45deg);
}
.chat-fab.attention .cube3d { animation: fabCubeSpin 1.3s cubic-bezier(.32, .68, .3, 1); }
@keyframes fabCubeSpin {
  0%   { transform: rotateX(-35.264deg) rotateY(45deg)      scale(.8); }
  16%  { transform: rotateX(-35.264deg) rotateY(185deg)     scale(1.08); }
  100% { transform: rotateX(-35.264deg) rotateY(765deg) scale(1); } /* 45deg + 720deg (two full turns), lands back on the resting pose */
}
/* Six real faces, each pushed out along Z to a side of a 22px cube (half
   the side length). Bottom is never actually seen — the constant -35.264°
   tilt keeps it facing away for the entire Y sweep — so it's given the
   seam ink rather than a fifth invented blue. Front/back and left/right
   alternate the same two brand tones so whichever pair is on screen at any
   point in the spin still reads as two distinct faces meeting at a seam,
   not one flat color. */
.cube3d-face {
  position: absolute;
  inset: 0;
  border: 2px solid var(--ca-seam, #0B1220);
  box-shadow: inset 0 0 5px rgba(255, 255, 255, .18);
}
.cube3d-face.top    { background: var(--ca-top, #93C5FD);   transform: rotateX(90deg)  translateZ(11px); }
.cube3d-face.bottom { background: var(--ca-seam, #0B1220);  transform: rotateX(-90deg) translateZ(11px); }
.cube3d-face.front  { background: var(--ca-left, #2B4BB8);  transform: translateZ(11px); }
.cube3d-face.right  { background: var(--ca-right, #5B8CF5); transform: rotateY(90deg)  translateZ(11px); }
.cube3d-face.back   { background: var(--ca-left, #2B4BB8);  transform: rotateY(180deg) translateZ(11px); }
.cube3d-face.left   { background: var(--ca-right, #5B8CF5); transform: rotateY(-90deg) translateZ(11px); }

@media (prefers-reduced-motion: reduce) {
  .chat-fab.attention .icon-chat,
  .chat-fab.attention .icon-cube3d,
  .chat-fab.attention .cube3d { animation: none; }
}
.chat-fab-icon { position: absolute; display: flex; align-items: center; justify-content: center; transition: opacity .18s, transform .18s; }
.chat-fab-icon.icon-chat { width: 30px; height: 35px; }
.chat-fab-icon.icon-cube3d { width: 22px; height: 22px; }
.chat-fab-icon.icon-close { width: 22px; height: 22px; opacity: 0; transform: scale(.6) rotate(-45deg); }
.chat-fab[aria-expanded="true"] .icon-chat { opacity: 0; transform: scale(.6) rotate(45deg); }
.chat-fab[aria-expanded="true"] .icon-close { opacity: 1; transform: none; }
.chat-fab-dot {
  position: absolute; top: -3px; right: -3px; width: 14px; height: 14px;
  border-radius: 50%; background: var(--amber); border: 2px solid var(--surface);
}
.chat-fab-dot::after {
  content: ""; position: absolute; inset: -3px; border-radius: 50%; border: 2px solid var(--amber);
  opacity: .55; animation: dotPing 1.8s ease-out infinite;
}
@keyframes dotPing { 0% { transform: scale(.7); opacity: .6; } 100% { transform: scale(1.7); opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .chat-fab-dot::after { animation: none; display: none; }
}

/* ---------- Teaser bubble ---------- */
.chat-teaser {
  position: absolute;
  right: 4px;
  bottom: 74px;
  width: min(272px, calc(100vw - 44px));
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 18px;
  padding: 13px 30px 13px 12px;
  box-shadow: 0 20px 48px rgba(15, 23, 42, .18);
  cursor: pointer;
  animation: teaserIn .45s var(--ease);
}
.chat-teaser[hidden] { display: none; }
.chat-teaser::after {
  content: "";
  position: absolute;
  bottom: -7px; right: 26px;
  width: 14px; height: 14px;
  background: var(--surface);
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  transform: rotate(45deg);
  border-radius: 0 0 3px 0;
}
.chat-teaser-body strong { display: block; font-family: var(--font-display); font-weight: 700; font-size: .86rem; color: var(--ink); margin-bottom: 1px; }
.chat-teaser-body p { font-size: .82rem; line-height: 1.42; color: var(--slate); }
.chat-teaser-close {
  position: absolute; top: 7px; right: 7px; width: 22px; height: 22px; border: none; border-radius: 50%;
  background: transparent; color: var(--slate); display: flex; align-items: center; justify-content: center;
  transition: background .15s, color .15s;
}
.chat-teaser-close svg { width: 13px; height: 13px; }
.chat-teaser-close:hover { background: var(--surface-2); color: var(--ink); }
@keyframes teaserIn { from { opacity: 0; transform: translateY(10px) scale(.94); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .chat-teaser { animation: none; } }

/* ---------- Panel ---------- */
.chat-panel {
  position: absolute;
  right: 0;
  bottom: 74px;
  width: min(380px, calc(100vw - 44px));
  height: min(580px, calc(100vh - 140px));
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 20px;
  box-shadow: 0 28px 64px rgba(15, 23, 42, .2);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transform-origin: bottom right;
  animation: chatIn .22s var(--ease);
}
.chat-panel[hidden] { display: none; }
@keyframes chatIn { from { opacity: 0; transform: scale(.94) translateY(8px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .chat-panel { animation: none; } }

.chat-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 16px 15px; border-bottom: 1px solid var(--rule);
  background: linear-gradient(120deg, var(--blue) 0%, var(--navy) 130%); color: #fff;
}
.chat-panel-head-id { display: flex; align-items: center; gap: 11px; }
/* Ink ground stays per the kit's default rule; a hairline ring keeps the
   circle reading as a distinct chip against the blue/navy header gradient
   instead of melting into it at the navy end. */
.chat-panel-head-id .cube-avatar { border: 1px solid rgba(255, 255, 255, .3); }
.chat-panel-head-id strong { display: block; font-family: var(--font-display); font-weight: 700; font-size: 1rem; }
.chat-panel-head-id span { display: flex; align-items: center; gap: 5px; font-size: .76rem; color: rgba(255, 255, 255, .78); }
.chat-online-dot { width: 7px; height: 7px; border-radius: 50%; background: #34D399; box-shadow: 0 0 0 2px rgba(52, 211, 153, .25); }
.chat-panel-close {
  background: transparent; border: none; color: rgba(255, 255, 255, .78); width: 30px; height: 30px;
  border-radius: var(--r-sm); display: flex; align-items: center; justify-content: center; transition: background .15s, color .15s;
}
.chat-panel-close:hover { background: rgba(255, 255, 255, .14); color: #fff; }
.chat-panel-close svg { width: 17px; height: 17px; }

/* ---------- Messages ---------- */
.chat-messages {
  flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 12px;
  scroll-behavior: smooth; background: var(--paper);
}
.chat-row { display: flex; align-items: flex-end; gap: 8px; max-width: 88%; }
.chat-row.bot { align-self: flex-start; }
.chat-row.user { align-self: flex-end; flex-direction: row-reverse; }
.chat-msg {
  padding: 10px 13px; border-radius: 16px; font-size: .89rem; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word; box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}
.chat-row.bot .chat-msg { background: var(--surface); border: 1px solid var(--rule); color: var(--ink); border-bottom-left-radius: 4px; }
.chat-row.user .chat-msg { background: linear-gradient(135deg, var(--blue), var(--blue-deep)); color: #fff; border-bottom-right-radius: 4px; }
.chat-row.error .chat-msg { background: #FEF2F2; border: 1px solid #FECACA; color: #991B1B; }
.chat-msg.lead {
  align-self: center; max-width: 100%; text-align: center; background: #ECFDF5; border: 1px solid #A7F3D0;
  color: #065F46; font-size: .8rem; font-weight: 500; border-radius: 999px; padding: 7px 14px;
}

.chat-typing-row { align-self: flex-start; display: flex; align-items: flex-end; gap: 8px; }
.chat-typing { display: flex; gap: 4px; padding: 12px 14px; background: var(--surface); border: 1px solid var(--rule); border-radius: 16px; border-bottom-left-radius: 4px; box-shadow: 0 1px 2px rgba(15, 23, 42, .04); }
.chat-typing span { width: 6px; height: 6px; border-radius: 50%; background: #93C5FD; animation: chatBounce 1.1s infinite ease-in-out; }
.chat-typing span:nth-child(2) { background: #5B8CF5; animation-delay: .15s; }
.chat-typing span:nth-child(3) { background: #2B4BB8; animation-delay: .3s; }
@keyframes chatBounce { 0%, 60%, 100% { transform: translateY(0); opacity: .5; } 30% { transform: translateY(-4px); opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .chat-typing span { animation: none; opacity: .8; } }

/* ---------- Input ---------- */
.chat-input-row { display: flex; align-items: flex-end; gap: 8px; padding: 12px; border-top: 1px solid var(--rule); background: var(--surface); }
.chat-input-row textarea {
  flex: 1; resize: none; max-height: 96px; padding: 10px 15px; border: 1px solid var(--rule);
  border-radius: 18px; font: inherit; font-size: .88rem; color: var(--ink); background: var(--paper);
  transition: border-color .2s, box-shadow .2s;
}
.chat-input-row textarea:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px rgba(37, 99, 235, .14); background: var(--surface); }
.chat-send {
  flex-shrink: 0; width: 38px; height: 38px; border: none; border-radius: 50%;
  background: linear-gradient(135deg, var(--blue), var(--blue-deep)); color: #fff;
  display: flex; align-items: center; justify-content: center; transition: transform .18s, box-shadow .18s, opacity .18s;
  box-shadow: 0 6px 16px rgba(37, 99, 235, .32);
}
.chat-send:hover { transform: translateY(-1px); }
.chat-send:disabled { opacity: .5; cursor: not-allowed; box-shadow: none; transform: none; }
.chat-send svg { width: 17px; height: 17px; }

.chat-disclaimer { padding: 0 14px 12px; font-size: .72rem; color: var(--slate); text-align: center; }
.chat-disclaimer a { color: var(--blue); font-weight: 500; }

/* ---------- Small screens ---------- */
@media (max-width: 480px) {
  .chat-widget { right: 14px; bottom: 14px; }
  .chat-panel { width: calc(100vw - 28px); height: calc(100vh - 110px); bottom: 70px; }
  .chat-teaser { right: 0; width: calc(100vw - 28px); }
}
