/* Nibbi, drawn — the frames. One SVG (public/nibbi.mascot.js); each state is
   keyframes on its groups. The fill is the reader's highlight color. */
.nbx{overflow:visible}
.nbx-ink{fill:#0B0B08}
.nbx-fill{fill:var(--accent)}
.nbx-eyes path{fill:#070905}
.nbx-eyes .nbx-shine{fill:#fff}
.nbx-mouth{fill:#170600}
.nbx-tongue{fill:#F13B28}
.nbx-shut{fill:none; stroke:#0B0B08; stroke-width:4.5; stroke-linecap:round}
.nbx-glasses{fill:none; stroke:#0B0B08; stroke-width:4.5; stroke-linecap:round}
.nbx-glasses circle{fill:rgba(255,255,255,.12)}
.nbx-shadow{fill:#000; opacity:.1}
.nbx-sparks{fill:none; stroke:var(--accent); stroke-width:6; stroke-linecap:round; transform-box:view-box; transform-origin:212px 92px}
.nbx-dots circle{fill:var(--chrome-muted, #9C9A92); stroke:none}
.nbx[data-state="idea"] .nbx-sparks{animation:nbxSpark .9s ease-in-out infinite}
.nbx[data-state="think"] .nbx-dots circle{animation:nbxDot 1.2s ease-in-out infinite}
.nbx-dots circle:nth-child(2){animation-delay:.2s} .nbx-dots circle:nth-child(3){animation-delay:.4s}
@keyframes nbxSpark{0%,100%{opacity:.35; transform:scale(.85)} 40%{opacity:1; transform:scale(1.08)}}
@keyframes nbxDot{0%,100%{opacity:.25} 50%{opacity:1}}
.nbx-bubble{fill:color-mix(in srgb, #9FD3F0 55%, transparent); stroke:#5A9CC2; stroke-width:2}

.nbx-all, .nbx-foot, .nbx-eyes, .nbx-bubble, .nbx-shadow{transform-box:view-box}
.nbx-all{transform-origin:123px 228px}
.nbx-foot{transform-origin:145px 210px}
.nbx-eyes{transform-origin:130px 115px}
.nbx-bubble{transform-origin:140px 132px}
.nbx-shadow{transform-origin:123px 230px}

/* idle: breathing, a blink now and then, a small wave */
.nbx[data-state="idle"] .nbx-all{animation:nbxBreathe 3.2s ease-in-out infinite}
.nbx[data-state="idle"] .nbx-eyes{animation:nbxBlink 4.6s infinite}
/* think: looks up and rocks */
.nbx[data-state="think"] .nbx-all{animation:nbxRock 1.4s ease-in-out infinite}
.nbx[data-state="think"] .nbx-eyes{transform:translate(4px,-7px)}
/* idea: arm up high, a hop */
.nbx[data-state="idea"] .nbx-all{animation:nbxHop .9s cubic-bezier(.3,.7,.4,1) infinite}
.nbx[data-state="idea"] .nbx-shadow{animation:nbxShadow .9s cubic-bezier(.3,.7,.4,1) infinite}
/* sleep: slow breathing, the bubble swells and shrinks */
.nbx[data-state="sleep"] .nbx-all{animation:nbxBreathe 3.6s ease-in-out infinite}
.nbx[data-state="sleep"] .nbx-bubble{animation:nbxBubble 3.6s ease-in-out infinite}

@keyframes nbxBreathe{0%,100%{transform:scale(1,1)} 50%{transform:scale(1.025,.975)}}
@keyframes nbxBlink{0%,92%,100%{transform:scaleY(1)} 95%{transform:scaleY(.1)}}
@keyframes nbxRock{0%,100%{transform:rotate(-3deg)} 50%{transform:rotate(3deg)}}
@keyframes nbxHop{0%,100%{transform:translateY(0) scale(1.03,.97)} 35%{transform:translateY(-14px) scale(.97,1.04)} 60%{transform:translateY(0) scale(1.04,.96)}}
@keyframes nbxShadow{0%,60%,100%{transform:scale(1)} 35%{transform:scale(.8)}}
@keyframes nbxBubble{0%,100%{transform:scale(.3)} 50%{transform:scale(1.25)}}
@media (prefers-reduced-motion:reduce){ .nbx *{animation:none !important} }

/* hello: one bounce as the chat opens */
.nbx[data-state="hello"] .nbx-all{animation:nbxHop .7s cubic-bezier(.3,.7,.4,1) 2}
.nbx[data-state="hello"] .nbx-sparks{animation:nbxSpark .7s ease-in-out 2}
/* happy: eyes smiling, a hop and sparks — something was done */
.nbx[data-state="happy"] .nbx-all{animation:nbxHop .8s cubic-bezier(.3,.7,.4,1) infinite}
.nbx[data-state="happy"] .nbx-sparks{animation:nbxSpark .8s ease-in-out infinite}
.nbx[data-state="happy"] .nbx-shadow{animation:nbxShadow .8s cubic-bezier(.3,.7,.4,1) infinite}
/* oops: a wobbly mouth, a drop of sweat, a small shake */
.nbx-drop{fill:#9FD3F0; stroke:#5A9CC2; stroke-width:2; transform-box:view-box; transform-origin:74px 84px}
.nbx[data-state="oops"] .nbx-all{animation:nbxShake .45s ease-in-out 2}
.nbx[data-state="oops"] .nbx-drop{animation:nbxDrop 1.4s ease-in infinite}
@keyframes nbxShake{0%,100%{transform:translateX(0)} 25%{transform:translateX(-5px)} 75%{transform:translateX(5px)}}
@keyframes nbxDrop{0%{transform:translateY(-4px); opacity:0} 25%{opacity:1} 100%{transform:translateY(14px); opacity:0}}

/* His lines are black on every color and in both modes — outline, eyes,
   glasses and the send arrow beside him — so he always reads as the same
   character. A light outline made him look like a different drawing on each
   color. Goth is the one exception and has its own look below. */

/* Goth: his own look (see EMO in nibbi.mascot.js). Black body and a light
   outline in BOTH modes; a fringe over one eye, a winged liner, a smirk. */
.nbx-emo{display:none}
:root[data-accent="goth"] .nbx-emo{display:inline}
:root[data-accent="goth"] .nbx-fill{fill:#1F1E1C}
:root[data-accent="goth"] .nbx-ink{fill:#E8E6DC}
:root[data-accent="goth"] .nbx-fringe{fill:#0B0B08; stroke:#E8E6DC; stroke-width:3; stroke-linejoin:round}
:root[data-accent="goth"] .nbx-liner, :root[data-accent="goth"] .nbx-smirk{fill:none; stroke:#E8E6DC; stroke-width:4.5; stroke-linecap:round}
:root[data-accent="goth"] .nbx-eyes path{fill:#E8E6DC}
:root[data-accent="goth"] .nbx-eyes .nbx-shine{fill:#0B0B08}
:root[data-accent="goth"] .nbx-mouth, :root[data-accent="goth"] .nbx-tongue{display:none}
:root[data-accent="goth"] .nbx-shut, :root[data-accent="goth"] .nbx-glasses{stroke:#E8E6DC}
:root[data-accent="goth"] .nbx-sparks{stroke:#E8E6DC}

/* No tuft on Goth — just the hair. The body is clipped below the tuft (the
   clip id is per drawing, so the stylesheet reads it from data-clip) and a
   fresh crown edge closes the head where the tuft was. */
.nbx-crown{display:none; fill:none}
:root[data-accent="goth"] .nbx-crown{display:inline; stroke:#E8E6DC; stroke-width:5; stroke-linecap:round}

/* ------------------------------------------------- Nibbi, plain -----------
   The other drawing (public/nibbi.orb.js): a sphere and two eyes. Same seven
   states and the same motions as the character, so it is as alive — it just
   is not a character. The expression is entirely the eyes, so the eyes carry
   what the mouth and the arm carry on the drawn one. */
.nbo-fill{fill:var(--accent)}
.nbo-edge{fill:none; stroke:#1F1E1C; stroke-width:5}
.nbo-eyes rect{fill:#1F1E1C}
.nbo-line{fill:none; stroke:#1F1E1C; stroke-width:7; stroke-linecap:round}
.nbo-ring{fill:none; stroke:#1F1E1C; stroke-width:3; opacity:.8}
.nbo-dots circle{fill:#1F1E1C}
.nbo-sparks{fill:none; stroke:#1F1E1C; stroke-width:5; stroke-linecap:round}
.nbo-drop{fill:#1F1E1C}
.nbo-bubble{fill:none; stroke:#1F1E1C; stroke-width:3; opacity:.8}
.nbo-shadow{fill:#1F1E1C; opacity:.10}

/* Breathing, and a blink now and then. The blink squashes the eyes rather
   than hiding them, which is one property and cannot leave them stuck shut. */
.nbo-ball{transform-box:view-box; transform-origin:130px 152px; animation:nbo-breathe 4.2s ease-in-out infinite}
.nbo-eyes{transform-box:view-box; transform-origin:130px 115px; animation:nbo-blink 6.5s ease-in-out infinite}
@keyframes nbo-breathe{ 0%,100%{transform:scale(1)} 50%{transform:scale(1.025)} }
@keyframes nbo-blink{ 0%,95%,100%{transform:scaleY(1)} 97%{transform:scaleY(.08)} }

/* Working: it rocks, and the dots climb. */
.nbo[data-state="think"] .nbo-all{transform-box:view-box; transform-origin:130px 204px; animation:nbo-rock 1.9s ease-in-out infinite}
@keyframes nbo-rock{ 0%,100%{transform:rotate(-3deg)} 50%{transform:rotate(3deg)} }
.nbo-dots circle{animation:nbo-dot 1.5s ease-in-out infinite}
.nbo-dots circle:nth-child(2){animation-delay:.18s}
.nbo-dots circle:nth-child(3){animation-delay:.36s}
@keyframes nbo-dot{ 0%,100%{opacity:.25} 50%{opacity:1} }

/* A hop when the chat opens, when an idea lands and when something was done. */
.nbo[data-state="hello"] .nbo-all,
.nbo[data-state="idea"] .nbo-all,
.nbo[data-state="happy"] .nbo-all{transform-box:view-box; animation:nbo-hop .62s ease-out}
.nbo[data-state="idea"] .nbo-all{animation-iteration-count:infinite; animation-duration:1.5s}
@keyframes nbo-hop{ 0%,100%{transform:translateY(0)} 35%{transform:translateY(-14px)} 70%{transform:translateY(-3px)} }
.nbo-sparks{animation:nbo-spark 1.4s ease-in-out infinite}
@keyframes nbo-spark{ 0%,100%{opacity:.3} 50%{opacity:1} }

/* It could not be done: a shake, and the eyes are crossed. */
.nbo[data-state="oops"] .nbo-all{transform-box:view-box; animation:nbo-shake .5s ease-in-out}
@keyframes nbo-shake{ 0%,100%{transform:translateX(0)} 20%{transform:translateX(-7px)} 45%{transform:translateX(6px)} 70%{transform:translateX(-3px)} }

/* Asleep: slower breath, and the bubble at the side swells. */
.nbo[data-state="sleep"] .nbo-ball{animation-duration:6.5s}
.nbo[data-state="sleep"] .nbo-eyes{animation:none}
.nbo-bubble{transform-box:view-box; transform-origin:182px 150px; animation:nbo-bub 3.4s ease-in-out infinite}
@keyframes nbo-bub{ 0%,100%{transform:scale(.5); opacity:.35} 55%{transform:scale(1); opacity:.85} }

/* GOTH is its own look here too: black with a cream edge in the light, and
   inverted at night, because a white disc on a dark page is a hole in it. */
:root[data-accent="goth"] .nbo-fill{fill:#1F1E1C}
:root[data-accent="goth"] .nbo-edge,
:root[data-accent="goth"] .nbo-line,
:root[data-accent="goth"] .nbo-ring,
:root[data-accent="goth"] .nbo-sparks,
:root[data-accent="goth"] .nbo-bubble{stroke:#E8E6DC}
:root[data-accent="goth"] .nbo-eyes rect,
:root[data-accent="goth"] .nbo-dots circle,
:root[data-accent="goth"] .nbo-drop{fill:#E8E6DC}
:root[data-mode="night"][data-accent="goth"] .nbo-fill{fill:#E8E6DC}
:root[data-mode="night"][data-accent="goth"] .nbo-edge,
:root[data-mode="night"][data-accent="goth"] .nbo-line,
:root[data-mode="night"][data-accent="goth"] .nbo-ring,
:root[data-mode="night"][data-accent="goth"] .nbo-sparks,
:root[data-mode="night"][data-accent="goth"] .nbo-bubble{stroke:#1F1E1C}
:root[data-mode="night"][data-accent="goth"] .nbo-eyes rect,
:root[data-mode="night"][data-accent="goth"] .nbo-dots circle,
:root[data-mode="night"][data-accent="goth"] .nbo-drop{fill:#1F1E1C}

@media (prefers-reduced-motion:reduce){ .nbo *{animation:none !important} }
