/* ---- design keyframes, lifted verbatim from Hebrew Learning Hub.dc.html <helmet> ---- */
@keyframes popIn{0%{transform:scale(.6);opacity:0}100%{transform:scale(1);opacity:1}}
@keyframes bounceCeleb{0%,100%{transform:scale(1)}30%{transform:scale(1.14) rotate(-2deg)}55%{transform:scale(1.04) rotate(1deg)}75%{transform:scale(1.1) rotate(-1deg)}}
@keyframes shakeX{0%,100%{transform:translateX(0)}20%{transform:translateX(-9px)}40%{transform:translateX(9px)}60%{transform:translateX(-5px)}80%{transform:translateX(5px)}}
@keyframes confettiFall{0%{transform:translateY(-10px) rotate(0deg);opacity:1}100%{transform:translateY(240px) rotate(400deg);opacity:0}}
@keyframes floatY{0%,100%{transform:translateY(0)}50%{transform:translateY(-7px)}}
@keyframes visitorEnter{0%{transform:translateX(180%) scale(.8);opacity:0}70%{transform:translateX(-8%) scale(1.04);opacity:1}100%{transform:translateX(0) scale(1);opacity:1}}
@keyframes bubblePop{0%{transform:scale(.4);opacity:0}100%{transform:scale(1);opacity:1}}
@keyframes bubbleClose{0%{transform:scale(1);opacity:1}100%{transform:scaleY(.1);opacity:0}}
@keyframes visitorLeave{0%{transform:rotateX(0) translateX(0);opacity:1}100%{transform:rotateX(180deg) translateX(120%);opacity:0}}
@keyframes bigPop{0%{transform:scale(.4) translateY(10px);opacity:0}60%{transform:scale(1.08) translateY(0);opacity:1}100%{transform:scale(1);opacity:1}}
@keyframes dotPulse{0%,100%{opacity:.25}50%{opacity:1}}
@keyframes coinFloat{0%{transform:translateY(0);opacity:0}20%{opacity:1}100%{transform:translateY(-50px);opacity:0}}

:root {
    --blue: #4C8DFF;
    --coral: #FF6B6B;
    --green: #2ED47A;
    --yellow: #F5C518;
    --purple: #8B7CF6;
    --orange: #F97316;
    --bg: #FBF7EF;
    --ink: #1E2A3A;
    --muted: #5B6B82;
}

*, *::before, *::after {
    box-sizing: border-box;
}

[x-cloak] {
    display: none !important;
}

body {
    margin: 0;
    font-family: 'Rubik', sans-serif;
    color: var(--ink);
    background: var(--bg);
}

.page {
    width: 100%;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    position: relative;
}

/* ---- shared card (answer tiles, cafe tray items — reused by later sessions) ---- */
.card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 22px;
    border-radius: 24px;
    background: #fff;
    border: 3px solid transparent;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06);
    min-width: 150px;
    transition: transform .15s, box-shadow .15s, border-color .15s, opacity .2s;
}

/* ---- buttons ---- */
.btn {
    font-family: inherit;
    font-weight: 700;
    border: none;
    border-radius: 14px;
    padding: 12px 22px;
    cursor: pointer;
    font-size: 16px;
}

.btn-primary {
    background: var(--blue);
    color: #fff;
}

.btn-secondary {
    background: #fff;
    color: #33415C;
    box-shadow: 0 3px 8px rgba(0, 0, 0, 0.08);
}

/* ---- hub screen ---- */
.hub {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 36px 48px;
    overflow: auto;
}

.hub-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
}

.hub-header-left {
    display: flex;
    align-items: center;
    gap: 16px;
}

.hub-title {
    font-size: 34px;
    font-weight: 800;
}

.hub-subtitle {
    font-size: 17px;
    color: var(--muted);
    font-weight: 500;
}

.mascot {
    width: 56px;
    height: 56px;
    border-radius: 18px;
    background: var(--blue);
    position: relative;
    animation: floatY 3s ease-in-out infinite;
    box-shadow: 0 6px 14px rgba(76, 141, 255, 0.35);
    flex-shrink: 0;
}

.mascot::before, .mascot::after {
    content: '';
    position: absolute;
    top: 20px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #fff;
}

.mascot::before { right: 14px; }
.mascot::after { left: 14px; }

.mascot-mouth {
    position: absolute;
    bottom: 12px;
    left: 18px;
    right: 18px;
    height: 6px;
    border-radius: 0 0 10px 10px;
    border-bottom: 4px solid #fff;
}

.gear-btn {
    width: 52px;
    height: 52px;
    border-radius: 16px;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);
    font-size: 24px;
    border: none;
}

/* ---- hub tile grid ---- */
.tile-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 26px;
    margin-top: 28px;
}

.tile {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    text-align: center;
    padding: 26px 18px;
    border-radius: 26px;
    background: #fff;
    box-shadow: 0 5px 16px rgba(0, 0, 0, 0.06);
    cursor: pointer;
    border: none;
    font-family: inherit;
    color: inherit;
}

.tile--locked {
    opacity: 0.6;
}

.tile-badge {
    width: 56px;
    height: 56px;
    border-radius: 16px;
    color: #fff;
    font-size: 26px;
    font-weight: 800;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 6px;
}

.tile-title {
    font-size: 19px;
    font-weight: 700;
}

.tile-subtitle {
    font-size: 14px;
    color: var(--muted);
    font-weight: 500;
}

.tile-lock {
    position: absolute;
    top: 14px;
    left: 14px;
    font-size: 20px;
}

/* ---- lock toast ---- */
.toast {
    position: fixed;
    bottom: 36px;
    left: 50%;
    transform: translateX(-50%);
    background: var(--ink);
    color: #fff;
    padding: 14px 26px;
    border-radius: 16px;
    font-size: 16px;
    font-weight: 600;
    animation: popIn .25s ease;
}

/* ---- modal (shop — reused by later sessions) ---- */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(30, 42, 58, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 30;
}

.modal {
    background: #fff;
    border-radius: 26px;
    padding: 34px 38px;
    width: 420px;
    max-width: calc(100vw - 32px);
    max-height: calc(100vh - 32px);
    overflow-y: auto;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
    animation: popIn .25s ease;
}

.modal-title {
    font-size: 22px;
    font-weight: 800;
    margin-bottom: 22px;
}

.modal-label {
    font-size: 15px;
    font-weight: 600;
    color: var(--muted);
    margin-bottom: 10px;
}

/* ---- settings: difficulty buttons ---- */
.difficulty-row {
    display: flex;
    gap: 10px;
    margin-bottom: 24px;
}

.difficulty-btn {
    flex: 1;
    text-align: center;
    padding: 11px;
    border-radius: 12px;
    font-weight: 700;
    font-size: 14px;
    cursor: pointer;
    border: none;
    font-family: inherit;
    background: #F2F2ED;
    color: var(--muted);
}

.difficulty-btn.active {
    background: var(--blue);
    color: #fff;
}

.range-input {
    width: 100%;
    margin-bottom: 26px;
}

/* ---- settings: parent OTP gate (scoped to #settings-fields so the
   unlocked settings controls below are untouched) ---- */
#settings-fields form {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

#settings-fields .otp-code-input {
    width: 100%;
    text-align: center;
    font-size: 30px;
    font-weight: 800;
    letter-spacing: 12px;
    padding: 16px 12px 16px 24px;
    border: 2px solid #E2E6EC;
    border-radius: 18px;
    background: var(--bg);
    color: #1E2A3A;
    transition: border-color .15s, box-shadow .15s;
}
#settings-fields .otp-code-input::placeholder {
    color: #C9D0DA;
    letter-spacing: 8px;
}
#settings-fields .otp-code-input:focus {
    outline: none;
    border-color: var(--blue);
    box-shadow: 0 0 0 4px rgba(76, 141, 255, 0.18);
}
#settings-fields .otp-code-input--error {
    border-color: var(--coral);
    animation: shakeX .5s ease;
}

#settings-fields .otp-email {
    display: inline-block;
    margin: 2px 0 16px;
    padding: 5px 14px;
    border-radius: 100px;
    background: #F3F6FF;
    color: var(--blue);
    font-size: 13px;
    font-weight: 700;
}

#settings-fields .otp-submit {
    width: 100%;
    font-size: 17px;
}

#settings-fields .otp-error {
    color: var(--coral);
    font-weight: 600;
    font-size: 14px;
    margin: 14px 0 0;
    text-align: center;
    animation: popIn .25s ease;
}

#settings-fields .otp-resend {
    width: 100%;
    margin-top: 14px;
    font-size: 14px;
}

.stub-panel {
    text-align: center;
    font-size: 20px;
    font-weight: 700;
    color: var(--muted);
    padding: 60px 20px;
}

/* ---- game screen (Sentence Detective — cafe reuses .card modifiers, .game-header
   layout and .icon-btn for its own ✕/tray in session 04) ---- */
.game-screen {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 30px 48px;
    box-sizing: border-box;
    overflow: auto;
}

.game-header {
    display: flex;
    align-items: center;
    gap: 18px;
    margin-bottom: 22px;
}

.icon-btn {
    width: 44px;
    height: 44px;
    border-radius: 14px;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    box-shadow: 0 3px 8px rgba(0, 0, 0, 0.08);
    font-size: 20px;
    font-weight: 700;
    color: var(--muted);
    border: none;
    font-family: inherit;
}

.progress-track {
    flex: 1;
    height: 14px;
    background: #E9E4D8;
    border-radius: 10px;
    overflow: hidden;
}

.progress-fill {
    height: 100%;
    background: var(--blue);
    border-radius: 10px;
    transition: width .4s ease;
}

.chapter-counter {
    font-size: 16px;
    font-weight: 700;
    color: var(--muted);
    min-width: 64px;
    text-align: left;
    unicode-bidi: isolate;
}

.chapter-card {
    background: #fff;
    border-radius: 28px;
    padding: 34px 40px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);
    position: relative;
    margin-bottom: 26px;
}

.chapter-sentence {
    font-size: 40px;
    font-weight: 800;
    line-height: 1.5;
    text-align: center;
}

.hint-chip-wrap {
    text-align: center;
    margin-top: 10px;
}

.hint-chip {
    display: inline-block;
    background: #F2F6FF;
    color: var(--blue);
    font-size: 14px;
    font-weight: 600;
    padding: 6px 14px;
    border-radius: 20px;
}

.chapter-question {
    font-size: 22px;
    font-weight: 600;
    text-align: center;
    margin-top: 22px;
    color: #33415C;
}

/* ---- answer cards (.card is the shared base from session 01 — cafe's tray
   items reuse .card + these same state modifiers in session 04) ---- */
.answer-grid {
    display: flex;
    justify-content: center;
    gap: 24px;
    flex-wrap: wrap;
}

.card--clickable {
    cursor: pointer;
    font-family: inherit;
}

.card--correct {
    border-color: var(--green);
    box-shadow: 0 0 0 6px rgba(46, 212, 122, 0.18);
}

.card--correct.card--bounce {
    animation: bounceCeleb .7s ease;
}

.card--wrong {
    border-color: var(--coral);
    animation: shakeX .5s ease;
}

.card--dim {
    opacity: 0.45;
}

.card-swatch {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    border: 3px solid rgba(0, 0, 0, 0.08);
}

.card-icon {
    font-size: 52px;
    line-height: 1;
}

.card-label {
    font-size: 16px;
    font-weight: 700;
    margin-top: 6px;
}

/* ---- post-celebration "choose what happens next" prompt — swaps in for
   .answer-grid's spot when phase=='choose', replacing the now-answered
   question's old answer cards ---- */
.choice-prompt {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    margin-bottom: 20px;
    animation: popIn .4s ease;
}

.choice-prompt-icon {
    font-size: 26px;
    animation: floatY 1.8s ease-in-out infinite;
}

.choice-prompt-text {
    background: rgba(139, 124, 246, 0.12);
    color: var(--purple);
    font-size: 18px;
    font-weight: 700;
    padding: 10px 20px;
    border-radius: 20px;
}

/* ---- celebration overlay + confetti (cafe reuses verbatim for its own
   correctCeleb phase in session 04) ---- */
.celeb-overlay {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 18vh;
    pointer-events: none;
    z-index: 20;
}

.celeb-confetti {
    position: absolute;
    top: 10%;
    width: 10px;
    height: 10px;
    border-radius: 3px;
    animation: confettiFall 1.1s ease-in forwards;
}

.celeb-banner {
    background: rgba(46, 212, 122, 0.97);
    color: #fff;
    font-size: 44px;
    font-weight: 800;
    padding: 22px 46px;
    border-radius: 26px;
    animation: bigPop .5s ease;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
}

.text-input {
    font-family: 'Rubik', sans-serif;
    font-size: 18px;
    padding: 14px 20px;
    border-radius: 16px;
    border: 2px solid #C9D9FF;
    width: 320px;
    box-sizing: border-box;
}

/* ---- "AI is thinking" wait state ---- */
.thinking {
    margin-top: 28px;
    text-align: center;
    font-size: 18px;
    font-weight: 600;
    color: var(--muted);
}

.thinking .dot {
    animation: dotPulse 1s infinite;
}

.thinking .dot:nth-child(2) {
    animation-delay: .2s;
}

.thinking .dot:nth-child(3) {
    animation-delay: .4s;
}

/* ---- wrong-answer reveal (cafe reuses for its own wrongReveal phase) ---- */
.wrong-reveal {
    margin-top: 28px;
    background: #FFF7ED;
    border-radius: 24px;
    padding: 26px 32px;
    text-align: center;
    animation: popIn .3s ease;
}

.wrong-reveal-title {
    font-size: 19px;
    font-weight: 700;
    margin-bottom: 6px;
}

.wrong-reveal-sub {
    font-size: 17px;
    color: var(--muted);
    margin-bottom: 16px;
}

.proceed-btn {
    display: inline-block;
    background: var(--orange);
    color: #fff;
    font-size: 17px;
    font-weight: 700;
    padding: 12px 26px;
    border-radius: 16px;
    cursor: pointer;
    border: none;
    font-family: inherit;
}

/* ---- story recap ---- */
.recap-screen {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 36px 48px;
    overflow: auto;
    box-sizing: border-box;
}

.recap-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 24px;
}

.recap-title {
    font-size: 30px;
    font-weight: 800;
}

.recap-actions {
    display: flex;
    gap: 12px;
}

.recap-list {
    display: flex;
    flex-direction: column;
    gap: 18px;
    max-width: 760px;
    margin: 0 auto;
    width: 100%;
}

.recap-page {
    background: #fff;
    border-radius: 22px;
    padding: 22px 28px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.05);
    display: flex;
    gap: 20px;
    align-items: flex-start;
}

.recap-chip {
    width: 64px;
    height: 64px;
    border-radius: 18px;
    background: #F7F5EE;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.recap-swatch {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    border: 2px solid rgba(0, 0, 0, 0.08);
}

.recap-icon {
    font-size: 30px;
}

.recap-sentence {
    font-size: 22px;
    font-weight: 700;
    margin-bottom: 6px;
}

.recap-connector {
    font-size: 15px;
    color: #8A93A6;
    font-style: italic;
}

/* ---- pet cafe (reuses .card modifiers, .icon-btn, .game-header shape,
   .celeb-overlay/.celeb-confetti/.celeb-banner and .wrong-reveal from above) ---- */
.cafe-screen {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 30px 48px;
    box-sizing: border-box;
    overflow: auto;
    background: linear-gradient(#FFF7E8, #FBF7EF);
}

.cafe-header {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 22px;
}

.cafe-title {
    font-size: 24px;
    font-weight: 800;
    flex: 1;
}

.coin-pill {
    display: flex;
    align-items: center;
    gap: 8px;
    background: #fff;
    border-radius: 16px;
    padding: 10px 18px;
    box-shadow: 0 3px 8px rgba(0, 0, 0, 0.08);
    font-size: 20px;
    font-weight: 800;
}

.shop-open-btn {
    background: var(--yellow);
    color: #4A3B00;
    font-weight: 700;
    padding: 12px 22px;
    border-radius: 16px;
    cursor: pointer;
    box-shadow: 0 3px 8px rgba(0, 0, 0, 0.08);
    border: none;
    font-family: inherit;
    font-size: 16px;
}

.room {
    position: relative;
    aspect-ratio: 3 / 2;
    max-width: 720px;
    margin: 0 auto 26px;
    border-radius: 28px;
    overflow: hidden;
    perspective: 600px;
    background: url(/static/img/room.webp) center/cover;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.10);
}

.room::after {
    content: '';
    position: absolute;
    inset: 0;
    background: radial-gradient(ellipse at 50% 55%, transparent 55%, rgba(0, 0, 0, 0.16) 100%);
    pointer-events: none;
}

.room__decor {
    position: absolute;
    inset: 0;
}

.room-item, .room-wear {
    position: absolute;
    /* `left` in the pos data is the item's horizontal CENTER, not its edge
       (app/cafe.py CLAUDE.md placement schema) — center the sprite on it. */
    transform: translateX(-50%);
    filter: drop-shadow(0 6px 6px rgba(0, 0, 0, 0.18));
    transition: filter .15s ease;
    pointer-events: auto;
}

.room-item, .room-wear, .room__avatar-photo {
    animation: popIn .3s ease;
}

.draggable {
    cursor: grab;
    /* Stops the browser's own scroll/pinch from hijacking the gesture on
       touch/pen input (mobile, iPad) so pointer events alone drive the drag. */
    touch-action: none;
}

/* .room__avatar is pointer-events:none by default (see below) so decor behind it
   stays clickable. Don't opt the whole box back in like .room-item/.room-wear do —
   opt in only the visible piece: the photo in photo mode (its box is sized to
   exactly match the photo, see .room__avatar-photo below, so this doesn't shrink
   the hit area vs. opting in the outer box) or the box itself in fallback mode
   (there the box IS the visible shape). This does shave off the ::before shadow
   blob below, which isn't visible content and shouldn't be clickable — it was
   stealing clicks from decor peeking out past the avatar's own bottom edge, e.g.
   the default rug (#29, whose main fix was giving it a non-overlapping default
   spot — app/cafe.py SHOP_ITEMS — since the avatar's own opaque footprint can't
   be shrunk around it without breaking the avatar's own drag capture). Either the
   photo or the fallback box bubbles up to the outer .room__avatar via
   closest(".draggable") in app.js, so drag/position logic (which reads/writes
   el.style on the outer div) is unaffected. */
.room__avatar.draggable .room__avatar-photo,
.room__avatar.draggable.room__avatar--fallback {
    pointer-events: auto;
}

.room-item.dragging, .room-wear.dragging, .room__avatar.dragging {
    cursor: grabbing;
    transform: translateX(-50%) scale(1.1);
    filter: drop-shadow(0 16px 20px rgba(0, 0, 0, 0.32));
    transition: none;
    /* Each item also carries its own inline z-index (server-rendered, from its
       SHOP_ITEMS pos) — inline style always beats a stylesheet rule, so lifting
       above everything else while dragging needs !important to actually win. */
    z-index: 50 !important;
}

.room__avatar.dragging {
    /* app.js moves the avatar via inline `transform` during the drag (not
       left/top, see pointermove) so the browser doesn't relayout its photo +
       wear-item children on every pointermove — this just pre-promotes it to
       its own compositor layer so the first move isn't the one paying for that. */
    will-change: transform;
}

@keyframes placedGlow {
    0%, 100% {
        filter: drop-shadow(0 6px 6px rgba(0, 0, 0, 0.18)) drop-shadow(0 0 0 rgba(245, 197, 24, 0));
    }
    50% {
        filter: drop-shadow(0 6px 6px rgba(0, 0, 0, 0.18)) drop-shadow(0 0 16px rgba(245, 197, 24, 0.85));
    }
}

.room-item--new, .room-wear--new {
    animation: popIn .3s ease, placedGlow 1s ease-in-out 3;
}

.room__avatar {
    position: absolute;
    bottom: 6%;
    left: 50%;
    transform: translateX(-50%);
    height: 62%;
    /* Above every decor item's z (max 4 in app/cafe.py SHOP_ITEMS) so the avatar
       stands on the rug/floor decor instead of decor covering its feet. Worn
       items (hat, glasses, ...) are descendants — their own z only orders them
       against each other, unaffected by this. pointer-events:none keeps this
       (now-topmost) box from stealing drag clicks off decor items placed
       underneath it — .room-wear below opts itself back in so worn items are
       still draggable. */
    z-index: 5;
    pointer-events: none;
    /* Without this, the box has no width until the <img> finishes loading and
       reports its natural size — worn items' width:X% (a % of THIS box) would
       then resolve against whatever width was available at that moment, giving
       inconsistent sizes race-dependent on image load timing. The avatar pipeline
       (app/imaging.py normalize aspect=(2,3)) always outputs exactly 2:3, so this
       is knowable upfront, same as .room__avatar--fallback already does below. */
    aspect-ratio: 2 / 3;
}

.room__avatar::before {
    content: '';
    position: absolute;
    bottom: -6%;
    left: 50%;
    transform: translateX(-50%);
    width: 65%;
    height: 12%;
    background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.32) 0%, rgba(0, 0, 0, 0) 72%);
    z-index: -1;
}

.room__avatar-photo {
    height: 100%;
    display: block;
}

.room__avatar--fallback {
    width: 34%;
    aspect-ratio: 2 / 3;
    border-radius: 20px;
    background: var(--blue);
    box-shadow: 0 8px 20px rgba(76, 141, 255, 0.35);
}

.room__avatar-eye {
    position: absolute;
    top: 30%;
    width: 10%;
    height: 7%;
    border-radius: 50%;
    background: #fff;
}

.room__avatar-eye--l { left: 28%; }
.room__avatar-eye--r { right: 28%; }

.room__avatar-mouth {
    position: absolute;
    bottom: 30%;
    left: 32%;
    right: 32%;
    height: 8%;
    border-radius: 0 0 12px 12px;
    border-bottom: 5px solid #fff;
}

.cafe-visitor {
    position: absolute;
    bottom: 4%;
    right: 4%;
    font-size: 118px;
    line-height: 1;
    z-index: 5;
    filter: drop-shadow(0 6px 6px rgba(0, 0, 0, 0.18));
}

.cafe-visitor--enter {
    animation: visitorEnter .6s cubic-bezier(.2, .7, .3, 1) both;
}

.cafe-visitor--leave {
    animation: visitorLeave .45s ease-in .2s both;
}

.cafe-speech-bubble {
    background: #fff;
    border-radius: 20px;
    padding: 14px 20px;
    position: absolute;
    bottom: 30%;
    right: 2%;
    max-width: 48%;
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12);
    z-index: 6;
}

.cafe-speech-bubble--enter {
    animation: bubblePop .35s ease-out .55s both;
}

.cafe-speech-bubble--leave {
    transform-origin: bottom right;
    animation: bubbleClose .2s ease-in both;
}

.cafe-speech-bubble::after {
    content: '';
    position: absolute;
    bottom: -12px;
    right: 30px;
    border: 9px solid transparent;
    border-top-color: #fff;
    border-bottom: 0;
}

.cafe-order-text {
    font-size: 20px;
    font-weight: 700;
    line-height: 1.5;
}

.coin-popup {
    position: fixed;
    top: 90px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 34px;
    font-weight: 800;
    color: var(--yellow);
    animation: coinFloat 1.1s ease forwards;
    pointer-events: none;
    text-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}

.cafe-celeb-banner {
    font-size: 38px;
    padding: 18px 40px;
}

/* ---- cafe shop modal (reuses .modal/.modal-title/.btn from session 01) ---- */
.shop-modal {
    width: 560px;
}

.shop-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
}

.shop-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
}

.shop-item {
    background: #F7F5EE;
    border-radius: 20px;
    padding: 16px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    text-align: center;
}

.shop-item-emoji {
    font-size: 38px;
}

.shop-item-label {
    font-size: 14px;
    font-weight: 700;
}

.shop-action {
    font-weight: 700;
    font-size: 14px;
    padding: 9px 16px;
    border-radius: 12px;
    text-align: center;
    border: none;
    font-family: inherit;
    width: 100%;
}

.shop-action--buy {
    background: var(--blue);
    color: #fff;
    cursor: pointer;
}

.shop-action--disabled {
    background: #F2F2ED;
    color: #C3CAD6;
}

.shop-action--owned {
    background: #F2F2ED;
    color: #8A93A6;
}

.shop-action--worn {
    background: var(--green);
    color: #fff;
    cursor: pointer;
}

/* ---- login gate ---- */
.login-screen {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: linear-gradient(160deg, #EFE9FF, var(--bg) 55%);
}

.login-card {
    background: #fff;
    border-radius: 28px;
    padding: 44px 40px;
    width: 380px;
    max-width: 100%;
    text-align: center;
    box-shadow: 0 20px 50px rgba(30, 42, 58, 0.12);
    animation: popIn .3s ease;
}

.login-mascot {
    width: 76px;
    height: 76px;
    margin: 0 auto 20px;
    border-radius: 22px;
    background: var(--purple);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 8px 20px rgba(139, 124, 246, 0.35);
    animation: floatY 3s ease-in-out infinite;
}

.login-mascot-lock {
    font-size: 34px;
}

.login-title {
    font-size: 26px;
    font-weight: 800;
    margin: 0 0 6px;
}

.login-subtitle {
    font-size: 15px;
    color: var(--muted);
    font-weight: 500;
    margin: 0 0 26px;
}

.login-form {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.login-input {
    width: 100%;
    text-align: center;
    letter-spacing: 2px;
    border: 0.5px solid; border-radius: 100px;
    padding: 8px 8px 8px 16px; background: var(--white);
}

.login-input--error {
    border-color: var(--coral);
    animation: shakeX .5s ease;
}

.login-submit {
    width: 100%;
    font-size: 17px;
}

.google-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    text-decoration: none;
}

.google-g {
    width: 20px;
    height: 20px;
}

.login-error {
    color: var(--coral);
    font-weight: 600;
    font-size: 14px;
    margin: 16px 0 0;
    animation: popIn .25s ease;
}

.hidden {
    display: none !important;
}

.login-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    color: var(--muted);
    font-size: 13px;
    font-weight: 600;
    margin: 22px 0 18px;
}
.login-divider::before,
.login-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: #E2E6EC;
}

.login-input[type="email"] {
    letter-spacing: 0;
    border: 0.5px solid; border-radius: 100px;
    padding: 8px 8px 8px 16px; background: var(--white);
}

.otp-hint {
    font-size: 13px;
    color: var(--muted);
    margin: 16px 0 6px;
}
.otp-link {
    color: var(--blue);
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
}
.otp-link:hover {
    text-decoration: underline;
}

/* ---- invitation-only denied page (reuses .login-screen/.login-card) ---- */
.denied-screen {
    background: linear-gradient(160deg, #FFE9E9, var(--bg) 55%);
}
.denied-mascot {
    background: var(--coral);
    box-shadow: 0 8px 20px rgba(255, 107, 107, 0.35);
}
.denied-msg {
    font-size: 16px;
    line-height: 1.6;
    color: var(--muted);
    font-weight: 500;
    margin: 0 0 28px;
}
.denied-cta {
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    margin-top: 4px;
}
.denied-back {
    display: inline-block;
    margin-top: 22px;
}

/* ---- avatar onboarding screen (reuses .login-screen/.login-card) ---- */
.avatar-note {
    color: var(--muted);
    font-size: 13px;
    margin: -14px 0 22px;
}

.avatar-preview {
    width: 140px;
    height: 140px;
    object-fit: cover;
    border-radius: 24px;
    margin: 0 auto 18px;
    display: block;
    box-shadow: 0 8px 20px rgba(30, 42, 58, 0.16);
}

.avatar-preview--full {
    height: 210px;
    object-fit: contain;
}

.avatar-pick-btn {
    width: 100%;
    display: block;
}

#avatar-form.htmx-request {
    opacity: .6;
    pointer-events: none;
}

.avatar-skip-btn {
    width: 100%;
    margin-top: 14px;
}

.avatar-retake-btn {
    width: 100%;
    margin-top: 10px;
}

.mascot--photo {
    background: none;
}

.mascot--photo::before, .mascot--photo::after {
    display: none;
}

.mascot--photo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
    border-radius: inherit;
}

/* ---- mobile (phones ≤640px; ipad/desktop keep the layout above) ---- */
@media (max-width: 640px) {
    .hub, .game-screen, .recap-screen, .cafe-screen {
        padding: 20px 16px;
    }

    .hub-title {
        font-size: 24px;
    }

    .hub-subtitle {
        font-size: 14px;
    }

    .mascot {
        width: 44px;
        height: 44px;
    }

    .gear-btn {
        width: 44px;
        height: 44px;
        font-size: 20px;
    }

    .tile-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 14px;
        margin-top: 20px;
    }

    .tile {
        padding: 18px 10px;
    }

    .tile-badge {
        width: 46px;
        height: 46px;
        font-size: 21px;
    }

    .tile-title {
        font-size: 16px;
    }

    .tile-subtitle {
        font-size: 12px;
    }

    .modal {
        padding: 26px 22px;
    }

    .chapter-card {
        padding: 22px 20px;
    }

    .chapter-sentence {
        font-size: 26px;
    }

    .chapter-question {
        font-size: 18px;
        margin-top: 16px;
    }

    .answer-grid {
        gap: 14px;
    }

    .card {
        min-width: 120px;
        padding: 16px;
    }

    .card-icon {
        font-size: 38px;
    }

    .card-swatch {
        width: 48px;
        height: 48px;
    }

    .text-input {
        width: 100%;
    }

    .celeb-banner {
        font-size: 26px;
        padding: 16px 26px;
    }

    .recap-title {
        font-size: 22px;
    }

    .recap-page {
        padding: 16px 18px;
        gap: 14px;
    }

    .recap-sentence {
        font-size: 17px;
    }

    .cafe-header {
        flex-wrap: wrap;
    }

    .cafe-title {
        flex: 1 1 100%;
        order: -1;
        text-align: center;
        font-size: 19px;
    }

    .room {
        max-width: none;
        margin-inline: -16px;
        margin-bottom: 20px;
        border-radius: 0;
    }

    .room__avatar {
        height: 55%;
    }

    .cafe-visitor {
        font-size: 84px;
    }

    /* Move bubble to the top of the room so it no longer overlaps the
       centered avatar or the animal visitor. The tail still points down
       toward the animal below. */
    .cafe-speech-bubble {
        top: 5%;
        bottom: auto;
        right: 4%;
        max-width: 72%;
        font-size: 15px;
    }

    .cafe-order-text {
        font-size: 19px;
    }

    .shop-modal {
        width: 100%;
    }

    .shop-grid {
        grid-template-columns: 1fr 1fr;
        gap: 10px;
    }
}

/* ---- hub switcher (avatar/name button → profile picker) ---- */
.hub-switcher {
    display: flex;
    align-items: center;
    gap: 16px;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    font-family: inherit;
    text-align: right;
    border-radius: 18px;
    transition: opacity .15s;
}

.hub-switcher:hover {
    opacity: 0.7;
}

/* ---- onboarding (first-child name input — reuses .login-card) ---- */
.onboarding-screen {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: linear-gradient(160deg, #EFE9FF, var(--bg) 55%);
}

.onboarding-name-input {
    text-align: center;
    width: 100%;
    border-radius: 18px;
}

/* ---- profile picker (multi-child switch) ---- */
.profiles-screen {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 36px 48px;
    overflow: auto;
}

.profiles-header {
    text-align: center;
    margin-bottom: 28px;
}

.profiles-title {
    font-size: 30px;
    font-weight: 800;
    margin: 0;
}

.profile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 24px;
    max-width: 720px;
    margin: 0 auto;
    width: 100%;
}

.profile-card {
    background: none;
    border: none;
    padding: 0;
    margin: 0;
}

.profile-card-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    padding: 26px 18px;
    border-radius: 26px;
    background: #fff;
    color: var(--ink);
    box-shadow: 0 5px 16px rgba(0, 0, 0, 0.06);
    cursor: pointer;
    border: none;
    font-family: inherit;
    width: 100%;
    transition: transform .15s, box-shadow .15s;
}

.profile-card-btn:hover {
    transform: translateY(-4px);
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.10);
}

.profile-mascot {
    width: 72px;
    height: 72px;
    border-radius: 22px;
}

.profile-name {
    font-size: 20px;
    font-weight: 700;
}

.profiles-add-link {
    display: block;
    margin: 28px auto 0;
    width: fit-content;
}

/* ---- settings modal: children management ---- */
.settings-child-row {
    background: #F7F5EE;
    border-radius: 18px;
    padding: 18px;
    margin-bottom: 14px;
}

.settings-child-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
}

.rename-input {
    flex: 1;
    font-family: inherit;
    font-size: 18px;
    font-weight: 700;
    padding: 8px 12px;
    border: 2px solid transparent;
    border-radius: 12px;
    background: #fff;
    color: var(--ink);
    transition: border-color .15s;
}

.rename-input:focus {
    outline: none;
    border-color: var(--blue);
}

.child-remove-btn {
    width: 38px;
    height: 38px;
    border-radius: 10px;
    border: none;
    background: #FFE5E5;
    font-size: 16px;
    cursor: pointer;
    flex-shrink: 0;
}

.settings-child-row .difficulty-row {
    margin-bottom: 14px;
}

.settings-child-row .range-input {
    margin-bottom: 12px;
}

#settings-fields .settings-add-child {
    flex-direction: row;
    gap: 10px;
    margin-top: 4px;
}

/* ---- parent settings page (OTP gate + child management — reuses .login-screen/.login-card) ---- */
.settings-screen {
    /* Flat, not gated-gradient like login/denied/onboarding: this screen is
       reached via in-app htmx swap from the home hub, not a separate app
       entry, so it should read as the same page, not a mood shift. */
    background: var(--bg);
}

.settings-mascot {
    background: var(--orange);
    box-shadow: 0 8px 20px rgba(249, 115, 22, 0.35);
}

.settings-card--wide {
    width: 520px;
}

.settings-close-btn {
    width: 100%;
    margin-top: 28px;
}
