/* Sandy the companion: snack pack, petting, comfort and night notes (behaviour in assets/sandy.js). */

/* Sprite shells retain each page's existing size and drag/tap target. */
.sandy-sprite-shell { position: relative; display: block; overflow: hidden; isolation: isolate; }
.sandy-sprite-fallback { display: block; width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
.sandy-sprite-art { position: absolute; display: block; opacity: 0; overflow: hidden; background-repeat: no-repeat; pointer-events: none; }
.sandy-sprite-ready .sandy-sprite-fallback { visibility: hidden; }
.sandy-sprite-ready .sandy-sprite-art { opacity: 1; }
button.today-sandy { padding: 0; border: 0; background: transparent; cursor: pointer; touch-action: manipulation; }
button.today-sandy:focus-visible { outline: 3px solid var(--accent-focus); outline-offset: 5px; border-radius: 16px; }
.has-sandy-sprites .sandy-eye { display: none; }
.has-sandy-sprites .sandy-image,
.has-sandy-sprites .sandy-art,
.has-sandy-sprites .floating-sandy-image,
.has-sandy-sprites.sandy-purring,
.has-sandy-sprites.sandy-hop,
.has-sandy-sprites.sandy-wobble,
.has-sandy-sprites.sandy-tilt { animation: none !important; transform: none !important; transition: none !important; }
.sandy-sprite-shell:is([data-sandy-state="sleep"], [data-sandy-state^="sleep-breath"])::after { content: "z z z"; position: absolute; top: 5%; right: 8%; color: var(--muted, #4a6f82); font-size: .65rem; letter-spacing: .12em; pointer-events: none; }
.has-sandy-sprites[data-sandy-motion="paused"] .sandy-sprite-shell::after,
.floating-sandy-toggle .sandy-sprite-shell::after { content: none; }

/* Petting: a little hand when you hover Sandy */
.sandy-pettable { cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Ctext x='2' y='26' font-size='26'%3E%F0%9F%A4%9A%3C/text%3E%3C/svg%3E") 12 12, grab; }
/* Android long press: no callout menu, text selection or image drag on Sandy; the list dock Sandy is a drag/tap handle */
.sandy-pettable, .sandy-pettable img, .floating-sandy-toggle { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; -webkit-user-drag: none; }
.floating-controls .floating-sandy-toggle { touch-action: none; }
.sandy-heart { position: fixed; z-index: var(--z-effect); color: #e2678a; font-size: 1.1rem; pointer-events: none; animation: sandy-heart 1.2s ease-out forwards; }
@keyframes sandy-heart { from { opacity: 0; transform: translate(-50%, 0) scale(.6); } 20% { opacity: 1; } to { opacity: 0; transform: translate(-50%, -48px) scale(1.15); } }
.sandy-purring { animation: sandy-purr .5s ease-in-out infinite alternate; }
@keyframes sandy-purr { from { transform: scale(1); } to { transform: scale(1.03, .98); } }

/* Reactions */
.sandy-hop { animation: sandy-hop .45s ease-in-out 2; }
@keyframes sandy-hop { 50% { transform: translateY(-10px) rotate(2deg); } }
.sandy-tilt { animation: sandy-tilt 1.4s ease-in-out; }
@keyframes sandy-tilt { 25%, 75% { transform: rotate(-8deg); } }
.sandy-wobble { animation: sandy-wobble .8s ease-in-out; }
@keyframes sandy-wobble { 20% { transform: skewX(6deg); } 40% { transform: skewX(-5deg); } 60% { transform: skewX(3deg); } 80% { transform: skewX(-2deg); } }
.sandy-pop { position: fixed; z-index: var(--z-effect); font-size: 2rem; pointer-events: none; animation: sandy-pop-up .95s ease-out forwards; }
@keyframes sandy-pop-up { from { opacity: 0; transform: translate(-50%, 16px) scale(.55); } 30% { opacity: 1; } to { opacity: 0; transform: translate(-50%, -54px) scale(1.25); } }

/* Snack pack. Today: inside the Sandy card, part of the page. Every other page: a popover the learner opens with the
   🍙 button next to the dock Sandy; it covers the page only while open (assets/sandy.js). */
.sandy-pack { position: static; min-width: 0; width: fit-content; max-width: min(360px, 100%); padding: 10px; border: 1px solid var(--line); border-radius: 18px; background: rgb(var(--surface-rgb) / .95); box-shadow: 0 10px 26px rgb(var(--shadow-rgb) / .14); }
.sandy-pack-title { display: inline-flex; align-items: center; gap: 4px; min-height: 44px; margin: 0 0 6px; padding: 0 4px; border: 0; background: transparent; color: var(--blue-600, var(--blue-600)); font: inherit; font-size: .8rem; font-weight: 500; cursor: pointer; }
.sandy-pack-title::after { content: "⌃"; transition: transform .16s ease; }
.sandy-pack.collapsed { padding: 6px 10px; }
.sandy-pack.collapsed .sandy-pack-list, .sandy-pack.collapsed .sandy-pack-hint { display: none; }
.sandy-pack.collapsed .sandy-pack-title { margin: 0; }
.sandy-pack.collapsed .sandy-pack-title::after { transform: rotate(180deg); }
.sandy-pack-list { display: flex; gap: 6px; overflow-x: auto; overflow-y: hidden; padding: 8px 8px 2px 2px; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
/* more snacks off to one side: fade that edge so the row reads as scrollable (classes set in assets/sandy.js) */
.sandy-pack-list.has-more-right { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); }
.sandy-pack-list.has-more-left { -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to left, #000 calc(100% - 32px), transparent); }
.sandy-pack-list.has-more-left.has-more-right { -webkit-mask-image: linear-gradient(to right, transparent, #000 32px, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, transparent, #000 32px, #000 calc(100% - 32px), transparent); }
.sandy-pack-list::-webkit-scrollbar { display: none; }
.sandy-snack { position: relative; flex-shrink: 0; width: 44px; height: 44px; border: 0; border-radius: 12px; background: var(--tint-1); font-size: 1.4rem; cursor: grab; touch-action: none; transition: transform .15s ease; }
/* touch: when the row scrolls, a sideways swipe scrolls it; a press-and-hold or an upward/downward drag feeds Sandy */
.sandy-pack-list.is-scrollable .sandy-snack { touch-action: pan-x; }
.sandy-snack:hover { transform: translateY(-2px); }
.sandy-snack.is-special { background: var(--warm-bg); }
.sandy-snack.dragging { opacity: .45; cursor: grabbing; }
.sandy-snack .sandy-count { position: absolute; top: -6px; right: -6px; min-width: 18px; padding: 0 5px; border-radius: 999px; background: #b35f16; color: white; font-size: .7rem; font-weight: 700; line-height: 18px; text-align: center; }
.sandy-pack-hint { margin: 4px 2px 0; color: var(--muted, var(--muted)); font-size: .75rem; }
.sandy-ghost { position: fixed; z-index: var(--z-effect); display: grid; place-items: center; width: 48px; height: 48px; border-radius: 14px; background: var(--surface); box-shadow: 0 8px 16px rgb(var(--shadow-rgb) / .22); font-size: 1.6rem; pointer-events: none; transform: translate(-50%, -50%); }
/* On 今天 the pack sits inside the Sandy card */
.sandy-pack.is-inline { max-width: 100%; margin-top: 12px; box-shadow: none; }
.sandy-pack.is-inline:not(.collapsed) { width: auto; }

/* Everywhere else: the 🍙 button beside the dock Sandy and the popover it opens.
   Wide screens: 🍙 sits just above 💤 in Sandy's column and the popover opens above it (left/top set in assets/sandy.js).
   Phones: 🍙 takes 💤's place in the tab bar; the popover rises above the bar and also holds the sleep toggle. */
.sandy-snack-toggle { position: fixed; z-index: var(--z-dock); display: inline-grid; place-items: center; min-width: 44px; height: 44px; padding: 0; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--ink); font: inherit; font-size: 1.05rem; line-height: 1; box-shadow: 0 4px 12px rgb(var(--shadow-rgb) / .14); cursor: pointer; touch-action: manipulation; }
.sandy-snack-toggle.is-asleep { padding: 0 10px; background: var(--blue-100); font-size: .8rem; font-weight: 500; }
.sandy-snack-toggle[aria-expanded="true"] { background: var(--tint-2); border-color: var(--accent-focus); }
.sandy-snack-toggle[hidden] { display: none; }
.sandy-snack-toggle:focus-visible { outline: 3px solid var(--blue-400); outline-offset: 3px; }
.sandy-pack.is-popover { position: fixed; z-index: var(--z-popover); width: min(372px, calc(100vw - 16px)); max-width: none; background: var(--surface); animation: sandy-pack-in .16s ease-out; }
.sandy-pack.is-popover[hidden] { display: none; }
.sandy-pack.is-popover:focus { outline: none; }
.sandy-pack-heading { margin: 0 0 2px; padding: 4px 4px 0; color: var(--blue-600); font-size: .8rem; font-weight: 500; }
/* in the popover the snacks wrap instead of scrolling, so a press drags straight away */
.sandy-pack.is-popover .sandy-pack-list { flex-wrap: wrap; overflow: visible; padding: 8px 2px 2px; }
.sandy-snack:focus-visible { outline: 3px solid var(--blue-400); outline-offset: 2px; }
.sandy-pack-actions { display: none; margin: 8px 0 0; padding-top: 8px; border-top: 1px solid var(--line); }
.sandy-pack-sleep { min-height: 44px; padding: 0 14px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--ink); font: inherit; font-size: .85rem; font-weight: 500; cursor: pointer; touch-action: manipulation; }
.sandy-pack-sleep.is-asleep { background: var(--blue-100); }
.sandy-pack-sleep:focus-visible { outline: 3px solid var(--blue-400); outline-offset: 2px; }
body.fb-open .sandy-snack-toggle { visibility: hidden; }
@keyframes sandy-pack-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* Gentle notes from Sandy (night reminders). Placed by assets/sandy.js:
   at the top of the page it joins the page flow under the heading (covers nothing, stays until closed);
   once the learner has scrolled it is a short toast low on the screen, clear of the header and the dock. */
.sandy-note { display: flex; align-items: center; gap: 8px; padding: 8px 4px 8px 12px; border: 1px solid var(--line); border-radius: var(--radius-card); background: var(--surface); color: var(--ink); font-size: .875rem; line-height: 1.7; animation: sandy-note-in .25s ease-out; }
.sandy-note[hidden] { display: none; }
.sandy-note.is-inline { margin: 0 0 var(--space-4); }
/* Sandy's speech toast sits beside her on wide screens: just left of her dock column (the rail plus the 🍙/💤 buttons) */
.sandy-note.is-toast { position: fixed; z-index: var(--z-toast); right: calc(var(--layout-companion-rail) + 2 * var(--dock-gap)); bottom: max(var(--dock-gap), env(safe-area-inset-bottom, 0px)); max-width: min(380px, calc(100vw - var(--sn-sidebar-width, 0px) - var(--layout-companion-rail) - 2 * var(--dock-gap))); box-shadow: 0 14px 34px rgb(var(--shadow-rgb) / .18); }
.sandy-note-text { flex: 1; min-width: 0; }
.sandy-note img, .sandy-note-cat { width: 32px; height: 32px; flex-shrink: 0; object-fit: contain; }
.sandy-note-cat { background: url("cats/sandy-v2/idle.png") 0 0 / 300% 200% no-repeat; }
.sandy-note-close { display: grid; flex-shrink: 0; place-items: center; width: 44px; height: 44px; border: 0; border-radius: var(--radius-control); background: transparent; color: var(--muted); font: inherit; font-size: 1.1rem; cursor: pointer; }
.sandy-note-close:hover { background: var(--tint-2); }
.sandy-note-close:focus-visible { outline: 3px solid var(--accent-focus); outline-offset: 2px; }
@keyframes sandy-note-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

@media (max-width: 900px) {
  /* phones: the toast sits just above the tab bar, never over the top bar or section tabs */
  .sandy-note.is-toast { left: 8px; right: 8px; bottom: calc(var(--sn-bottom-bar, 66px) + 8px + env(safe-area-inset-bottom, 0px)); max-width: none; }
}
@media (prefers-reduced-motion: reduce) {
  .sandy-purring, .sandy-hop, .sandy-tilt, .sandy-wobble, .sandy-note, .sandy-pack.is-popover { animation: none; }
  .sandy-snack { transition: none; }
  .sandy-snack:hover { transform: none; }
}

/* Sleep: the 💤 / 叫醒 button that follows Sandy (assets/sandy.js). A full 44px target. */
.sandy-sleep { position: fixed; z-index: var(--z-dock); display: inline-grid; place-items: center; min-width: 44px; height: 44px; padding: 0 10px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--ink); font: inherit; font-size: .8rem; font-weight: 500; line-height: 1; box-shadow: 0 4px 12px rgb(var(--shadow-rgb) / .14); cursor: pointer; touch-action: manipulation; }
.sandy-sleep:not(.is-asleep) { padding: 0; font-size: 1.05rem; }
.sandy-sleep.is-asleep { background: var(--blue-100); }
.sandy-sleep[hidden] { display: none; }
.sandy-sleep:focus-visible { outline: 3px solid var(--blue-400); outline-offset: 3px; }
.sandy-asleep .sandy-snack[aria-disabled="true"] { opacity: .55; cursor: default; }
body.fb-open .sandy-sleep { visibility: hidden; }
.sandy-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Corner Sandy on pages without their own (assets/sandy.js setupFloatingSandy) */
.sandy-float { position: fixed; z-index: var(--z-dock); right: max(var(--dock-gap), env(safe-area-inset-right)); bottom: max(var(--dock-gap), env(safe-area-inset-bottom)); }
.sandy-float-button { display: block; width: 88px; height: 88px; padding: 0; border: 0; border-radius: 18px; background: none; cursor: pointer; touch-action: manipulation; filter: drop-shadow(0 8px 12px rgb(var(--shadow-rgb) / .16)); }
.sandy-float-button .floating-sandy-image, .sandy-float-button .sandy-sprite-shell { width: 100%; height: 100%; }
.sandy-float-button:focus-visible { outline: 3px solid var(--blue-400); outline-offset: 4px; }
@media print { .sandy-float, .sandy-sleep, .sandy-snack-toggle, .sandy-pack.is-popover { display: none; } }

/* ── Sandy's dock: one bottom-right home for Sandy, 💤 and the progress pill on every page ──
   Pages with their own Sandy (.floating-controls on the lists, #sandy on the quizzes) and the corner Sandy share it.
   Wide screens: a column inside the companion rail (assets/nav.css reserves the rail), so it never meets the content.
   A drag still moves Sandy on wide screens (the pages write left/top inline, which wins over these rules). */
body.has-sandy-dock :is(.floating-controls, .sandy) { z-index: var(--z-dock); right: max(var(--dock-gap), env(safe-area-inset-right)); bottom: max(var(--dock-gap), env(safe-area-inset-bottom)); }
body.has-sandy-dock .floating-progress { max-width: calc(var(--layout-companion-rail) - var(--dock-gap)); }
body.has-sandy-dock .floating-sandy-bubble { max-width: calc(var(--layout-companion-rail) - var(--dock-gap)); }
body.has-sandy-dock .sandy .sandy-image { width: 104px; height: 104px; }
body.has-sandy-dock .sandy .sandy-speech { max-width: calc(var(--layout-companion-rail) - var(--dock-gap)); }

/* Phones and small tablets: the dock is the right end of the bottom tab bar (the tabs keep that space free).
   Sandy is always there, but she sits on the bar, not on the page. Her lines float above her only while she talks. */
@media (max-width: 900px) {
  body.has-sandy-dock :is(.floating-controls, .sandy, .sandy-float) {
    position: fixed; z-index: var(--z-dock-bar);
    left: auto !important; top: auto !important;
    right: max(8px, env(safe-area-inset-right)) !important;
    bottom: calc(3px + env(safe-area-inset-bottom, 0px)) !important;
  }
  /* one 44px button beside Sandy: 🍙 opens snacks + the sleep toggle; while she sleeps it reads 叫醒 */
  body.has-sandy-dock .sandy-sleep { display: none; }
  body.has-sandy-dock .sandy-snack-toggle { z-index: var(--z-dock-bar); }
  body.has-sandy-dock .sandy-pack.is-popover { left: auto; top: auto; right: max(8px, env(safe-area-inset-right)); bottom: calc(var(--sn-bottom-bar, 66px) + 8px + env(safe-area-inset-bottom, 0px)); }
  body.has-sandy-dock .sandy-pack-actions { display: flex; }
  body.has-sandy-dock :is(.floating-sandy-toggle, .sandy-float-button) { width: var(--dock-sandy) !important; height: var(--dock-sandy); filter: none; }
  body.has-sandy-dock :is(.floating-sandy-image, .sandy-float-button .sandy-sprite-shell, .sandy .sandy-image) { width: var(--dock-sandy) !important; height: var(--dock-sandy) !important; }
  /* list pages: auto-read on shows as a ring around Sandy instead of a label over the cards */
  body.has-sandy-dock .floating-sandy-toggle[aria-pressed="true"] { border-radius: 16px; background: var(--tint-3); box-shadow: inset 0 0 0 2px var(--accent-focus); }
  /* quizzes: the speech bubble rises above Sandy while she talks, and never takes taps meant for the page */
  body.has-sandy-dock .sandy .sandy-speech { position: absolute; right: 0; bottom: calc(100% + 8px); display: none; width: max-content; max-width: min(220px, calc(100vw - 24px)); margin: 0; pointer-events: none; }
  body.has-sandy-dock .sandy.is-talking .sandy-speech { display: block; }
  body.has-sandy-dock.fb-open .sandy .sandy-speech { display: none; }
}
