/* SewConnect — upper-atmosphere theme.
   A bright purple sky: lavender→periwinkle gradient, white glass cards,
   birds drifting by, stars only barely visible at the very top. */

@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url(fonts/figtree-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2212, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url(fonts/figtree-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --ink: #2B2350;
  --ink-soft: #473C78;
  --muted: #665C97;
  --dim: #7F75AB;
  --faint: #9A91C4;
  --card: rgba(255, 255, 255, 0.52);
  --card-brd: rgba(88, 70, 160, 0.16);
  --chip-bg: rgba(255, 255, 255, 0.46);
  --accent: #7C5CE0;
  --accent-dim: rgba(124, 92, 224, 0.3);
  --accent-ink: #FFFFFF;
  --good: #2F9F7B;
  --bad: #D9558C;
  --flow: #E0699E;
  --fertile: #4A79C9;
}

/* [hidden] must always beat any display rule. */
[hidden] { display: none !important; }

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body { margin: 0; padding: 0; height: 100%; }

body {
  font-family: 'Figtree', system-ui, sans-serif;
  color: var(--ink);
  background: #C8B9F1;
  min-height: 100dvh;
  overflow: hidden;
  /* This is a toy, not a document. A long press should pet the animal, not
     start a text selection or raise a "copy / share" bar over the room. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
/* Except where the words are the point: what you wrote to each other, and
   anything you are typing. */
.messages, .bubble, input, textarea { -webkit-user-select: text; user-select: text; }

/* Tapping a control must not leave a focus box sitting on it afterwards. On
   touch this fires on every tap; :focus-visible still keeps a real ring for
   anyone driving the app from a keyboard. */
button:focus, [role="button"]:focus, a:focus { outline: none; }
button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* SVG groups draw the UA ring as a hard black rectangle — the boxes that were
   showing up around the fridge, the arcade and the notepad. */
svg .hot:focus, svg .hot:focus-visible, svg [tabindex]:focus { outline: none; }

#sky {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: linear-gradient(180deg, #EAE4FC 0%, #CDBFF3 48%, #A48DE2 100%);
}

#app { position: relative; z-index: 1; height: 100dvh; display: flex; flex-direction: column; }

input, button, textarea, select { font-family: 'Figtree', system-ui, sans-serif; }
input:focus, textarea:focus { outline: none; }
::placeholder { color: #8D84B8; opacity: 1; }
::-webkit-scrollbar { display: none; }
button { cursor: pointer; }

@keyframes glowPulse { 0%,100% { opacity: .55; } 50% { opacity: 1; } }
/* glowPulse's half-fade suits a small sparkle glyph but leaves the logo looking
   washed out, so the mark breathes instead of blinking. */
@keyframes markBreathe {
  0%,100% { transform: translateY(0) scale(1); opacity: .9; }
  50% { transform: translateY(-5px) scale(1.03); opacity: 1; }
}
@keyframes floaty { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-8px); } }
@keyframes popIn { 0% { transform: scale(.5); opacity: 0; } 70% { transform: scale(1.08); } 100% { transform: scale(1); opacity: 1; } }
@keyframes riseIn { 0% { transform: translateY(14px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } }
@keyframes dotB { 0%,60%,100% { opacity: .25; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }
@keyframes petBob { 0%,100% { transform: translateY(0) rotate(-1deg); } 50% { transform: translateY(-7px) rotate(1.4deg); } }
@keyframes petSleep { 0%,100% { transform: scaleY(1) translateY(0); } 50% { transform: scaleY(0.96) translateY(2px); } }
@keyframes heartUp { 0% { transform: translateY(0) scale(.6); opacity: 0; } 20% { opacity: 1; } 100% { transform: translateY(-70px) scale(1.25); opacity: 0; } }
@keyframes zz { 0% { transform: translate(0,0); opacity: 0; } 25% { opacity: .9; } 100% { transform: translate(14px,-34px); opacity: 0; } }
@keyframes toastIn { 0% { transform: translate(-50%, 18px); opacity: 0; } 100% { transform: translate(-50%, 0); opacity: 1; } }

/* ---------- screens & pages ---------- */

.screen {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  opacity: 0; transform: translateY(12px);
  pointer-events: none;
  transition: opacity .45s ease, transform .45s ease;
}
.screen.active { opacity: 1; transform: translateY(0); pointer-events: auto; }
/* An inactive screen must never swallow taps — not even via descendants that
   set their own pointer-events (e.g. the still-.active page inside #main
   after logout, which sat invisibly on top of the auth screen). */
.screen:not(.active), .screen:not(.active) * { pointer-events: none !important; }

.page {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  opacity: 0; transform: translateY(10px);
  pointer-events: none;
  transition: opacity .35s ease, transform .35s ease;
}
.page.active { opacity: 1; transform: translateY(0); pointer-events: auto; }

/* ---------- shared ---------- */

.star { color: var(--accent); text-shadow: 0 0 18px var(--accent-dim); }

.h1 { font-size: 28px; font-weight: 700; letter-spacing: -0.01em; color: var(--ink); }
.sub { font-size: 15px; color: var(--muted); line-height: 1.55; }
.label {
  font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
  font-weight: 700; color: var(--dim);
}

.card {
  background: var(--card);
  border: 1px solid var(--card-brd);
  border-radius: 22px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 8px 26px rgba(63, 48, 130, 0.1);
}

.btn {
  height: 54px; width: 100%;
  border-radius: 999px; border: none;
  background: var(--accent); color: var(--accent-ink);
  font-size: 16px; font-weight: 700;
  box-shadow: 0 8px 26px var(--accent-dim);
  transition: transform .12s ease, opacity .2s ease;
}
.btn:active { transform: translateY(1px) scale(.99); }
.btn:disabled { opacity: .38; box-shadow: none; }

.btn-ghost {
  height: 46px; border-radius: 999px;
  background: var(--chip-bg); color: var(--ink);
  border: 1px solid var(--card-brd);
  font-size: 14px; font-weight: 700;
  padding: 0 18px;
}
.btn-text { background: none; border: none; color: var(--dim); font-size: 14px; font-weight: 700; padding: 8px; }

/* Your-data controls. Set apart from the settings above them with a rule, and
   the destructive one is red — but not hidden. An app holding a cycle history
   should not make "delete all of this" a thing you have to hunt for. */
.data-rights {
  margin-top: 26px; padding-top: 18px;
  border-top: 1px solid rgba(88, 70, 160, 0.14);
  display: flex; flex-direction: column; gap: 8px;
}
.data-rights .dr-title {
  font-size: 13px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--faint); margin-bottom: 2px;
}
.data-rights .dr-note { font-size: 12.5px; color: var(--faint); line-height: 1.45; margin: -2px 0 8px; }
.btn-danger {
  background: rgba(214, 69, 90, 0.10); border: 1px solid rgba(214, 69, 90, 0.30);
  color: #C0384E; font-weight: 700; font-size: 14px;
  padding: 11px 14px; border-radius: 14px;
}
.btn-danger:active { background: rgba(214, 69, 90, 0.18); }

/* The confirm step. Typing the username is the point: a two-tap "are you sure"
   is muscle memory, spelling your own name out is a decision. */
.danger-box { display: flex; flex-direction: column; gap: 10px; }
.danger-box .db-list { font-size: 13.5px; line-height: 1.6; color: var(--ink); margin: 0; padding-left: 18px; }
.danger-box .db-list li { margin-bottom: 3px; }
.danger-box .db-warn {
  font-size: 13px; line-height: 1.5; color: #A32F44;
  background: rgba(214, 69, 90, 0.08); border-radius: 12px; padding: 10px 12px;
}

/* Consent. Two rules it has to obey to be worth anything: the box is never
   pre-ticked, and the cycle opt-in is visibly its own decision rather than a
   sub-clause of the first one. Hence the tinted card around it. */
.consent { display: flex; flex-direction: column; gap: 12px; }
.consent-row {
  display: flex; gap: 10px; align-items: flex-start;
  font-size: 13px; line-height: 1.5; color: var(--ink-soft);
  text-align: left;
}
.consent-row input[type="checkbox"] {
  width: 20px; height: 20px; margin: 0; flex: none;
  accent-color: var(--accent);
}
.consent-row a { color: var(--accent); font-weight: 700; }
.consent-sep {
  border: 1px solid var(--card-brd); background: var(--chip-bg);
  border-radius: 14px; padding: 12px 13px;
}
.consent-sep .consent-why {
  font-size: 12px; color: var(--faint); line-height: 1.45; margin: 7px 0 0 30px;
}
/* "Our relationship" is a page of sections. Period tracking is the first one;
   the heading exists now so that adding the second is not a redesign. */
.rel-head {
  font-size: 12px; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; color: var(--faint); margin: 2px 0 -4px 6px;
}
.rel-more {
  font-size: 12px; color: var(--faint); line-height: 1.5;
  text-align: center; padding: 6px 14px 2px;
}

.dob-row { display: flex; flex-direction: column; gap: 6px; }
.dob-row .dob-hint { font-size: 12px; color: var(--faint); line-height: 1.45; }
/* Safari and Chrome render a bare date input a head shorter than a text one
   and leave it text-left-aligned inside our pill, so it is pinned by hand. */
input[type="date"].input {
  height: 52px; -webkit-appearance: none; appearance: none;
  font-family: inherit; color: var(--ink);
}

.chip {
  padding: 9px 18px; border-radius: 999px;
  background: var(--chip-bg); color: var(--ink-soft);
  border: 1px solid var(--card-brd);
  font-size: 13px; font-weight: 700;
  transition: all .22s ease;
}
.chip.on { background: var(--accent); color: var(--accent-ink); border-color: transparent; box-shadow: 0 4px 16px var(--accent-dim); }

.input {
  width: 100%; height: 52px;
  border-radius: 999px;
  border: 1px solid var(--card-brd);
  background: rgba(255, 255, 255, 0.62);
  color: var(--ink); font-size: 15px;
  padding: 0 20px;
  transition: border-color .2s ease;
}
.input:focus { border-color: var(--accent); }

.error { color: var(--bad); font-size: 13.5px; font-weight: 700; }

.swatch {
  width: 36px; height: 36px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.65); padding: 0;
  transition: all .15s ease;
  box-shadow: 0 3px 10px rgba(63, 48, 130, 0.18);
}
.swatch.on { border-color: var(--ink); transform: scale(1.12); }

#toast {
  position: fixed; left: 50%; bottom: 108px; transform: translateX(-50%);
  /* Above #gallery (70) and #game-layer (80). A toast is the app talking to
     you — "your partner wants to play" arrives while the games menu is open,
     which is exactly when it was being drawn underneath it. */
  z-index: 90;
  background: rgba(255, 255, 255, 0.96); color: var(--ink);
  border: 1px solid var(--accent-dim);
  border-radius: 999px; padding: 11px 20px;
  font-size: 13.5px; font-weight: 700;
  animation: toastIn .3s ease both;
  max-width: 86vw; text-align: center;
  box-shadow: 0 10px 30px rgba(63, 48, 130, 0.18);
}

/* ---------- install gate ---------- */

#gate { align-items: center; justify-content: center; padding: 32px; text-align: center; }
#gate .gate-icon {
  width: 92px; height: 92px; border-radius: 26px;
  box-shadow: 0 16px 40px rgba(63, 48, 130, 0.3);
  animation: floaty 5s ease-in-out infinite;
}
#gate .gate-box { max-width: 340px; display: flex; flex-direction: column; gap: 14px; align-items: center; }
#gate .manual {
  font-size: 13.5px; color: var(--muted); line-height: 1.6;
  background: var(--chip-bg); border: 1px solid var(--card-brd);
  border-radius: 16px; padding: 12px 16px;
}

/* ---------- auth ---------- */

#auth, #consent { align-items: center; justify-content: center; padding: 28px; }
/* The consent screen can outgrow a short phone, and a gate you cannot scroll to
   the bottom of is a gate nobody gets through. */
#consent { overflow-y: auto; justify-content: flex-start; padding-top: 40px; }
.auth-hero { text-align: center; margin-bottom: 26px; }
/* The mark is the logo image now, not a glyph, so it is sized by width and
   keeps the artwork's own proportions. */
.auth-hero .mark { width: 96px; height: auto; }
.auth-hero .hero-mark { animation: markBreathe 3.6s ease-in-out infinite; }
.auth-hero h1 { font-size: 40px; font-weight: 700; letter-spacing: -0.02em; margin: 10px 0 4px; color: var(--ink); }
.auth-hero .tag { font-size: 16px; color: var(--muted); }

.auth-box { width: 100%; max-width: 360px; display: flex; flex-direction: column; gap: 12px; }

.seg {
  display: flex; gap: 4px; padding: 4px;
  background: rgba(88, 70, 160, 0.14); border-radius: 999px;
}
.seg button {
  flex: 1; padding: 10px; border: 0; border-radius: 999px;
  background: transparent; color: var(--dim); font-weight: 700; font-size: 14px;
  transition: all .25s ease;
}
.seg button.on { background: rgba(255, 255, 255, 0.75); color: var(--ink); box-shadow: 0 2px 8px rgba(63, 48, 130, 0.12); }

.lang-row { display: flex; justify-content: center; gap: 8px; margin-top: 18px; }
.lang-row button { background: none; border: none; color: var(--faint); font-size: 13px; font-weight: 700; padding: 6px 10px; }
.lang-row button.on { color: var(--accent); }

/* ---------- topbar + nav ---------- */

.topbar {
  display: flex; align-items: center; gap: 8px;
  padding: 14px 22px 10px;
  padding-top: max(14px, env(safe-area-inset-top));
}
.topbar .brand {
  display: flex; align-items: center; gap: 8px;
  font-size: 17px; font-weight: 700; color: var(--ink);
  /* The home bar carries a coin chip and the customize button too; the title
     is the part that can afford to give ground. */
  flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap;
}
/* flex-shrink:0 or the logo is the first thing squeezed when a long room title
   runs out of room, and the brand loses its mark instead of its text. */
.topbar .brand .mark { width: 24px; height: auto; flex: 0 0 auto; }

/* Redecorating lives up here now, in the corner, out of the room's way. */
.room-edit {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--chip-bg); border: 1px solid var(--card-brd);
  border-radius: 999px; padding: 7px 12px;
  color: var(--ink-soft); font-size: 11.5px; font-weight: 800;
  white-space: nowrap; transition: transform .15s ease;
}
.room-edit:active { transform: scale(.94); background: var(--accent-dim); }
.room-edit .ce-ico { font-size: 12.5px; letter-spacing: -1px; }

#nav {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 30;
  height: 88px;
  padding: 8px 30px calc(14px + env(safe-area-inset-bottom));
  display: flex; align-items: center; justify-content: space-between;
  background: linear-gradient(180deg, rgba(214, 203, 246, 0), rgba(226, 217, 250, 0.96) 46%);
}
.nav-tab {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  background: none; border: none; padding: 6px 8px; width: 66px;
  color: var(--faint); transition: color .25s ease;
}
.nav-tab svg { display: block; }
.nav-tab span { font-size: 11px; font-weight: 700; }
.nav-tab.on { color: var(--accent); }
.nav-pet {
  width: 60px; height: 60px; margin-top: -26px;
  border-radius: 50%;
  background: linear-gradient(135deg, #9678EC, var(--accent));
  border: 2px solid rgba(255, 255, 255, 0.55);
  box-shadow: 0 10px 30px var(--accent-dim);
  display: flex; align-items: center; justify-content: center;
  color: #fff;
  transition: transform .2s ease;
}
.nav-pet.on { transform: scale(1.08); }

.page-body { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 20px 120px; }
/* With a pet, the home page is a fixed-height column rather than a scroller:
   the room fills whatever the room bar and pet bar leave, like the chat screen.
   The adopt and "find a partner" views keep the normal scrolling body. */
#pet-body.room-mode {
  display: flex; flex-direction: column;
  overflow: hidden; padding-bottom: 96px;
}

/* ---------- match ---------- */

#page-match .page-body { display: flex; flex-direction: column; gap: 16px; }
.match-hero { text-align: center; padding: 26px 12px 6px; }
.match-hero .orbit { font-size: 34px; animation: floaty 5s ease-in-out infinite; }

.req-item {
  display: flex; align-items: center; gap: 10px;
  padding: 13px 16px; border-radius: 18px;
  background: var(--chip-bg); border: 1px solid var(--card-brd);
}
.req-item .who { flex: 1; font-size: 14.5px; color: var(--ink-soft); }
.req-item .who strong { color: var(--ink); }
.btn-mini {
  border: none; border-radius: 999px; padding: 9px 16px;
  font-size: 13px; font-weight: 700;
}
.btn-mini.yes { background: var(--accent); color: var(--accent-ink); }
.btn-mini.no { background: var(--chip-bg); color: var(--ink-soft); border: 1px solid var(--card-brd); }

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

.chat-head {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 20px 10px;
  padding-top: max(14px, env(safe-area-inset-top));
  background: linear-gradient(180deg, rgba(234, 228, 252, 0.9), rgba(234, 228, 252, 0));
}
.avatar {
  width: 44px; height: 44px; flex: 0 0 auto;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.65);
  border: 1px solid var(--card-brd);
  display: flex; align-items: center; justify-content: center;
  font-size: 17px; font-weight: 700; color: var(--accent);
}
.chat-head .meta { flex: 1; min-width: 0; }
.chat-head .name { font-size: 16px; font-weight: 700; color: var(--ink); }
.chat-head .status { font-size: 12px; color: var(--muted); display: flex; align-items: center; gap: 5px; }
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--faint); display: inline-block; }
.dot.online { background: var(--good); box-shadow: 0 0 8px var(--good); }

.messages {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 8px 20px 12px;
  display: flex; flex-direction: column;
}
.bubble {
  max-width: 76%; padding: 10px 15px;
  font-size: 15px; line-height: 1.4; margin: 3px 0;
  word-wrap: break-word; white-space: pre-wrap;
  animation: riseIn .3s ease both;
}
.bubble.me {
  align-self: flex-end;
  background: var(--accent); color: var(--accent-ink);
  border-radius: 20px 20px 6px 20px;
  box-shadow: 0 4px 14px var(--accent-dim);
}
.bubble.them {
  align-self: flex-start;
  background: rgba(255, 255, 255, 0.8); color: var(--ink);
  border-radius: 20px 20px 20px 6px;
  box-shadow: 0 4px 14px rgba(63, 48, 130, 0.08);
}
.bubble .time { display: block; font-size: 10.5px; opacity: .6; margin-top: 2px; text-align: right; }
.day-sep {
  align-self: center; font-size: 11.5px; font-weight: 700; color: var(--muted);
  background: rgba(88, 70, 160, 0.12);
  padding: 6px 14px; border-radius: 999px; margin: 10px 0 6px;
}
.typing-bubble {
  align-self: flex-start;
  background: rgba(255, 255, 255, 0.8);
  padding: 13px 16px; border-radius: 20px 20px 20px 6px;
  margin: 3px 0; display: flex; gap: 5px;
}
.typing-bubble span { width: 6px; height: 6px; border-radius: 50%; background: var(--dim); animation: dotB 1.2s infinite; }
.typing-bubble span:nth-child(2) { animation-delay: .18s; }
.typing-bubble span:nth-child(3) { animation-delay: .36s; }

.chat-form {
  display: flex; gap: 10px; align-items: center;
  padding: 10px 16px calc(96px + env(safe-area-inset-bottom));
}
.chat-form input { flex: 1; height: 48px; }
.send-btn {
  width: 48px; height: 48px; flex: 0 0 auto;
  border-radius: 50%; border: none;
  background: var(--accent); color: var(--accent-ink);
  font-size: 18px;
  box-shadow: 0 4px 16px var(--accent-dim);
}

/* ---------- cycle ---------- */

.cycle-status { padding: 18px; display: flex; flex-direction: column; gap: 8px; }
.cycle-status .big { font-size: 22px; font-weight: 700; color: var(--ink); }
.phase-pill {
  align-self: flex-start;
  font-size: 12px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  border-radius: 999px; padding: 6px 13px;
}
.phase-pill.period { background: rgba(224, 105, 158, .16); color: #C24B82; }
.phase-pill.fertile { background: rgba(74, 121, 201, .15); color: #3D68B2; }
.phase-pill.follicular { background: rgba(47, 159, 123, .15); color: #22815F; }
.phase-pill.luteal { background: rgba(176, 126, 43, .15); color: #96691E; }

.cta-card { padding: 20px 18px; display: flex; flex-direction: column; gap: 14px; }
.cta-card .cta-line { font-size: 15px; color: var(--ink-soft); font-weight: 600; }
.when-row { display: flex; gap: 8px; flex-wrap: wrap; }
.when-row .chip { flex: 1; text-align: center; white-space: nowrap; }
.when-row input[type="date"] {
  flex: 1; height: 40px; border-radius: 999px;
  border: 1px solid var(--card-brd); background: var(--chip-bg);
  color: var(--ink-soft); font-size: 13px; font-weight: 700; padding: 0 14px;
}

.stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.stat-tile { padding: 16px 8px; text-align: center; display: flex; flex-direction: column; gap: 5px; }
.stat-tile .stat-val { font-size: 20px; font-weight: 800; color: var(--ink); }
.stat-tile .stat-val small { font-size: 12px; font-weight: 700; color: var(--muted); }
.stat-tile .stat-lbl { font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; font-weight: 700; color: var(--dim); }

.hist-row {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 4px; border-bottom: 1px solid var(--card-brd);
}
.hist-row:last-child { border-bottom: none; }
.hist-row .h-dates { flex: 1; font-size: 14px; color: var(--ink-soft); font-weight: 600; }
.hist-row .h-days { font-size: 12.5px; color: var(--muted); font-weight: 700; }
.hist-row .h-del { background: none; border: none; color: var(--faint); font-size: 12px; font-weight: 700; padding: 6px; }

.feel-row { display: flex; gap: 10px; align-items: baseline; padding: 9px 4px; border-bottom: 1px solid var(--card-brd); }
.feel-row:last-child { border-bottom: none; }
.feel-row .f-date { font-size: 12px; font-weight: 700; color: var(--dim); white-space: nowrap; }
.feel-row .f-body { flex: 1; font-size: 13.5px; color: var(--ink-soft); }

/* bottom sheet */
#sheet-veil {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(43, 35, 80, 0.4);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  opacity: 0; pointer-events: none; transition: opacity .3s ease;
}
#sheet-veil.open { opacity: 1; pointer-events: auto; }
#sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 41;
  background: rgba(248, 245, 255, 0.99);
  border-top: 1px solid var(--accent-dim);
  border-radius: 26px 26px 0 0;
  padding: 14px 22px calc(26px + env(safe-area-inset-bottom));
  transform: translateY(105%);
  transition: transform .34s cubic-bezier(.3, 1, .4, 1);
  max-height: 82dvh; overflow-y: auto;
  box-shadow: 0 -14px 44px rgba(63, 48, 130, 0.22);
}
#sheet.open { transform: translateY(0); }
.sheet-grip { width: 44px; height: 5px; border-radius: 999px; background: var(--card-brd); margin: 0 auto 14px; }

.flow-row { display: flex; gap: 8px; }
.flow-row .chip { flex: 1; text-align: center; padding: 11px 4px; }
.chip.flow-on-light { background: rgba(224, 105, 158, .3); color: #8E2F5C; border-color: transparent; }
.chip.flow-on-medium { background: rgba(224, 105, 158, .6); color: #fff; border-color: transparent; }
.chip.flow-on-heavy { background: var(--flow); color: #fff; border-color: transparent; }

.sym-grid { display: flex; flex-wrap: wrap; gap: 8px; }

/* ---------- house ---------- */

.house-stage {
  position: relative;
  border-radius: 26px;
  overflow: hidden;
  border: 1px solid var(--card-brd);
  box-shadow: 0 12px 34px rgba(63, 48, 130, 0.16);
  line-height: 0;
  /* Fill whatever the room bar and pet bar leave behind. */
  flex: 1 1 auto;
  min-height: 0;
  touch-action: pan-y;
}
.house-stage svg.house { width: 100%; height: 100%; display: block; }

/* The Pou-style wanderer: absolutely positioned over the room, JS moves it. */
#pet-walker {
  position: absolute;
  left: 34%;
  bottom: 5%;
  width: 34%;
  background: none;
  border: none;
  padding: 0;
  z-index: 6;
  transform-origin: bottom center;
}
#pet-walker svg { width: 100%; height: auto; display: block; }
#pet-flip { transition: transform .4s ease; }
#pet-walker.walking #pet-flip svg { animation: walkBounce .55s ease-in-out infinite; }
#pet-walker.asleep #pet-flip svg { animation: petSleep 5s ease-in-out infinite; }
#pet-walker:not(.walking):not(.asleep) #pet-flip svg { animation: petBob 4.2s ease-in-out infinite; }
@keyframes walkBounce {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50% { transform: translateY(-6px) rotate(2deg); }
}


.house-preview {
  border-radius: 18px;
  overflow: hidden;
  border: 1px solid var(--card-brd);
  margin-top: 12px;
  line-height: 0;
}

/* ---------- pet ---------- */

.pet-stage {
  position: relative;
  border-radius: 26px;
  overflow: hidden;
  border: 1px solid var(--card-brd);
  min-height: 300px;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 12px 34px rgba(63, 48, 130, 0.16);
}
.pet-stage svg.pet { position: relative; z-index: 2; }
.pet-stage .wrap-bob { animation: petBob 4.2s ease-in-out infinite; }
.pet-stage .wrap-sleep { animation: petSleep 5s ease-in-out infinite; }
.scene-night { background: radial-gradient(140% 100% at 50% 0%, #201A56 0%, #0C0A28 70%); }
.scene-meadow { background: linear-gradient(180deg, #9FD8F0 0%, #7FCF9E 78%, #5FBE7E 100%); }
.scene-beach { background: linear-gradient(180deg, #8FD4F2 0%, #5EB1E8 60%, #F2DCA6 88%, #E7C687 100%); }
.scene-aurora { background: linear-gradient(160deg, #1B1650 10%, #1E6E82 45%, #45B58C 70%, #1B1650 100%); }

.pet-id .pname { font-size: 24px; font-weight: 700; color: var(--ink); }
.pet-id .plevel { font-size: 12.5px; font-weight: 800; color: var(--accent); letter-spacing: .08em; }

.meters { display: flex; flex-direction: column; gap: 10px; padding: 14px 4px 4px; }
.meter { display: flex; align-items: center; gap: 10px; }
.meter .m-label { width: 84px; font-size: 12px; font-weight: 700; color: var(--dim); display: flex; gap: 5px; align-items: center; }
.meter .m-bar { flex: 1; height: 9px; border-radius: 999px; background: rgba(43, 35, 80, 0.14); overflow: hidden; }
.meter .m-fill { height: 100%; border-radius: 999px; transition: width .6s cubic-bezier(.3, 1, .4, 1); }
.m-fill.hunger { background: linear-gradient(90deg, #E8B54A, #E0699E); }
.m-fill.happiness { background: linear-gradient(90deg, var(--accent), #E0699E); }
.m-fill.energy { background: linear-gradient(90deg, #4A79C9, #2F9F7B); }
.meter .m-val { width: 34px; font-size: 11.5px; color: var(--muted); text-align: right; font-weight: 700; }

.xp-row { padding: 2px 4px 0; display: flex; align-items: center; gap: 10px; }
.xp-bar { flex: 1; height: 6px; border-radius: 999px; background: rgba(43, 35, 80, 0.14); overflow: hidden; }
.xp-fill { height: 100%; background: var(--accent); border-radius: 999px; transition: width .6s ease; }
.xp-row .xp-txt { font-size: 11px; color: var(--faint); font-weight: 700; }

/* The row of action buttons that used to live under the room is gone: you
   cuddle by tapping the pet, sleep by tapping the bed, dress it at the
   wardrobe and redecorate from the header. */

.pet-feed-list { display: flex; flex-direction: column; gap: 6px; padding: 14px; }
.feed-item { display: flex; gap: 10px; align-items: center; font-size: 13px; color: var(--muted); padding: 4px 2px; }
.feed-item .fk { font-size: 15px; }
.feed-item .ft { color: var(--faint); font-size: 11.5px; margin-left: auto; }

.fx-layer { position: absolute; inset: 0; z-index: 5; pointer-events: none; overflow: hidden; }
.fx-heart { position: absolute; font-size: 22px; animation: heartUp 1.15s ease-out both; }
.fx-zz { position: absolute; font-size: 17px; color: rgba(255,255,255,.8); animation: zz 2.4s ease-out infinite; }

/* adopt / intro */
.adopt-preview { display: flex; align-items: center; justify-content: center; padding: 6px 0 2px; }
.intro-list { display: flex; flex-direction: column; gap: 14px; padding: 16px 18px; }
.intro-item { display: flex; gap: 12px; align-items: flex-start; }
.intro-item .ii { font-size: 17px; margin-top: 1px; }
.intro-item .it { flex: 1; }
.intro-item .it b { display: block; font-size: 14.5px; color: var(--ink); }
.intro-item .it span { font-size: 13px; color: var(--muted); line-height: 1.45; }

.opt-row { display: flex; gap: 9px; flex-wrap: wrap; padding: 4px 0 2px; }
.species-opt {
  width: 64px; height: 64px; border-radius: 20px;
  background: var(--chip-bg); border: 1.5px solid var(--card-brd);
  display: flex; align-items: center; justify-content: center;
  transition: all .2s ease; padding: 6px;
}
.species-opt.on { border-color: var(--accent); background: rgba(124, 92, 224, 0.12); box-shadow: 0 4px 16px var(--accent-dim); }
.species-opt svg { width: 100%; height: 100%; }
.acc-opt {
  padding: 9px 16px; border-radius: 999px;
  background: var(--chip-bg); border: 1px solid var(--card-brd);
  color: var(--ink-soft); font-size: 13px; font-weight: 700;
  display: flex; align-items: center; gap: 6px;
}
.acc-opt.on { border-color: var(--accent); color: var(--ink); background: rgba(124, 92, 224, 0.12); }
.acc-opt:disabled { opacity: .42; }
.acc-opt .lock { font-size: 11px; color: var(--faint); }

/* ---------- you ---------- */

#page-you .page-body { display: flex; flex-direction: column; gap: 24px; }
.you-head { display: flex; align-items: center; gap: 16px; padding-top: 8px; }
.you-avatar {
  width: 76px; height: 76px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.65);
  border: 1px solid var(--accent-dim);
  display: flex; align-items: center; justify-content: center;
  font-size: 28px; font-weight: 700; color: var(--accent);
}
.you-head .uname { font-size: 21px; font-weight: 700; color: var(--ink); }
.you-head .usub { font-size: 13px; color: var(--dim); }

.settings-group { display: flex; flex-direction: column; gap: 10px; }
.opt-pills { display: flex; gap: 10px; flex-wrap: wrap; }

.switch-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.switch-row .sw-text { flex: 1; }
.switch-row .sw-title { font-size: 14.5px; font-weight: 700; color: var(--ink); }
.switch-row .sw-sub { font-size: 12.5px; color: var(--dim); margin-top: 2px; }
.switch {
  width: 52px; height: 30px; border-radius: 999px; flex: 0 0 auto;
  background: rgba(43, 35, 80, 0.18); border: 1px solid var(--card-brd);
  position: relative; transition: background .25s ease;
}
.switch::after {
  content: ''; position: absolute; top: 3px; left: 3px;
  width: 22px; height: 22px; border-radius: 50%;
  background: #fff; transition: all .25s ease;
  box-shadow: 0 2px 6px rgba(43, 35, 80, 0.25);
}
.switch.on { background: var(--accent); border-color: transparent; }
.switch.on::after { left: 25px; }

.num-row { display: flex; align-items: center; gap: 12px; }
.num-row .num-label { flex: 1; font-size: 14px; color: var(--ink-soft); font-weight: 600; }
.stepper { display: flex; align-items: center; gap: 2px; }
.stepper button {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--chip-bg); border: 1px solid var(--card-brd);
  color: var(--ink); font-size: 16px; font-weight: 700;
}
.stepper .num { min-width: 34px; text-align: center; font-size: 15px; font-weight: 700; color: var(--ink); }

#install-card {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px; text-align: left; width: 100%;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.6), rgba(124, 92, 224, 0.14));
  border: 1px solid var(--accent-dim); border-radius: 22px;
}
#install-card .ic { font-size: 24px; }
#install-card .it { flex: 1; }
#install-card .it b { display: block; font-size: 15px; color: var(--ink); }
#install-card .it span { font-size: 12.5px; color: var(--muted); }

/* update prompt bar */
#update-bar {
  position: fixed; left: 50%; transform: translateX(-50%);
  top: max(14px, env(safe-area-inset-top));
  z-index: 70;
  display: flex; align-items: center; gap: 10px;
  background: rgba(255, 255, 255, 0.97);
  border: 1px solid var(--accent-dim);
  border-radius: 999px; padding: 9px 10px 9px 18px;
  box-shadow: 0 10px 30px rgba(63, 48, 130, 0.22);
  animation: toastIn .35s ease both;
}
#update-bar .ut { font-size: 13.5px; font-weight: 700; color: var(--ink); }
#update-bar button {
  border: none; border-radius: 999px;
  background: var(--accent); color: var(--accent-ink);
  font-size: 13px; font-weight: 700; padding: 9px 16px;
}

/* ---------- rooms, coins, fridge & shop ---------- */

/* Five rooms sharing the width evenly, each with its icon over its name. */
/* The room header. There used to be a row of five pills here, which meant the
   room was something you picked off a menu. Now you walk: arrows or a swipe,
   and this just tells you where you are. The dots are position, not controls —
   they say how many rooms there are and which one this is, the way a carousel
   does, without inviting a tap at a 6px target. */
.room-bar {
  display: flex; gap: 6px; align-items: center; justify-content: center;
  padding: 2px 0 10px; flex: 0 0 auto;
  position: relative;
}
.room-name {
  display: flex; align-items: center; gap: 8px; justify-content: center;
  min-width: 0; flex: 0 1 auto;
  padding: 8px 16px; border-radius: 999px;
  background: var(--chip-bg); border: 1px solid var(--card-brd);
}
.room-name .ri { font-size: 17px; line-height: 1; }
.room-name .rn {
  font-size: 14px; font-weight: 700; letter-spacing: -0.01em; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.room-arrow {
  flex: 0 0 auto;
  width: 40px; height: 40px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  font-size: 26px; font-weight: 700; line-height: 1;
  color: var(--ink-soft); border: none; background: none;
}
.room-arrow:active { background: var(--chip-bg); color: var(--ink); }
.room-dots {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; gap: 4px; justify-content: center; pointer-events: none;
}
.room-dots i {
  width: 4px; height: 4px; border-radius: 999px;
  background: var(--faint); opacity: .45; transition: all .2s ease;
}
.room-dots i.on { background: var(--accent); opacity: 1; width: 12px; }

/* The pet's own strip, below the room. */
.pet-bar { flex: 0 0 auto; padding-top: 10px; }
.pet-id { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.pet-id .pmood { font-size: 13px; color: var(--muted); font-weight: 600; }
/* Cuddling lost its button to the pet itself, so this is where the timer that
   used to sit on that button went. */
.pet-tip {
  margin-top: 8px; text-align: center;
  font-size: 12.5px; font-weight: 700; color: var(--dim);
}

/* Tappable things drawn inside the room SVG. */
.house-stage .hot { cursor: pointer; }
.house-stage .hot-hit { fill: transparent; }
.house-stage .hot-ring {
  fill: rgba(255, 255, 255, 0.14);
  stroke: var(--accent); stroke-width: 2.5; stroke-dasharray: 7 6;
  animation: hotPulse 2.4s ease-in-out infinite;
}
.house-stage .hot-tag rect { fill: var(--accent); }
.house-stage .hot-tag text {
  fill: #fff; font-size: 11px; font-weight: 800;
  font-family: 'Figtree', system-ui, sans-serif;
}
.house-stage .hot:active .hot-ring { fill: rgba(255, 255, 255, 0.3); }
.house-stage .hot.quiet .hot-ring { opacity: .3; animation: none; stroke-dasharray: 4 7; }
@keyframes hotPulse {
  0%, 100% { opacity: .55; }
  50% { opacity: 1; }
}
/* Food waiting on the table is the thing you tap to actually feed. */
.plate-food.tappable { cursor: pointer; animation: floaty 2.6s ease-in-out infinite; }
.plate-hint {
  position: absolute; left: 50%; transform: translateX(-50%);
  top: 10px; z-index: 7;
  background: rgba(255, 255, 255, 0.94); color: var(--ink);
  border: 1px solid var(--accent-dim); border-radius: 999px;
  padding: 7px 16px; font-size: 12.5px; font-weight: 700; line-height: 1.2;
  box-shadow: 0 6px 18px rgba(63, 48, 130, 0.18);
}
.coin-chip {
  flex: 0 0 auto;
  background: rgba(245, 215, 142, 0.55); color: #7A5A14;
  border: 1px solid rgba(201, 162, 74, 0.5);
  border-radius: 999px; padding: 7px 13px;
  font-size: 13px; font-weight: 800; white-space: nowrap;
}
/* In sheets the coin chip is pushed to the right of its title row. */
.sheet-title-row .coin-chip { margin-left: auto; }

.sleep-hint {
  position: absolute; left: 50%; bottom: 10%;
  transform: translateX(-50%); z-index: 6;
  background: rgba(255, 255, 255, 0.88); color: var(--ink-soft);
  border-radius: 999px; padding: 9px 16px;
  font-size: 13px; font-weight: 700; white-space: nowrap;
}

.sheet-title-row { display: flex; align-items: center; gap: 10px; }
.sheet-title-row .coin-chip { margin-left: auto; }

.food-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.food-item {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 11px 2px; border-radius: 16px;
  background: var(--chip-bg); border: 1px solid var(--card-brd);
  transition: transform .15s ease;
}
.food-item:active:not(:disabled) { transform: scale(.93); }
.food-item.empty { opacity: .38; }
.food-item .fe { font-size: 24px; }
.food-item .fq { font-size: 11.5px; font-weight: 800; color: var(--accent); }
.food-item .fn { font-size: 10px; font-weight: 700; color: var(--dim); }

.shop-list { display: flex; flex-direction: column; padding: 6px 14px; }
.shop-row { display: flex; align-items: center; gap: 12px; padding: 10px 2px; border-bottom: 1px solid var(--card-brd); }
.shop-row:last-child { border-bottom: none; }
.shop-row .fe { font-size: 20px; }
.shop-row .sn { flex: 1; font-size: 14px; font-weight: 700; color: var(--ink); }
.shop-row .sn small { display: block; font-size: 11.5px; font-weight: 600; color: var(--dim); }
.acc-opt .price { font-size: 11.5px; font-weight: 800; color: #7A5A14; }
.acc-opt.buy { border-style: dashed; }
.acc-opt.buy.previewing {
  border-style: solid; border-color: var(--accent);
  background: rgba(124, 92, 224, 0.16); color: var(--ink);
}

/* Preview-before-buy: a tag under the room preview + a buy bar. */
.preview-tag {
  text-align: center; margin: 10px 0 0;
  font-size: 12px; letter-spacing: .08em; text-transform: uppercase;
  font-weight: 800; color: var(--accent);
}
.buy-bar { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }
.buy-bar .bb-name { text-align: center; font-size: 15px; font-weight: 700; color: var(--ink); }
.buy-bar .btn { height: 50px; }

/* ---------- pet behavior FX + animations ---------- */

.fx-thought {
  position: absolute; z-index: 7;
  font-size: 19px; line-height: 1;
  background: rgba(255, 255, 255, 0.92);
  border-radius: 14px 14px 14px 3px;
  padding: 7px 9px;
  box-shadow: 0 4px 14px rgba(63, 48, 130, 0.18);
  animation: thoughtUp 2.2s ease-out both;
}
.fx-zz-once { position: absolute; z-index: 7; font-size: 17px; animation: zz 2.2s ease-out both; }
@keyframes thoughtUp {
  0% { transform: translateY(6px) scale(.5); opacity: 0; }
  15% { transform: translateY(0) scale(1); opacity: 1; }
  80% { opacity: 1; }
  100% { transform: translateY(-16px) scale(1); opacity: 0; }
}

#pet-walker.eating #pet-flip svg { animation: chomp .28s ease-in-out 3; }
#pet-walker.loved #pet-flip svg { animation: wiggle .5s ease-in-out 2; }
#pet-walker.yawning #pet-flip svg { animation: yawnStretch .95s ease-in-out 1; }
@keyframes chomp {
  0%, 100% { transform: scale(1, 1); }
  50% { transform: scale(1.08, .9); }
}
@keyframes wiggle {
  0%, 100% { transform: rotate(0); }
  25% { transform: rotate(-7deg) scale(1.05); }
  75% { transform: rotate(7deg) scale(1.05); }
}
@keyframes yawnStretch {
  0%, 100% { transform: scale(1, 1); }
  40% { transform: scale(.94, 1.1) translateY(-4px); }
  70% { transform: scale(1.05, .94); }
}

/* MSN nudge: the whole app rattles */
.shaking { animation: appShake .6s ease-in-out both; }
@keyframes appShake {
  0%, 100% { transform: translate(0, 0); }
  10% { transform: translate(-7px, 2px); }
  25% { transform: translate(6px, -3px); }
  40% { transform: translate(-6px, -2px); }
  55% { transform: translate(5px, 3px); }
  70% { transform: translate(-4px, 1px); }
  85% { transform: translate(3px, -1px); }
}

.nudge-btn {
  width: 40px; height: 40px; flex: 0 0 auto;
  border-radius: 50%; border: 1px solid var(--card-brd);
  background: var(--chip-bg); font-size: 17px;
  transition: transform .15s ease;
}
.nudge-btn:active { transform: scale(.88) rotate(-10deg); }

/* ---------- games ---------- */

.game-list { display: flex; flex-direction: column; gap: 10px; }
.game-card {
  display: flex; align-items: center; gap: 14px;
  padding: 15px 16px; text-align: left; width: 100%;
  background: var(--chip-bg); border: 1px solid var(--card-brd);
  border-radius: 20px; transition: transform .15s ease;
}
.game-card:active:not(:disabled) { transform: scale(.98); }
.game-card:disabled { opacity: .5; }
.game-card.ping { border-style: dashed; border-color: var(--accent-dim); }
.game-card.ping .gt span { color: var(--accent); font-weight: 700; }
.game-card .gi { font-size: 26px; }
.game-card .gt { flex: 1; }
.game-card .gt b { display: block; font-size: 15px; color: var(--ink); }
.game-card .gt span { font-size: 12.5px; color: var(--muted); }
.game-card .ga { color: var(--faint); font-size: 20px; }

#game-layer {
  position: fixed; inset: 0; z-index: 80;
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, #EAE4FC 0%, #CDBFF3 60%, #A48DE2 100%);
  /* The bigger boards (Reversi, Snakes & Ladders) can outgrow a short phone. */
  overflow-y: auto; overflow-x: hidden;
}
.g-head {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 18px 10px;
  padding-top: max(14px, env(safe-area-inset-top));
}
.g-close {
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--chip-bg); border: 1px solid var(--card-brd);
  color: var(--ink-soft); font-size: 15px; font-weight: 700;
}
.g-title { font-size: 16px; font-weight: 800; color: var(--ink); }
.g-right {
  margin-left: auto; font-size: 13.5px; font-weight: 700; color: var(--ink-soft);
  /* A long status must never widen the layer and push the board off-screen. */
  min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.g-hint {
  text-align: center; font-size: 12.5px; color: var(--muted); font-weight: 600;
  padding: 10px 20px calc(16px + env(safe-area-inset-bottom));
}

.g-stage { position: relative; flex: 1; min-height: 0; overflow: hidden; touch-action: none; }
.g-catcher {
  position: absolute; bottom: 6px; left: 50%;
  transform: translateX(-50%);
  pointer-events: none; z-index: 2;
}
.g-item {
  position: absolute; top: 0; font-size: 26px; z-index: 1;
  pointer-events: none; will-change: transform;
}
.g-item.caught { animation: popIn .22s ease reverse both; }
.g-item.boom { animation: glowPulse .22s ease 1; filter: saturate(3); }

.g-result {
  flex: 1; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 14px; padding: 20px 32px;
}
.g-big { font-size: 54px; animation: popIn .4s ease both; }
.g-score { font-size: 19px; font-weight: 800; color: var(--ink); }
.g-earn {
  font-size: 15px; font-weight: 800; color: #7A5A14;
  background: rgba(245, 215, 142, 0.55);
  border: 1px solid rgba(201, 162, 74, 0.5);
  border-radius: 999px; padding: 9px 18px;
}
.g-earn.dim { background: var(--chip-bg); color: var(--muted); border-color: var(--card-brd); }
.g-result .btn-ghost { min-width: 140px; }

.mem-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  padding: 12px 22px; flex: 1; align-content: center;
  max-width: 400px; width: 100%; margin: 0 auto;
}
.mem-card {
  position: relative; aspect-ratio: 3 / 3.4;
  border-radius: 16px; border: none; padding: 0;
  background: transparent; perspective: 400px; transform-style: preserve-3d;
}
.mem-card .mf, .mem-card .mb {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 16px; font-size: 30px;
  backface-visibility: hidden;
  transition: transform .35s ease;
}
.mem-card .mf { background: var(--accent); color: rgba(255, 255, 255, 0.75); font-size: 20px; }
.mem-card .mb { background: #FFFFFF; transform: rotateY(180deg); }
.mem-card.open .mf, .mem-card.done .mf { transform: rotateY(-180deg); }
.mem-card.open .mb, .mem-card.done .mb { transform: rotateY(0); }
.mem-card.done .mb { background: #C6E9D3; }

.xox-status { text-align: center; font-size: 15px; font-weight: 700; color: var(--muted); padding: 10px; }
.xox-status.mine { color: var(--accent); }
.g-board {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  width: min(78vw, 330px); margin: 8px auto;
}
.g-cell {
  aspect-ratio: 1; border-radius: 18px;
  background: rgba(255, 255, 255, 0.62); border: 1px solid var(--card-brd);
  font-size: 42px; font-weight: 800; color: var(--ink);
  transition: background .2s ease;
}
.g-cell:disabled { color: var(--ink); opacity: 1; }
.g-cell:not(.set):not(:disabled):active { background: var(--accent-dim); }
.g-cell.hit { background: rgba(183, 229, 200, 0.9); border-color: var(--good); }

.duel-status { text-align: center; font-size: 15px; font-weight: 700; color: var(--muted); padding: 10px; }
.duel-status.mine { color: var(--accent); }

/* Connect Four: tighter grid of round discs. */
.g-board.c4 {
  width: min(94vw, 380px);
  gap: 5px; padding: 8px; border-radius: 18px;
  background: rgba(124, 92, 224, 0.14);
}
.g-board.c4 .g-cell {
  aspect-ratio: 1; border-radius: 50%; font-size: 0;
  background: rgba(255, 255, 255, 0.78); border: none;
  display: flex; align-items: center; justify-content: center;
}
.g-board.c4 .g-cell.hit { background: rgba(183, 229, 200, 0.95); }
.disc { width: 78%; height: 78%; border-radius: 50%; display: block; }
.disc.x { background: radial-gradient(circle at 35% 30%, #FF8FA8, #E0405F); }
.disc.o { background: radial-gradient(circle at 35% 30%, #FBE28A, #E7B93E); }
.g-board.c4 .g-cell.drop .disc { animation: dropIn .28s cubic-bezier(.4,1.4,.5,1) both; }
@keyframes dropIn { 0% { transform: translateY(-120px); } 100% { transform: translateY(0); } }

/* Star Hunt (whack-a-mole) */
.star-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;
  width: min(80vw, 340px); margin: 14px auto; flex: 1; align-content: center;
}
.star-cell {
  aspect-ratio: 1; border-radius: 22px; font-size: 34px;
  background: rgba(255, 255, 255, 0.5); border: 1px solid var(--card-brd);
  transition: transform .12s ease, background .2s ease;
}
.star-cell.lit { background: rgba(124, 92, 224, 0.18); animation: starPulse .5s ease-in-out infinite alternate; }
.star-cell.pop { transform: scale(.86); background: rgba(183, 229, 200, 0.7); }
@keyframes starPulse { from { transform: scale(1); } to { transform: scale(1.06); } }

/* Sequence (Simon-says) */
.seq-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 22px; }
.seq-pads {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px;
  width: min(74vw, 300px); aspect-ratio: 1;
}
.seq-pad {
  border-radius: 26px; border: none; background: var(--pad);
  opacity: .5; transition: opacity .12s ease, transform .12s ease;
  box-shadow: 0 6px 18px rgba(63, 48, 130, 0.18);
}
.seq-pad.lit { opacity: 1; transform: scale(1.05); box-shadow: 0 0 28px var(--pad); }
.seq-status { font-size: 16px; font-weight: 700; color: var(--ink-soft); }

/* ---- Rock · Paper · Scissors ------------------------------------------------
   Their unknowns along the top, the arena in the middle, your three hands
   waiting faintly along the bottom. Nothing is in a circle: the emoji are the
   whole picture, and a ring around them just made them look like buttons in a
   settings screen. */
.rps-arena {
  flex: 1; display: flex; flex-direction: column;
  padding: 4px 0; min-height: 0;
}
.rps-row {
  display: flex; justify-content: center; gap: 26px;
  padding: 6px 20px;
}
/* Their unknowns stay at the top, your hands sit at the bottom of the screen
   where your thumb already is, and the arena floats between them — the auto
   margins split whatever height is left over. */
.rps-row.mine { padding-bottom: 10px; }
.rps-row .rps-q, .rps-row .rps-pick {
  font-size: 44px; line-height: 1;
  background: none; border: none; padding: 0;
  opacity: .32; filter: saturate(.55);
  transition: opacity .25s ease, transform .18s ease, filter .25s ease;
}
.rps-row.them .rps-q { font-size: 36px; }
.rps-row .rps-pick:not(:disabled):active { transform: scale(.86); }
.rps-row .rps-pick:not(:disabled) { opacity: .5; }
/* The one you chose has left the row — it is up in the arena now. */
.rps-row .gone { opacity: 0; transform: translateY(-14px) scale(.6); }

.rps-mid {
  flex: 0 0 auto; height: 230px; margin: auto 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; position: relative;
}
.rps-hand {
  font-size: 62px; line-height: 1;
  filter: drop-shadow(0 4px 10px rgba(63, 48, 130, .22));
  transition: transform .2s ease;
}
/* Facing each other: theirs comes from the far side, so it is mirrored. */
.rps-hand.top { transform: scaleX(-1); }
.rps-hand.empty { font-size: 30px; opacity: .22; }
.rps-vs {
  font-size: 13px; font-weight: 800; color: var(--dim);
  text-transform: uppercase; letter-spacing: .12em;
  transition: opacity .2s ease;
}
.rps-vs.hush { opacity: 0; }

/* The clash. The winner travels into the loser and does what its shape does:
   the rock lands on top, the scissors snip, the paper swells over the rock. */
.rps-hand.win { z-index: 2; }
.rps-hand.lose { z-index: 1; animation: rpsCrush .7s ease both .18s; }
.rps-hand.tie { animation: rpsTie .6s ease both; }

.rps-hand.bot.win.rock { animation: rpsSlamUp .8s cubic-bezier(.4, 1.5, .5, 1) both; }
.rps-hand.top.win.rock { animation: rpsSlamDown .8s cubic-bezier(.4, 1.5, .5, 1) both; }
.rps-hand.bot.win.scissors { animation: rpsSnipUp .8s ease both; }
.rps-hand.top.win.scissors { animation: rpsSnipDown .8s ease both; }
.rps-hand.bot.win.paper { animation: rpsWrapUp .8s ease both; }
.rps-hand.top.win.paper { animation: rpsWrapDown .8s ease both; }

@keyframes rpsSlamUp {
  0% { transform: translateY(0); }
  30% { transform: translateY(14px) scale(.94); }
  55% { transform: translateY(-52px) scale(1.2) rotate(-14deg); }
  70% { transform: translateY(-42px) scale(1.24) rotate(4deg); }
  100% { transform: translateY(-40px) scale(1.14) rotate(0); }
}
@keyframes rpsSlamDown {
  0% { transform: scaleX(-1) translateY(0); }
  30% { transform: scaleX(-1) translateY(-14px) scale(.94); }
  55% { transform: scaleX(-1) translateY(52px) scale(1.2) rotate(14deg); }
  70% { transform: scaleX(-1) translateY(42px) scale(1.24) rotate(-4deg); }
  100% { transform: scaleX(-1) translateY(40px) scale(1.14) rotate(0); }
}
@keyframes rpsSnipUp {
  0% { transform: translateY(0) rotate(0); }
  40% { transform: translateY(-38px) rotate(-30deg) scale(1.1); }
  55% { transform: translateY(-38px) rotate(10deg) scale(1.12); }
  70% { transform: translateY(-38px) rotate(-22deg) scale(1.12); }
  100% { transform: translateY(-36px) rotate(-6deg) scale(1.08); }
}
@keyframes rpsSnipDown {
  0% { transform: scaleX(-1) translateY(0) rotate(0); }
  40% { transform: scaleX(-1) translateY(38px) rotate(30deg) scale(1.1); }
  55% { transform: scaleX(-1) translateY(38px) rotate(-10deg) scale(1.12); }
  70% { transform: scaleX(-1) translateY(38px) rotate(22deg) scale(1.12); }
  100% { transform: scaleX(-1) translateY(36px) rotate(6deg) scale(1.08); }
}
@keyframes rpsWrapUp {
  0% { transform: translateY(0) scale(1); }
  100% { transform: translateY(-40px) scale(1.7); }
}
@keyframes rpsWrapDown {
  0% { transform: scaleX(-1) translateY(0) scale(1); }
  100% { transform: scaleX(-1) translateY(40px) scale(1.7); }
}
@keyframes rpsCrush {
  0% { opacity: 1; }
  100% { transform: scale(.62) rotate(-12deg); opacity: .45; }
}
@keyframes rpsTie {
  0%, 100% { transform: translateY(0); }
  40% { transform: translateY(-10px) scale(1.06); }
}
/* The mirrored hand needs its own versions, or scaleX(-1) is thrown away. */
.rps-hand.top.lose { animation-name: rpsCrushFlip; }
.rps-hand.top.tie { animation-name: rpsTieFlip; }
@keyframes rpsCrushFlip {
  0% { transform: scaleX(-1); opacity: 1; }
  100% { transform: scaleX(-1) scale(.62) rotate(12deg); opacity: .45; }
}
@keyframes rpsTieFlip {
  0%, 100% { transform: scaleX(-1) translateY(0); }
  40% { transform: scaleX(-1) translateY(10px) scale(1.06); }
}

/* ---- shared duel palette ---------------------------------------------------
   The a/b games colour one player purple and the other pink, matching the
   🦊 / 🐧 tokens the scoreboards use. */
#game-layer { --pa: #7C5CE0; --pb: #D9558C; }

/* ---- the score ------------------------------------------------------------
   It used to be 13px of grey inside a pill, in the corner of the header, in
   every game. You cannot see what you are winning by out of the corner of your
   eye, which is the only time anyone looks at a score mid-game. So: no pill,
   no corner, and big enough to read while looking at something else. */
.score-hud {
  display: flex; align-items: flex-start; justify-content: center;
  gap: 18px; padding: 0 14px 2px; flex-wrap: nowrap;
}
.score-col {
  display: flex; flex-direction: column; align-items: center; gap: 0;
  min-width: 0; max-width: 46vw;
}
.score-who {
  font-size: 11.5px; font-weight: 800; color: var(--muted);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.score-big {
  font-size: 34px; font-weight: 900; line-height: 1.02; color: var(--ink);
  font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
  /* Tabular figures plus a floor on the width: a score rolling from 9 to 10
     must not shove the other player's number sideways. */
  min-width: 1.1em; text-align: center;
  transition: color .18s ease;
}
.score-cap {
  font-size: 10.5px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); margin-top: -1px;
}
.score-col.on .score-big, .score-col.on .score-who { color: var(--accent); }
/* The moment it goes up: swell, glow, settle. */
.score-big.bump { animation: scoreBump .42s cubic-bezier(.2, 1.5, .4, 1); }
@keyframes scoreBump {
  0% { transform: scale(1); }
  35% { transform: scale(1.34); color: var(--accent); text-shadow: 0 0 14px var(--accent); }
  100% { transform: scale(1); }
}
/* And what it went up by, rising out of the number. */
.score-col { position: relative; }
.score-pop {
  position: absolute; left: 50%; top: 10px; transform: translateX(-50%);
  font-size: 15px; font-weight: 900; color: var(--accent);
  pointer-events: none; white-space: nowrap;
  animation: scorePop .85s ease-out forwards;
}
@keyframes scorePop {
  0% { opacity: 0; transform: translate(-50%, 6px) scale(.7); }
  22% { opacity: 1; transform: translate(-50%, -6px) scale(1.1); }
  100% { opacity: 0; transform: translate(-50%, -34px) scale(1); }
}
/* The clock and other running commentary stay small — they are not the score. */
.score-side {
  font-size: 13px; font-weight: 800; color: var(--ink-soft);
  align-self: center;
}
@media (prefers-reduced-motion: reduce) {
  .score-big.bump { animation: none; }
  .score-pop { animation: scorePopStill .85s ease-out forwards; }
  @keyframes scorePopStill { 0% { opacity: 1; } 100% { opacity: 0; } }
}

/* Memory Duel: 15 pairs on a 5x6 board, plus a tint for who won each pair.
   The column count is set inline — the server owns the board size. */
.mem-grid.duo { max-width: 380px; gap: 6px; padding: 8px 14px; }
.mem-grid.duo .mem-card { aspect-ratio: 1; }
.mem-grid.duo .mem-card .mf, .mem-grid.duo .mem-card .mb { font-size: 24px; border-radius: 11px; }
.mem-grid.duo .mem-card .mf { font-size: 15px; }
/* Solid faces: a translucent tint over the room gradient washed the emoji out. */
.mem-card.own-a .mb { background: #DCD2F7; }
.mem-card.own-b .mb { background: #F6D3E2; }
/* Cards you may not tap are marked with a class, never `disabled` — the browser
   greys a disabled button's contents, which faded the emoji faces. */
.mem-card.dead { cursor: default; }

/* Dots & Boxes: the grid tracks are written inline (they depend on board size). */
.db-grid {
  display: grid; place-content: stretch;
  width: min(86vw, 340px); aspect-ratio: 1;
  margin: 6px auto; flex: 0 0 auto;
}
.db-dot { display: block; margin: auto; width: 7px; height: 7px; border-radius: 50%; background: rgba(43, 35, 80, 0.5); }
.db-edge {
  border: none; background: transparent; padding: 0;
  border-radius: 4px; margin: 2px;
}
.db-edge::after {
  content: ''; display: block; border-radius: 4px;
  background: rgba(255, 255, 255, 0.5);
  width: 100%; height: 100%;
}
.db-edge.h::after { height: 4px; margin-top: 5px; }
.db-edge.v::after { width: 4px; margin-left: 5px; }
.db-edge:disabled::after { opacity: 0; }
.db-edge:active::after { background: var(--accent); }
.db-line { display: block; border-radius: 4px; margin: 2px; }
.db-line.h { height: 5px; align-self: center; }
.db-line.v { width: 5px; justify-self: center; height: 100%; }
.db-line.own-a { background: var(--pa); }
.db-line.own-b { background: var(--pb); }
.db-line.fresh { animation: popIn .22s ease both; }
.db-box {
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-style: normal; border-radius: 6px;
}
.db-box.own-a { background: rgba(124, 92, 224, 0.24); animation: popIn .3s ease both; }
.db-box.own-b { background: rgba(217, 85, 140, 0.22); animation: popIn .3s ease both; }

/* Reversi: a felt-green 8x8 with dots marking the legal moves. */
.oth-board {
  display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px;
  width: min(92vw, 400px); aspect-ratio: 1; margin: 6px auto; flex: 0 0 auto;
  padding: 5px; border-radius: 12px;
  background: rgba(47, 159, 123, 0.5);
}
.oth-cell {
  position: relative; padding: 0; border: none;
  background: rgba(47, 159, 123, 0.35); border-radius: 4px;
  display: flex; align-items: center; justify-content: center;
}
.oth-cell.fresh { box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.8); }
.odisc {
  width: 78%; height: 78%; border-radius: 50%;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}
.odisc.a { background: radial-gradient(circle at 34% 28%, #6B5AA8, #2B2350); }
.odisc.b { background: radial-gradient(circle at 34% 28%, #FFFFFF, #D8D2EC); }
.odisc.turn { animation: popIn .25s ease both; }
.ohint {
  position: absolute; width: 28%; height: 28%; border-radius: 50%;
  background: rgba(255, 255, 255, 0.55);
}
.oth-cell:active .ohint { background: #fff; transform: scale(1.3); }

/* Snakes & Ladders: 10x10, numbered from the bottom-left and snaking upward. */
.snl-board {
  display: grid; grid-template-columns: repeat(10, 1fr); gap: 1px;
  width: min(94vw, 380px); aspect-ratio: 1; margin: 4px auto 18px; flex: 0 0 auto;
  padding: 4px; border-radius: 12px; background: rgba(255, 255, 255, 0.35);
  /* Tokens travel over the board and start just off its corner. */
  position: relative; overflow: visible;
}
/* The drawing and the tokens share the grid's box exactly — inset to the
   board's own padding — because the token positions are computed from square
   numbers in the same 0-100 space the SVG is drawn in. If these two boxes
   disagree, a token climbing a ladder walks beside it. */
.snl-tokens { position: absolute; inset: 4px; pointer-events: none; }
.snl-paint {
  position: absolute; inset: 4px; width: auto; height: auto;
  pointer-events: none; overflow: visible;
}
/* Ladders: two rails and their rungs, drawn end to end. */
.snl-rail { stroke: #C98A3E; stroke-width: .9; fill: none; stroke-linecap: round; }
.snl-rung { stroke: #E3AC63; stroke-width: .7; fill: none; stroke-linecap: round; }
/* Snakes: a bowed body with its head on the square that catches you. */
.snl-snake-body {
  stroke: #E0567F; stroke-width: 2.2; fill: none;
  stroke-linecap: round; opacity: .85;
}
.snl-snake-head { fill: #C93B65; }
/* The two roads out of a fork. Dashed, because they are a choice rather than a
   thing that happens to you. */
.snl-road {
  fill: none; stroke-width: 1.5; stroke-linecap: round;
  stroke-dasharray: 3 2.4; opacity: .8;
}
.snl-road.risky { stroke: #E0567F; }
.snl-road.safe { stroke: #35B98A; }

.snl-sq {
  position: relative; border-radius: 3px; font-style: normal;
  background: rgba(255, 255, 255, 0.5);
  display: flex; align-items: center; justify-content: center;
}
.snl-sq.alt { background: rgba(124, 92, 224, 0.12); }
.snl-sq b { position: absolute; top: 1px; left: 2px; font-size: 7px; font-weight: 700; color: var(--muted); }
/* The ends of things. A cell no longer has to say where a ladder goes — the
   ladder does — so it only marks that something starts or finishes here. */
.snl-sq.foot-up { box-shadow: inset 0 0 0 1.5px rgba(201, 138, 62, .75); }
.snl-sq.mouth { box-shadow: inset 0 0 0 1.5px rgba(224, 86, 127, .8); }
.snl-sq.tail { background: rgba(255, 255, 255, 0.72); }
.snl-sq.fork {
  box-shadow: inset 0 0 0 1.5px var(--accent);
  background: rgba(124, 92, 224, 0.2);
}
.snl-sq u.fork {
  text-decoration: none; font-style: normal; font-size: 10px; line-height: 1;
  color: var(--accent); font-weight: 900;
}
.snl-sq.fresh { box-shadow: inset 0 0 0 2px var(--accent); animation: glowPulse 1.4s ease-in-out 2; }
/* JS sets left/top as percentages of the grid; the transition travels. `left`
   is the token's centre, hence the translate — and every keyframe below has to
   carry it, or the token jumps sideways for the length of the animation. */
.snl-tok {
  position: absolute; font-size: 15px; font-style: normal; line-height: 1;
  transform: translateX(-50%);
  transition: left .15s linear, top .15s linear;
  filter: drop-shadow(0 1px 1px rgba(43, 35, 80, .35));
  will-change: left, top;
  z-index: 2;
}
.snl-tok.hop { animation: tokHop .16s ease; }
/* A snake, a ladder or a chosen road is walked in stages along the drawn line,
   so each leg is a short glide rather than one long teleport. */
.snl-tok.travel {
  transition: left .13s linear, top .13s linear;
  filter: drop-shadow(0 2px 5px rgba(43, 35, 80, .45));
}
@keyframes tokHop {
  0%, 100% { transform: translateX(-50%) translateY(0) scale(1); }
  50% { transform: translateX(-50%) translateY(-7px) scale(1.15); }
}
.snl-roll {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  font-size: 13.5px; font-weight: 700; color: var(--ink-soft);
  padding: 4px 20px; text-align: center; min-height: 34px;
}
.snl-roll.dim { color: var(--muted); font-weight: 600; }
.snl-die { font-size: 30px; line-height: 1; display: inline-block; animation: popIn .3s ease both; }
.snl-die.tumbling { animation: dieTumble .28s linear infinite; }
@keyframes dieTumble {
  0% { transform: rotate(0) scale(1); }
  50% { transform: rotate(180deg) scale(1.18); }
  100% { transform: rotate(360deg) scale(1); }
}
/* It comes to rest with a bounce rather than simply stopping. */
.snl-die.landed { animation: dieLand .38s cubic-bezier(.2, 1.6, .4, 1); }
@keyframes dieLand {
  0% { transform: rotate(-24deg) scale(1.3); }
  60% { transform: rotate(6deg) scale(.94); }
  100% { transform: rotate(0) scale(1); }
}
.snl-actions { display: flex; justify-content: center; gap: 10px; padding: 6px 20px 0; }
.snl-actions .btn:disabled, .snl-actions .btn-ghost:disabled { opacity: .45; }
.snl-actions.forked { align-items: stretch; }
/* The two roads. Each says where it lands, because "risky" without a
   destination is not enough to decide on. */
.snl-road-btn {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  flex: 1 1 0; max-width: 168px; padding: 8px 10px; line-height: 1.15;
}
.snl-road-btn span { font-size: 13.5px; font-weight: 800; }
.snl-road-btn small { font-size: 10.5px; font-weight: 700; opacity: .8; }
.snl-road-btn.risky { background: linear-gradient(135deg, #E0567F, #C93B65); }
.snl-road-btn.safe { border-color: #35B98A; color: #2C9B74; }
.snl-waiting { font-size: 13px; font-weight: 700; color: var(--muted); }


/* ---- Gem Rush + Gem Duel ---------------------------------------------------
   One grid style, three sizes: your solo board, your board in the duel, and
   the partner's board underneath it at a glance-able size. */
.gem-grid {
  display: grid; gap: 3px;
  width: min(92vw, 360px); margin: 6px auto; flex: 0 0 auto;
  padding: 5px; border-radius: 14px; background: rgba(255, 255, 255, 0.32);
}
/* The board you play on is dragged across, so the browser must not read a
   swipe over it as a scroll. */
.gem-grid.mine { touch-action: none; }
.gem {
  position: relative; overflow: hidden;
  aspect-ratio: 1; padding: 0; border: none; border-radius: 9px;
  background: rgba(255, 255, 255, 0.55);
  font-size: 22px; line-height: 1;
  transition: transform .12s ease, background .16s ease;
  /* The board is dragged, not scrolled. Without this a swipe across it pans
     the page on a phone and the swap never happens. */
  touch-action: none;
}
.gem.on {
  background: var(--accent); transform: scale(1.12);
  box-shadow: 0 0 0 2px var(--accent), 0 4px 14px var(--accent-dim);
}
.gem.pop { animation: gemPop .19s ease forwards; }
@keyframes gemPop {
  0% { transform: scale(1); }
  60% { transform: scale(1.35); filter: brightness(1.5); }
  100% { transform: scale(0); opacity: 0; }
}
/* Everything above a cleared cell has moved down a row, so it arrives from
   above rather than simply being there. */
.gem.fall { animation: gemFall .17s cubic-bezier(.3, 1.4, .5, 1); }
@keyframes gemFall {
  0% { transform: translateY(-42%); }
  100% { transform: translateY(0); }
}
/* A swap that matched nothing: lean in, bounce back. Silence read as a
   dropped tap. */
.gem.nope { animation: gemNope .3s ease; }
@keyframes gemNope {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-18%) rotate(-6deg); }
  75% { transform: translateX(18%) rotate(6deg); }
}
/* A powerup arriving. */
.gem.born { animation: gemBorn .34s cubic-bezier(.2, 1.6, .4, 1); }
@keyframes gemBorn {
  0% { transform: scale(.3) rotate(-40deg); filter: brightness(2.2); }
  100% { transform: scale(1) rotate(0); filter: none; }
}

/* ---- the three powerups ----------------------------------------------------
   Drawn in CSS over the fruit rather than replacing it, because you still need
   to read the colour to plan the match that fires it. Comet, Supernova and
   Constellation are our names for them — see docs/ip-risk.md. */
.gem.comet-row::before, .gem.comet-col::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  background: linear-gradient(var(--comet-angle, 90deg),
    transparent 0%, rgba(255, 255, 255, .95) 42%, rgba(255, 255, 255, .95) 58%, transparent 100%);
  opacity: .75;
}
.gem.comet-row { --comet-angle: 90deg; }
.gem.comet-col { --comet-angle: 0deg; }
.gem.comet-row, .gem.comet-col {
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .9);
}
.gem.supernova {
  background: radial-gradient(circle at 50% 50%, #FFF 0%, #FFE9A8 38%, var(--accent) 100%);
  box-shadow: 0 0 10px var(--accent-dim), inset 0 0 0 2px rgba(255, 255, 255, .95);
  animation: gemNova 1.6s ease-in-out infinite;
}
@keyframes gemNova {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.28); }
}
.gem.constellation {
  box-shadow: inset 0 0 0 2px #FFF, 0 0 8px var(--accent-dim);
}
.gem.constellation::after {
  content: ''; position: absolute; inset: 14%; border-radius: 50%;
  border: 2px dashed rgba(255, 255, 255, .95);
  animation: gemSpin 4s linear infinite;
}
@keyframes gemSpin { to { transform: rotate(360deg); } }

.gem-grid.mini { width: min(56vw, 210px); gap: 2px; padding: 3px; margin: 2px auto; }
.gem-grid.mini .gem { font-size: 12px; border-radius: 5px; }
.gem-grid.mini .gem.constellation::after { inset: 18%; border-width: 1px; }
.gem.dead { cursor: default; pointer-events: none; }
.gem-theirs {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 4px 0 2px; opacity: .82;
}
.gem-theirs .gt-name {
  font-size: 11.5px; font-weight: 800; color: var(--dim);
  max-width: 70vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---- Pet Jump --------------------------------------------------------------
   The pet runs on the spot, the world slides past. Cactuses want jumping over,
   birds want staying put. */
.jump-stage {
  position: relative; flex: 1 0 auto; min-height: 260px;
  margin: 8px 0; overflow: hidden; touch-action: none;
  background: linear-gradient(180deg, rgba(255,255,255,.28) 0%, rgba(255,255,255,.06) 100%);
  border-top: 1px solid var(--card-brd); border-bottom: 1px solid var(--card-brd);
}
.jump-ground {
  position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
  background: rgba(63, 48, 130, 0.35);
}
/* Centred on the 20% mark with a margin rather than a translate, because the
   loop owns `transform` — that is where the jump lives. */
.jump-pet {
  position: absolute; left: 20%; bottom: 4px; width: 74px; margin-left: -37px;
  will-change: transform; z-index: 2;
}
.jump-pet svg { width: 100%; height: auto; display: block; }
.jump-pet.hurt { animation: jumpHurt .5s ease both; }
@keyframes jumpHurt {
  0%, 100% { filter: none; }
  50% { filter: saturate(2.4) hue-rotate(-30deg); transform: translateY(6px) rotate(-14deg); }
}
.jump-ob {
  position: absolute; bottom: 0; font-size: 26px; line-height: 1;
  font-style: normal; z-index: 1; will-change: left;
}
.jump-ob.bird { animation: birdFlap .34s ease-in-out infinite alternate; }
@keyframes birdFlap { from { transform: translateY(0); } to { transform: translateY(-7px); } }

/* ---- Ping Pong -------------------------------------------------------------
   Your baseline is always the near one, whichever role you drew. */
.pong-table {
  position: relative; width: min(92vw, 380px); aspect-ratio: 3 / 4.2;
  margin: 8px auto; flex: 0 0 auto; touch-action: none;
  border-radius: 16px; overflow: hidden;
  background: linear-gradient(180deg, rgba(94, 139, 224, .28), rgba(124, 92, 224, .18));
  border: 2px solid rgba(255, 255, 255, 0.5);
}
.pong-net {
  position: absolute; left: 6%; right: 6%; top: 50%; height: 2px;
  background: repeating-linear-gradient(90deg, rgba(255,255,255,.8) 0 10px, transparent 10px 20px);
}
.pong-pad {
  position: absolute; height: 10px; border-radius: 999px;
  transform: translateX(-50%);
  transition: left .05s linear, width .3s ease;
}
.pong-pad.you { bottom: 3.4%; background: var(--pa); box-shadow: 0 0 14px var(--pa); }
.pong-pad.them { top: 3.4%; background: var(--pb); box-shadow: 0 0 14px var(--pb); }
.pong-ball {
  position: absolute; width: 14px; height: 14px; border-radius: 50%;
  background: #fff; box-shadow: 0 0 12px rgba(255, 255, 255, .9);
  transform: translate(-50%, 50%);
  transition: left .04s linear, bottom .04s linear;
}

/* ---- Mind Meld -------------------------------------------------------------- */
.meld-dots { display: flex; justify-content: center; gap: 7px; padding: 8px 0 2px; }
.meld-dots .md {
  width: 10px; height: 10px; border-radius: 50%;
  background: rgba(43, 35, 80, 0.16);
}
.meld-dots .md.hit { background: var(--good); box-shadow: 0 0 8px var(--good); }
.meld-dots .md.miss { background: var(--bad); opacity: .55; }
.meld-mid {
  flex: 1; min-height: 150px;
  display: flex; align-items: center; justify-content: center; gap: 22px;
  padding: 10px 0;
}
.meld-face {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  font-size: 52px; line-height: 1;
}
.meld-face span {
  font-size: 12px; font-weight: 800; color: var(--dim);
  max-width: 34vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.meld-mid.met .meld-face { animation: popIn .4s ease both; }
.meld-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  width: min(84vw, 320px); margin: 0 auto 4px;
}
.meld-card {
  aspect-ratio: 1; border-radius: 20px; font-size: 34px;
  background: rgba(255, 255, 255, 0.6); border: 1px solid var(--card-brd);
  transition: transform .12s ease, background .2s ease;
}
.meld-card:active:not(:disabled) { transform: scale(.9); }
.meld-card.on { background: var(--accent-dim); border-color: var(--accent); }
.meld-card:disabled:not(.on) { opacity: .38; }

/* ---- Reaction Duel ---------------------------------------------------------- */
.react-pad {
  flex: 1; min-height: 220px; margin: 10px 20px 4px;
  border-radius: 30px; border: 2px solid transparent;
  font-size: 68px; line-height: 1;
  transition: background .1s ease, transform .1s ease;
}
.react-pad.wait { background: rgba(217, 85, 140, 0.2); border-color: rgba(217, 85, 140, .5); }
.react-pad.go {
  background: rgba(47, 159, 123, 0.55); border-color: var(--good);
  box-shadow: 0 0 40px rgba(47, 159, 123, .6); transform: scale(1.02);
}
.react-pad.result { background: var(--chip-bg); border-color: var(--card-brd); }
.react-pad:active:not(:disabled) { transform: scale(.98); }

/* ---- Hot Potato ------------------------------------------------------------- */
.bomb-fuse {
  height: 8px; border-radius: 999px; margin: 6px 22px 0;
  background: rgba(43, 35, 80, 0.14); overflow: hidden;
}
.bomb-fuse i {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, #E8B54A, #D9558C);
  transition: width .3s linear;
}
.bomb-arena {
  flex: 1; min-height: 240px;
  display: flex; flex-direction: column; align-items: center; justify-content: space-between;
  padding: 18px 20px 8px;
}
.bomb-arena .bomb-side {
  font-size: 12.5px; font-weight: 800; color: var(--dim);
  max-width: 70vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bomb {
  border: none; background: none; padding: 0;
  font-size: 76px; line-height: 1;
  /* It creeps toward whoever is holding it, and gets more agitated as the fuse
     burns down. */
  transition: transform .35s cubic-bezier(.4, 1.4, .5, 1);
  animation: bombShake calc(.7s - var(--heat, 0) * .45s) ease-in-out infinite;
}
.bomb-arena.here .bomb { transform: translateY(46px) scale(1.12); }
.bomb-arena.there .bomb { transform: translateY(-46px) scale(.86); opacity: .7; animation: none; }
.bomb-arena.boom .bomb { animation: popIn .35s ease both; transform: scale(1.5); }
@keyframes bombShake {
  0%, 100% { rotate: -5deg; }
  50% { rotate: 5deg; }
}

/* ---------- full-screen gallery (fridge, arcade, diary) ---------- */

#gallery {
  position: fixed; inset: 0; z-index: 70;
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, #F2EDFD 0%, #E2D8F8 55%, #CDBFF3 100%);
  opacity: 0; transition: opacity .22s ease;
}
#gallery.open { opacity: 1; }
.gal-head {
  display: flex; align-items: center; gap: 12px; flex: 0 0 auto;
  padding: 14px 18px 12px;
  padding-top: max(14px, env(safe-area-inset-top));
  border-bottom: 1px solid var(--card-brd);
}
.gal-close {
  width: 38px; height: 38px; border-radius: 50%; flex: 0 0 auto;
  background: var(--chip-bg); border: 1px solid var(--card-brd);
  color: var(--ink-soft); font-size: 15px; font-weight: 700;
}
.gal-titles { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.gal-titles b { font-size: 17px; font-weight: 800; color: var(--ink); }
.gal-titles span {
  font-size: 12px; color: var(--muted); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gal-body {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 14px 18px calc(24px + env(safe-area-inset-bottom));
}
.gal-label {
  font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
  font-weight: 700; color: var(--dim); margin: 18px 2px 10px;
}
.gal-label:first-child { margin-top: 2px; }
/* The arcade has two shelves — alone and together — each split into moods. */
.gal-shelf {
  font-size: 17px; font-weight: 800; color: var(--ink);
  margin: 20px 2px 2px; padding-bottom: 8px;
  border-bottom: 1px solid var(--card-brd);
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
}
/* "Your partner is away" is one fact about the whole shelf, so it is said once
   here rather than repeated on every card. */
.gal-shelf .shelf-note {
  font-size: 12px; font-weight: 700; color: var(--faint);
  letter-spacing: 0; line-height: 1.35;
}
.gal-shelf:first-child { margin-top: 2px; }
.gal-shelf + .gal-label { margin-top: 12px; }

/* The app-gallery grid: big art, name under it. */
.gal-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 10px;
}
.gal-card {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 14px 8px 12px; border-radius: 20px;
  background: var(--card); border: 1px solid var(--card-brd);
  box-shadow: 0 6px 18px rgba(63, 48, 130, 0.08);
  transition: transform .14s ease, box-shadow .2s ease;
  text-align: center;
}
.gal-card:active { transform: scale(.95); box-shadow: 0 2px 8px rgba(63, 48, 130, 0.1); }
.gal-card .gc-art {
  font-size: 34px; line-height: 1.1;
  filter: drop-shadow(0 2px 3px rgba(63, 48, 130, .2));
}
.gal-card .gc-name {
  font-size: 13px; font-weight: 800; color: var(--ink); line-height: 1.25;
  /* Two lines rather than an ellipsis — "Snakes & Ladders" deserves its name. */
  max-width: 100%; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.gal-card .gc-sub {
  font-size: 11px; color: var(--muted); font-weight: 600; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.gal-card .gc-badge {
  position: absolute; top: 7px; right: 8px;
  background: var(--accent); color: var(--accent-ink);
  border-radius: 999px; padding: 2px 8px;
  font-size: 11px; font-weight: 800;
}
/* Tapping one of these nudges the partner instead of starting a game. A dashed
   edge says "not quite ready" without shouting; the shelf heading above already
   said why, and the sub-line is back to describing the game. */
.gal-card.ping { border-color: var(--accent-dim); border-style: dashed; }

.gal-empty {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 34px 20px; text-align: center;
  font-size: 13.5px; color: var(--muted); font-weight: 600;
}
.gal-empty { font-size: 40px; }
.gal-empty span { font-size: 13.5px; }
