/* The public site: what index.html (Home), song.html and collection.html share — the sticky site
   bar, the breadcrumb, song cards and their covers, shelves, chips, ad slots and the footer. Not
   loaded by the four practice pages, which keep their own shell (layout.css, header.css). Its
   TOKENS are in tokens.css, in their own :root block: the Unlock dialog is drawn on the sheet
   pages too, and this sheet's page shell must never be.

   It sits ON TOP of tokens.css and header.css, so the pink, the rounded face, the 38px control
   and the one-gradient-per-screen rule are the app's own. Dark only, on the client's call
   (2026-09-28): the practice pages are dark, a glowing tile needs a dark sky (DESIGN.md §2), and
   the catalogue in front of them is the same material. The design record is docs/mockups/. */

/* tokens.css pins the app to one un-scrollable dark screen with no text selection — right for
   a player, wrong for a catalogue. */
html, body { height: auto; overflow: visible; }
body {
  overflow-x: hidden; user-select: auto; -webkit-user-select: auto;
  font-size: 15px; line-height: 1.5; color: var(--text);
  background: var(--page);
  min-height: 100vh;
}
a { color: inherit; }
h1, h2, h3 { margin: 0; letter-spacing: -.02em; line-height: 1.15; }
p { margin: 0; }
.wrap { width: min(100% - 2 * var(--gutter), var(--wrap)); margin-inline: auto; }

/* Chrome icons are files in assets/icons/ with their colour baked in (CLAUDE.md, "Icons are
   files"); `.i` only sizes them. The paw and the cat's head are the exception and stay inline
   (ui.js's <defs>): they are recoloured per context — pink in a level meter, dark ink on the
   Get the app button, cream as the cat — which is the note shapes' exemption, for the same reason. */
.i { width: 16px; height: 16px; }
.paw-i { width: 14px; height: 14px; flex: none; fill: currentColor; }
.fur { fill: var(--cat); }

/* header.css's control language, re-pointed at the site's tokens. */
.load { color: var(--control-ink); border-color: var(--control-line); background: var(--control-bg); text-decoration: none; }
.load:hover { background: var(--control-hover); border-color: var(--control-line); }
.play { text-decoration: none; }
.play img, .load img { width: 15px; height: 15px; }
/* select too: without it a sort menu or the footer's language picker drew the browser's blue ring. */
/* .chips too: a scrolling row is focusable, and landing on #chips gave it the browser's blue ring. */
a:focus-visible, button:focus-visible, select:focus-visible, .chips:focus-visible { outline: 3px solid #ffd2e5; outline-offset: 2px; }
.eyebrow {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--accent);
}

/* ---- the site bar --------------------------------------------------------
   The player's <header> floats and fades; this one is a browse page's bar, so it STAYS —
   sticky, always opaque. Same blur as the player bar, so clicking through feels like one app. */
header.site-bar {
  position: sticky; top: 0; z-index: 40; height: auto; opacity: 1;
  display: flex; align-items: center; gap: 18px;
  padding: 11px max(var(--gutter), calc((100% - var(--wrap)) / 2));
  background: var(--bar); border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; flex: none; }
.brand-tile {
  width: 36px; height: 36px; border-radius: 11px; display: grid; place-items: center;
  background: linear-gradient(135deg, #ff8fc0, #ff4f9a); box-shadow: 0 6px 18px rgba(255,79,154,.35);
}
.brand-tile img { width: 24px; height: 23px; }
.brand-word { font-size: 19px; font-weight: 800; letter-spacing: -.02em; color: var(--text); }
.brand-word b { color: var(--accent); font-weight: 800; }
.site-nav { display: flex; gap: 4px; }
.site-nav a {
  height: 34px; display: inline-flex; align-items: center; padding: 0 13px; border-radius: 999px;
  text-decoration: none; font-size: 14px; font-weight: 700; color: var(--muted);
  transition: background 160ms ease, color 160ms ease;
}
.site-nav a:hover { background: var(--control-hover); color: var(--text); }
.site-nav a[aria-current] { background: var(--tint); color: var(--text); }
.search {
  flex: 1; min-width: 0; max-width: 360px; margin-left: auto;
  display: flex; align-items: center; gap: 8px; height: var(--control-h); padding: 0 12px;
  border-radius: var(--control-r); border: 1px solid var(--control-line); color: var(--dim);
  background: var(--field-bg); box-shadow: var(--field-shadow);   /* a field, so sunk (tokens.css) */
}
.search:focus-within { outline: 3px solid #ffd2e5; outline-offset: 2px; border-color: transparent; }
.search input {
  flex: 1; min-width: 0; width: 100%; border: 0; outline: 0; background: none; color: var(--text);
  font-size: 16px;   /* 16, not 15: anything smaller makes iOS zoom the page on focus */
}
.search input::placeholder { color: var(--dim); }
.bar-actions { display: flex; align-items: center; gap: 8px; flex: none; }
.bar-actions .play { min-width: 0; }
.login span { max-width: 14ch; overflow: hidden; text-overflow: ellipsis; }
/* Signed in, the account's cat on a round chip of its avatar's tint (profile.css .p-avatar): Mochi
   on pink, the Maine Coon on gold. A little bigger than the icon it replaces, so the heart reads. */
.login .avatar-cat { flex: none; width: 26px; height: 26px; margin-left: -5px; border-radius: 50%; background: var(--tint); }
.login .avatar-cat:has(.coon-cat) { background: color-mix(in srgb, var(--gold) 22%, transparent); }
.avatar-cat .blush { fill: #ff8fb8; opacity: .6; }
.avatar-cat .shine { fill: #fff; }
.avatar-heart { fill: var(--pink); }
.site-bar:has(.account-menu:popover-open) .login { background: var(--control-hover); }

/* The account menu (ui.js): a popover of exactly two rows, under the cat button. It sits in the
   top layer, so it is placed in viewport pixels by ui.js; the UA's centring is undone here. Sign
   out is muted, not red: nothing is lost by it. */
.account-menu {
  position: fixed; inset: auto; margin: 0; min-width: 196px; padding: 6px;
  border: 1px solid rgba(255,145,191,.22); border-radius: 14px;
  background: var(--surface); color: var(--text); box-shadow: 0 16px 40px var(--shadow);
}
.account-menu > * {
  width: 100%; min-height: 44px; display: flex; align-items: center; gap: 10px; padding: 0 12px;
  border: 0; border-radius: 10px; background: none; cursor: pointer; text-decoration: none;
  font: inherit; font-size: 14.5px; font-weight: 700; color: var(--control-ink);
}
.account-menu > :hover { background: var(--control-hover); color: var(--text); }
.account-menu .sign-out { color: var(--muted); }
.account-menu .i { width: 17px; height: 17px; }

/* ---- the breadcrumb: the song page and the collection page --------------- */
.crumbs { margin-top: 22px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 13.5px; color: var(--dim); }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--text); text-decoration: underline; }
.crumbs [aria-current] { color: var(--text); font-weight: 700; }

/* ---- home: group chips + shelves --------------------------------------- */
.chips { display: flex; gap: 8px; overflow-x: auto; padding: 36px 2px 10px; scrollbar-width: none; scroll-margin-top: 36px; }
.chip {
  flex: none; height: 36px; display: inline-flex; align-items: center; gap: 7px; padding: 0 15px;
  border-radius: 999px; border: 1px solid var(--control-line); background: var(--control-bg);
  font-size: 14px; font-weight: 700; color: var(--control-ink); text-decoration: none;
  transition: background 160ms ease;
}
.chip:hover { background: var(--control-hover); }
.chip[aria-current] { background: var(--tint); border-color: transparent; color: var(--text); }

.shelf { margin-top: 44px; scroll-margin-top: 80px; }
.shelf-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 4px; }
.shelf-head h2 { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; font-size: var(--text-xl); font-weight: 800; }
.shelf-head h2 .paw-i { width: 18px; height: 18px; color: var(--pink); }
.shelf-head p { margin-top: 4px; font-size: 14px; color: var(--muted); }
.shelf-head .tag { font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
                   padding: 3px 8px; border-radius: 999px; background: var(--control-hover); color: var(--dim); }
.see-all { flex: none; display: inline-flex; align-items: center; gap: 4px; font-size: 14px; font-weight: 700;
           color: var(--accent); text-decoration: none; padding: 8px 0; }
.see-all:hover { text-decoration: underline; text-underline-offset: 3px; }
.see-all img { width: 14px; height: 14px; }

/* A shelf is a row that scrolls sideways — the Spotify/Netflix shelf, which every catalogue
   visitor already knows how to read. The top padding is room for the cat behind each card
   to peek out on hover; the negative margin gives the room back so the layout does not move. */
.shelf-row {
  display: grid; grid-auto-flow: column; grid-auto-columns: 188px; gap: 16px;
  overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior-x: contain;
  padding: 24px 4px 14px; margin: -8px -4px 0; scrollbar-width: thin;
  scrollbar-color: var(--control-line) transparent;
}

/* ---- the song card -------------------------------------------------------
   The cover is a scene for the song's GENRE with one sticker for the song itself (covers.js):
   no album art to license, and a new song costs one word of data. No price badge: every song is
   free to play, and an unlock is the song page's business, not the shelf's. The cat hiding behind
   the card pokes its ears up on hover. */
.song-card { position: relative; display: block; text-decoration: none; scroll-snap-align: start; border-radius: 16px; }
.song-card .card-cat {
  overflow: visible;
  position: absolute; z-index: 0; top: 18px; left: 50%; width: 58px; height: 55px;
  transform: translateX(-50%); transition: transform 260ms cubic-bezier(.22,1.4,.5,1);
}
.card-body {
  position: relative; z-index: 1; height: 100%; border-radius: 16px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line);
  transition: transform 200ms ease, border-color 200ms ease, box-shadow 200ms ease;
}
.song-card:hover .card-body, .song-card:focus-visible .card-body {
  transform: translateY(-3px); border-color: color-mix(in srgb, var(--pink) 45%, transparent);
  box-shadow: 0 16px 34px var(--shadow);
}
.song-card:hover .card-cat, .song-card:focus-visible .card-cat, .song-card.peeking .card-cat { transform: translate(-50%, -34px); }
.cover { position: relative; aspect-ratio: 16 / 10; background: #1a0f17; }
/* Cover colours: the genre sets the hue (--gh, from covers.js) and these four tones are all a
   scene draws with, besides its sticker — so a shelf is one family. --gh-dark exists for the
   one hue that muddies when darkened (Kids' butter goes olive, so it warms to honey). */
.cover-art {
  --g-bg: oklch(.36 .08 var(--gh-dark, var(--gh)));
  --g-mid: oklch(.48 .11 var(--gh-dark, var(--gh)));
  --g-deep: oklch(.24 .06 var(--gh-dark, var(--gh)));
}
.cover-art .sticker path, .cover-art .sticker circle, .cover-art .sticker ellipse, .cover-art .sticker use {
  stroke: #fff; stroke-width: 2.2; stroke-linejoin: round; paint-order: stroke;
}
.cover-art { display: block; width: 100%; height: 100%; }
.card-meta { padding: 11px 12px 12px; }
.card-meta h3 { font-size: 15px; font-weight: 800; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.card-meta .by { font-size: 13px; color: var(--muted); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.card-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 9px; }
.card-foot .modes { display: flex; gap: 5px; }
.card-foot .modes img { width: 14px; height: 14px; }

/* Difficulty in paws, because it is a cat's app — paws only; the label carries the word. */
.level { display: inline-flex; align-items: center; gap: 2px; }
.level .paw-i { width: 12px; height: 12px; color: var(--pink); }

/* Best Effortless accuracy, ON the cover rather than under the meta, so a played card is not a
   line taller than its neighbours. Brand pink: blue and green mean channels 1 and 2. */
.acc { position: absolute; left: 0; right: 0; bottom: 0; height: 4px; background: rgba(20,10,16,.55); }
.acc i { display: block; height: 100%; width: calc(var(--acc) * 100%); background: var(--pink); border-radius: 0 2px 2px 0; }
.acc-chip {
  position: absolute; right: 7px; bottom: 10px; height: 20px; padding: 0 7px; border-radius: 999px;
  display: inline-flex; align-items: center; font-size: 11px; font-weight: 800; font-variant-numeric: tabular-nums;
  color: #fff; background: rgba(20,10,16,.78); border: 1px solid rgba(255,255,255,.14);
}

/* ---- a collection's cover: three song covers fanned (ui.js fanCovers) ------
   The collection page's hero and Profile's Saved collections. */
.coll-stack { position: relative; display: block; aspect-ratio: 16 / 10; }
.coll-card {
  position: absolute; left: 14%; top: 18%; width: 72%; aspect-ratio: 16 / 10; border-radius: 18px; overflow: hidden;
  border: 1px solid var(--line); box-shadow: 0 22px 50px var(--shadow); background: #1a0f17;
}
.coll-card .cover-art { position: absolute; inset: 0; }
.coll-card.left { transform: translate(-21%, -4%) rotate(-9deg) scale(.86); }
.coll-card.right { transform: translate(21%, -4%) rotate(9deg) scale(.86); }
.coll-card.front { z-index: 2; }
/* As a card in a list: smaller corners (it is a thumbnail now), the name under it, and the front
   cover alone at rest. Pointed at or tabbed to, the other two fan out from behind it and the
   front lifts — the collection opening like a hand of cards (the client, 2026-10-21; the fan was
   always out, and a row of fans read as clutter). Tucked, the two sit inside the front's edges,
   so they are hidden behind it rather than faded. Reduced motion: the fan, without the move. */
.coll-tile { display: block; text-decoration: none; scroll-snap-align: start; border-radius: 16px; }
.coll-tile .coll-card { border-radius: 10px; box-shadow: 0 10px 22px var(--shadow); }
.coll-tile .coll-card:not(.front) { transform: scale(.86); }
.coll-tile:is(:hover, :focus-visible) .coll-card.left { transform: translate(-21%, -4%) rotate(-9deg) scale(.86); }
.coll-tile:is(:hover, :focus-visible) .coll-card.right { transform: translate(21%, -4%) rotate(9deg) scale(.86); }
.coll-tile:is(:hover, :focus-visible) .coll-card.front { transform: translateY(-4px); border-color: color-mix(in srgb, var(--pink) 45%, transparent); }
@media (prefers-reduced-motion: no-preference) {
  .coll-tile .coll-card { transition: transform 320ms cubic-bezier(.3, 1.35, .5, 1), border-color 200ms ease; }
  .coll-tile .coll-card.right { transition-delay: 40ms; }
}
.coll-tile-meta { display: grid; padding: 4px 4px 0; }
.coll-tile-meta b { font-size: 15px; font-weight: 800; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.coll-tile-meta small { font-size: 13px; color: var(--muted); }

/* ---- Golden Paws: the paw in --gold (tokens.css) ---------------------------- */
.paw-i.gold { color: var(--gold); }

/* ---- an empty list (ui.js emptyState): a line, one way forward, and the two cats ----
   The cats sit on the card's right, centred, the card's padding round them, eyes on their list's
   thing (cats.js). They sat on the card's left, on its bottom edge, and the client read that as
   cats squashed against the edge (2026-10-21); a tile in their thing's colour, round them, was
   dropped the same day. The SVG still clips (overflow), for the climb below. */
.empty {
  display: flex; align-items: center; gap: clamp(14px, 3vw, 28px); padding: clamp(16px, 2.4vw, 22px);
  border-radius: 18px; border: 1px solid var(--line); background: var(--surface); overflow: hidden;
}
.empty .two-cats { flex: none; width: clamp(120px, 16vw, 176px); height: auto; overflow: hidden; }
.empty-text { flex: 1; min-width: 0; display: grid; justify-items: start; gap: 4px; }
.empty-text h3 { font-size: 17px; font-weight: 800; }
.empty-text p { font-size: 14.5px; color: var(--muted); max-width: 52ch; text-wrap: pretty; }
.empty-cta { margin-top: 10px; }
.two-cats .ink-line { stroke-width: 1.2; }
.two-cats .looks { transform: translate(var(--lx, 0), var(--ly, 0)); transition: opacity 150ms ease; }
.two-cats .lids { transition: opacity 150ms ease; }
.two-cats .lids + .looks { opacity: 0; }   /* a happy or napping cat: lids down, eyes under them */
/* Each list's thing over the cats (cats.js TWO_CATS), in its own colour: five cards in a row read
   as five places, even scrolled past. The colours are the site's own — pink, the Coon's gold — and
   the note channels' lilac, blue, green and orange. Only the things differ: the card is every
   other card's --surface (Profile's .p-card, Requests' rows), so the page is one set of cards.
   Tried on 2026-10-21 and dropped: a wash in each thing's colour; Home's pink gifts band, which
   made "nothing here yet" the loudest thing on Profile; and --surface-2, which on this page read
   as a hole, darker than the cards around it. */
.empty[data-mood="curious"] { --prop: var(--pink); }
.empty[data-mood="nap"] { --prop: #aea6fe; }
.empty[data-mood="hopeful"] { --prop: var(--gold); }
.empty[data-mood="whisper"] { --prop: #62a9ff; }
.empty[data-mood="excited"] { --prop: #71d99a; }
.empty[data-mood="puzzled"] { --prop: #f49a52; }
.two-cats .prop { fill: var(--prop); font-weight: 900; }
.two-cats .prop-line { fill: none; stroke: var(--prop); stroke-width: 3; stroke-linecap: round; }
.two-cats .prop-flap { fill: none; stroke: #1d4f8f; stroke-width: 1.4; stroke-linejoin: round; }
.two-cats .prop-seal { fill: var(--pink); }
/* Its one move, when the button is pointed at or tabbed to — the thing that fills the list, so the
   cause is plain: the bookmark swings, the key turns, the seal's heart beats, the notes hop, the ?
   tips. The eyes stay on the thing; only the napping Coon's change, because its line says so
   ("Save a collection to wake it up"): it wakes, and the z's float off. It was the whole card's
   hover, turning every cat's eyes to the button, and nobody could tell what had set them off.
   Reduced motion keeps the cats exactly as they rest. */
.two-cats .prop-move, .two-cats .prop-alt { transform-box: fill-box; transform-origin: 50% 0; transition: transform 300ms cubic-bezier(.3, 1.5, .5, 1), opacity 200ms ease; }
.empty[data-mood="hopeful"] .prop-move { transform-origin: 0 50%; }
.empty[data-mood="whisper"] .prop-move, .empty[data-mood="puzzled"] .prop-move { transform-origin: 50% 100%; }
@media (prefers-reduced-motion: no-preference) {
  .empty[data-mood="curious"]:has(.empty-cta:is(:hover, :focus-visible)) .prop-move { transform: rotate(12deg); }
  .empty[data-mood="nap"]:has(.empty-cta:is(:hover, :focus-visible)) .prop-move { transform: translate(4px, -6px); opacity: 0; }
  .empty[data-mood="nap"]:has(.empty-cta:is(:hover, :focus-visible)) .lids { opacity: 0; }
  .empty[data-mood="nap"]:has(.empty-cta:is(:hover, :focus-visible)) .looks { opacity: 1; }
  .empty[data-mood="hopeful"]:has(.empty-cta:is(:hover, :focus-visible)) .prop-move { transform: rotate(-18deg); }
  .empty[data-mood="whisper"]:has(.empty-cta:is(:hover, :focus-visible)) .prop-move { transform: scale(1.4); }
  .empty[data-mood="excited"]:has(.empty-cta:is(:hover, :focus-visible)) .prop-move { transform: translateY(-4px); }
  .empty[data-mood="excited"]:has(.empty-cta:is(:hover, :focus-visible)) .prop-alt { transform: translateY(-3px); transition-delay: 60ms; }
  .empty[data-mood="puzzled"]:has(.empty-cta:is(:hover, :focus-visible)) .prop-move { transform: rotate(-12deg); }
}
/* Arriving (ui.js peekWhenSeen, which sets data-peek): the heads wait below the SVG's bottom edge,
   which clips them, and climb up the first time the card is seen — Mochi first and the Coon a beat after,
   as over the footer — then their thing pops in. cat.css's cat-in, the site's own climb; fill-box,
   so 110% is each head's own height. Picked on 2026-10-21 (docs/mockups/profile-empty-cats.html). */
@media (prefers-reduced-motion: no-preference) {
  .empty[data-peek] .two-cats :is(.peek, .prop) { transform-box: fill-box; }
  .empty[data-peek] .two-cats .prop { transform-origin: 50% 100%; }
  .empty[data-peek="wait"] .peek { transform: translateY(110%); }
  .empty[data-peek="wait"] .prop { transform: scale(0); opacity: 0; }
  .empty[data-peek="up"] .peek { --cat-from: 110%; animation: cat-in 650ms cubic-bezier(.3, 1.45, .5, 1) both; }
  .empty[data-peek="up"] .peek-c { animation-delay: 140ms; }
  .empty[data-peek="up"] .prop { animation: prop-pop 420ms cubic-bezier(.3, 1.5, .5, 1) 560ms both; }
  /* The napping Coon breathes, as Home's sleeping one does (cat.css nap-bob, nap-z) and on its
     3.2 s: a slow bob, and the z's drifting up one after another. The page's one loop, because
     asleep is the one state where moving is the point. Waking (above) fades the z's away. */
  .empty[data-mood="nap"] .bob { transform-box: fill-box; transform-origin: 50% 100%; animation: coon-doze 3.2s ease-in-out infinite; }
  .empty[data-mood="nap"] .prop-move text { animation: z-drift 3.2s ease-in-out infinite both; }
  .empty[data-mood="nap"] .prop-move text:nth-child(2) { animation-delay: .5s; }
  .empty[data-mood="nap"] .prop-move text:nth-child(3) { animation-delay: 1s; }
}
@keyframes prop-pop { from { transform: scale(0); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes coon-doze { 50% { transform: translateY(-1.2px) scale(1.02, 1.04); } }
@keyframes z-drift { 0% { opacity: 0; transform: translateY(2px); } 20%, 60% { opacity: 1; transform: none; } 85%, 100% { opacity: 0; transform: translateY(-3px); } }

/* ---- ad slots ------------------------------------------------------------
   Space is RESERVED at the ad's own size before it loads, or the page jumps when AdSense
   fills it. Placed between shelves — never beside a Buy/Start button (AdSense policy on
   accidental clicks), never in the hero, never inside a practice view. Everyone sees it: with Plus
   gone there is no ad-free tier. */
.ad-slot { margin-top: 44px; display: grid; justify-items: center; gap: 6px; }
.ad-box {
  width: 100%; max-width: 728px; min-height: 90px; display: grid; place-content: center; text-align: center;
  border-radius: 12px; border: 1.5px dashed var(--ad-line); background: var(--surface-2);
  color: var(--dim); font-size: 12px; font-weight: 700; letter-spacing: .04em;
}
.ad-box b { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }

/* ---- toast: one line of news (ui.js toast()), never focused ---------------- */
.toast {
  /* A popover in the top layer (ui.js toast): the UA's centring, border and canvas colours undone. */
  inset: auto; margin: 0; overflow: visible;
  position: fixed; z-index: 60; left: 50%; bottom: max(20px, env(safe-area-inset-bottom));
  max-width: min(440px, calc(100% - 32px)); padding: 12px 18px; border-radius: 14px;
  background: var(--surface); border: 1px solid color-mix(in srgb, var(--pink) 45%, transparent);
  box-shadow: 0 16px 40px var(--shadow); font-size: 14px; font-weight: 700; color: var(--text);
  transform: translate(-50%, 12px); opacity: 0; pointer-events: none;
  transition: opacity 180ms ease, transform 220ms ease;
}
.toast[data-show] { opacity: 1; transform: translate(-50%, 0); }

/* ---- footer: the big wordmark (ui.js siteFoot) -----------------------------------
   The content, then an oversized MiaoScore cut off by the page's bottom edge with two cats
   peeking over it. Clipped on BOTH axes: the word is wider than a phone, and its letters run past
   .foot-word's bottom — clip-path hides their paint but not their layout, so with overflow-x alone
   the page scrolled ~67px past the footer onto a bare strip of page. */
.site-foot { margin-top: 96px; background: var(--foot); position: relative; overflow: clip; }
.foot-grid { display: grid; grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr)); gap: 32px; padding: 48px 0 32px; }
.foot-brand p { color: var(--muted); font-size: 14px; margin: 14px 0 18px; max-width: 34ch; }
.stores { display: flex; flex-wrap: wrap; gap: 8px; }
.store {
  display: inline-flex; align-items: center; gap: 9px; height: 44px; padding: 0 14px 0 11px; border-radius: 11px;
  background: #0d070b; border: 1px solid rgba(255,255,255,.2); color: #fff; text-decoration: none;
}
.store .i { width: 20px; height: 20px; }
.store span { display: grid; line-height: 1.1; font-size: 15px; font-weight: 800; }
.store small { font-size: 10px; font-weight: 600; color: #c4a3b6; }
.foot-col h3 { font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--dim); margin-bottom: 12px; }
.foot-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.foot-col a { display: inline-block; padding: 4px 0; font-size: 14px; color: var(--control-ink); text-decoration: none; }
.foot-col a:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.foot-base {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px;
  padding: 18px 0 26px; border-top: 1px solid var(--line); font-size: 12.5px; color: var(--dim);
}
.foot-base a { color: var(--muted); }
.foot-base .lang { margin-left: auto; }
.socials { display: flex; gap: 6px; }
.socials a { width: 36px; height: 36px; display: grid; place-items: center; border-radius: 10px;
             border: 1px solid var(--control-line); background: var(--control-bg); }
.socials a:hover { background: var(--control-hover); }
.socials .i { width: 17px; height: 17px; }
.lang { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px; border-radius: 9px;
        border: 1px solid var(--control-line); background: var(--control-bg); color: var(--control-ink); font-weight: 700; }
.lang select { background: none; border: 0; color: inherit; font-weight: 700; cursor: pointer; }
.lang option { color: #26141e; }
/* Cut off by the page's bottom edge and by nothing else: clip-path keeps the cats' ears, which
   rise above the box, while the letters' lower half runs off the page. pointer-events: none, or
   an ear over the base row would take its clicks. */
.foot-word { position: relative; z-index: 1; height: clamp(90px, 13vw, 190px); clip-path: inset(-120px 0 0 0); pointer-events: none; }
/* Centred WITHOUT a transform: a translate makes the span its own stacking context, and then the
   cats' z-index -1 paints above the letters' clipped background instead of under it. The box runs
   a quarter past each side because a line too long for its box is START-aligned — on a phone the
   word only ran off the right — and the clip-path above trims it back to the page. */
.foot-word > span {
  position: absolute; left: -25%; right: -25%; top: 0; text-align: center; white-space: nowrap;
  font-weight: 800; letter-spacing: -.05em; font-size: clamp(120px, 19vw, 280px); line-height: .9;
  /* OPAQUE, fading into the footer: a cat hides behind a letter, so the letter must hide it. */
  background: linear-gradient(180deg, #5c2346, #2a1422 50%, #170d14 85%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
/* Each o paints its OWN clipped gradient. Positioned (the cat's containing block), it gets its own
   layer in WebKit, and WebKit's background-clip: text leaves a layered child's text out of the
   parent's clip — on iOS Safari both o's were bare transparent text, i.e. missing. An inline-block
   exactly one line tall (it inherits line-height .9) has the span's box, so the same gradient
   lands on the same colours. */
.foot-word b {
  position: relative; display: inline-block; font-weight: inherit;
  background: inherit; -webkit-background-clip: text; background-clip: text;
}
/* Inside an o, behind it (z-index -1 paints under the letter's clipped background), its lower
   third or so covered by the o's top. In em, so it rides the wordmark's clamp; `top` is read off
   the system font's x-height, measured from the line's top (the inline-block's) — the inline b's
   box started .14em higher, so these were .2em and .14em. The Maine Coon is bigger (it is a Maine
   Coon), so it sits higher in its o to show the same face. */
.foot-peek { position: absolute; z-index: -1; width: .26em; left: 50%; translate: -50% 0; top: .06em; }
.foot-peek svg { display: block; width: 100%; height: auto; overflow: visible; }
.foot-peek.coon { width: .36em; top: 0; }
/* Peekaboo, on a loop. Each cat pops up from behind its o — stretched on the way up, squashed as it
   lands — leans toward the other, wiggles, blinks twice, crouches and ducks. Mochi goes first and
   the Maine Coon half a second after, so it reads as two cats playing rather than one animation
   twice, and for ~2s of every 8 both are hiding. `both` holds the hidden first frame through the
   Maine Coon's delay, and the loop is paused there until the wordmark is seen (ui.js mountSite).
   The clip-path is the hiding place: it cuts each cat at its own RESTING bottom edge, which lies
   inside the o's top stroke, so a ducking cat goes behind the letter instead of showing through
   the o's counter. Every keyframe spells out all three functions, so they interpolate as such. */
.foot-peek { --lean: 9deg; clip-path: inset(-60% -40% 0 -40%); }
.foot-peek.coon { --lean: -9deg; }   /* each leans toward the other: Mochi right, the Maine Coon left */
.foot-peek svg { transform-origin: 50% 100%; animation: peekaboo 8s ease-in-out infinite both paused; }
.foot-peek .eye { transform-box: fill-box; transform-origin: center; animation: blink 8s linear infinite both paused; }
.foot-peek.coon svg, .foot-peek.coon .eye { animation-delay: .5s; }
.foot-word[data-seen] .foot-peek svg, .foot-word[data-seen] .foot-peek .eye { animation-play-state: running; }
@keyframes peekaboo {
  0%, 66%, 100% { transform: translateY(115%) rotate(0deg) scale(1, 1); animation-timing-function: cubic-bezier(.2, .8, .3, 1); }
  5%  { transform: translateY(-12%) rotate(0deg) scale(.92, 1.1); }
  9%  { transform: translateY(0) rotate(0deg) scale(1.08, .92); }
  12%, 24% { transform: translateY(0) rotate(0deg) scale(1, 1); }
  30%, 38% { transform: translateY(0) rotate(var(--lean)) scale(1, 1); }
  42% { transform: translateY(0) rotate(calc(var(--lean) * -.5)) scale(1, 1); }
  46%, 56% { transform: translateY(0) rotate(0deg) scale(1, 1); }
  60% { transform: translateY(6%) rotate(0deg) scale(1.06, .94); animation-timing-function: cubic-bezier(.5, 0, .9, .4); }
}
@keyframes blink {
  0%, 49%, 51%, 52.5%, 54.5%, 100% { transform: scaleY(1); }
  50%, 53.5% { transform: scaleY(.1); }
}
.coon-cat { --cat: #f6bd4f; }
.coon-cat .coon-ruff { fill: #ffe3a3; }
.coon-cat .coon-stripe { fill: none; stroke: #d4842a; stroke-width: 1.3; stroke-linecap: round; }
.coon-cat .coon-wisp { fill: none; stroke: #ffe3a3; stroke-width: .8; stroke-linecap: round; }


@media (max-width: 960px) {
  .site-nav { display: none; }
  .foot-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .foot-brand { grid-column: 1 / -1; }
}
@media (max-width: 640px) {
  header.site-bar { gap: 10px; }
  .site-bar .brand-word { display: none; }
  .search { padding: 0 10px; }
  .bar-actions .load span, .bar-actions .play span { display: none; }
  .bar-actions .load, .bar-actions .play { width: var(--control-h); padding: 0; justify-content: center; }
  .shelf-row { grid-auto-columns: 158px; gap: 12px; }
  /* Beside the line, the cats' window left the text a column too narrow for its button: under it. */
  .empty { flex-direction: column; align-items: stretch; }
  .empty .two-cats { align-self: center; width: 140px; }
  .ad-box { max-width: 320px; min-height: 100px; }
  .foot-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .foot-base .lang { margin-left: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .card-cat, .card-body, .toast, .two-cats .looks, .two-cats .lids { transition: none; }
  .foot-peek svg, .foot-peek .eye { animation: none; }   /* the cats simply stay up */
}
