/* ============================================================
   OLO FACE — text-based animated mascot faces
   ============================================================
   The OLO face is the "OLO" of the Studyolo wordmark, set live in
   Jersey 15 (Google Fonts): two O eyes raised one notch, an L nose
   lowered one notch, oxide pupils behind the O counters, and an
   ellipsis mouth below. Eyebrows are accented O glyphs from the font
   itself (Ò Ó Ô Ō — verified present in Jersey 15 v4).

   Geometry is derived from the font metrics (upem 1350, cap 750)
   and the studyolo-light.svg wordmark:
     1 notch        = cap/4      = 187.5u = 0.1389em
     nose drop      = 2 notches  = 375u   = 0.2778em
     letter gap     = 100u                = 0.0741em
     O advance      = 600u                = 0.4444em
     L advance      = 500u                = 0.3704em
     pupil          = 326u × 544u         = 0.2415em × 0.4030em
     baseline       = ascent 1050/1350    = 0.7778em from line top
     dot (period)   = 150u                = 0.1111em, pitch 250u

   Usage:
     <span data-olo="happy"></span>          (built by olo-face.js)
   or set a size/color on the element:
     <span data-olo="normal" style="font-size:3rem;color:#292524"></span>
   Letters inherit `color` (currentColor). Pupils are always oxide.
   Moods: normal · bored · confused · angry · sad · happy
   ============================================================ */

.olo-face {
    --olo-pupil: #f5ecd2;
    color: #9a3b2f;
    position: relative;
    display: inline-block;
    width: 1.4074em;
    height: 1.32em;
    font-family: 'Jersey 15', monospace;
    font-weight: 400;
    line-height: 1;
    white-space: nowrap;
    user-select: none;
    vertical-align: middle;
}

/* The palette rule: RED letters on light surfaces (default above — oxide
   letters, tan pupils), TAN on dark surfaces (below). The light variant
   is MONOCHROME TAN with TRANSPARENT eye internals: lid and shine both
   go transparent, so the dark ground shows through the counter around
   the tan pupil — happy keeps its notch ABOVE the pupil and sad its
   notch BELOW, exactly where the white shine sits in the red variant,
   but as see-through space. The only color is the tan. */
.olo-face--light {
    color: #f5ecd2;
    --olo-pupil: currentColor;
}
.olo-face--light .olo-lid,
.olo-face--light .olo-shine { background: transparent; }
/* The tan pupil must always float in dark space to read as an eye:
   inset it so the transparent frame survives every gaze slide. */
.olo-face--light .olo-pupil {
    left: 0.0952em;
    top: 0.1278em;
    width: 0.18em;
    height: 0.30em;
}

/* ---------- letters ---------- */
.olo-eye,
.olo-nose {
    position: relative;
    display: inline-block;
    line-height: 1;
    transition: transform 0.12s ease;
}
.olo-nose,
.olo-eye--r { margin-left: 0.0741em; }

/* The L drops two notches below the eye band (as in the wordmark) */
.olo-nose { transform: translateY(0.2778em); }

/* Eye internals, back to front: the counter (a clipping window over the
   glyph's drawn box, so sliding pupils can never escape the letterform)
   holding three same-shaped layers — lid (letter color; what a blink
   reveals), shine (white; Matthew's Illustrator recipe: a white copy of
   the pupil under the pupil), pupil on top; then the O glyph. For happy
   the pupil drops one notch, exposing the shine's rounded TOP; for sad
   it rises one notch, exposing the shine's rounded BOTTOM. */
.olo-eye .olo-glyph { position: relative; z-index: 2; }
.olo-counter {
    position: absolute;
    z-index: 0;
    left: 0.0370em;
    top: 0.2222em;
    width: 0.3704em;
    height: 0.5556em;
    overflow: hidden;
}
.olo-lid,
.olo-shine,
.olo-pupil {
    position: absolute;
    left: 0.0645em;
    top: 0.0763em;
    width: 0.2415em;
    height: 0.4030em;
}
.olo-lid { z-index: 0; background: currentColor; }
.olo-shine { z-index: 1; background: #fff; display: none; }
.olo-pupil { z-index: 2; background: var(--olo-pupil); }

/* ---------- blinks ----------
   Default: only the inside of the eye blinks — the pupil squashes to a
   sliver mid-counter while the O stays put. --olo-pupil-y carries each
   mood's gaze offset through the blink. Legacy whole-eye squash lives
   on as the .olo-face--fullblink variant. */
.olo-eye {
    transform-origin: 50% 0.5em;
    transition: transform 0.07s ease-in;
}
.olo-pupil {
    transform-origin: 50% 50%;
    transition: transform 0.07s ease-in;
}
.olo-face.is-blinking .olo-pupil,
.olo-wordmark.is-blinking .olo-pupil {
    animation: none;
    transform: translateY(var(--olo-pupil-y, 0em)) scaleY(0.1);
}
.olo-face--bored.is-blinking .olo-pupil,
.olo-face--sad.is-blinking .olo-pupil { transition-duration: 0.16s; }

/* A closed eye carries no shine */
.olo-face.is-blinking .olo-shine { display: none; }

/* Legacy variant: the entire O squashes */
.olo-face--fullblink.is-blinking .olo-pupil {
    transform: translateY(var(--olo-pupil-y, 0em));
}
.olo-face--fullblink.is-blinking .olo-eye { transform: scaleY(0.12); }

/* ---------- mouth (ellipsis) ---------- */
/* Three period glyphs at the ellipsis pitch, centered under the
   L + right-O span, one notch below the bottom of the L. */
.olo-mouth {
    position: absolute;
    left: 0.6852em;
    top: 0.5278em;
    line-height: 1;
    letter-spacing: 0;
    pointer-events: none;
}
.olo-dot {
    display: inline-block;
    line-height: 1;
    transition: transform 0.25s ease;
}

/* ============================================================
   LIVE WORDMARK — "STUDYOLO" as text, for navs
   ============================================================
   The full wordmark set live in Jersey 15: STUDY on the baseline,
   O eyes raised one notch, L nose lowered one notch — no mouth,
   no brows. Pupils blink every few seconds (JS) and glance right
   on a long cycle: enough to suggest he is alive, nothing more.

   Server-render this markup (works before JS; JS only adds blinks):
     <span class="olo-wordmark" role="img" aria-label="Studyolo"
           style="font-size:2.117rem; color:#f5ecd2;">
       <span class="olo-letter">S</span>...<span class="olo-letter">Y</span>
       <span class="olo-eye olo-eye--l"><span class="olo-pupil"></span><span class="olo-glyph">O</span></span>
       <span class="olo-nose">L</span>
       <span class="olo-eye olo-eye--r"><span class="olo-pupil"></span><span class="olo-glyph">O</span></span>
     </span>
   Size: for the same letter height as the SVG at height H,
   font-size = H × (40 / 64.6) / 0.5556  (1.9rem img → 2.117rem).
   ============================================================ */
.olo-wordmark {
    position: relative;
    display: inline-block;
    font-family: 'Jersey 15', monospace;
    font-weight: 400;
    line-height: 1;
    white-space: nowrap;
    user-select: none;
    vertical-align: middle;
    --olo-pupil: #9a3b2f;
}
.olo-wordmark > * { display: inline-block; line-height: 1; }
.olo-wordmark > * + * { margin-left: 0.0741em; }
.olo-wordmark .olo-eye { position: relative; transform: translateY(-0.1389em); }
.olo-wordmark .olo-nose { position: relative; transform: translateY(0.1389em); }
.olo-wordmark .olo-pupil { animation: olo-look 5s ease-in-out infinite; }

/* Hover: OLO goes happy — the O·L·O trio bounces (STUDY stays put),
   pupils drop a notch, the white shine shows at the top, and the
   ellipsis mouth pops in under the L, exactly like the happy face.
   Resumes wandering, mouthless, when you leave. */
.olo-wordmark:hover .olo-eye { animation: olo-happy-eye 1.9s cubic-bezier(0.36, 0, 0.2, 1) infinite; }
.olo-wordmark:hover .olo-nose { animation: olo-happy-nose 1.9s cubic-bezier(0.36, 0, 0.2, 1) infinite; }
.olo-wordmark:hover .olo-pupil { animation: none; transform: translateY(0.1389em); }
.olo-wordmark:hover .olo-shine { display: block; }

/* The mouth rides inside the nose span so it bounces with the L.
   Geometry mirrors the face: one notch below the L's foot, shifted
   toward the right eye — and it is the HAPPY mouth: dots in the
   smile arc, running the same cheer wave as the happy face. */
.olo-wm-mouth {
    position: absolute;
    left: 0.1667em;
    top: 0.25em;
    line-height: 1;
    display: none;
    pointer-events: none;
}
.olo-wordmark:hover .olo-wm-mouth { display: block; }
.olo-wm-mouth .olo-dot:nth-child(1) { --olo-dot-rest: -0.04em; }
.olo-wm-mouth .olo-dot:nth-child(2) { --olo-dot-rest: 0.012em; }
.olo-wm-mouth .olo-dot:nth-child(3) { --olo-dot-rest: -0.04em; }
.olo-wordmark:hover .olo-wm-mouth .olo-dot { animation: olo-cheer 0.95s ease-in-out infinite; }
.olo-wordmark:hover .olo-wm-mouth .olo-dot:nth-child(2) { animation-delay: 0.12s; }
.olo-wordmark:hover .olo-wm-mouth .olo-dot:nth-child(3) { animation-delay: 0.24s; }

/* ============================================================
   OLO'S NAME IN RUNNING TEXT — a mini wordmark
   ============================================================
   Wherever prose mentions OLO, his name is set in his own pixels
   with his own geometry: Jersey, oxide, O's raised a notch, L
   dropped a notch, wordmark tracking. Markup:
     <span class="olo-name"><span>O</span><span>L</span><span>O</span></span>
   ============================================================ */
/* The letterpress stamp — the brand's one action shape. Pages may
   override sizes locally; this base makes the shared header's CTA
   correct everywhere the partial renders. */
.stamp {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.55rem;
    background: #2b251b;
    color: #faf7ef;
    border: none;
    border-radius: 2px;
    font-family: 'Jersey 15', 'Instrument Sans', sans-serif;
    font-size: 1.05rem;
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    padding: 0.55rem 1.2rem;
    cursor: pointer;
    text-decoration: none;
    box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.35);
    transition: background 0.12s ease, box-shadow 0.12s ease;
    white-space: nowrap;
}
.stamp:hover { background: #3d3526; }
.stamp:active { box-shadow: inset 0 3px 6px rgba(0, 0, 0, 0.45); }
.stamp svg { width: 1rem; height: 1rem; flex-shrink: 0; }
.stamp--sm { font-size: 1rem; padding: 0.4rem 0.9rem; }
.stamp--sm svg { width: 0.85rem; height: 0.85rem; }
.stamp--oxide { background: #9a3b2e; }
.stamp--oxide:hover { background: #7c2c21; }
.stamp--emerald { background: #0b5a45; }
.stamp--emerald:hover { background: #084535; }

/* Numbers set in the brand pixels inside otherwise non-Jersey text */
.jnum {
    font-family: 'Jersey 15', monospace;
    font-weight: 400;
    font-size: 1.14em;
    line-height: 1;
}

.olo-name {
    font-family: 'Jersey 15', monospace;
    font-weight: 400;
    font-style: normal; /* his name never slants, even inside italic prose */
    font-size: 1.12em;
    color: #9a3b2f;
    display: inline-block;
    line-height: 1;
    white-space: nowrap;
}
/* On dark surfaces (emerald buttons, ink cards) his name goes cream,
   matching .olo-face--light */
.olo-name--light { color: #f5ecd2; }
.olo-name > span { display: inline-block; }
.olo-name > span + span { margin-left: 0.0741em; }
.olo-name > span:nth-child(odd) { transform: translateY(-0.1389em); }
.olo-name > span:nth-child(2) { transform: translateY(0.1389em); }

/* At rest the name is plain staggered letters; hovering it reveals
   his whole happy face — pupils and shine appear in the O's, the
   smile mouth pops in under the L, and the trio bounces. */
.olo-name .olo-counter,
.olo-name .olo-wm-mouth { display: none; }
.olo-name:hover .olo-counter { display: block; }
.olo-name:hover .olo-wm-mouth { display: block; }
.olo-name:hover .olo-shine { display: block; }
.olo-name:hover .olo-pupil { transform: translateY(0.1389em); }
.olo-name:hover .olo-eye { animation: olo-happy-eye 1.9s cubic-bezier(0.36, 0, 0.2, 1) infinite; }
.olo-name:hover .olo-nose { animation: olo-happy-nose 1.9s cubic-bezier(0.36, 0, 0.2, 1) infinite; }
.olo-name:hover .olo-wm-mouth .olo-dot { animation: olo-cheer 0.95s ease-in-out infinite; }
.olo-name:hover .olo-wm-mouth .olo-dot:nth-child(2) { animation-delay: 0.12s; }
.olo-name:hover .olo-wm-mouth .olo-dot:nth-child(3) { animation-delay: 0.24s; }

/* ============================================================
   MOODS
   Rest pose = glyphs (brows) + pupil position + mouth arc.
   Motion = one idle animation per mood + dot life.
   Everything stops under prefers-reduced-motion; the rest pose
   alone still reads the personality.
   ============================================================ */

/* ---------- normal ---------- */
.olo-face--normal { animation: olo-idle 4.6s ease-in-out infinite; }
.olo-face--normal .olo-pupil { animation: olo-gaze-normal 9s steps(1, end) infinite; }
.olo-face--normal .olo-dot { animation: olo-breathe 2.6s ease-in-out infinite; }
.olo-face--normal .olo-dot:nth-child(2) { animation-delay: 0.4s; }
.olo-face--normal .olo-dot:nth-child(3) { animation-delay: 0.8s; }

/* ---------- bored ---------- */
.olo-face--bored {
    --olo-pupil-y: 0.055em;
    animation: olo-slump 5.8s ease-in-out infinite;
}
.olo-face--bored .olo-eye { transform: scaleY(0.94); }
.olo-face--bored .olo-pupil {
    transform: translateY(var(--olo-pupil-y));
    animation: olo-gaze-bored 11s steps(1, end) infinite;
}
.olo-face--bored .olo-dot { animation: olo-yawn 5.2s ease-in-out infinite; }
.olo-face--bored .olo-dot:nth-child(2) { animation-delay: 0.9s; }
.olo-face--bored .olo-dot:nth-child(3) { animation-delay: 1.8s; }

/* ---------- confused ---------- */
.olo-face--confused {
    transform: rotate(7deg);
    animation: olo-cock 5.4s ease-in-out infinite;
}
.olo-face--confused .olo-pupil { animation: olo-dart 3.4s steps(1, end) infinite; }
.olo-face--confused .olo-dot:nth-child(1) { transform: translateY(0); }
.olo-face--confused .olo-dot:nth-child(2) { transform: translateY(-0.028em); }
.olo-face--confused .olo-dot:nth-child(3) { transform: translateY(0.02em); }
.olo-face--confused .olo-dot { animation: olo-ponder 1.8s ease-in-out infinite; }
.olo-face--confused .olo-dot:nth-child(2) { animation-delay: 0.35s; }
.olo-face--confused .olo-dot:nth-child(3) { animation-delay: 0.8s; }

/* ---------- angry ---------- */
.olo-face--angry {
    --olo-pupil-y: 0.04em;
    animation: olo-fume 0.38s linear infinite;
}
.olo-face--angry .olo-eye { transform: scaleY(0.86); }
.olo-face--angry .olo-pupil { transform: translateY(var(--olo-pupil-y)); }
.olo-face--angry .olo-dot { animation: olo-stomp 0.9s steps(1, end) infinite; }
.olo-face--angry .olo-dot:nth-child(2) { animation-delay: 0.3s; }
.olo-face--angry .olo-dot:nth-child(3) { animation-delay: 0.6s; }

/* ---------- sad ---------- */
/* The mirror of happy: pupil rises one notch; the shine's rounded
   bottom shows beneath it */
.olo-face--sad {
    --olo-pupil-y: -0.1389em;
    transform: translateY(0.05em) rotate(-2deg);
    animation: olo-sigh 5.6s ease-in-out infinite;
}
.olo-face--sad .olo-pupil { transform: translateY(var(--olo-pupil-y)); }
.olo-face--sad .olo-shine { display: block; }
.olo-face--sad .olo-dot:nth-child(1) { transform: translateY(0.045em); }
.olo-face--sad .olo-dot:nth-child(2) { transform: translateY(0.018em); }
.olo-face--sad .olo-dot:nth-child(3) { transform: translateY(0.045em); }
.olo-face--sad .olo-dot { animation: olo-sink 3.8s ease-in-out infinite; }
.olo-face--sad .olo-dot:nth-child(2) { animation-delay: 0.5s; }
.olo-face--sad .olo-dot:nth-child(3) { animation-delay: 1s; }

/* ---------- happy ---------- */
/* Pupil drops one notch; the shine's rounded top shows above it */
.olo-face--happy {
    --olo-pupil-y: 0.1389em;
    animation: olo-bounce 1.9s cubic-bezier(0.36, 0, 0.2, 1) infinite;
}
.olo-face--happy .olo-pupil { transform: translateY(var(--olo-pupil-y)); }
.olo-face--happy .olo-shine { display: block; }
.olo-face--happy .olo-dot:nth-child(1) { transform: translateY(-0.04em); }
.olo-face--happy .olo-dot:nth-child(2) { transform: translateY(0.012em); }
.olo-face--happy .olo-dot:nth-child(3) { transform: translateY(-0.04em); }
.olo-face--happy .olo-dot { animation: olo-cheer 0.95s ease-in-out infinite; }
.olo-face--happy .olo-dot:nth-child(2) { animation-delay: 0.12s; }
.olo-face--happy .olo-dot:nth-child(3) { animation-delay: 0.24s; }

/* ---------- looking ---------- */
/* Deliberate glances: slide left, hold, slide right, hold, return */
.olo-face--looking { animation: olo-idle 4.6s ease-in-out infinite; }
.olo-face--looking .olo-pupil { animation: olo-look 5s ease-in-out infinite; }
.olo-face--looking .olo-dot { animation: olo-breathe 2.6s ease-in-out infinite; }
.olo-face--looking .olo-dot:nth-child(2) { animation-delay: 0.4s; }
.olo-face--looking .olo-dot:nth-child(3) { animation-delay: 0.8s; }

/* ---------- thinking ---------- */
/* Gaze up-left (recall), dots type like a chat indicator */
.olo-face--thinking { animation: olo-idle 4.6s ease-in-out infinite; }
.olo-face--thinking .olo-pupil { animation: olo-think 7s steps(1, end) infinite; }
.olo-face--thinking .olo-dot { opacity: 0; animation: olo-type 2.1s ease-in-out infinite; }
.olo-face--thinking .olo-dot:nth-child(2) { animation-delay: 0.35s; }
.olo-face--thinking .olo-dot:nth-child(3) { animation-delay: 0.7s; }

/* ---------- sleeping ---------- */
/* Eyes closed to a low sliver, deep slow breathing, dots barely there */
.olo-face--sleeping { animation: olo-snooze 4.4s ease-in-out infinite; }
.olo-face--sleeping .olo-pupil { transform: translateY(0.11em) scaleY(0.14); }
.olo-face--sleeping .olo-dot { animation: olo-dim 4.4s ease-in-out infinite; }
.olo-face--sleeping .olo-dot:nth-child(2) { animation-delay: 1.4s; }
.olo-face--sleeping .olo-dot:nth-child(3) { animation-delay: 2.8s; }

/* ---------- surprised ---------- */
/* Both brows up (Ô Ô), periodic startle pop */
.olo-face--surprised { animation: olo-pop 6s ease-out infinite; }
.olo-face--surprised .olo-pupil { transform: translateY(-0.03em); }

/* ---------- excited ---------- */
/* The celebration state: happy eyes, bigger jumps, leaping dots */
.olo-face--excited {
    --olo-pupil-y: 0.1389em;
    animation: olo-jig 1.5s ease-in-out infinite;
}
.olo-face--excited .olo-pupil { transform: translateY(var(--olo-pupil-y)); }
.olo-face--excited .olo-shine { display: block; }
.olo-face--excited .olo-dot { animation: olo-leap 0.75s ease-in-out infinite; }
.olo-face--excited .olo-dot:nth-child(2) { animation-delay: 0.1s; }
.olo-face--excited .olo-dot:nth-child(3) { animation-delay: 0.2s; }

/* ---------- wink ---------- */
/* The right eye drops closed for a beat, every few seconds */
.olo-face--wink { animation: olo-idle 4.6s ease-in-out infinite; }
.olo-face--wink .olo-eye--r .olo-pupil { animation: olo-wink 4.5s ease-in-out infinite; }
.olo-face--wink .olo-dot:nth-child(1) { transform: translateY(-0.035em); }
.olo-face--wink .olo-dot:nth-child(2) { transform: translateY(0.01em); }
.olo-face--wink .olo-dot:nth-child(3) { transform: translateY(-0.035em); }

/* ---------- dizzy ---------- */
/* Slashed-O eyes (Ø Ø), woozy wobble, pupils circling */
.olo-face--dizzy { animation: olo-wobble 2.6s ease-in-out infinite; }
.olo-face--dizzy .olo-pupil { animation: olo-orbit 2.2s linear infinite reverse; }
.olo-face--dizzy .olo-dot { animation: olo-ponder 1.3s ease-in-out infinite; }
.olo-face--dizzy .olo-dot:nth-child(2) { animation-delay: 0.5s; }
.olo-face--dizzy .olo-dot:nth-child(3) { animation-delay: 0.2s; }

/* ---------- working ---------- */
/* Pupils orbit steadily; the mouth runs like a tiny progress loader */
.olo-face--working .olo-pupil { animation: olo-orbit 1.6s linear infinite; }
.olo-face--working .olo-dot { opacity: 0.25; animation: olo-loader 1.2s steps(1, end) infinite; }
.olo-face--working .olo-dot:nth-child(2) { animation-delay: 0.4s; }
.olo-face--working .olo-dot:nth-child(3) { animation-delay: 0.8s; }

/* ---------- smug ---------- */
/* One raised brow (Ó), a knowing sway, the corners of the mouth up */
.olo-face--smug { animation: olo-sway 5s ease-in-out infinite; }
.olo-face--smug .olo-pupil { transform: translateX(0.03em); }
.olo-face--smug .olo-dot:nth-child(1) { transform: translateY(-0.035em); }
.olo-face--smug .olo-dot:nth-child(2) { transform: translateY(0.01em); }
.olo-face--smug .olo-dot:nth-child(3) { transform: translateY(-0.035em); }
.olo-face--smug .olo-dot { animation: olo-breathe 3.4s ease-in-out infinite; }
.olo-face--smug .olo-dot:nth-child(2) { animation-delay: 0.5s; }
.olo-face--smug .olo-dot:nth-child(3) { animation-delay: 1s; }

/* ---------- curious ---------- */
/* Leans in, pupils drifting in a slow small circle */
.olo-face--curious {
    transform: scale(1.03);
    animation: olo-lean 5.4s ease-in-out infinite;
}
.olo-face--curious .olo-pupil { animation: olo-drift 5.5s ease-in-out infinite; }
.olo-face--curious .olo-dot { animation: olo-breathe 1.9s ease-in-out infinite; }
.olo-face--curious .olo-dot:nth-child(2) { animation-delay: 0.3s; }
.olo-face--curious .olo-dot:nth-child(3) { animation-delay: 0.6s; }

/* ---------- shy ---------- */
/* Head down and away, gaze low, one quick peek per cycle */
.olo-face--shy {
    transform: rotate(2.5deg) translateY(0.035em);
    animation: olo-bashful 6s ease-in-out infinite;
}
.olo-face--shy .olo-pupil { animation: olo-peek 6s ease-in-out infinite; }
.olo-face--shy .olo-dot { opacity: 0.55; }

/* ---------- proud ---------- */
/* The medal stance: heavy satisfied lids (Ō Ō), gaze lidded low, chin
   up and chest puffed on a slow strut-and-hold, composed shallow smile */
.olo-face--proud {
    --olo-pupil-y: 0.045em;
    animation: olo-strut 4.2s ease-in-out infinite;
}
.olo-face--proud .olo-eye { transform: scaleY(0.88); }
.olo-face--proud .olo-pupil { transform: translateY(var(--olo-pupil-y)); }
.olo-face--proud .olo-dot:nth-child(1) { --olo-dot-rest: -0.03em; transform: translateY(-0.03em); }
.olo-face--proud .olo-dot:nth-child(2) { --olo-dot-rest: 0.008em; transform: translateY(0.008em); }
.olo-face--proud .olo-dot:nth-child(3) { --olo-dot-rest: -0.03em; transform: translateY(-0.03em); }
.olo-face--proud .olo-dot { animation: olo-breathe 3.4s ease-in-out infinite; }
.olo-face--proud .olo-dot:nth-child(2) { animation-delay: 0.5s; }
.olo-face--proud .olo-dot:nth-child(3) { animation-delay: 1s; }

/* ---------- giddy ---------- */
/* Fizzing over: brows flown up (Ô Ô), pupils spinning a fast little
   orbit, the whole face jigging quick, dots racing the cheer wave */
.olo-face--giddy { animation: olo-giddy 0.85s ease-in-out infinite; }
.olo-face--giddy .olo-pupil { animation: olo-orbit 0.9s linear infinite; }
.olo-face--giddy .olo-dot:nth-child(1) { --olo-dot-rest: -0.04em; transform: translateY(-0.04em); }
.olo-face--giddy .olo-dot:nth-child(2) { --olo-dot-rest: 0.012em; transform: translateY(0.012em); }
.olo-face--giddy .olo-dot:nth-child(3) { --olo-dot-rest: -0.04em; transform: translateY(-0.04em); }
.olo-face--giddy .olo-dot { animation: olo-cheer 0.55s ease-in-out infinite; }
.olo-face--giddy .olo-dot:nth-child(2) { animation-delay: 0.07s; }
.olo-face--giddy .olo-dot:nth-child(3) { animation-delay: 0.14s; }

/* ---------- beaming ---------- */
/* Pure sunshine: eyes closed into low smiling slits (the ^^ look —
   pupils squashed thick and dropped), a soft glow-pulse, and the
   deepest mouth arc in the set, cheering slow and warm */
.olo-face--beaming { animation: olo-beam 3.6s ease-in-out infinite; }
.olo-face--beaming .olo-pupil { transform: translateY(0.13em) scaleY(0.26); }
.olo-face--beaming .olo-dot:nth-child(1) { --olo-dot-rest: -0.055em; transform: translateY(-0.055em); }
.olo-face--beaming .olo-dot:nth-child(2) { --olo-dot-rest: 0.02em; transform: translateY(0.02em); }
.olo-face--beaming .olo-dot:nth-child(3) { --olo-dot-rest: -0.055em; transform: translateY(-0.055em); }
.olo-face--beaming .olo-dot { animation: olo-cheer 1.6s ease-in-out infinite; }
.olo-face--beaming .olo-dot:nth-child(2) { animation-delay: 0.2s; }
.olo-face--beaming .olo-dot:nth-child(3) { animation-delay: 0.4s; }

/* ============================================================
   KEYFRAMES
   ============================================================ */
@keyframes olo-idle {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-0.018em); }
}
@keyframes olo-gaze-normal {
    0%, 62%, 100% { transform: translate(0, 0); }
    66%, 78%      { transform: translate(0.035em, 0); }
    82%, 92%      { transform: translate(-0.03em, 0); }
}
@keyframes olo-breathe {
    0%, 100% { opacity: 0.4; }
    50%      { opacity: 1; }
}
@keyframes olo-slump {
    0%, 100% { transform: translateY(0.045em) rotate(-1.6deg); }
    50%      { transform: translateY(0.06em) rotate(-2.1deg); }
}
@keyframes olo-gaze-bored {
    0%, 44%, 100% { transform: translate(0, var(--olo-pupil-y, 0.055em)); }
    48%, 70%      { transform: translate(-0.04em, var(--olo-pupil-y, 0.055em)); }
    74%, 96%      { transform: translate(0.025em, var(--olo-pupil-y, 0.055em)); }
}
@keyframes olo-yawn {
    0%, 100% { opacity: 0.25; }
    50%      { opacity: 0.85; }
}
@keyframes olo-cock {
    0%, 55%, 100% { transform: rotate(7deg); }
    62%, 84%      { transform: rotate(9.5deg) translateY(-0.01em); }
}
@keyframes olo-dart {
    0%, 30%        { transform: translate(0.05em, 0); }
    34%, 60%       { transform: translate(-0.05em, 0); }
    64%, 82%       { transform: translate(0.03em, -0.02em); }
    86%, 100%      { transform: translate(0.05em, 0); }
}
@keyframes olo-ponder {
    0%, 100% { opacity: 0.3; }
    45%      { opacity: 1; }
}
@keyframes olo-fume {
    0%   { transform: translate(0.012em, 0); }
    25%  { transform: translate(-0.01em, 0.008em); }
    50%  { transform: translate(0.008em, -0.008em); }
    75%  { transform: translate(-0.012em, 0.006em); }
    100% { transform: translate(0.012em, 0); }
}
@keyframes olo-stomp {
    0%, 100% { transform: scale(1); }
    12%      { transform: scale(1.3); }
    30%      { transform: scale(1); }
}
@keyframes olo-sigh {
    0%, 100% { transform: translateY(0.05em) rotate(-2deg); }
    45%      { transform: translateY(0.075em) rotate(-2.4deg); }
    60%      { transform: translateY(0.045em) rotate(-1.8deg); }
}
@keyframes olo-sink {
    0%, 100% { opacity: 0.9; }
    50%      { opacity: 0.35; }
}
@keyframes olo-bounce {
    0%, 100% { transform: translateY(0); }
    28%      { transform: translateY(-0.05em); }
    44%      { transform: translateY(0.008em); }
    58%      { transform: translateY(-0.018em); }
    70%      { transform: translateY(0); }
}
@keyframes olo-cheer {
    0%, 100% { transform: translateY(var(--olo-dot-rest, 0)) scale(1); }
    35%      { transform: translateY(calc(var(--olo-dot-rest, 0) - 0.05em)) scale(1.12); }
}
@keyframes olo-look {
    0%, 8%    { transform: translateX(0); }
    16%, 42%  { transform: translateX(-0.1em); }
    52%, 78%  { transform: translateX(0.1em); }
    86%, 100% { transform: translateX(0); }
}
@keyframes olo-think {
    0%, 58%, 100% { transform: translate(-0.045em, -0.05em); }
    62%, 90%      { transform: translate(0.04em, -0.05em); }
}
@keyframes olo-type {
    0%, 6%    { opacity: 0; }
    12%, 68%  { opacity: 1; }
    78%, 100% { opacity: 0; }
}
@keyframes olo-snooze {
    0%, 100% { transform: translateY(0.05em) scale(1); }
    50%      { transform: translateY(0.062em) scale(1.012); }
}
@keyframes olo-dim {
    0%, 100% { opacity: 0.15; }
    50%      { opacity: 0.5; }
}
@keyframes olo-pop {
    0%, 76%, 100% { transform: scale(1); }
    81%           { transform: scale(1.07); }
    87%           { transform: scale(0.995); }
    92%           { transform: scale(1.025); }
}
@keyframes olo-jig {
    0%, 60%, 100% { transform: translateY(0) rotate(0deg); }
    15%           { transform: translateY(-0.07em) rotate(-2.5deg); }
    30%           { transform: translateY(0) rotate(0deg); }
    45%           { transform: translateY(-0.07em) rotate(2.5deg); }
}
@keyframes olo-leap {
    0%, 100% { transform: translateY(0) scale(1); }
    35%      { transform: translateY(-0.06em) scale(1.15); }
}
@keyframes olo-wink {
    0%, 66%, 82%, 100% { transform: scaleY(1); }
    70%, 78%           { transform: scaleY(0.1); }
}
@keyframes olo-wobble {
    0%, 100% { transform: rotate(-4deg) translateY(0.01em); }
    50%      { transform: rotate(4deg) translateY(-0.01em); }
}
@keyframes olo-orbit {
    0%   { transform: translate(0.045em, 0); }
    25%  { transform: translate(0, 0.05em); }
    50%  { transform: translate(-0.045em, 0); }
    75%  { transform: translate(0, -0.05em); }
    100% { transform: translate(0.045em, 0); }
}
@keyframes olo-loader {
    0%, 100% { opacity: 1; }
    34%      { opacity: 0.25; }
}
@keyframes olo-sway {
    0%, 100% { transform: rotate(-1.2deg); }
    50%      { transform: rotate(1.2deg); }
}
@keyframes olo-lean {
    0%, 100% { transform: scale(1.03) rotate(0deg); }
    50%      { transform: scale(1.045) rotate(-1deg); }
}
@keyframes olo-drift {
    0%, 100% { transform: translate(0.03em, -0.02em); }
    25%      { transform: translate(0.01em, 0.03em); }
    50%      { transform: translate(-0.03em, 0.015em); }
    75%      { transform: translate(-0.01em, -0.03em); }
}
@keyframes olo-bashful {
    0%, 100% { transform: rotate(2.5deg) translateY(0.035em); }
    50%      { transform: rotate(3.2deg) translateY(0.042em); }
}
@keyframes olo-peek {
    0%, 62%, 100% { transform: translate(-0.045em, 0.06em); }
    68%, 76%      { transform: translate(0.03em, -0.02em); }
}
@keyframes olo-strut {
    0%, 100% { transform: translateY(0) scale(1, 1); }
    16%      { transform: translateY(-0.05em) scale(1.05, 0.98); }
    60%      { transform: translateY(-0.035em) scale(1.03, 1); }
    82%      { transform: translateY(0) scale(1, 1); }
}
@keyframes olo-giddy {
    0%, 100% { transform: translateY(0) rotate(0deg); }
    22%      { transform: translateY(-0.05em) rotate(1.6deg); }
    40%      { transform: translateY(0.006em) rotate(0deg); }
    62%      { transform: translateY(-0.035em) rotate(-1.6deg); }
    80%      { transform: translateY(0) rotate(0deg); }
}
@keyframes olo-beam {
    0%, 100% { transform: scale(1) translateY(0); }
    50%      { transform: scale(1.045) translateY(-0.015em); }
}
@keyframes olo-happy-eye {
    0%, 70%, 100% { transform: translateY(-0.1389em); }
    28%           { transform: translateY(-0.1889em); }
    44%           { transform: translateY(-0.1309em); }
    58%           { transform: translateY(-0.1569em); }
}
@keyframes olo-happy-nose {
    0%, 70%, 100% { transform: translateY(0.1389em); }
    28%           { transform: translateY(0.0889em); }
    44%           { transform: translateY(0.1469em); }
    58%           { transform: translateY(0.1209em); }
}

/* Happy dots need their rest offset preserved through the bounce */
.olo-face--happy .olo-dot:nth-child(1) { --olo-dot-rest: -0.04em; }
.olo-face--happy .olo-dot:nth-child(2) { --olo-dot-rest: 0.012em; }
.olo-face--happy .olo-dot:nth-child(3) { --olo-dot-rest: -0.04em; }

/* ============================================================
   REDUCED MOTION — rest poses only, no movement
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
    .olo-face,
    .olo-face .olo-eye,
    .olo-face .olo-pupil,
    .olo-face .olo-dot,
    .olo-wordmark,
    .olo-wordmark .olo-pupil {
        animation: none !important;
        transition: none !important;
    }
}
