/*
 * Pip for the web: the character (.pip) and its chat card (.pip-chat).
 *
 * Ported from the app (CharacterView.swift, PromptCardView.swift, WaveDots.swift).
 * Everything is drawn with CSS, nothing is an image. All geometry is a fraction of
 * --pip-size, so Pip is crisp at any size from 20px to 160px and beyond.
 *
 * Theming: every colour and metric below is a custom property with a default, set
 * it on .pip, .pip-chat or any ancestor. This sheet is dark only.
 *
 *   Pip:      --pip-size (set by createPip), --pip-shell-light, --pip-shell-shade,
 *             --pip-eye, --pip-cheek, --pip-sweat, --pip-snore
 *   Speech:   --pip-say-bg, --pip-say-fg, --pip-say-border, --pip-say-radius
 *   Chat:     --pip-card-bg, --pip-card-border, --pip-card-radius, --pip-card-width,
 *             --pip-card-height, --pip-accent (the send button and your messages; stone by
 *             default), --pip-on-accent (the arrow on it), --pip-text, --pip-text-2,
 *             --pip-text-3, --pip-msg-pip, --pip-msg-user, --pip-divider, --pip-field-bg,
 *             --pip-field-border, --pip-font
 */

/* ------------------------------------------------------------------ Pip --- */

.pip {
  /* The orb is 90% of the size; the box is 1.12x as tall to leave room for the bob. */
  --pip-d: calc(var(--pip-size, 96px) * 0.9);
  position: relative;
  display: block;
  flex: none;
  width: var(--pip-size, 96px);
  height: calc(var(--pip-size, 96px) * 1.12);
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.pip.is-held { z-index: 60; }

/* Outermost layer: the drag offset and the lift while held. */
.pip__drag {
  position: absolute;
  inset: 0;
  transition: scale 0.18s ease-out;
}
.pip.is-held .pip__drag { scale: 1.07; }

/* The mood's own loop (bob, hop) lives here, apart from the reactions on .pip__figure. */
.pip__bob, .pip__figure { position: absolute; inset: 0; }
.pip[data-mood="idle"] .pip__bob,
.pip[data-mood="talking"] .pip__bob { animation: pip-bob 1.6s ease-in-out infinite alternate; }
.pip[data-mood="thinking"] .pip__bob { animation: pip-bob-small 0.8s ease-in-out infinite alternate; }
/* A reaction takes the hop and the shake out of the way, like the app does. */
.pip[data-mood="notify"] .pip__bob { animation: pip-hop 0.5s infinite; }
.pip[data-mood="error"] .pip__bob { animation: pip-shake 0.4s linear 1; }
.pip[data-mood="notify"][data-reacting] .pip__bob,
.pip[data-mood="error"][data-reacting] .pip__bob { animation: none; }

@keyframes pip-bob { from { translate: 0 calc(var(--pip-size) * 0.025); } to { translate: 0 calc(var(--pip-size) * -0.025); } }
@keyframes pip-bob-small { from { translate: 0 calc(var(--pip-size) * 0.012); } to { translate: 0 calc(var(--pip-size) * -0.012); } }
@keyframes pip-hop {
  0% { translate: 0 0; animation-timing-function: ease-out; }
  45% { translate: 0 calc(var(--pip-size) * -0.08); animation-timing-function: ease-in; }
  100% { translate: 0 0; }
}
@keyframes pip-shake {
  0% { translate: 0 0; }
  20% { translate: calc(var(--pip-size) * -0.05) 0; }
  40% { translate: calc(var(--pip-size) * 0.05) 0; }
  60% { translate: calc(var(--pip-size) * -0.03) 0; }
  80% { translate: calc(var(--pip-size) * 0.03) 0; }
  100% { translate: 0 0; }
}

/* The orb squashes from its base; breathing is always on. */
.pip__body {
  position: absolute;
  left: 5%;
  top: calc(var(--pip-size) * 0.11);
  width: var(--pip-d);
  height: var(--pip-d);
  transform-origin: 50% 100%;
  animation: pip-breathe 1.6s ease-in-out infinite alternate;
}
@keyframes pip-breathe { from { scale: 1.025 0.975; } to { scale: 0.985 1.02; } }

/* A white ball, drawn flat: one solid white and a crisp shade along its lower right. */
.pip__shell {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--pip-shell-light, #fff);
  box-shadow: inset calc(var(--pip-d) * -0.07) calc(var(--pip-d) * -0.08) 0 0 var(--pip-shell-shade, rgb(222, 226, 236));
}
.pip.is-held .pip__shell { box-shadow: inset calc(var(--pip-d) * -0.07) calc(var(--pip-d) * -0.08) 0 0 var(--pip-shell-shade, rgb(222, 226, 236)), 0 calc(var(--pip-size) * 0.12) calc(var(--pip-size) * 0.25) rgba(0, 0, 0, 0.35); }
.pip__sheen { display: none; }

/* The face turns toward the pointer (--lx/--ly are -1...1). Thinking drifts on its own. */
.pip__face {
  position: absolute;
  inset: 0;
  translate: calc(var(--lx, 0) * var(--pip-d) * 0.08) calc(var(--ly, 0) * var(--pip-d) * 0.06);
  transition: translate 0.18s ease;
}
.pip[data-mood="thinking"] .pip__face { animation: pip-glance 3.4s ease-in-out infinite; }
@keyframes pip-glance {
  0%   { translate: 0 0; }
  10%  { translate: calc(var(--pip-size) * -0.07) calc(var(--pip-size) * -0.05); }
  34%  { translate: calc(var(--pip-size) * -0.07) calc(var(--pip-size) * -0.05); }
  46%  { translate: calc(var(--pip-size) * 0.07) calc(var(--pip-size) * -0.05); }
  70%  { translate: calc(var(--pip-size) * 0.07) calc(var(--pip-size) * -0.05); }
  82%  { translate: 0 calc(var(--pip-size) * -0.065); }
  92%  { translate: 0 calc(var(--pip-size) * -0.065); }
  100% { translate: 0 0; }
}

/* Everything on the face is placed by its centre (--cx, --cy) and size (--w, --h), as a
   share of the orb. */
.pip__eye, .pip__mark, .pip__flat, .pip__cheek {
  position: absolute;
  left: var(--cx);
  top: var(--cy, 44%);
  width: var(--w);
  height: var(--h);
  margin-left: calc(var(--w) * -0.5);
  margin-top: calc(var(--h) * -0.5);
}
.pip__eye--l, .pip__mark--l, .pip__flat--l { --cx: 35%; }
.pip__eye--r, .pip__mark--r, .pip__flat--r { --cx: 65%; }

/* Upright capsule eyes, solid, a little above the middle. */
.pip__eye { --w: 11%; --h: 20%; }
.pip__eye > i {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 999px;
  background: var(--pip-eye, rgb(23, 23, 31));
}
.pip[data-mood="thinking"] .pip__eye { scale: 1 0.88; }
/* The eyes bounce with the words. */
.pip[data-mood="talking"]:not([data-reacting]) .pip__eye { animation: pip-chatter 0.5s linear infinite; }
@keyframes pip-chatter { 0%, 100% { scale: 1 1; } 25% { scale: 1 0.72; } 50% { scale: 1 1; } 75% { scale: 1 0.82; } }

/* "^ ^" when there's news, "– –" when something went wrong, "× ×" when swung around. */
.pip__mark { --w: 16.5%; --h: 16.5%; display: none; }
.pip__mark svg { display: block; width: 100%; height: 100%; overflow: visible; }
.pip__mark path {
  fill: none;
  stroke: var(--pip-eye, rgb(23, 23, 31));
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.pip__happy path { stroke-width: max(1.4px, calc(var(--pip-d) * 0.055)); }
.pip__dizzy path { stroke-width: max(1.3px, calc(var(--pip-d) * 0.045)); }
.pip__flat {
  --w: 15.4%;
  --h: max(1.4px, calc(var(--pip-d) * 0.05));
  --cy: 46%;
  display: none;
  border-radius: 999px;
  background: var(--pip-eye, rgb(23, 23, 31));
}
/* Pink cheeks, when shy. */
.pip__cheek {
  --w: 16%;
  --h: 9%;
  --cy: 60%;
  border-radius: calc(var(--pip-d) * 0.045);
  background: var(--pip-cheek, rgba(255, 115, 153, 0.55));
  opacity: 0;
  transition: opacity 0.2s ease;
}
.pip__cheek--l { --cx: 23%; }
.pip__cheek--r { --cx: 77%; }
.pip[data-blush] .pip__cheek { opacity: 1; }

.pip[data-face="happy"] .pip__eye,
.pip[data-face="flat"] .pip__eye,
.pip[data-face="dizzy"] .pip__eye { display: none; }
.pip[data-face="happy"] .pip__happy { display: block; }
.pip[data-face="flat"] .pip__flat { display: block; }
.pip[data-face="dizzy"] .pip__dizzy { display: block; }
.pip[data-roll] .pip__dizzy { animation: pip-roll 0.6s linear infinite; }
@keyframes pip-roll { to { rotate: -360deg; } }

/* A drop of sweat, when panicking. */
.pip__sweat {
  position: absolute;
  left: 76%;
  top: 6%;
  width: 20%;
  height: 18%;
  overflow: visible;
  fill: var(--pip-sweat, rgba(115, 191, 255, 0.95));
  opacity: 0;
}
.pip[data-sweat] .pip__sweat { opacity: 1; animation: pip-sweat 0.7s linear 3; }
.pip[data-sweat="held"] .pip__sweat { animation-iteration-count: infinite; }
@keyframes pip-sweat { to { translate: 0 calc(var(--pip-d) * 0.25); } }

/* "z" floating up, when dozing. */
.pip__snore {
  position: absolute;
  left: 87.8%;
  top: 20%;
  width: calc(var(--pip-d) * 0.3);
  height: calc(var(--pip-d) * 0.3);
  margin: calc(var(--pip-d) * -0.15) 0 0 calc(var(--pip-d) * -0.15);
  display: grid;
  place-items: center;
  font: 700 max(7px, calc(var(--pip-d) * 0.22)) / 1 var(--pip-font, -apple-system, BlinkMacSystemFont, "SF Pro Rounded", system-ui, sans-serif);
  color: var(--pip-snore, rgba(255, 255, 255, 0.9));
  opacity: 0;
  pointer-events: none;
}
.pip[data-doze] .pip__snore { opacity: 1; animation: pip-snore 2.2s linear infinite; }
@keyframes pip-snore {
  0% { translate: 0 0; opacity: 0; }
  50% { translate: calc(var(--pip-d) * 0.06) calc(var(--pip-d) * -0.15); opacity: 1; }
  100% { translate: calc(var(--pip-d) * 0.12) calc(var(--pip-d) * -0.3); opacity: 0; }
}

/* The part of Pip that takes the pointer: the orb, not the empty corners of its box. */
.pip__hit {
  position: absolute;
  left: 5%;
  top: calc(var(--pip-size) * 0.11);
  width: var(--pip-d);
  height: var(--pip-d);
  border-radius: 50%;
  pointer-events: none;
  outline: none;
}
.pip--interactive .pip__hit {
  pointer-events: auto;
  cursor: grab;
  touch-action: none;
}
.pip.is-held .pip__hit { cursor: grabbing; }
.pip__hit:focus-visible { box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85), 0 0 0 5px rgba(10, 132, 255, 0.55); }

/* Offscreen Pips keep their layers but stop their clock. */
.pip.is-offscreen, .pip.is-offscreen * { animation-play-state: paused !important; }

/* ----------------------------------------------------------- speech bubble --- */

/* A small HUD bubble above Pip. Pass data-bubble="light" for the app's own white bubble. */
.pip__say {
  --_dx: 0px;
  position: absolute;
  left: 50%;
  bottom: calc(100% - var(--pip-size) * 0.11 + 6px);
  z-index: 5;
  width: max-content;
  max-width: min(220px, 80vw);
  padding: 8px 12px;
  border-radius: var(--pip-say-radius, 14px);
  background: var(--pip-say-bg, rgba(32, 32, 35, 0.88));
  color: var(--pip-say-fg, rgba(255, 255, 255, 0.92));
  border: 1px solid var(--pip-say-border, rgba(255, 255, 255, 0.12));
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45), 0 1px 3px rgba(0, 0, 0, 0.3);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter: blur(14px) saturate(1.4);
  font: 600 12.5px / 1.35 var(--pip-say-font, ui-rounded, "SF Pro Rounded", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif);
  text-align: left;
  overflow-wrap: anywhere;
  pointer-events: none;
  opacity: 0;
  translate: calc(-50% + var(--_dx)) 4px;
  scale: 0.96;
  transform-origin: 50% 100%;
  transition: opacity 0.16s ease-out, translate 0.16s ease-out, scale 0.16s ease-out;
}
.pip__say[hidden] { display: none; }
.pip__say[data-side="below"] { bottom: auto; top: calc(100% - var(--pip-size) * 0.11 + 6px); transform-origin: 50% 0; translate: calc(-50% + var(--_dx)) -4px; }
.pip__say.is-on { opacity: 1; translate: calc(-50% + var(--_dx)) 0; scale: 1; }
.pip__say.is-off { transition-duration: 0.2s; }
.pip[data-bubble="light"] .pip__say {
  --pip-say-bg: #fff;
  --pip-say-fg: rgba(0, 0, 0, 0.85);
  --pip-say-border: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* --------------------------------------------------------------- wave dots --- */

/* Three dots rising and falling one after another, a beat apart (WaveDots.swift). */
.pip-dots {
  --dot: 6px;
  --gap: 5px;
  --amp: 3px;
  display: inline-flex;
  align-items: center;
  gap: var(--gap);
  height: calc(var(--dot) + var(--amp) * 2 + 2px);
  color: var(--pip-text-2, rgba(255, 255, 255, 0.55));
}
.pip-dots > i {
  width: var(--dot);
  height: var(--dot);
  border-radius: 50%;
  background: currentColor;
  opacity: 0.5;
  animation: pip-wave 0.52s ease-in-out infinite alternate;
}
.pip-dots > i:nth-child(2) { animation-delay: 0.15s; }
.pip-dots > i:nth-child(3) { animation-delay: 0.3s; }
@keyframes pip-wave {
  from { translate: 0 var(--amp); opacity: 0.5; }
  to { translate: 0 calc(var(--amp) * -1); opacity: 0.85; }
}

/* --------------------------------------------------------------- chat card --- */

/* The card is the <section>. :where() keeps the specificity of a plain class (a page's own
   rules still win) and keeps a host div that also carries the class from becoming a second card. */
:where(section).pip-chat {
  --_bg: var(--pip-card-bg, #161616);
  --_line: var(--pip-card-border, rgba(255, 255, 255, 0.12));
  --_divider: var(--pip-divider, rgba(255, 255, 255, 0.1));
  --_accent: var(--pip-accent, #d3cbbb);
  --_on-accent: var(--pip-on-accent, #16130f);
  --_text: var(--pip-text, rgba(255, 255, 255, 0.85));
  --_text2: var(--pip-text-2, rgba(255, 255, 255, 0.55));
  --_text3: var(--pip-text-3, rgba(255, 255, 255, 0.25));
  --_msg-pip: var(--pip-msg-pip, rgba(255, 255, 255, 0.07));
  --_msg-user: var(--pip-msg-user, color-mix(in srgb, var(--_accent) 26%, transparent));
  --_field: var(--pip-field-bg, rgba(255, 255, 255, 0.055));
  --_field-line: var(--pip-field-border, rgba(255, 255, 255, 0.08));
  position: relative;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  /* Never wider than a phone's screen (a page's gutters are about 20px a side). */
  width: min(var(--pip-card-width, 360px), 100vw - 40px);
  max-width: 100%;
  margin-inline: auto;
  /* The card never changes size: the log inside it scrolls. */
  height: var(--pip-card-height, 500px);
  max-height: 100%;
  overflow: hidden;
  isolation: isolate;
  color: var(--_text);
  background: var(--_bg);
  border: 1px solid var(--_line);
  border-radius: var(--pip-card-radius, 22px);
  box-shadow: 0 28px 60px rgba(0, 0, 0, 0.36), 0 3px 10px rgba(0, 0, 0, 0.3);
  font: 13px / 1.38 var(--pip-font, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", "Segoe UI", system-ui, sans-serif);
  -webkit-font-smoothing: antialiased;
  text-align: left;
}
.pip-chat *, .pip-chat *::before, .pip-chat *::after { box-sizing: border-box; }

/* Header: Pip, its name, what it is doing, and three small tools. */
.pip-chat__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--_divider);
}
.pip-chat__avatar { flex: none; width: 26px; height: 29.12px; }
.pip-chat__who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.pip-chat__name { font-size: 13px; font-weight: 600; line-height: 1.25; }
.pip-chat__status {
  font-size: 11px;
  line-height: 1.3;
  color: var(--_text2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: opacity 0.18s ease;
}
.pip-chat__status.is-swap { opacity: 0; }
.pip-chat__tools { display: flex; align-items: center; }
.pip-chat__tool {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--_text2);
  cursor: pointer;
  transition: color 0.15s ease, background-color 0.15s ease;
}
.pip-chat__tool:hover { color: var(--_text); background: rgba(255, 255, 255, 0.07); }
.pip-chat__tool svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.pip-chat__tool[aria-pressed="true"] svg { fill: currentColor; }

/* The log: messages and nothing else. Yours on the right, Pip's on the left. */
.pip-chat__log {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 14px 12px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
}

/* Before anything is said: Pip's face and one line. Laid over the empty log, so it leaves
   when the first message comes without moving it. */
.pip-chat__empty {
  position: absolute;
  inset: 0;
  padding: 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.pip-chat__empty b { font-size: 13px; font-weight: 500; color: var(--_text2); }
.pip-chat__empty.is-gone { opacity: 0; }

.pip-chat__row { display: flex; }
.pip-chat__row--user { justify-content: flex-end; padding-left: 30px; }
.pip-chat__row--pip { justify-content: flex-start; padding-right: 28px; }
.pip-chat__msg {
  max-width: 100%;
  padding: 8px 11px;
  border-radius: 14px;
  background: var(--_msg-pip);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  -webkit-user-select: text;
  user-select: text;
  transform-origin: 0 100%;
  animation: pip-rise 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.pip-chat__row--pip .pip-chat__msg { border-bottom-left-radius: 5px; }
.pip-chat__row--user .pip-chat__msg { border-bottom-right-radius: 5px; background: var(--_msg-user); transform-origin: 100% 100%; }
.pip-chat__msg strong { font-weight: 600; color: #fff; }
.pip-chat__msg code {
  padding: 1px 4px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.09);
  font: 12px ui-monospace, "SF Mono", Menlo, monospace;
}
/* A message rises a little from below into its place, fading in, like one arriving. */
@keyframes pip-rise {
  from { opacity: 0; translate: 0 14px; scale: 0.96; }
  to { opacity: 1; translate: 0 0; scale: 1; }
}
/* While Pip is working the bubble holds the three dots; they take exactly one line of text,
   so the first word replaces them without the bubble changing height. */
.pip-chat__msg.is-typing { padding-inline: 12px; }
.pip-chat__msg .pip-dots { display: flex; height: 18px; }

/* The composer: one rounded field with the text, and a row of controls beneath it. */
.pip-chat__composer {
  display: flex;
  flex: none;
  flex-direction: column;
  gap: 8px;
  margin: 0 10px 10px;
  padding: 10px 10px 8px;
  border: 1px solid var(--_field-line);
  border-radius: 16px;
  background: var(--_field);
  transition: border-color 0.15s ease;
  cursor: text;
}
.pip-chat__composer:focus-within { border-color: rgba(255, 255, 255, 0.18); }
.pip-chat__input {
  display: block;
  width: 100%;
  min-height: 18px;
  max-height: calc(13px * 1.38 * 6);
  padding: 0 2px;
  margin: 0;
  border: 0;
  outline: 0;
  resize: none;
  background: transparent;
  color: var(--_text);
  font: inherit;
  caret-color: var(--_accent);
  scrollbar-width: thin;
  /* Safari refuses to type into a field whose ancestor says user-select: none (the website's
     demo screen does), so the field says it again. */
  -webkit-user-select: text;
  user-select: text;
}
.pip-chat__input::placeholder { color: var(--_text3); opacity: 1; }
.pip-chat__controls { display: flex; align-items: center; justify-content: space-between; }
.pip-chat__go { display: flex; align-items: center; gap: 4px; }
.pip-chat__plus {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  margin-left: -2px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--_text2);
  cursor: pointer;
  transition: color 0.15s ease;
}
.pip-chat__plus:hover { color: var(--_text); }
.pip-chat__plus svg, .pip-chat__send svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* After the last answer: a quiet way back to the start. It always takes its place in the
   row, so showing it moves nothing. */
.pip-chat__again {
  height: 24px;
  padding: 0 8px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--_text2);
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, color 0.15s ease, background-color 0.15s ease, visibility 0s linear 0.25s;
}
.pip-chat.is-done .pip-chat__again { opacity: 1; visibility: visible; transition-delay: 0s; }
.pip-chat__again:hover { color: var(--_text); background: rgba(255, 255, 255, 0.07); }

/* Send: dark and quiet with nothing to send, the accent when there is. */
.pip-chat__send {
  position: relative;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.08);
  color: var(--_text3);
  transition: background-color 0.15s ease, color 0.15s ease, scale 0.12s ease;
}
.pip-chat__send svg { width: 13px; height: 13px; stroke-width: 2.2; }
.pip-chat__send.is-ready { background: var(--_accent); color: var(--_on-accent); }
.pip-chat__send.is-ready:active { scale: 0.92; }
.pip-chat__send.is-empty { cursor: default; }
.pip-chat__send.is-stop { background: rgba(255, 255, 255, 0.85); color: #161616; }
.pip-chat__send.is-stop svg { fill: currentColor; stroke-width: 1; width: 10px; height: 10px; }
/* Before the first send, a ring spreads from the button now and then: this is what to press. */
.pip-chat.is-fresh .pip-chat__send.is-ready::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1.5px solid var(--_accent);
  pointer-events: none;
  animation: pip-nudge 2.2s ease-out 0.6s infinite;
}
@keyframes pip-nudge {
  0% { scale: 1; opacity: 0.7; }
  70%, 100% { scale: 1.9; opacity: 0; }
}

.pip-chat button:focus-visible,
.pip-chat__input:focus-visible { outline: 2px solid color-mix(in srgb, var(--_accent) 70%, white); outline-offset: 1px; }
.pip-chat__input:focus-visible { outline: 0; }

/* ----------------------------------------------------------- reduced motion --- */

/* Faces still change, but nothing wobbles, bobs, breathes or rises. */
@media (prefers-reduced-motion: reduce) {
  .pip *, .pip-chat *, .pip-chat *::before, .pip-chat *::after {
    animation: none !important;
    transition-duration: 0s !important;
  }
  .pip-dots > i { opacity: 0.7; }
}
/* A link in Pip's words (where it put something, if the browser held the tab back). */
.pip-chat__msg a { color: #fff; font-weight: 600; text-decoration: underline; text-underline-offset: 2px; text-decoration-color: rgba(255, 255, 255, 0.45); }
.pip-chat__msg a:hover { text-decoration-color: #fff; }

/* ---- Pip's life (as in the app, 14 October) ------------------------------------------
   Faces change smoothly instead of swapping drawings, the eyes take on looks (wide,
   squint, lidded, closed, cross), one can wink, and the whole body leans toward the
   pointer while the eyes lead. */
.pip__mark, .pip__flat {
  display: block;
  opacity: 0;
  scale: 0.55;
  transition: opacity 0.16s ease, scale 0.34s cubic-bezier(0.3, 1.35, 0.5, 1);
}
.pip[data-face="happy"] .pip__happy,
.pip[data-face="flat"] .pip__flat,
.pip[data-face="dizzy"] .pip__dizzy { opacity: 1; scale: 1; }
.pip[data-face] .pip__eye { display: block; }
.pip__eye > i { transition: opacity 0.14s ease, scale 0.32s cubic-bezier(0.3, 1.3, 0.5, 1), rotate 0.32s cubic-bezier(0.3, 1.3, 0.5, 1); }
.pip[data-face="happy"] .pip__eye > i,
.pip[data-face="flat"] .pip__eye > i,
.pip[data-face="dizzy"] .pip__eye > i { opacity: 0; scale: 1 0.25; }
.pip[data-eyes="wide"] .pip__eye > i { scale: 1.24 1.3; }
.pip[data-eyes="lidded"] .pip__eye > i { scale: 1 0.62; translate: 0 8%; }
.pip[data-eyes="squint"] .pip__eye > i { scale: 1.16 0.34; }
.pip[data-eyes="closed"] .pip__eye > i { scale: 1.3 0.12; translate: 0 18%; }
.pip[data-eyes="angry"] .pip__eye > i { scale: 1.2 0.4; }
.pip[data-eyes="angry"] .pip__eye--l > i { rotate: 24deg; }
.pip[data-eyes="angry"] .pip__eye--r > i { rotate: -24deg; }
.pip[data-wink="l"] .pip__eye--l > i,
.pip[data-wink="r"] .pip__eye--r > i { opacity: 0; scale: 1 0.25; }
.pip[data-wink="l"] .pip__happy.pip__mark--l,
.pip[data-wink="r"] .pip__happy.pip__mark--r { opacity: 1; scale: 1; }
.pip__body {
  rotate: calc(var(--lx, 0) * 8deg);
  translate: calc(var(--lx, 0) * var(--pip-size) * 0.03) 0;
  transition: rotate 0.6s cubic-bezier(0.2, 0.9, 0.25, 1), translate 0.6s cubic-bezier(0.2, 0.9, 0.25, 1);
}
.pip__face {
  translate: calc(var(--lx, 0) * var(--pip-d) * 0.13) calc(var(--ly, 0) * var(--pip-d) * 0.1);
  transition: translate 0.35s cubic-bezier(0.2, 0.9, 0.25, 1);
}
@media (prefers-reduced-motion: reduce) {
  .pip__body { rotate: none; translate: none; }
}
