/* ============================================
   CuteMods.com – 3D Animated Website Styles
   Author: CuteMods Design System
   ============================================ */

/* ===== SELF-HOSTED FONTS =====
   The three latin files the site actually renders with, served from our own
   origin instead of fonts.googleapis.com. That removes one render-blocking
   cross-origin stylesheet (18.5 KB, 49 @font-face rules) and ~200 KB of font
   bytes: Nunito and Fredoka are variable, so one file each covers every weight
   the CSS asks for, where five + three static instances used to be fetched.

   The declarations are Google's own latin blocks (same unicode-range, same
   font-display: swap), only src: points at /assets/fonts. Weight coverage is
   unchanged for anything in range; out-of-range weights keep synthesizing
   exactly as before (Poppins 900 from the 800 face, Fredoka 800 from 700).
   Text outside the latin range - emoji in the logo and card backs, or any
   future non-latin copy - was never covered by these three families and still
   falls through to system-ui, sans-serif as it did with Google's URL.

   Rebuild them with:  python tools/_mkassets.py   (dev-only, not deployed)
*/
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url('../fonts/nunito-latin-var.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Fredoka';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/fredoka-latin-var.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('../fonts/poppins-latin-800.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ===== CSS CUSTOM PROPERTIES ===== */
:root {
  --primary:        #e8003d;
  --primary-dark:   #b30030;
  --primary-light:  #ff4d8d;
  /* Small bold red labels that sit on a blush pill (kicker, "View all").
     --primary is only 3.4-4.4:1 on those backgrounds, below the 4.5:1 AA floor
     for this text size. This token is a deeper red in the light theme and a
     brighter red in the dark theme so the label clears AA in both, while
     staying on the same hue as the brand mark (not a redesign). */
  --label-red:      #b30030;
  --accent:         #ff6b9d;
  --accent-soft:    #ffb3cc;
  --blush:          #fff0f5;
  --blush-mid:      #ffd6e7;
  --bg:             #fff5f8;
  --white:          #ffffff;
  --text-dark:      #1a0a2e;
  --text-mid:       #4a3060;
  --text-muted:     #9a7aaa;
  --footer-bg:      #1a0a2e;
  --gold:           #f4c72f;
  --shadow-sm:      0 2px 8px rgba(232,0,61,.12);
  --shadow-md:      0 8px 24px rgba(232,0,61,.18);
  --shadow-lg:      0 16px 48px rgba(232,0,61,.22);
  --radius-sm:      10px;
  --radius-md:      16px;
  --radius-lg:      24px;
  --radius-pill:    999px;
  --nav-h:          76px;
  --safe-t:         env(safe-area-inset-top, 0px);
  --transition:     cubic-bezier(.34,1.56,.64,1);
  --ease:           cubic-bezier(.4,0,.2,1);
  --nav-indicator:  linear-gradient(90deg, var(--primary), var(--accent));
}

[data-theme="dark"] {
  --bg:             #0d0618;
  --blush:          #1a0a2e;
  --blush-mid:      #2d1b4e;
  --white:          #1e0f35;
  --text-dark:      #fff0f5;
  --text-mid:       #d4b8f0;
  --text-muted:     #9a7aaa;
  /* Deeper reds fail on the dark blush pills; brighten here instead */
  --label-red:      #ff5c96;
  /* Footer keeps a dark panel in both themes; --text-dark flips to near-white */
  --footer-bg:      #150a26;
  --shadow-sm:      0 2px 8px rgba(0,0,0,.4);
  --shadow-md:      0 8px 24px rgba(0,0,0,.5);
  --shadow-lg:      0 16px 48px rgba(0,0,0,.6);
  /* Brighter indicator so it stays readable against the dark bar */
  --nav-indicator:  linear-gradient(90deg, #ff2d63, var(--accent-soft));
}

/* ===== RESET & BASE ===== */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
  font-size: 16px;
}

body {
  font-family: 'Nunito', 'Poppins', system-ui, sans-serif;
  background: var(--bg);
  color: var(--text-dark);
  overflow-x: hidden;
  line-height: 1.6;
  transition: background .4s var(--ease), color .4s var(--ease);
}

a {
  text-decoration: none;
  color: inherit;
}

ul { list-style: none; }

img, svg { display: block; max-width: 100%; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

/* ===== SAKURA PETAL FIELD (every page) =====
   One fixed layer over the whole viewport. script.js §1 creates it and appends
   it to <body>, so it exists on every page that loads that script - all of
   them, plus any page generated later - without a line of markup in any of
   them. The effect is a component of the shared layout, not of a screen.

   Stacking. z-index 20 is above every content element on the site (the highest
   is the search bar at 10) and below all the chrome a visitor has to see and
   hit: the mobile drawer at 999, the navbar at 1000, the Legal dropdown at
   1001, the page curtain at 3000, toasts at 9999. Strictly *under* the page
   content is not available on this site: every section paints an opaque
   background, and that is exactly what made the old fixed canvas field
   invisible. pointer-events: none, overflow: hidden, and inset pushed past the
   viewport, so the field never takes a click, never slices a petal at the edge
   of the wind lean, and cannot add a horizontal scrollbar.

   One petal is three boxes, because each movement needs its own clock: the
   drop, the sway and the spin run at unrelated periods, so no petal repeats a
   path and the field never looks mechanical.
     .sk-fall   the drop, linear, the long one
     .sk-drift  the sway: ease-in-out + alternate leans a petal left, then
                right, then left again while it is still on the way down
     .sk-petal  the spin, and with it a scaleX squeeze at the half turn, which
                is what makes a flat shape read as a petal rolling edge-on
   Only transform and opacity animate, so the compositor carries the field and
   no frame touches layout. There is no will-change on purpose: a running
   transform animation is composited already, and 90 permanent layers would
   cost more memory than the frames they would save. */
.sakura-field {
  --y0: -84px;                              /* above the visible top         */
  --y1: 900px;                              /* script.js: layer height + 66  */
  --wind: clamp(9px, 1.2vw, 20px);          /* how far the field leans over  */
  position: fixed;
  inset: -24px;
  z-index: 20;
  overflow: hidden;
  pointer-events: none;
  animation: petalWind 21s ease-in-out infinite alternate;
}

.sk-fall {
  position: absolute;
  top: 0;
  left: var(--x);
  animation: petalFall var(--dur) linear var(--delay) infinite;
}

/* a span with no content is 0x0 and inline, and an inline box ignores
   transform: it has to be a block for the sway to move it */
.sk-drift {
  display: block;
  animation: petalDrift var(--sway-dur) ease-in-out var(--delay) infinite alternate;
}

.sk-petal {
  position: absolute;
  width: var(--size);
  height: calc(var(--size) * 1.16);
  top: calc(var(--size) * -.58);
  left: calc(var(--size) * -.5);
  border-radius: 52% 52% 46% 46% / 64% 64% 36% 36%;
  background: linear-gradient(155deg, var(--c1) 0%, var(--c1) 24%, var(--c2) 100%);
  box-shadow: 0 1px 3px rgba(190, 40, 90, .16);
  animation: petalSpin var(--spin-dur) linear var(--delay) infinite;
}

@keyframes petalFall {
  from { transform: translateY(var(--y0)); opacity: 0; }
  12%  { opacity: var(--op); }
  86%  { opacity: var(--op); }
  to   { transform: translateY(var(--y1)); opacity: 0; }
}

@keyframes petalDrift {
  from { transform: translateX(calc(var(--sway) * -1)); }
  to   { transform: translateX(var(--sway)); }
}

@keyframes petalSpin {
  from { transform: rotate(var(--r0)) scaleX(1); }
  50%  { transform: rotate(calc(var(--r0) + var(--turn) / 2)) scaleX(var(--edge)); }
  to   { transform: rotate(calc(var(--r0) + var(--turn))) scaleX(1); }
}

/* one shared, slow lean: the petals drift as a field, not as 30 individuals */
@keyframes petalWind {
  from { transform: translateX(calc(var(--wind) * -1)); }
  to   { transform: translateX(var(--wind)); }
}

/* the field is fixed, so it is never scrolled out of view: what it can do
   instead is stand down while nobody is looking (script.js §1 sets the class
   on visibilitychange) */
.sakura-field.is-paused,
.sakura-field.is-paused span { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .sakura-field { display: none; }   /* script.js §1 builds no field at all */
}

/* ===== NAVBAR ===== */
.navbar {
  position: sticky;
  top: 0;
  z-index: 1000;
  background: linear-gradient(180deg, #ffffff 0%, #fff1f6 100%);
  border-bottom: 1px solid rgba(232,0,61,.10);
  height: calc(var(--nav-h) + var(--safe-t));
  padding-top: var(--safe-t);
  box-shadow: 0 2px 16px rgba(232,0,61,.08);
  transition: background .4s var(--ease), box-shadow .3s var(--ease);
  overflow: hidden;
}

[data-theme="dark"] .navbar {
  background: linear-gradient(180deg, #130824 0%, #1b0c30 100%);
  border-bottom-color: rgba(232,0,61,.2);
}

/* set by script.js §4 when the page leaves the top; a class instead of an
   inline style so the shadow is written once per crossing, not per event */
.navbar.is-scrolled {
  box-shadow: 0 4px 24px rgba(232,0,61,.2);
}

/* the 1px mark script.js §4 watches to set that class. It has to be absolute,
   not fixed: a fixed mark never leaves the viewport, so it could not report a
   scroll position. Body is unpositioned, so its containing block is the
   document itself and it scrolls away at 10px on every page. */
.nav-scroll-sentinel {
  position: absolute;
  top: 10px;
  left: 0;
  width: 1px;
  height: 1px;
  visibility: hidden;
  pointer-events: none;
}

/* ── Sakura corner decorations ── */
.nav-blossom {
  position: absolute;
  top: var(--safe-t);
  height: calc(100% - var(--safe-t));
  width: clamp(140px, 16vw, 230px);
  pointer-events: none;
  z-index: 0;
  opacity: .85;
  transition: opacity .4s var(--ease);
}

.nav-blossom--left  { left: 0; }
.nav-blossom--right { right: 0; transform: scaleX(-1); }

[data-theme="dark"] .nav-blossom { opacity: .3; }

.nav-container {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 24px;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  gap: 0;
  position: relative;
  z-index: 1;
  /* The least room the three groups - logo, links, actions - need between
     them to still read as three groups. script.js §3a adds two of these to
     the measured width of the groups and only then decides on the hamburger,
     so the switch comes from what the navbar actually contains instead of a
     guessed viewport width. Raise it and the drawer arrives earlier. */
  --nav-min-gap: 16px;
}

/* ── Logo ── */
.nav-logo {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  margin-right: 32px;
  transition: transform .3s var(--transition);
}

.nav-logo:hover { transform: scale(1.03); }

.logo-avatar {
  width: 50px; height: 50px;
  border-radius: 14px;
  overflow: hidden;
  border: 2px solid rgba(232,0,61,.25);
  background: #ffd9e6;
  box-shadow: 0 3px 10px rgba(232,0,61,.2);
  flex-shrink: 0;
  animation: logoFloat 3s ease-in-out infinite;
}

@keyframes logoFloat {
  0%, 100% { transform: translateY(0); }
  50%       { transform: translateY(-3px); }
}

.avatar-inner { width: 100%; height: 100%; }

/* Mascot artwork (profile-icon.svg is 1126x944, so cover-crop into the square) */
.logo-img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 22%;
}

/* The <picture> that offers the AVIF mascot in front of the PNG is inline by
   default, and a line box cannot answer the width/height:100% the img above is
   sized with. The wrapper takes over the box .avatar-inner already owned, so
   the mark reads, sits and cover-crops exactly where it did before. */
.logo-pic { display: block; width: 100%; height: 100%; }

.logo-text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}

/* "CuteMods" must stay on a single line, domain below */
.logo-name {
  display: flex;
  align-items: baseline;
  line-height: 1.1;
  white-space: nowrap;
}

.logo-cute {
  font-size: 1.4rem;
  font-weight: 900;
  color: var(--text-dark);
  font-family: 'Poppins', sans-serif;
}

.logo-mods {
  font-size: 1.4rem;
  font-weight: 900;
  color: var(--primary);
  font-family: 'Poppins', sans-serif;
}

.logo-cute, .logo-mods { display: inline; }

.logo-domain {
  font-size: .68rem;
  color: var(--text-muted);
  font-weight: 600;
  letter-spacing: .02em;
  display: block;
}

/* Nav Links */
/* ── Nav Links (center group) ── */
.nav-links {
  display: flex;
  align-items: center;
  gap: 2px;
  margin: 0 auto;               /* centred between logo and actions */
  /* Never squeeze the tiles to make them fit: §3a measures this row's real
     width to decide between the bar and the drawer, and a shrunk row would
     report a width the design never asked for. */
  flex-shrink: 0;
}

.nav-item {
  display: flex;
  flex-direction: column;       /* icon ON TOP, label BELOW */
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 7px 18px;
  border-radius: 14px;
  font-size: .78rem;
  font-weight: 800;
  color: var(--text-dark);
  transition: color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
  cursor: pointer;
  position: relative;           /* anchors the selection indicator */
  min-width: 62px;
  text-align: center;
}

/* SVG icon inside nav-item */
.nav-item .nav-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  line-height: 1;
  flex-shrink: 0;
}

.nav-label {
  line-height: 1;
  letter-spacing: .01em;
}

.nav-item:hover {
  color: var(--primary);
  background: rgba(232,0,61,.07);
  transform: translateY(-1px);
}

/* Active = red rounded tile with white icon+text */
.nav-item.active {
  background: var(--primary);
  color: #ffffff;
  border-radius: 14px;
  box-shadow: 0 4px 16px rgba(232,0,61,.45);
  padding: 7px 20px;
}

.nav-item.active .nav-icon svg { fill: #ffffff; }

.nav-item.active:hover {
  background: var(--primary-dark);
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(232,0,61,.55);
}

/* Selection indicator: a coloured bar that grows under the active item.
   Sits below the red tile, on the bar's own background, so it reads
   clearly in both themes. Hover previews it at a shorter width. */
.nav-item::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -9px;
  width: 0;
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--nav-indicator);
  box-shadow: 0 1px 6px rgba(232,0,61,.45);
  transform: translateX(-50%);
  transition: width .32s var(--ease), opacity .32s var(--ease);
  opacity: .55;
  pointer-events: none;
}

.nav-item:hover::after  { width: 34%; opacity: .6; }
.nav-item.active::after { width: 62%; opacity: 1; }

/* ── Navbar group with a dropdown (Legal) ── */
/* The bar clips its own overflow, so the panel cannot be parked inside it:
   it is fixed to the viewport and starts exactly at the bar's lower edge.
   script.js §3b measures the group's centre into --drop-x; the 50% fallback
   keeps the panel reachable with scripting off. */
.nav-group { position: relative; display: flex; }

/* transparent hit-area that bridges the dead strip between the item and the
   panel, so the pointer never leaves the group on its way down */
.nav-group::before {
  content: '';
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 22px;
}

/* touch and keyboard toggle: a tap on the Legal label has to keep navigating
   to the Privacy Policy it points at, so opening needs its own control */
.nav-drop-btn {
  position: absolute;
  top: 5px;
  right: 2px;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-muted);
  border-radius: 50%;
  cursor: pointer;
  transition: color .2s var(--ease), background .2s var(--ease);
}

.nav-drop-btn:hover { color: var(--primary); background: rgba(232,0,61,.07); }
.nav-drop-btn svg { fill: currentColor; transition: transform .25s var(--ease); }

/* the active tile is solid red, so the caret would sink into it */
.nav-item.active ~ .nav-drop-btn { color: rgba(255,255,255,.82); }
.nav-item.active ~ .nav-drop-btn:hover { color: #fff; background: rgba(255,255,255,.16); }

.nav-group:hover > .nav-drop-btn svg,
.nav-group:focus-within > .nav-drop-btn svg,
.nav-group.is-open > .nav-drop-btn svg { transform: rotate(180deg); }

.nav-drop {
  position: fixed;
  top: calc(var(--nav-h) + var(--safe-t));
  left: var(--drop-x, 50%);
  min-width: 236px;
  display: grid;
  gap: 2px;
  padding: 8px;
  background: var(--white);
  border: 1px solid rgba(232,0,61,.12);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  box-shadow: var(--shadow-md);
  z-index: 1001;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 8px);
  transition: opacity .22s var(--ease), transform .22s var(--ease), visibility .22s;
}

/* hover on desktop, focus for the keyboard, is-open for the tap toggle.
   Scoped to the bar layout: in drawer mode (§3a gives the navbar the
   is-compact class when its own content no longer fits) the same class would
   re-apply translate(-50%) to a panel that is a row list in the flow.
   :where() keeps this exactly as specific as the plain selectors below it. */
:where(.navbar:not(.is-compact)) .nav-group:hover > .nav-drop,
:where(.navbar:not(.is-compact)) .nav-group:focus-within > .nav-drop,
:where(.navbar:not(.is-compact)) .nav-group.is-open > .nav-drop {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

.nav-drop-link {
  display: flex;
  align-items: center;
  padding: 10px 14px;
  border-radius: 12px;
  font-size: .84rem;
  font-weight: 800;
  color: var(--text-dark);
  white-space: nowrap;
  transition: background .2s var(--ease), color .2s var(--ease);
}

.nav-drop-link:hover { background: var(--bg); color: var(--primary); }
.nav-drop-link[aria-current] { color: var(--primary); }

/* ── Nav Actions (right side) ── */
.nav-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

/* Theme toggle: solid red pill with sun + moon icons */
.theme-toggle {
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
}

.toggle-track {
  display: flex;
  align-items: center;
  gap: 0;
  height: 32px;
  padding: 4px 9px;
  background: var(--primary);
  border: none;
  border-radius: var(--radius-pill);
  transition: background .4s var(--ease), box-shadow .3s var(--ease);
  box-shadow: 0 3px 10px rgba(232,0,61,.35);
  min-width: 62px;
  justify-content: space-between;
  position: relative;
  color: #ffffff;
}

[data-theme="dark"] .toggle-track {
  background: #2d1b4e;
  box-shadow: 0 3px 10px rgba(0,0,0,.45);
}

.toggle-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  transition: opacity .3s, transform .3s;
  z-index: 1;
}

.toggle-sun  { opacity: 1; }
.toggle-moon { opacity: .8; }

[data-theme="dark"] .toggle-sun  { opacity: .5; }
[data-theme="dark"] .toggle-moon { opacity: 1; color: #ffd166; }

/* Circular icon button – Search */
.nav-search-btn {
  width: 36px; height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1.5px solid rgba(232,0,61,.15);
  background: #ffffff;
  border-radius: 50%;
  cursor: pointer;
  color: var(--primary);
  transition: all .25s var(--ease);
  flex-shrink: 0;
}

.nav-search-btn:hover {
  background: var(--primary);
  border-color: var(--primary);
  color: white;
  transform: scale(1.1);
  box-shadow: 0 4px 12px rgba(232,0,61,.35);
}

[data-theme="dark"] .nav-search-btn {
  background: #2d1b4e;
  border-color: rgba(255,77,141,.2);
  color: #ffb3cc;
}

/* ── Hamburger (mobile only) ── */
.hamburger {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
  border-radius: 8px;
  transition: background .2s;
}

.hamburger:hover { background: rgba(232,0,61,.08); }

.hamburger span {
  display: block;
  width: 22px; height: 2px;
  background: var(--text-dark);
  border-radius: 2px;
  transition: all .3s var(--ease);
}

.hamburger.open span:nth-child(1) { transform: rotate(45deg) translate(4px,4px); }
.hamburger.open span:nth-child(2) { opacity: 0; transform: scaleX(0); }
.hamburger.open span:nth-child(3) { transform: rotate(-45deg) translate(4px,-4px); }

/* ===== HERO (Full-Screen Design) ===== */
/* Fonts for hero display text */
:root {
  --display-font: "Fredoka", "Nunito", ui-rounded, system-serif;
}

.hero-fullscreen {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: #e1051c;
  height: clamp(460px, 58vh, 600px);
  min-height: 420px;
}

.hero-fullscreen .hero-pic {
  /* the banner's <picture> takes the place the image held in the stacking and
     the layout: #scene below still positions itself against this box */
  position: absolute;
  inset: 0;
  display: block;
  z-index: 1;
}

.hero-fullscreen #scene {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  max-width: none; max-height: none;
  display: block;
  z-index: 1;
  object-fit: cover;
  object-position: center center;
}

.hero-fullscreen::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 48% 52% at 30% 62%, rgba(34,8,52,.5) 0%, rgba(46,10,60,.22) 60%, rgba(60,10,60,0) 100%),
    linear-gradient(180deg, rgba(255,120,160,.22) 0%, rgba(255,120,160,0) 22%, rgba(255,120,160,0) 78%, rgba(255,120,160,.28) 100%);
}

.hero-fullscreen::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  box-shadow:
    inset 0 0 90px 10px rgba(255,150,185,.55),
    inset 0 0 0 3px rgba(255,214,228,.55);
}

#deco {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  z-index: 1;
  pointer-events: none;
}

/* Text UI overlay */
.hero-fullscreen .ui {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: clamp(14px, 2vw, 22px);
  padding:
    clamp(40px, 6vw, 80px)
    clamp(18px, 5vw, 72px)
    clamp(40px, 6vw, 80px)
    clamp(18px, 15vw, 230px);
}

.hero-fullscreen .badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 30px;
  padding: 0 14px 0 11px;
  border-radius: 999px;
  font: 700 clamp(11.5px, 1.2vw, 13.5px)/1 var(--body-font, 'Nunito', sans-serif);
  color: #fff;
  background: rgba(200,6,38,.92);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.55), 0 6px 16px -6px rgba(90,0,25,.7);
}

.hero-fullscreen .badge svg {
  width: 13px; height: 13px;
  color: #ffd1e0;
}

.hero-fullscreen h1 {
  margin: 0;
  font-family: var(--display-font);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -.005em;
  color: #fff;
  font-size: clamp(40px, 4.9vw, 66px);
  text-shadow:
    0 2px 0 rgba(150,10,60,.5),
    0 0 18px rgba(255,120,170,.65),
    0 6px 22px rgba(40,0,50,.55);
}

.hero-fullscreen h1 b {
  font-weight: 700;
  color: #ffd0e0;
}

.hero-fullscreen h1 i {
  font-style: normal;
  font-weight: 500;
}

.hero-fullscreen .stroke {
  display: block;
  width: clamp(210px, 38vw, 480px);
  height: auto;
  margin-top: -.1em;
  filter: drop-shadow(0 3px 6px rgba(80,0,30,.5));
}

.hero-fullscreen .lead {
  margin: 0;
  max-width: 15em;
  font: 800 clamp(18px, 2vw, 26px)/1.3 var(--body-font, 'Nunito', sans-serif);
  color: #fff;
  text-shadow: 0 1px 2px rgba(20,0,30,.8), 0 3px 16px rgba(30,0,40,.75);
}

.hero-fullscreen .chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 12px;
  margin: clamp(2px, 1vw, 10px) 0 0;
  padding: 0;
  list-style: none;
}

.hero-fullscreen .chips li {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: clamp(38px, 4vw, 46px);
  padding: 0 clamp(14px, 1.8vw, 20px) 0 10px;
  border-radius: 999px;
  font: 700 clamp(13px, 1.3vw, 16px)/1 var(--body-font, 'Nunito', sans-serif);
  color: #fff;
  background: rgba(96,6,34,.80);
  -webkit-backdrop-filter: blur(10px) saturate(1.3);
  backdrop-filter: blur(10px) saturate(1.3);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.28), 0 8px 18px -8px rgba(50,0,25,.7);
  transition: transform .2s var(--ease), box-shadow .2s var(--ease);
}

.hero-fullscreen .chips li:hover {
  transform: translateY(-2px);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.4), 0 12px 24px -8px rgba(50,0,25,.8);
}

.hero-fullscreen .chips svg {
  width: clamp(20px, 2vw, 24px);
  height: clamp(20px, 2vw, 24px);
  flex: none;
  padding: 3px;
  border-radius: 7px;
  background: #e1051c;
  color: #fff;
}

.hero-fullscreen .chips li:nth-child(2) svg { background: #ff5a1f; }
.hero-fullscreen .chips li:nth-child(3) svg { background: #d6246e; }

/* The SVG breathe/glow rules that used to sit here animated the hand-traced
   hero (`.breathe`, `.glow`, `.glow.b`). That artwork is now the single
   hero.webp banner and no page, script or generated template carries those
   classes any more, so the two keyframe sets and their compositing hints were
   dead weight on every stylesheet parse. */

/* ===== SEARCH BAR (shared by the home page and the catalog pages) ===== */
/* 70% of the screen on every device (zero inner padding keeps the bar
   itself at exactly 70%).
   Everything below is driven by the --sb-* tokens, which are single fluid
   expressions, so the bar grows continuously from phone to 4K instead of
   hitting a clamp ceiling at ~500px and freezing. */
.search-section,
.page-search {
  --sb-h:    clamp(46px, 2.2vw + 34px, 76px);   /* bar height: 46 -> 76 */
  --sb-padY: clamp(5px, .5vw + 3px, 9px);
  --sb-padL: clamp(12px, 1vw + 8px, 22px);
  --sb-gap:  clamp(6px, .8vw + 3px, 11px);
  --sb-btn:  calc(var(--sb-h) - var(--sb-padY) * 2);
  position: relative;
  z-index: 10;
  width: 70%;
  max-width: 100%;
  padding: 0;
  background: transparent;
}

/* the home bar overlaps the hero, the catalog bars sit under the page title */
.search-section { margin: clamp(-34px, -3.5vw, -14px) auto 0; }
.page-search    { margin: clamp(8px, 1.2vw, 18px) auto clamp(28px, 3.4vw, 48px); }

.search-bar {
  display: flex;
  align-items: center;
  background: white;
  border-radius: var(--radius-pill);
  height: var(--sb-h);
  min-height: var(--sb-h);
  padding: var(--sb-padY) var(--sb-padY) var(--sb-padY) var(--sb-padL);
  box-shadow: 0 8px 32px rgba(0,0,0,.18), 0 2px 8px rgba(232,0,61,.12);
  gap: var(--sb-gap);
  transition: box-shadow .3s var(--ease), transform .3s var(--ease);
  animation: searchSlide .8s var(--ease) .6s both;
}

@keyframes searchSlide {
  from { opacity:0; transform: translateY(20px); }
  to   { opacity:1; transform: translateY(0); }
}

.search-bar:focus-within {
  box-shadow: 0 8px 32px rgba(232,0,61,.25), 0 0 0 3px rgba(232,0,61,.15);
  transform: translateY(-2px);
}

.search-icon {
  flex-shrink: 0;
  width: clamp(16px, 1vw + 12px, 24px);
  height: clamp(16px, 1vw + 12px, 24px);
}

.search-input {
  flex: 1;
  border: none;
  outline: none;
  font-family: inherit;
  /* Never below 16px: smaller text makes mobile browsers zoom the page on focus */
  font-size: clamp(1rem, .55vw + .85rem, 1.3rem);
  font-weight: 600;
  /* The bar itself stays white in both themes (it is a white pill floating on
     the hero), so the typed text must be dark: the inherited dark-mode text
     colour made it lavender-on-white and barely readable. */
  color: #2d2233;
  background: transparent;
  min-width: 0;
}

.search-input::placeholder { color: #9a94a3; font-weight: 500; font-size: .95em; }

.search-submit {
  flex-shrink: 0;
  /* Fills the bar's inner height at every size, so it never looks undersized */
  width: var(--sb-btn);
  height: var(--sb-btn);
  background: linear-gradient(135deg, var(--primary), var(--primary-light));
  border: none;
  border-radius: var(--radius-pill);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all .3s var(--transition);
  box-shadow: 0 4px 14px rgba(232,0,61,.5);
}

.search-submit svg {
  width: clamp(16px, 1vw + 11px, 22px);
  height: clamp(16px, 1vw + 11px, 22px);
}

.search-submit:hover {
  background: linear-gradient(135deg, var(--primary-dark), var(--primary));
  transform: scale(1.1) rotate(5deg);
  box-shadow: 0 6px 20px rgba(232,0,61,.6);
}

/* Suggestion panel - built by script.js §6 and appended inside the form. Sized
   and coloured exactly as the inline styles it replaces were, so the panel
   itself looks unchanged; what changes is that the rules now live in one place
   instead of being rewritten into the page on every keystroke. */
.search-suggestions {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: 4px;
  background: white;
  border-radius: 0 0 16px 16px;
  box-shadow: 0 8px 24px rgba(0,0,0,.12);
  overflow: hidden;
  z-index: 999;
}

/* the panel is hidden with the hidden attribute, not with an inline style */
.search-suggestions[hidden] { display: none; }

.suggestion-item {
  padding: 10px 16px;
  cursor: pointer;
  font-size: .88rem;
  font-weight: 600;
  border-bottom: 1px solid #f5f5f5;
  transition: background .2s;
}

/* was an onmouseenter/onmouseleave pair rewriting this one property */
.suggestion-item:hover { background: #fff0f5; }

.suggestion-item mark {
  background: rgba(232,0,61,.15);
  color: #e8003d;
  border-radius: 3px;
}

/* Why the panel was unreadable: .search-bar is left holding an identity
   transform by `animation: searchSlide ... both`, so it forms a stacking
   context and the panel's own 999 stops at its edge. The level that competes
   with the page is the section's 10, which sits under the petal field (20) and
   under .apps-section (30) - so the section heading and the first card were
   painted straight over rows 2-6, and only row 1, which clears the grid, could
   be read. Raised while the panel is open only, so the petals keep crossing the
   closed bar exactly as before. Still under everything a visitor has to hit:
   drawer 999, navbar 1000, Legal dropdown 1001, curtain 3000, toasts 9999. */
.search-section.has-suggestions,
.page-search.has-suggestions { z-index: 40; }

/* ===== APPS SECTION ===== */
.apps-section {
  max-width: 1280px;
  margin: 40px auto;
  padding: 0 40px;
  position: relative;
  /* In front of the petal field, and it has to be the SECTION that does this.
     The field is a child of <body> at z-index 20, so nothing inside a section
     parked at 1 can outrank it: a stacking context caps its whole subtree. 30
     clears the field in the root context, which puts every card, its heading and
     its badge in front of the falling petals (the section is the context; the
     cards themselves no longer form one now that the 3D flip is gone).

     The petals stay visible: this section has no background of its own, so only
     what it actually paints - the cards and the text - comes forward, and they
     keep falling through the gaps around the grid and behind the whole page.
     Nothing else about the cards is touched. */
  z-index: 30;
}

.apps-section--games { margin-top: 0; }

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

.section-title {
  font-family: 'Poppins', sans-serif;
  font-size: clamp(1.3rem, 3vw, 1.6rem);
  font-weight: 800;
  color: var(--text-dark);
  display: flex;
  align-items: center;
  gap: 8px;
}

.fire-icon { animation: fireBounce 1s ease infinite alternate; display: inline-block; }

@keyframes fireBounce {
  from { transform: scale(1) rotate(-5deg); }
  to   { transform: scale(1.15) rotate(5deg); }
}

.view-all {
  font-size: .9rem;
  font-weight: 800;
  color: var(--label-red);
  transition: all .25s var(--ease);
  display: flex;
  align-items: center;
  gap: 4px;
}

.view-all:hover {
  color: var(--label-red);
  text-decoration: underline;
  transform: translateX(4px);
}

/* ===== APP CARDS GRID ===== */
.apps-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 20px;
}

/* Card: a gentle lift + zoom on precise pointers. It used to be a 3D flip that
   revealed a back face; the flip — and the JS tilt that fought it — are gone, so
   the front is ordinary flow content now and the back is never shown. */
.app-card {
  /* Responsive height pinned to the desktop reference (260px) as the upper
     bound. It tracks the smaller of viewport width and viewport height, so it
     adapts on laptops/tablets/phones and in short or landscape viewports, and
     floors at 230px (the smallest value the design ever used) so the icon, the
     info block and the Download button always fit with no clipping. Normal
     desktop/laptop viewports resolve to exactly 260px. */
  height: clamp(230px, min(20vw + 60px, 45vh + 40px), 260px);
  cursor: pointer;
}

.card-inner {
  position: relative;
  width: 100%; height: 100%;
}

.card-front {
  background: var(--white);
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px 16px 16px;
  box-shadow: var(--shadow-md);
  border-radius: var(--radius-md);
  overflow: hidden;
  position: relative;
}

/* Only ever seen from the front now, so the old back face is taken out of the
   layout: its feature list and "Get Now" button are unreachable once the flip
   is gone, and leaving it absolutely stacked would cover the front. */
.card-back { display: none; }

/* Only devices with a real hover and a precise pointer get the animation. A
   touch screen never matches this, which also avoids a stuck :hover after a tap
   (requirement: no hover effects and no gradient border on mobile). */
@media (hover: hover) and (pointer: fine) {
  .card-inner { transition: transform .3s var(--ease); }
  .app-card:hover .card-inner { transform: translateY(-5px) scale(1.02); }
  .app-card:hover .card-front { box-shadow: var(--shadow-lg); }
}

/* MOD Badge */
.mod-badge {
  position: absolute;
  top: 12px; right: 12px;
  background: var(--primary);
  color: white;
  font-size: .62rem;
  font-weight: 900;
  letter-spacing: .08em;
  padding: 3px 7px;
  border-radius: var(--radius-pill);
  box-shadow: 0 2px 8px rgba(232,0,61,.5);
  z-index: 2;
}

/* The pulse is a second glow layer that fades in and out, not a box-shadow that
   is re-drawn every frame. Animating box-shadow is a paint-only property, which
   is what made these twelve badges Lighthouse's "non-composited animations";
   opacity is composited, so the halo costs nothing per frame. The look is the
   same soft red bloom at the peak of the 2s cycle. */
.mod-badge::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 2px 16px rgba(232,0,61,.8);
  opacity: 0;
  pointer-events: none;
  animation: badgePulse 2s ease-in-out infinite;
}

@keyframes badgePulse {
  0%,100% { opacity: 0; }
  50%     { opacity: 1; }
}

/* App Icon */
.app-icon-wrap {
  margin-bottom: 10px;
  position: relative;
}

.app-icon {
  width: 80px; height: 80px;
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 6px 20px rgba(0,0,0,.18);
  transition: transform .3s var(--transition);
}

/* A slight zoom only — no rotation (the old rotate(-3deg) is gone). */
@media (hover: hover) and (pointer: fine) {
  .app-card:hover .app-icon { transform: scale(1.08); }
}

/* App Info */
.app-info {
  text-align: center;
  flex: 0 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.app-name {
  font-size: .88rem;
  font-weight: 800;
  color: var(--text-dark);
  line-height: 1.3;
}

.app-desc {
  font-size: .75rem;
  color: var(--text-muted);
  font-weight: 600;
}

.app-meta {
  display: flex;
  gap: 10px;
  font-size: .72rem;
  font-weight: 700;
  color: var(--text-mid);
  margin-top: 4px;
  align-items: center;
}

.rating {
  color: var(--gold);
  display: flex;
  align-items: center;
  gap: 3px;
}

.rating svg { width: 13px; height: 13px; fill: var(--gold); }

.downloads {
  color: var(--text-muted);
  display: flex;
  align-items: center;
  gap: 3px;
}

.downloads svg { width: 13px; height: 13px; fill: var(--text-muted); }

/* Download Button */
.download-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  background: linear-gradient(135deg, var(--primary), var(--primary-light));
  color: white;
  border: none;
  border-radius: var(--radius-pill);
  padding: 9px 0;
  font-family: inherit;
  font-size: .8rem;
  font-weight: 800;
  cursor: pointer;
  margin-top: auto;
  box-shadow: 0 4px 14px rgba(232,0,61,.4);
  transition: all .3s var(--transition);
  letter-spacing: .02em;
  text-transform: uppercase;
}

.download-btn svg { width: 16px; height: 16px; fill: white; flex-shrink: 0; }

@media (hover: hover) and (pointer: fine) {
  .download-btn:hover {
    background: linear-gradient(135deg, var(--primary-dark), var(--primary));
    transform: translateY(-2px) scale(1.03);
    box-shadow: 0 6px 20px rgba(232,0,61,.55);
  }
}

/* ===== CATALOG STATES =====
   Every card now comes from the database, so a grid is briefly empty and can
   also come back with nothing in it. Both cases are stated instead of being a
   blank hole in the page. */

/* Placeholder: catalog.js reuses the real card markup with the parts emptied,
   so the box is already sized and nothing below the grid jumps when the rows
   land. Only the bars get painted here. */
.app-card.is-skeleton {
  cursor: default;
  pointer-events: none;
}

.app-card.is-skeleton .card-front { box-shadow: var(--shadow-sm); }

.app-card.is-skeleton .app-icon,
.app-card.is-skeleton .app-name,
.app-card.is-skeleton .app-desc,
.app-card.is-skeleton .rating,
.app-card.is-skeleton .download-btn {
  color: transparent;
  background: rgba(154, 122, 170, .28);   /* --text-muted, reads on both themes */
  box-shadow: none;
  border-radius: var(--radius-sm);
  animation: catalogShimmer 1.3s ease-in-out infinite;
}

.app-card.is-skeleton .app-icon { border-radius: 20px; }

@keyframes catalogShimmer {
  0%, 100% { opacity: 1; }
  50%      { opacity: .45; }
}

/* Empty or unreachable catalog: one panel across the whole grid. */
.catalog-notice {
  grid-column: 1 / -1;
  margin: 0;
  padding: 28px 18px;
  text-align: center;
  font-size: .9rem;
  font-weight: 700;
  color: var(--text-mid);
  background: var(--white);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}

@media (prefers-reduced-motion: reduce) {
  .app-card.is-skeleton .app-icon,
  .app-card.is-skeleton .app-name,
  .app-card.is-skeleton .app-desc,
  .app-card.is-skeleton .rating,
  .app-card.is-skeleton .download-btn { animation: none; }
}

/* Card Back — the flip is gone, so this face is never shown. It is kept in the
   markup for the (currently unused) feature list, but forced out of the layout
   here as well so a stray rule cannot re-reveal it. */
.card-back {
  display: none;
}

.card-back-icon {
  font-size: 2.5rem;
  animation: iconSpin 2s linear infinite;
}

@keyframes iconSpin {
  0%,100% { transform: rotate(-5deg) scale(1); }
  50%     { transform: rotate(5deg) scale(1.1); }
}

.card-back h3 {
  font-size: .95rem;
  font-weight: 800;
  text-align: center;
  margin-bottom: 4px;
}

.feature-list {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.feature-list li {
  font-size: .78rem;
  font-weight: 600;
  color: rgba(255,255,255,.9);
  padding: 3px 0;
  border-bottom: 1px solid rgba(255,255,255,.1);
}

.card-back .download-btn {
  background: white;
  color: var(--primary);
  margin-top: 8px;
  box-shadow: 0 4px 14px rgba(0,0,0,.2);
}

.card-back .download-btn:hover {
  background: var(--blush);
  transform: translateY(-2px);
}

/* ===== FEATURES STRIP ===== */
.features-strip {
  background: linear-gradient(135deg, var(--primary), #9b1a2e);
  padding: 48px 40px;
  position: relative;
  z-index: 1;
  overflow: hidden;
}

.features-strip::before {
  content: '';
  position: absolute;
  inset: 0;
  background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.04'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}

.features-container {
  max-width: 1280px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(4,1fr);
  gap: 32px;
  position: relative;
  z-index: 1;
}

.feature-item {
  text-align: center;
  color: white;
  transition: transform .3s var(--transition);
}

.feature-item:hover { transform: translateY(-8px); }

.feature-icon-3d {
  font-size: 2.8rem;
  margin-bottom: 12px;
  display: block;
  filter: drop-shadow(0 4px 8px rgba(0,0,0,.3));
  animation: iconFloat 3s ease-in-out infinite;
}

.feature-item:nth-child(2) .feature-icon-3d { animation-delay: .5s; }
.feature-item:nth-child(3) .feature-icon-3d { animation-delay: 1s; }
.feature-item:nth-child(4) .feature-icon-3d { animation-delay: 1.5s; }

@keyframes iconFloat {
  0%,100% { transform: translateY(0) rotate(-2deg); }
  50%     { transform: translateY(-6px) rotate(2deg); }
}

.feature-item h3 {
  font-size: 1rem;
  font-weight: 800;
  margin-bottom: 4px;
}

.feature-item p {
  font-size: .78rem;
  opacity: .8;
  font-weight: 500;
}

/* ===== STATS ===== */
.stats-section {
  background: var(--blush);
  padding: 60px 40px;
  position: relative;
  z-index: 1;
  border-top: 1px solid var(--blush-mid);
}

.stats-container {
  max-width: 1280px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(4,1fr);
  gap: 24px;
  text-align: center;
}

.stat-item {
  padding: 24px;
  background: var(--white);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  transition: all .3s var(--transition);
}

.stat-item:hover {
  transform: translateY(-6px) scale(1.03);
  box-shadow: var(--shadow-md);
}

.stat-number {
  font-family: 'Poppins', sans-serif;
  font-size: clamp(2rem, 4vw, 2.8rem);
  font-weight: 900;
  color: var(--primary);
  line-height: 1;
  margin-bottom: 4px;
}

.stat-label {
  font-size: .85rem;
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: .06em;
}

/* ===== FOOTER ===== */
.footer {
  background: var(--footer-bg);
  color: rgba(255,255,255,.75);
  position: relative;
  z-index: 1;
  overflow: hidden;
}

.footer::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--primary), var(--accent), var(--primary));
}

.footer-top {
  max-width: 1280px;
  margin: 0 auto;
  padding: 60px 40px 40px;
  display: grid;
  grid-template-columns: 1.5fr 2fr;
  gap: 60px;
}

.footer-logo {
  font-family: 'Poppins', sans-serif;
  font-size: 1.8rem;
  font-weight: 900;
  margin-bottom: 12px;
}

.footer-logo .logo-cute { color: white; }
.footer-logo .logo-mods { color: var(--primary-light); }

.footer-tagline {
  font-size: .85rem;
  line-height: 1.6;
  margin-bottom: 20px;
  color: rgba(255,255,255,.6);
}

.footer-socials {
  display: flex;
  gap: 10px;
}

.footer-socials a {
  width: 38px; height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,.08);
  border-radius: var(--radius-sm);
  font-size: 1.1rem;
  transition: all .25s var(--ease);
}

.footer-socials a:hover {
  background: var(--primary);
  transform: translateY(-3px);
  box-shadow: 0 4px 12px rgba(232,0,61,.4);
}

.footer-links {
  display: grid;
  grid-template-columns: repeat(3,1fr);
  gap: 30px;
}

.footer-col h4 {
  font-size: .85rem;
  font-weight: 800;
  color: white;
  text-transform: uppercase;
  letter-spacing: .08em;
  margin-bottom: 16px;
}

.footer-col ul {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* The Categories labels are plain text rather than links, so they have to be
   named here as well: one declaration block keeps their type, colour and box
   identical to the footer links. The hover rule below still matches <a> only,
   which is what makes these labels non-clickable in behaviour *and* in look. */
.footer-col a,
.footer-col .footer-plain {
  font-size: .82rem;
  color: rgba(255,255,255,.55);
  transition: all .2s var(--ease);
  display: inline-flex;
  align-items: center;
}

.footer-col .footer-plain { cursor: default; }

.footer-col a:hover {
  color: var(--accent-soft);
  transform: translateX(4px);
}

.footer-bottom {
  border-top: 1px solid rgba(255,255,255,.08);
  text-align: center;
  padding: 20px;
  font-size: .78rem;
  color: rgba(255,255,255,.6);
}

/* ===== SCROLL ANIMATIONS ===== */
.reveal {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}

.reveal.visible {
  opacity: 1;
  transform: translateY(0);
}

/* Reduced motion: a target must never sit at opacity 0 waiting for a movement
   the visitor asked not to have. script.js §7 still adds and removes the
   classes; here they simply carry no visual state. */
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  /* Card hover motion off: no lift/zoom. The button and its states stay
   reachable — only the movement is removed. */
  .app-card:hover .card-inner { transform: none; }
  .card-inner, .card-front, .app-icon, .download-btn { transition: none; }
}

/* ============================================
   RESPONSIVE DESIGN — FULL DEVICE COVERAGE
   Breakpoints:
     1280px  Large desktop / wide monitor
      1024px  Laptop / small desktop
       768px  Tablet portrait
       600px  Large phone / small tablet
       480px  Mid phone
       380px  Small phone (iPhone SE, etc.)
   The navbar is not on this ladder: it switches between the bar and the
   drawer on its own measured content (script.js §3a).
   ============================================ */

/* ── 1280px: cap content width ── */
@media (max-width: 1280px) {
  .nav-container { padding: 0 20px; }
  .apps-section  { padding: 0 28px; }
  .features-strip { padding: 48px 28px; }
  .stats-section  { padding: 60px 28px; }
  .footer-top     { padding: 60px 28px 40px; }
}

/* ── 1024px: laptop ── */
@media (max-width: 1024px) {
  /* Nav */
  .nav-item { padding: 7px 12px; min-width: 52px; font-size: .68rem; }

  /* Hero */
  .hero-fullscreen { height: clamp(380px, 52vh, 540px); }
  .hero-fullscreen .ui {
    padding:
      clamp(28px, 4vw, 56px)
      clamp(14px, 4vw, 56px)
      clamp(28px, 4vw, 56px)
      clamp(14px, 8vw, 120px);
    gap: clamp(10px, 1.5vw, 18px);
  }

  /* Grids */
  .features-container { grid-template-columns: repeat(4, 1fr); gap: 24px; }
  .stats-container    { grid-template-columns: repeat(4, 1fr); gap: 16px; }
  .apps-grid { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 16px; }

  /* Footer */
  .footer-top { gap: 40px; }
}

/* ── NAVBAR: the drawer (list) form of the navigation ──
   Everything in this block is the same navigation the bar shows, turned into
   a list that drops from the navbar and is opened by the hamburger.

   It used to be gated by `@media (max-width: 900px)`, which measures the
   window rather than the navbar. Measured, the bar's own content - logo, its
   margin, four tiles, actions, and the air the tiles need on each side - is
   646px wide above 1024px, 590px from 1024 down to 601 and 555px below that,
   where the logo and the tiles are typed smaller. The row only runs short of
   that around 645px in a window with classic scrollbars and 622px with overlay
   ones, so between 900 and those widths the tiles were folded into a hamburger
   with up to three hundred pixels of empty navbar beside them, and the tablets
   in between were given a menu they did not need.

   The gate is now the `is-compact` class that script.js §3a sets from that
   measurement, so the drawer appears when the content genuinely stops
   fitting. Each selector is wrapped in :where() to keep the specificity it
   had as a plain media-query rule, which is what lets `.nav-item.active` and
   the other two-class selectors in this file resolve exactly as before. */

/* The frame before §3a has measured - and a phone with scripting off - still
   has to look like a navbar, so below the width where the bar content
   provably cannot fit it is drawn in the drawer form: the tiles away, the
   hamburger in place of the search button, the logo holding the actions at
   the right edge. 569px is that width: the narrowest bar this content makes
   is 555px of row, and a 569px window is 537px of row with the padding taken
   off and 522px with a scrollbar - less than the bar needs in every case, so
   nothing here contradicts what §3a will decide a moment later. `nav-measured`
   is the tag §3a raises on <html> after that first pass, so the drawer form
   ends exactly when the real decision begins. */
@media (max-width: 569px) {
  html:not(.nav-measured) .nav-links      { display: none; }
  html:not(.nav-measured) .nav-logo       { margin-right: auto; }
  html:not(.nav-measured) .hamburger      { display: flex; }
  html:not(.nav-measured) .nav-search-btn { display: none; }
}

:where(.navbar.is-compact) .nav-logo {
  /* the links leave the row in this mode, so the logo has to push the
     actions to the right edge on its own (it was `margin-right: auto` in the
     768px block, where it also pulled them off centre in the bar layout) */
  margin-right: auto;
}

:where(.navbar.is-compact) .nav-links {
  position: fixed;
  top: calc(var(--nav-h) + var(--safe-t)); left: 0;
  width: 100%;
  background: rgba(255,255,255,.97);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  flex-direction: column;
  align-items: stretch;
  padding: 16px;
  gap: 4px;
  box-shadow: var(--shadow-md);
  /* Fully off-screen: -100% alone left a white strip painted over the navbar */
  transform: translateY(calc(-100% - var(--nav-h) - var(--safe-t) - 8px));
  opacity: 0;
  visibility: hidden;
  transition: transform .4s var(--ease), opacity .3s var(--ease), visibility .4s;
  z-index: 999;
  margin: 0;
  max-height: calc(100vh - var(--nav-h) - var(--safe-t));
  max-height: calc(100dvh - var(--nav-h) - var(--safe-t));
  overflow-y: auto;
}

[data-theme="dark"] :where(.navbar.is-compact) .nav-links {
  background: rgba(13,6,24,.97);
}

:where(.navbar.is-compact) .nav-links.open {
  transform: translateY(0);
  opacity: 1;
  visibility: visible;
}

:where(.navbar.is-compact) .nav-item {
  flex-direction: row;
  padding: 12px 16px;
  font-size: .9rem;
  gap: 12px;
  min-width: unset;
  text-align: left;
  border-radius: 10px;
}

:where(.navbar.is-compact) .nav-item .nav-icon { width: 24px; height: 24px; flex-shrink: 0; }

/* Dropdown rows: indicator becomes a vertical bar in the left gutter */
:where(.navbar.is-compact) .nav-item::after {
  left: -10px;
  top: 50%;
  bottom: auto;
  width: 3px;
  height: 0;
  transform: translateY(-50%);
  box-shadow: none;
}

:where(.navbar.is-compact) .nav-item:hover::after  { width: 3px; height: 26%; }
:where(.navbar.is-compact) .nav-item.active::after { width: 3px; height: 58%; }

/* The drawer is a list, so the Legal pages do not need a hover panel:
   the group turns into the row plus its four rows, always open. That
   leaves no state to get out of sync when the drawer is closed and
   reopened, and every link is one tap away on a touch screen. */
:where(.navbar.is-compact) .nav-group      { display: block; position: static; }
:where(.navbar.is-compact) .nav-group::before { display: none; }
:where(.navbar.is-compact) .nav-drop-btn   { display: none; }

:where(.navbar.is-compact) .nav-drop {
  position: static;
  left: auto;
  top: auto;
  transform: none;
  min-width: 0;
  width: 100%;
  margin: 2px 0 6px;
  padding: 0 0 0 36px;
  background: none;
  border: 0;
  box-shadow: none;
  border-radius: 0;
  gap: 2px;
  /* the drawer keeps its own visibility guard: a panel that set itself
     visible would show through a closed menu */
  opacity: 0;
  visibility: hidden;
}

:where(.navbar.is-compact) .nav-links.open .nav-drop {
  opacity: 1;
  visibility: visible;
}

:where(.navbar.is-compact) .nav-drop-link {
  padding: 9px 12px;
  border-radius: 10px;
  font-size: .84rem;
  font-weight: 700;
  color: var(--text-muted);
  white-space: normal;
}

:where(.navbar.is-compact) .nav-drop-link:hover { color: var(--primary); background: var(--bg); }

:where(.navbar.is-compact) .hamburger      { display: flex; }
:where(.navbar.is-compact) .nav-search-btn { display: none; }

/* ── 768px: TABLET PORTRAIT ── */
@media (max-width: 768px) {
  :root { --nav-h: 60px; }

  /* Nav tighter */
  .nav-container { padding: 0 16px; }
  .nav-logo { gap: 10px; }   /* the drawer adds margin-right:auto itself */
  .logo-domain { display: none; } /* hide cutemods.com under logo on small screens */

  /* Hero */
  .hero-fullscreen {
    height: clamp(300px, 48vw, 420px);
    min-height: 300px;
  }
  .hero-fullscreen .ui {
    padding: 20px 20px 20px 20px;
    gap: 10px;
    align-items: flex-start;
    justify-content: flex-end;
    padding-bottom: 36px;
  }
  .hero-fullscreen h1  { font-size: clamp(28px, 7vw, 44px); }
  .hero-fullscreen .lead { font-size: clamp(14px, 3.5vw, 18px); max-width: 20em; }
  .hero-fullscreen .stroke { width: clamp(160px, 55vw, 320px); }
  .hero-fullscreen .chips li { height: 36px; font-size: .78rem; padding: 0 12px 0 8px; }
  .hero-fullscreen .chips svg { width: 18px; height: 18px; }
  .hero-fullscreen .chips { gap: 6px 8px; }
  .hero-fullscreen .badge { font-size: .7rem; height: 26px; }

  /* Search – fully fluid, no breakpoint overrides needed */

  /* Apps sections */
  .apps-section { padding: 0 16px; margin: 20px auto; }
  .section-title { font-size: clamp(1.1rem, 4vw, 1.4rem); }

  /* App Cards */
  .apps-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  .app-icon  { width: 68px; height: 68px; }
  .app-name  { font-size: .82rem; }
  .app-desc  { font-size: .7rem; }

  /* Features */
  .features-strip     { padding: 36px 16px; }
  .features-container { grid-template-columns: repeat(2, 1fr); gap: 20px; }
  .feature-icon-3d    { font-size: 2.2rem; }

  /* Stats */
  .stats-section  { padding: 40px 16px; }
  .stats-container { grid-template-columns: repeat(2, 1fr); gap: 14px; }
  .stat-item       { padding: 18px 12px; }

  /* Footer */
  .footer-top   { grid-template-columns: 1fr; gap: 32px; padding: 40px 16px; }
  .footer-links { grid-template-columns: repeat(3, 1fr); gap: 16px; }
}

/* ── 600px: LARGE PHONE ── */
@media (max-width: 600px) {
  :root { --nav-h: 56px; }

  /* Logo smaller */
  .logo-cute, .logo-mods { font-size: 1.1rem; }
  .logo-avatar { width: 40px; height: 40px; border-radius: 10px; }

  /* Hero: show more of the scene, reduce height */
  .hero-fullscreen {
    height: clamp(240px, 56vw, 360px);
    min-height: 240px;
  }
  .hero-fullscreen .ui {
    padding: 14px 14px 28px;
    gap: 8px;
    justify-content: flex-end;
  }
  .hero-fullscreen h1   { font-size: clamp(22px, 8vw, 36px); }
  .hero-fullscreen .lead { font-size: clamp(12px, 3.8vw, 16px); }
  .hero-fullscreen .stroke { width: clamp(130px, 60vw, 260px); }
  .hero-fullscreen .chips { gap: 5px 6px; }
  .hero-fullscreen .chips li { height: 32px; font-size: .72rem; gap: 6px; padding: 0 10px 0 7px; }
  .hero-fullscreen .chips svg { width: 15px; height: 15px; }

  /* Search */
  .search-bar { border-radius: var(--radius-pill); }

  /* Cards: 2 column */
  .apps-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }

  /* Games grid: also 2 col */
  .apps-section--games .apps-grid { grid-template-columns: repeat(2, 1fr); }

  /* Features: 2 col */
  .features-container { grid-template-columns: 1fr 1fr; gap: 14px; }
  .feature-item h3    { font-size: .88rem; }
  .feature-item p     { font-size: .72rem; }

  /* Stats: 2 col */
  .stats-container { grid-template-columns: 1fr 1fr; gap: 10px; }

  /* Footer: 2 col links */
  .footer-links      { grid-template-columns: repeat(2, 1fr); gap: 16px; }
  .footer-col:last-child { grid-column: 1 / -1; }
}

/* ── 480px: MID PHONE ── */
@media (max-width: 480px) {
  :root { --nav-h: 54px; }

  /* Nav actions */
  .nav-actions { gap: 6px; }
  .toggle-track { min-width: 52px; height: 28px; }

  /* Hero text even smaller */
  .hero-fullscreen { height: clamp(200px, 55vw, 300px); min-height: 200px; }
  .hero-fullscreen .ui { padding: 10px 12px 22px; }
  .hero-fullscreen h1 { font-size: clamp(18px, 7.5vw, 30px); }
  .hero-fullscreen .lead { font-size: clamp(11px, 3.5vw, 14px); }
  .hero-fullscreen .chips li { height: 28px; font-size: .66rem; gap: 5px; padding: 0 8px 0 6px; }
  .hero-fullscreen .chips svg { width: 13px; height: 13px; }
  .hero-fullscreen .badge { font-size: .62rem; height: 22px; padding: 0 10px 0 8px; }

  /* Search */
  .search-input::placeholder { font-size: inherit; }

  /* Cards: still 2 col but shorter */
  .apps-grid { gap: 8px; }
  .app-icon  { width: 60px; height: 60px; border-radius: 15px; }
  .app-name  { font-size: .78rem; }
  .card-front { padding: 16px 12px 12px; }

  /* Features */
  .features-strip     { padding: 28px 12px; }
  .feature-icon-3d    { font-size: 1.9rem; margin-bottom: 8px; }
  .features-container { gap: 12px; }

  /* Stats */
  .stats-section  { padding: 28px 12px; }
  .stat-number    { font-size: clamp(1.6rem, 7vw, 2rem); }
  .stat-item      { padding: 14px 8px; }

  /* Section header */
  .section-header { margin-bottom: 16px; }
  .apps-section   { margin: 16px auto; }

  /* Footer */
  .footer-top  { padding: 28px 12px; gap: 24px; }
  .footer-logo { font-size: 1.4rem; }
  .footer-links { grid-template-columns: 1fr 1fr; gap: 12px; }
  .footer-bottom { padding: 16px; font-size: .72rem; }
}

/* ── 380px: SMALL PHONE (iPhone SE, Galaxy A series) ── */
@media (max-width: 380px) {
  :root { --nav-h: 52px; }

  .logo-cute, .logo-mods { font-size: 1rem; }
  .logo-avatar { width: 36px; height: 36px; }

  /* Hero: minimal height so content shows */
  .hero-fullscreen {
    height: clamp(180px, 54vw, 260px);
    min-height: 180px;
  }
  .hero-fullscreen h1 { font-size: clamp(16px, 7vw, 26px); }
  .hero-fullscreen .lead { display: none; } /* hide on tiny screens */
  .hero-fullscreen .chips { flex-wrap: wrap; gap: 4px; }
  .hero-fullscreen .chips li { height: 26px; font-size: .6rem; }

  /* Search – stays exactly 60% of the screen, no padding tweaks here */

  /* Cards: 2 col minimal (height comes from the responsive clamp above) */
  .app-icon   { width: 54px; height: 54px; border-radius: 13px; }
  .app-name   { font-size: .74rem; }
  .app-desc   { font-size: .64rem; }
  .app-meta   { font-size: .64rem; gap: 6px; }
  .download-btn { font-size: .7rem; padding: 7px 0; }

  /* Features: stack to 1 col if needed */
  .features-container { grid-template-columns: 1fr 1fr; }
}

/* ── Landscape phone: prevent hero from being too tall ── */
@media (max-height: 500px) and (orientation: landscape) {
  .hero-fullscreen {
    height: clamp(180px, 45vh, 280px);
    min-height: 180px;
  }
  .hero-fullscreen .ui { padding: 10px 20px 20px; gap: 6px; }
  .hero-fullscreen h1 { font-size: clamp(18px, 5vh, 32px); }
  .hero-fullscreen .lead { display: none; }
  .hero-fullscreen .chips li { height: 28px; }
}

/* ── Large screens: wider content cap ── */
@media (min-width: 1400px) {
  .nav-container,
  .apps-section,
  .features-container,
  .stats-container,
  .footer-top {
    max-width: 1400px;
  }
  .hero-fullscreen { height: clamp(500px, 52vh, 660px); }
}

/* ── 4K / ultrawide ── */
@media (min-width: 1920px) {
  .hero-fullscreen { height: clamp(560px, 48vh, 720px); }
  html { font-size: 18px; }
}

/* ===== DOWNLOAD / APP DETAIL PAGE (download.html) ===== */
.dl-section {
  max-width: 1280px;
  margin: 0 auto;
  padding: clamp(14px, 2vw, 32px) clamp(14px, 3vw, 40px) clamp(34px, 4vw, 64px);
  position: relative;
  z-index: 1;
}

/* Breadcrumb */
.dl-crumb ol {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: clamp(6px, .6vw, 9px);
  list-style: none;
  font-size: clamp(.78rem, .35vw + .72rem, .95rem);
  font-weight: 700;
}

.dl-crumb a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--text-muted);
  text-decoration: none;
  transition: color .2s var(--ease);
}

.dl-crumb a:hover { color: var(--primary); }
.dl-crumb svg { width: 15px; height: 15px; fill: currentColor; }
.dl-crumb .crumb-sep { color: var(--accent-soft); font-weight: 800; }
.dl-crumb .crumb-now { color: var(--text-dark); font-weight: 800; }

/* Two column layout: content + sidebar */
.dl-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.62fr) minmax(0, 1fr);
  gap: clamp(16px, 1.6vw, 26px);
  align-items: start;
  margin-top: clamp(14px, 1.6vw, 22px);
}

/* the explicit minmax(0, 1fr) matters: with no track declared the implicit
   column is sized by max-content, so the four phones of .shot-row (470px)
   decided how wide the card was and the card ran off the right edge */
.dl-col-main,
.dl-col-side {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(14px, 1.6vw, 22px);
  min-width: 0;
}

.dl-card {
  background: var(--white);
  border: 1px solid rgba(232,0,61,.09);
  border-radius: var(--radius-lg);
  padding: clamp(16px, 1.9vw, 26px);
  box-shadow: var(--shadow-sm);
  /* sticky navbar would otherwise cover anchor targets */
  scroll-margin-top: calc(var(--nav-h) + var(--safe-t) + 16px);
}

/* ── App head ── */
.dl-head-top { display: flex; align-items: flex-start; gap: clamp(14px, 1.7vw, 24px); }

.dl-icon {
  flex-shrink: 0;
  width: clamp(74px, 6.4vw, 108px);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  border-radius: clamp(16px, 1.4vw, 24px);
  background: #0d0d12;
  box-shadow: var(--shadow-md);
}

.dl-icon svg { width: 82%; height: 82%; }

.dl-title-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: clamp(8px, 1vw, 14px);
}

.dl-head h1 {
  font-family: var(--display-font);
  font-size: clamp(1.5rem, 2vw + 1rem, 2.6rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -.01em;
  color: var(--text-dark);
}

.dl-tag {
  background: var(--blush-mid);
  color: var(--primary-dark);
  font-size: clamp(.68rem, .3vw + .62rem, .84rem);
  font-weight: 800;
  padding: .42em .9em;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}

.dl-sub {
  margin-top: 6px;
  color: var(--text-mid);
  font-weight: 700;
  font-size: clamp(.9rem, .5vw + .8rem, 1.08rem);
}

.dl-stats {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(12px, 2vw, 26px);
  list-style: none;
  margin-top: clamp(12px, 1.5vw, 20px);
}

.dl-stats li { display: flex; align-items: center; gap: 9px; }
.dl-stats svg { width: clamp(20px, 1.6vw, 26px); height: clamp(20px, 1.6vw, 26px); fill: var(--primary); flex-shrink: 0; }
.dl-stats li:nth-child(1) svg { fill: var(--gold); }
.dl-stats li:nth-child(4) svg { fill: #1DB954; }
.dl-stats b {
  display: block;
  font-size: clamp(.95rem, .55vw + .85rem, 1.2rem);
  font-weight: 800;
  line-height: 1.2;
  color: var(--text-dark);
}
.dl-stats small {
  display: block;
  font-size: clamp(.68rem, .3vw + .62rem, .82rem);
  font-weight: 700;
  color: var(--text-muted);
}

/* ── Buttons ── */
.dl-actions {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(10px, 1.2vw, 16px);
  margin-top: clamp(16px, 1.9vw, 24px);
}

.dl-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  min-height: clamp(46px, 3.4vw, 56px);
  padding: 0 clamp(20px, 2.4vw, 34px);
  border: 2px solid transparent;
  border-radius: var(--radius-pill);
  font-family: inherit;
  font-size: clamp(.92rem, .4vw + .84rem, 1.08rem);
  font-weight: 800;
  text-decoration: none;
  cursor: pointer;
  transition: transform .28s var(--transition), box-shadow .28s var(--ease),
              background .25s var(--ease), border-color .25s var(--ease);
}

.dl-btn svg { width: 20px; height: 20px; fill: currentColor; flex-shrink: 0; }

.dl-btn-primary {
  flex: 1 1 240px;
  background: linear-gradient(135deg, var(--primary-light), var(--primary));
  color: #fff;
  box-shadow: 0 6px 18px rgba(232,0,61,.3);
}

.dl-btn-primary:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 26px rgba(232,0,61,.4);
}

.dl-btn-ghost {
  flex: 1 1 180px;
  background: var(--white);
  border-color: rgba(232,0,61,.4);
  color: var(--primary);
}

.dl-btn-ghost:hover {
  transform: translateY(-3px);
  border-color: var(--primary);
  background: var(--blush);
}

.dl-btn-block { width: 100%; flex: none; margin-top: clamp(12px, 1.4vw, 18px); }
.dl-btn.is-busy { opacity: .7; pointer-events: none; }

.dl-note {
  margin-top: 12px;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  background: var(--blush-mid);
  color: var(--primary-dark);
  font-size: clamp(.8rem, .35vw + .74rem, .92rem);
  font-weight: 700;
}

/* ── Trust strip ── */
.dl-trust {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: clamp(8px, 1.4vw, 18px);
  list-style: none;
  margin-top: clamp(16px, 1.8vw, 22px);
  padding: clamp(10px, 1.1vw, 14px) clamp(12px, 1.4vw, 18px);
  background: var(--blush);
  border-radius: var(--radius-md);
}

.dl-trust li {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: clamp(.8rem, .4vw + .72rem, .98rem);
  font-weight: 800;
  color: var(--text-mid);
}

.dl-trust svg { width: 19px; height: 19px; fill: var(--primary); }

/* ── Card headings ── */
.dl-h2 {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: clamp(1.05rem, .7vw + .9rem, 1.4rem);
  font-weight: 800;
  color: var(--text-dark);
}

.dl-h2-ico {
  width: clamp(28px, 2.1vw, 36px);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  border-radius: 10px;
  background: var(--blush-mid);
  color: var(--primary-dark);
  flex-shrink: 0;
}

.dl-h2-ico svg { width: 58%; height: 58%; fill: currentColor; }
.dl-h2-ico.gold { background: #fff3d6; color: #e0a200; }
.dl-h2-ico.red  { background: #ffe1e8; color: var(--primary); }

/* ── Screenshots: CSS phone mockups (no image payload) ──
   The screens size themselves off the phone width with a container query,
   so the mockups stay legible in the grid and in the mobile strip. */
.phone {
  position: relative;
  aspect-ratio: 9 / 19;
  padding: clamp(4px, .5vw, 7px);
  border-radius: clamp(14px, 1.3vw, 22px);
  background: linear-gradient(160deg, #33333d, #0c0c11);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  container-type: inline-size;
  transition: transform .35s var(--transition), box-shadow .35s var(--ease);
}

.phone:hover { transform: translateY(-6px) scale(1.015); box-shadow: var(--shadow-lg); }

.phone-notch {
  position: absolute;
  top: clamp(5px, .6vw, 9px);
  left: 50%;
  transform: translateX(-50%);
  width: 34%;
  height: clamp(5px, .7vw, 8px);
  border-radius: var(--radius-pill);
  background: #0a0a0e;
  z-index: 2;
}

.phone-screen {
  display: flex;
  flex-direction: column;
  gap: 1em;
  height: 100%;
  padding: 1.6em 1.3em;
  border-radius: clamp(11px, 1vw, 17px);
  background: #0b0b0f;
  color: #fff;
  overflow: hidden;
  font-size: 8px;
  font-size: 5.2cqi;
}

/* the screen as a picture frame: no furniture, no inner padding, so the
   screenshot owns the whole glass area */
.phone-screen.is-shot { padding: 0; gap: 0; }

/* contain, not cover: cover re-cropped a 250x444 bitmap into a 715x1524 box,
   which is the zoomed, oversized image this section was showing. contain keeps
   the picture's own ratio, shows every pixel of it, and centres it; the dark
   screen of the mockup is what a differently-shaped image gets letterboxed on.
   min-height: 0 stops a replaced flex item asserting its intrinsic height. */
.shot-img {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 0;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  object-position: center;
}

/* ── Screenshot strip ── */
/* Flex, not grid: a grid of repeat(4, minmax(120px, 1fr)) tracks is 470px of
   content and a block box cannot shrink below that, so it pushed the whole
   card past the right edge. Here each phone takes the width the viewport
   allows and the row starts scrolling once they no longer fit. */
.shot-row {
  display: flex;
  gap: clamp(10px, 1.3vw, 18px);
  margin-top: clamp(12px, 1.4vw, 18px);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding-inline: 8px;
  padding-bottom: 8px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: var(--accent-soft) transparent;
}

.shot-row::-webkit-scrollbar { height: 6px; }
.shot-row::-webkit-scrollbar-track { background: transparent; }
.shot-row::-webkit-scrollbar-thumb {
  background: var(--accent-soft);
  border-radius: var(--radius-pill);
}

.shot {
  /* One width token for the whole strip, derived from the space available in
     BOTH axes. The frame is 9/19, so bounding the width bounds the height too,
     and the vh term is what keeps a single screenshot inside a short or
     landscape window instead of the 728 x 1538px phone one image used to
     produce in a 728px column. Growth is off (flex-grow 0) so one phone cannot
     swallow the row; shrinking stays on, so four phones still divide a wide
     card exactly as before, and 110px remains the floor where scrolling
     begins. Nothing here is a fixed size: it is fluid between the floor and
     the ceiling on every screen. */
  --shot-w: min(clamp(132px, 40vw, 232px), calc(62vh * 9 / 19));
  margin: 0;
  flex: 0 1 var(--shot-w);
  min-width: 110px;
  max-width: var(--shot-w);   /* the cap never lets the image fill the column */
  scroll-snap-align: start;
}

/* Fewer phones than the row can hold leaves the group centred in the leftover
   space; once the strip overflows there is no leftover, both margins resolve to
   0 and the first phone is still reachable by scrolling. */
.shot:first-child { margin-left: auto; }
.shot:last-child  { margin-right: auto; }

.shot figcaption {
  margin-top: 8px;
  text-align: center;
  font-size: clamp(.72rem, .35vw + .66rem, .86rem);
  font-weight: 800;
  letter-spacing: .02em;
  color: var(--text-muted);
}

.ph-title { font-size: 1.5em; font-weight: 800; }
.ph-strong { font-size: 1.4em; font-weight: 800; }
.ph-soft { font-size: 1em; font-weight: 600; color: rgba(255,255,255,.55); }

.ph-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .8em;
  /* grow so the mockup fills the whole screen instead of pooling at the top */
  flex: 2 1 0;
  min-height: 0;
}

.ph-tile {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: .4em;
  min-height: 0;
  padding: .7em;
  border-radius: .6em;
  font-size: .92em;
  font-weight: 700;
  line-height: 1.15;
  overflow: hidden;
}

.ph-tile i { display: block; width: 1.7em; height: 1.7em; border-radius: .35em; background: rgba(255,255,255,.92); }
.ph-tile.t1 { background: linear-gradient(150deg, #7b2ff7, #f107a3); }
.ph-tile.t2 { background: linear-gradient(150deg, #0b8790, #1DB954); }

.ph-row {
  display: flex;
  align-items: center;
  gap: .8em;
  font-size: 1em;
  flex: 1 1 0;
  min-height: 0;
}

.ph-row span { display: flex; flex-direction: column; line-height: 1.25; font-weight: 800; }
.ph-row em { font-style: normal; font-size: .85em; font-weight: 600; color: rgba(255,255,255,.5); }
.ph-row i.th { flex-shrink: 0; width: 2.7em; height: 2.7em; border-radius: .5em; }
.th.a { background: linear-gradient(140deg, #4a00b0, #c8006e); }
.th.b { background: linear-gradient(140deg, #e60000, #ff8a00); }
.th.c { background: linear-gradient(140deg, #00645a, #12b886); }
.th.d { background: linear-gradient(140deg, #8d67ab, #d5a8ff); }
.th.e { background: linear-gradient(140deg, #0079c1, #48c2fb); }
.th.f { background: linear-gradient(140deg, #b00000, #ff6b6b); }

.ph-pill {
  align-self: flex-start;
  padding: .5em 1.5em;
  border-radius: var(--radius-pill);
  font-size: 1em;
  font-weight: 800;
}

.ph-pill.green { background: #1DB954; color: #0b0b0f; }

.sc-splash { align-items: center; justify-content: center; }
.sc-mark { width: 5.6em; height: 5.6em; }
.sc-mark svg { width: 100%; height: 100%; }
.sc-word { font-size: 1.8em; font-weight: 800; }
.sc-player {
  align-items: center;
  text-align: center;
  justify-content: space-between;
}

.ph-cover {
  width: 8.6em;
  height: 8.6em;
  flex: none;
  border-radius: .7em;
  display: grid;
  place-items: center;
  background: linear-gradient(150deg, #5b2ea8, #c9317f);
  color: rgba(255,255,255,.92);
}

.ph-cover svg { width: 3em; height: 3em; fill: currentColor; }

.ph-bar {
  width: 100%;
  height: .35em;
  border-radius: var(--radius-pill);
  background: rgba(255,255,255,.22);
  overflow: hidden;
}

.ph-bar i { display: block; width: 42%; height: 100%; border-radius: inherit; background: #1DB954; }

.ph-ctrl { display: flex; align-items: center; justify-content: center; gap: 1.6em; }
.ph-ctrl span { width: 1.4em; height: 1.4em; border-radius: 50%; background: rgba(255,255,255,.55); }
.ph-ctrl b { width: 2.4em; height: 2.4em; border-radius: 50%; background: #fff; }

.ph-tabs { display: flex; gap: .5em; font-size: .95em; font-weight: 800; }
.ph-tabs b, .ph-tabs span { padding: .35em .85em; border-radius: var(--radius-pill); }
.ph-tabs b { background: rgba(255,255,255,.16); }
.ph-tabs span { color: rgba(255,255,255,.45); }

/* ── Sidebar cards ── */
.hl-list { list-style: none; display: grid; gap: clamp(6px, .6vw, 9px); }

.hl-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: clamp(8px, .8vw, 12px) clamp(10px, 1vw, 14px);
  border-radius: var(--radius-sm);
  background: var(--blush);
  font-size: clamp(.84rem, .45vw + .76rem, 1.02rem);
  font-weight: 700;
  color: var(--text-mid);
  transition: transform .2s var(--ease), background .2s var(--ease);
}

.hl-list li:hover { transform: translateX(4px); background: var(--blush-mid); }

.tick {
  flex-shrink: 0;
  width: 21px; height: 21px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--primary);
  color: #fff;
}

.tick svg { width: 12px; height: 12px; fill: currentColor; }
.hl-list li:nth-child(2n) .tick { background: var(--primary-light); }
.hl-list li:nth-child(3n) .tick { background: var(--gold); }

.dl-about p {
  margin-top: 4px;
  font-size: clamp(.88rem, .45vw + .8rem, 1.04rem);
  font-weight: 600;
  line-height: 1.7;
  color: var(--text-mid);
}

.dl-specs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 clamp(12px, 1.4vw, 20px);
  margin-top: clamp(14px, 1.5vw, 20px);
}

.dl-specs div {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 0;
  border-bottom: 1px dashed rgba(232,0,61,.16);
}

.dl-specs dt, .dl-specs dd {
  font-size: clamp(.78rem, .4vw + .72rem, .92rem);
  text-align: right;
}

.dl-specs dt { font-weight: 700; color: var(--text-muted); }
.dl-specs dd { font-weight: 800; color: var(--text-dark); }
.dl-specs a { color: var(--primary); text-decoration: none; }
.dl-specs .free { color: #12b886; }

.dl-cta {
  text-align: center;
  background: linear-gradient(165deg, var(--blush-mid), var(--blush));
  border-color: rgba(232,0,61,.16);
}

.dl-cta h2 {
  font-family: var(--display-font);
  font-size: clamp(1.15rem, .8vw + 1rem, 1.5rem);
  font-weight: 700;
  color: var(--text-dark);
}

.dl-cta p {
  margin: 6px 0 2px;
  font-size: clamp(.85rem, .45vw + .78rem, 1rem);
  font-weight: 700;
  color: var(--text-mid);
}

/* Dark mode patches for the pastel-only surfaces */
[data-theme="dark"] .dl-tag { background: rgba(255,77,141,.18); color: var(--accent-soft); }
[data-theme="dark"] .dl-note { background: rgba(232,0,61,.18); color: var(--accent-soft); }
[data-theme="dark"] .dl-h2-ico { background: var(--blush-mid); color: var(--accent-soft); }
[data-theme="dark"] .dl-h2-ico.gold { background: rgba(244,199,47,.16); color: var(--gold); }
[data-theme="dark"] .dl-h2-ico.red { background: rgba(232,0,61,.22); color: var(--accent-soft); }
[data-theme="dark"] .dl-btn-ghost:hover { background: var(--blush-mid); }
[data-theme="dark"] .dl-specs div { border-bottom-color: rgba(255,179,204,.14); }
[data-theme="dark"] .dl-cta { background: linear-gradient(165deg, var(--blush-mid), var(--footer-bg)); }

/* ── Responsive ── */
@media (max-width: 1080px) {
  .dl-layout { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); }
}

@media (max-width: 860px) {
  /* minmax(0, 1fr), not 1fr: the bare 1fr minimum is the track's content
     size, which let the cards grow to 566px inside a 362px column and pushed
     everything past the right edge on phones. */
  .dl-layout { grid-template-columns: minmax(0, 1fr); }

  .dl-trust { justify-content: center; }
}

@media (max-width: 560px) {
  .dl-head-top { flex-direction: column; align-items: center; text-align: center; }
  .dl-title-row { justify-content: center; }
  .dl-stats { justify-content: center; }
  .dl-actions .dl-btn { flex: 1 1 100%; }
  .dl-specs { grid-template-columns: minmax(0, 1fr); }
}

/* ===== MOD APP GUIDE (SEO article on download.html) ===== */
.article-section { margin-top: clamp(6px, 1vw, 14px); }

/* one reading column, even inside the wide card.
   the 100% matters: margin-inline:auto takes the grid item out of the stretch
   behaviour, so a bare `max-width: 86ch` sizes the card to min(max-content,
   86ch) and on a narrow column it runs past the right edge instead of wrapping */
.article-card {
  max-width: min(86ch, 100%);
  margin-inline: auto;
  font-size: clamp(.92rem, .4vw + .84rem, 1.06rem);
  line-height: 1.72;
  color: var(--text-mid);
}

.article-card > * + * { margin-top: clamp(12px, 1.3vw, 18px); }

.article-card .dl-h2 { margin-bottom: clamp(6px, .8vw, 10px); }

.article-lead {
  font-size: clamp(1rem, .55vw + .9rem, 1.18rem);
  font-weight: 600;
  color: var(--text-dark);
}

.article-h3 {
  font-family: var(--display-font);
  font-size: clamp(1.08rem, .6vw + .95rem, 1.36rem);
  font-weight: 700;
  line-height: 1.25;
  color: var(--text-dark);
  margin-top: clamp(22px, 2.4vw, 34px);
  /* the sticky navbar would otherwise cover anchor targets */
  scroll-margin-top: calc(var(--nav-h) + var(--safe-t) + 16px);
}

.article-card p { margin-top: clamp(8px, 1vw, 14px); }
.article-card strong { color: var(--text-dark); }

/* Prose links only. :not(.dl-btn) keeps a button out of this rule — one class
   plus a type selector would otherwise outrank .dl-btn-primary (a single
   class) and paint dark red text with a pink underline onto the red pill. */
.article-card a:not(.dl-btn) {
  color: var(--primary);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-color: var(--accent-soft);
  text-underline-offset: 3px;
  transition: color .2s var(--ease), text-decoration-color .2s var(--ease);
}

.article-card a:not(.dl-btn):hover { color: var(--primary-dark); text-decoration-color: currentColor; }

/* contents list */
.article-toc {
  padding: clamp(14px, 1.5vw, 20px) clamp(16px, 1.7vw, 22px);
  border-radius: var(--radius-md);
  background: var(--blush);
}

.article-toc .article-h3 { margin-top: 0; font-size: clamp(1rem, .4vw + .9rem, 1.12rem); }

.article-nav {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));
  gap: 6px 22px;
  margin-top: 10px;
  padding-left: 20px;
}

.article-nav li { font-weight: 700; }
.article-nav a { text-decoration: none; }

/* feature and benefit lists: tick bullets, same language as .hl-list */
.article-points,
.article-checks {
  list-style: none;
  padding-left: 0;
  display: grid;
  gap: clamp(8px, .9vw, 12px);
  margin-top: clamp(10px, 1.2vw, 16px);
}

.article-points li,
.article-checks li {
  position: relative;
  padding-left: 32px;
}

.article-points li::before,
.article-checks li::before {
  content: '';
  position: absolute;
  left: 2px;
  top: .52em;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background-color: var(--primary);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E");
  background-size: 11px;
  background-repeat: no-repeat;
  background-position: center;
}

.article-checks li::before { background-color: #12b886; }
.article-points li:nth-child(2n)::before { background-color: var(--primary-light); }
.article-points li:nth-child(3n)::before { background-color: var(--gold); }

/* numbered install steps — list-style off, the counter badge replaces the marker */
.article-steps {
  list-style: none;
  padding-left: 0;
  counter-reset: step;
  margin-top: clamp(10px, 1.2vw, 16px);
  display: grid;
  gap: clamp(10px, 1.1vw, 14px);
}

.article-steps li {
  position: relative;
  padding-left: 44px;
  counter-increment: step;
}

.article-steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: .05em;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--blush-mid);
  color: var(--primary-dark);
  font-family: var(--display-font);
  font-weight: 800;
  font-size: .92rem;
}

/* comparison table */
.article-table-wrap {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  border: 1px solid rgba(232, 0, 61, .12);
  border-radius: var(--radius-md);
  margin-top: clamp(10px, 1.2vw, 16px);
  scrollbar-width: thin;
  scrollbar-color: var(--accent-soft) transparent;
}

.article-table-wrap::-webkit-scrollbar { height: 6px; }
.article-table-wrap::-webkit-scrollbar-thumb {
  background: var(--accent-soft);
  border-radius: var(--radius-pill);
}

.article-table {
  width: 100%;
  min-width: 560px;
  border-collapse: collapse;
  font-size: clamp(.82rem, .35vw + .76rem, .96rem);
}

.article-table th,
.article-table td {
  padding: clamp(9px, .9vw, 13px) clamp(10px, 1vw, 15px);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid rgba(232, 0, 61, .1);
}

.article-table thead th {
  background: var(--blush);
  color: var(--text-dark);
  font-family: var(--display-font);
  font-weight: 700;
  white-space: nowrap;
}

.article-table tbody th {
  color: var(--text-dark);
  font-weight: 800;
  white-space: nowrap;
}

.article-table tbody tr:last-child th,
.article-table tbody tr:last-child td { border-bottom: 0; }

/* FAQ accordion — native details, no JS */
.article-faq { display: grid; gap: clamp(8px, .9vw, 12px); margin-top: clamp(10px, 1.2vw, 16px); }

.article-faq details {
  border: 1px solid rgba(232, 0, 61, .14);
  border-radius: var(--radius-md);
  background: var(--blush);
  padding: 0 clamp(14px, 1.4vw, 18px);
  transition: border-color .2s var(--ease), background .2s var(--ease);
}

.article-faq details[open] { border-color: var(--primary-light); }

.article-faq summary {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  list-style: none;
  padding: clamp(12px, 1.2vw, 16px) 0;
  font-family: var(--display-font);
  font-weight: 700;
  color: var(--text-dark);
  font-size: clamp(.94rem, .45vw + .86rem, 1.06rem);
}

.article-faq summary::-webkit-details-marker { display: none; }

.article-faq summary::after {
  content: '+';
  margin-left: auto;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--white);
  color: var(--primary);
  font-weight: 800;
  transition: transform .25s var(--ease);
}

.article-faq details[open] summary::after { transform: rotate(45deg); }

.article-faq details > p {
  padding-bottom: clamp(12px, 1.2vw, 16px);
  margin-top: 0;
}

.article-outro { font-weight: 600; color: var(--text-dark); }

/* .sr-only for the table caption comes from the utility block at the top */

[data-theme="dark"] .article-card { border-color: rgba(255, 179, 204, .14); }
[data-theme="dark"] .article-table-wrap { border-color: rgba(255, 179, 204, .16); }
[data-theme="dark"] .article-table th,
[data-theme="dark"] .article-table td { border-bottom-color: rgba(255, 179, 204, .12); }
[data-theme="dark"] .article-faq details { border-color: rgba(255, 179, 204, .16); }

/* ===== CATALOG PAGES (apps.html / games.html) ===== */
.page-head {
  max-width: 1280px;
  margin: clamp(18px, 2.4vw, 40px) auto 0;
  padding: 0 clamp(14px, 3vw, 40px);
  text-align: center;
  position: relative;
  z-index: 1;
}

.page-kicker {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: .5em 1.1em;
  border-radius: var(--radius-pill);
  background: var(--blush-mid);
  color: var(--label-red);
  font-size: clamp(.72rem, .3vw + .66rem, .86rem);
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.page-title {
  margin-top: clamp(10px, 1.2vw, 18px);
  font-family: var(--display-font);
  font-size: clamp(1.9rem, 3.4vw + 1rem, 3.4rem);
  font-weight: 700;
  line-height: 1.1;
  color: var(--text-dark);
}

.page-title b { color: var(--primary); }

.page-lead {
  margin: clamp(8px, 1vw, 14px) auto 0;
  max-width: 62ch;
  font-size: clamp(.92rem, .5vw + .82rem, 1.12rem);
  font-weight: 600;
  color: var(--text-mid);
}

.result-count {
  padding: .45em 1em;
  border-radius: var(--radius-pill);
  background: var(--blush);
  font-size: clamp(.8rem, .35vw + .74rem, .95rem);
  font-weight: 800;
  color: var(--text-mid);
}

/* filtered-out cards must leave the grid entirely */
.app-card[hidden] { display: none !important; }

/* ===== CATALOG PAGINATION (built by script.js §19) ===== */
/* Twenty cards per page on apps.html / games.html; the strip under the grid
   carries Prev, the page numbers and Next. Nothing here hard-codes a colour –
   every value is a token, so the dark theme flips with no extra rules. */
.pagination {
  display: grid;
  justify-items: center;
  gap: 8px;
  margin-top: clamp(18px, 2.4vw, 30px);
}

/* the class sets display:grid, so the hidden attribute needs its own rule */
.pagination[hidden] { display: none !important; }

.page-btns {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 8px;
}

.page-btn,
.page-num {
  min-width: 44px;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: var(--blush);
  color: var(--text-dark);
  font-family: inherit;
  font-size: clamp(.84rem, .35vw + .78rem, .98rem);
  font-weight: 800;
  line-height: 1;
  cursor: pointer;
  transition: background .25s var(--ease), color .25s var(--ease),
              box-shadow .25s var(--ease);
}

button.page-btn:hover:not([disabled]),
button.page-num:hover {
  background: var(--blush-mid);
}

button.page-btn:focus-visible,
button.page-num:focus-visible {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(232, 0, 61, .18);
}

/* No transform on hover anywhere in this strip, so there is nothing to
   switch off under prefers-reduced-motion. */
.page-btn[disabled] {
  opacity: .4;
  cursor: default;
}

/* The page you are on: filled rather than outlined, and a <span>, not a
   button that would do nothing when pressed. */
.page-num.is-current {
  background: var(--primary);
  color: #fff;
  cursor: default;
}

.page-gap {
  width: 18px;
  text-align: center;
  font-weight: 800;
  color: var(--text-muted);
}

.page-info {
  font-size: clamp(.78rem, .3vw + .72rem, .9rem);
  font-weight: 700;
  color: var(--text-mid);
}

.no-results {
  display: grid;
  justify-items: center;
  gap: 10px;
  text-align: center;
  padding: clamp(28px, 4vw, 56px) clamp(18px, 3vw, 40px);
  background: var(--white);
  border: 1px dashed rgba(232,0,61,.28);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

/* the class sets display:grid, so the hidden attribute needs its own rule */
.no-results[hidden] { display: none !important; }

.no-results-ico {
  width: clamp(54px, 5vw, 74px);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--blush-mid);
  color: var(--primary-dark);
}

.no-results-ico svg { width: 52%; height: 52%; fill: currentColor; }

.no-results h3 {
  font-size: clamp(1.05rem, .8vw + .9rem, 1.4rem);
  font-weight: 800;
  color: var(--text-dark);
}

.no-results h3 span { color: var(--primary); }

.no-results p {
  max-width: 46ch;
  font-size: clamp(.86rem, .4vw + .8rem, 1rem);
  font-weight: 600;
  color: var(--text-mid);
}

.no-results .dl-btn { flex: none; min-height: 44px; margin-top: 4px; }

@media (max-width: 600px) {
  .page-head { margin-top: clamp(12px, 3vw, 24px); }
  .section-header { flex-wrap: wrap; gap: 10px; }
}

/* ===== PAGE TRANSITIONS (first load + page to page) ===== */
/* One curtain, painted by CSS alone so it exists on the very first frame:
   it covers the page, wipes away as the content rises, and on an internal
   link click it wipes back in before the browser navigates. Because it is a
   body pseudo-element, every page gets it with no markup to keep in sync. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 3000;
  background: var(--bg);
  pointer-events: none;
}

/* arriving: curtain out, then the page lifts in behind it */
body:not(.is-leaving)::after  { animation: curtainOut .48s var(--ease) both; }
/* `backwards`, not `both`, and it matters. The last keyframe of pageIn is
   `transform: none`, which a forwards fill applies as an IDENTITY matrix - and
   any transform other than the keyword none makes <main> a stacking context
   forever. That would trap the whole document at level 0 of the root context,
   so no z-index inside <main> could ever outrank a layer that is a child of
   <body> (the sakura field at 20, and the card section raised to 30 above it).
   Visually identical: the filled end state was opacity 1 / transform none, and
   those are already <main>'s natural values, so reverting to them changes
   nothing. `backwards` still covers the .06s delay with the from-keyframe. */
body:not(.is-leaving) main    { animation: pageIn    .52s var(--ease) .06s backwards; }
/* the navbar sits outside <main>; it only fades, a transform on a sticky
   element would fight with its own offset */
body:not(.is-leaving) .navbar { animation: fadeIn    .42s var(--ease) both; }

/* leaving: the curtain comes back, js/script.js §20 navigates mid-fill */
body.is-leaving::after { animation: curtainIn .3s var(--ease) both; }

/* catalog pages only: the head block unfolds one line at a time */
.page-head > * { animation: pageIn .5s var(--ease) both; }
.page-kicker   { animation-delay: .16s; }
.page-title    { animation-delay: .24s; }
.page-lead     { animation-delay: .32s; }

@keyframes curtainOut { from { opacity: 1 } to { opacity: 0 } }
@keyframes curtainIn  { from { opacity: 0 } to { opacity: 1 } }
@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
@keyframes pageIn {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

/* A restored page (back / forward) is already composed: replaying the whole
   entrance over it reads as a glitch. §20 adds the class from the navigation
   type, and this rule also covers the no-JS case. */
body.no-enter::after { display: none; }
body.no-enter main   { animation: none; }
body.no-enter .navbar { animation: none; }

@media (prefers-reduced-motion: reduce) {
  body::after { display: none; }
  body:not(.is-leaving) main,
  body:not(.is-leaving) .navbar,
  .page-head > * { animation: none; }
}

/* ===== LEGAL & INFO PAGES =====
   (privacy-policy.html · terms-of-service.html · dmca.html · contact.html)
   These pages are built out of the blocks that already exist: .dl-section
   for the measure, .dl-crumb for the breadcrumb, .dl-layout / .dl-col-main /
   .dl-col-side for the two columns, .dl-card + .article-* for the copy and
   .dl-btn for the buttons. Only what has no equivalent yet is defined here. */

/* document meta: "last updated", the mailbox, the reading time */
.legal-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 18px;
  margin-bottom: clamp(14px, 1.6vw, 22px);
  padding-bottom: clamp(10px, 1.2vw, 16px);
  border-bottom: 1px solid rgba(232, 0, 61, .1);
  font-size: clamp(.76rem, .3vw + .7rem, .88rem);
  font-weight: 800;
  letter-spacing: .02em;
  color: var(--text-muted);
}

.legal-meta span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.legal-meta svg { width: 15px; height: 15px; fill: var(--primary); flex-shrink: 0; }
.legal-meta a { color: var(--primary); text-decoration: underline; text-underline-offset: 3px; }

/* the side column follows you down a long policy.
   .dl-layout sets align-items:start, which makes .dl-col-side exactly as tall
   as the aside itself — a sticky box with no room to travel simply scrolls away
   with the column, so the column is stretched and the aside kept content-high */
.legal-layout > .dl-col-side { align-self: stretch; }

/* contact.html has no side column at all — one card, full measure. Declared
   after .dl-layout so the single track replaces the 1.62fr/1fr split. */
.legal-single {
  grid-template-columns: minmax(0, 1fr);
}

.legal-aside {
  position: sticky;
  top: calc(var(--nav-h) + var(--safe-t) + 16px);
  align-self: start;
  display: grid;
  gap: clamp(14px, 1.6vw, 22px);
  min-width: 0;
}

/* contents list: the section on screen is marked (js/site.js §3) */
.article-nav a {
  color: var(--text-mid);
  transition: color .2s var(--ease);
}

.article-nav a:hover { color: var(--primary); }

.article-nav a.is-current {
  color: var(--primary-dark);
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-underline-offset: 3px;
}

/* callouts — an aside, a warning, and the verbatim complaint statement */
.legal-note,
.legal-warn {
  margin-top: clamp(14px, 1.5vw, 20px);
  padding: clamp(12px, 1.3vw, 18px) clamp(14px, 1.6vw, 20px);
  border-left: 4px solid var(--primary);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  background: var(--blush);
  color: var(--text-mid);
  font-size: clamp(.86rem, .35vw + .8rem, 1rem);
  font-weight: 600;
}

.legal-note strong,
.legal-warn strong { color: var(--text-dark); }

.legal-note a,
.legal-warn a {
  color: var(--primary);
  font-weight: 800;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.legal-warn {
  border-left-color: var(--primary-dark);
  background: #fff0f3;
  color: #6d1023;
}

.legal-warn strong { color: #4c0b19; }
.legal-warn a { color: var(--primary-dark); }

.legal-quote {
  margin-top: clamp(14px, 1.5vw, 20px);
  padding: clamp(15px, 1.7vw, 24px);
  border: 1px solid rgba(232, 0, 61, .16);
  border-radius: var(--radius-md);
  background: var(--blush-mid);
  color: var(--text-dark);
  font-weight: 700;
}

.legal-quote p { line-height: 1.66; }

/* plain lists: the reset at the top of the sheet takes every marker away,
   so copy that has to read like a document asks for its bullets back */
.legal-list,
.legal-steps {
  margin-top: clamp(10px, 1.2vw, 16px);
  display: grid;
  gap: 7px;
  line-height: 1.6;
}

.legal-list { list-style: disc;   padding-left: 24px; }
.legal-steps { list-style: decimal; padding-left: 26px; }
.legal-list > li::marker { color: var(--primary); font-weight: 800; }
.legal-steps > li::marker { color: var(--primary); font-weight: 800; }
.legal-list .legal-list { list-style: circle; margin-top: 6px; }
.legal-steps li,
.legal-list li { padding-left: 2px; }

/* a plain list of links, for the "other documents" card in the side column */
.legal-links {
  display: grid;
  gap: 9px;
  margin-top: clamp(12px, 1.3vw, 16px);
  font-weight: 800;
}

.legal-links a:not(.dl-btn) {
  color: var(--primary);
  text-decoration: underline;
  text-decoration-color: var(--accent-soft);
  text-underline-offset: 3px;
  transition: color .2s var(--ease), text-decoration-color .2s var(--ease);
}

.legal-links a:not(.dl-btn):hover { color: var(--primary-dark); text-decoration-color: currentColor; }

/* the article checklist, used as a compact list inside a card */
.legal-checks {
  margin-top: clamp(12px, 1.3vw, 16px);
  font-weight: 600;
  font-size: clamp(.84rem, .35vw + .78rem, .96rem);
  color: var(--text-mid);
}

.legal-checks a {
  color: var(--primary);
  font-weight: 800;
  text-decoration: underline;
  text-decoration-color: var(--accent-soft);
  text-underline-offset: 3px;
}

.legal-checks a:hover { color: var(--primary-dark); text-decoration-color: currentColor; }

/* the same list, but for the things that will NOT happen — a green tick would
   contradict the sentence next to it, so the badge turns to a cross */
.article-checks.is-no li::before {
  background-color: var(--primary);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3C/svg%3E");
  background-size: 10px;
}

/* inline code in legal copy: a key name or a value, not a code sample */
.article-card code {
  padding: .1em .4em;
  border-radius: 6px;
  background: var(--blush-mid);
  color: var(--primary-dark);
  font-family: ui-monospace, 'SFMono-Regular', Consolas, monospace;
  font-size: .92em;
}

/* ── Contact page ── */
.contact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(258px, 100%), 1fr));
  gap: clamp(12px, 1.4vw, 18px);
  margin-top: clamp(14px, 1.6vw, 22px);
}

.contact-card {
  display: grid;
  align-content: start;
  gap: 8px;
  min-width: 0;
  padding: clamp(15px, 1.7vw, 22px);
  background: var(--white);
  border: 1px solid rgba(232, 0, 61, .09);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  transition: transform .28s var(--transition), box-shadow .28s var(--ease);
}

.contact-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }

.contact-card h3 {
  font-family: var(--display-font);
  font-size: clamp(1rem, .5vw + .9rem, 1.18rem);
  font-weight: 600;
  color: var(--text-dark);
}

.contact-card p {
  font-size: clamp(.84rem, .35vw + .78rem, .96rem);
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-mid);
}

.contact-card a.cc-mail {
  justify-self: start;
  font-size: clamp(.82rem, .35vw + .76rem, .94rem);
  font-weight: 800;
  color: var(--primary);
  text-decoration: underline;
  text-decoration-color: var(--accent-soft);
  text-underline-offset: 3px;
  overflow-wrap: anywhere;
}

.contact-card a.cc-mail:hover { color: var(--primary-dark); text-decoration-color: currentColor; }

/* ── The form ── */
.cf {
  display: grid;
  gap: clamp(13px, 1.5vw, 19px);
  margin-top: clamp(16px, 1.8vw, 24px);
}

.cf-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));
  gap: clamp(13px, 1.5vw, 19px);
  min-width: 0;
}

.cf-field { display: grid; align-content: start; gap: 6px; min-width: 0; }

.cf-label {
  display: flex;
  align-items: baseline;
  gap: 5px;
  font-size: clamp(.8rem, .3vw + .74rem, .92rem);
  font-weight: 800;
  color: var(--text-dark);
}

.cf-req { color: var(--primary); font-weight: 900; }

.cf-input,
.cf-select,
.cf-textarea {
  width: 100%;
  min-width: 0;
  min-height: 48px;
  padding: 12px 14px;
  font-family: inherit;
  font-size: clamp(.9rem, .35vw + .84rem, 1rem);
  font-weight: 600;
  color: var(--text-dark);
  background: var(--white);
  border: 1.5px solid rgba(232, 0, 61, .16);
  border-radius: var(--radius-sm);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease), background .2s var(--ease);
}

.cf-textarea { min-height: 168px; resize: vertical; line-height: 1.65; }

.cf-input::placeholder,
.cf-textarea::placeholder { color: var(--text-muted); font-weight: 500; }

/* a hand-drawn chevron keeps the select on-palette in every browser */
.cf-select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 40px;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23e8003d'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 20px;
}

.cf-input:focus-visible,
.cf-select:focus-visible,
.cf-textarea:focus-visible,
.cf-btn:focus-visible,
.article-nav a:focus-visible {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(232, 0, 61, .18);
}

.cf-field.has-error .cf-input,
.cf-field.has-error .cf-select,
.cf-field.has-error .cf-textarea {
  border-color: var(--primary-dark);
  background: #fff7f9;
}

/* error + counter line under a field */
.cf-msg {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 18px;
  font-size: clamp(.76rem, .3vw + .7rem, .85rem);
  font-weight: 700;
}

.cf-error {
  display: flex;
  align-items: center;
  gap: 5px;
  color: var(--primary-dark);
  font-weight: 800;
}

.cf-error svg { width: 14px; height: 14px; fill: currentColor; flex-shrink: 0; }
.cf-error[hidden] { display: none; }
.cf-hint { color: var(--text-muted); margin-left: auto; }

.cf-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(10px, 1.2vw, 16px);
}

.cf-btn {
  flex: 1 1 220px;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* the mailto target can be a long address: let it wrap instead of escaping */
.cf-btn span { min-width: 0; }

.cf-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  font-size: clamp(.78rem, .3vw + .72rem, .88rem);
  font-weight: 700;
  color: var(--text-muted);
}

.cf-alt { display: flex; flex-wrap: wrap; gap: 10px; }
.cf-alt .dl-btn { flex: 0 1 auto; min-height: 44px; padding: 0 20px; font-size: .9rem; }

/* result of a submit: ok carries the hand-off, bad the summary */
.cf-alert {
  display: grid;
  gap: 8px;
  padding: clamp(14px, 1.6vw, 20px);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
}

.cf-alert[hidden] { display: none; }
.cf-alert:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }

.cf-alert-head {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--display-font);
  font-size: clamp(1rem, .5vw + .9rem, 1.2rem);
  font-weight: 600;
}

.cf-alert-ico {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  flex-shrink: 0;
  background: rgba(18, 184, 134, .16);
  color: #0f9d58;
}

.cf-alert-ico svg { width: 17px; height: 17px; fill: currentColor; }
.cf-alert p { font-weight: 600; line-height: 1.62; }
.cf-alert strong { font-weight: 900; }

.cf-alert.is-ok {
  background: #ecfdf3;
  border-color: #a7e8c0;
  color: #0f5132;
}

.cf-alert.is-bad {
  background: #fff0f3;
  border-color: rgba(232, 0, 61, .3);
  color: #6d1023;
}

.is-bad .cf-alert-ico { background: rgba(232, 0, 61, .14); color: var(--primary); }
.cf-alert.is-bad code { font-weight: 800; }

/* ── Dark theme ── */
[data-theme="dark"] .legal-meta { border-bottom-color: rgba(255, 179, 204, .16); }
[data-theme="dark"] .legal-note { background: var(--blush); color: var(--text-mid); }
[data-theme="dark"] .legal-warn { background: rgba(232, 0, 61, .16); color: var(--accent-soft); }
[data-theme="dark"] .legal-warn strong { color: #fff; }
[data-theme="dark"] .legal-warn a { color: var(--accent-soft); }
[data-theme="dark"] .legal-quote { background: var(--blush-mid); border-color: rgba(255, 179, 204, .18); }
[data-theme="dark"] .contact-card { border-color: rgba(255, 179, 204, .14); }
[data-theme="dark"] .cf-input,
[data-theme="dark"] .cf-select,
[data-theme="dark"] .cf-textarea { border-color: rgba(255, 179, 204, .2); }
[data-theme="dark"] .cf-select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ff4d8d'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
}
[data-theme="dark"] .cf-field.has-error .cf-input,
[data-theme="dark"] .cf-field.has-error .cf-select,
[data-theme="dark"] .cf-field.has-error .cf-textarea { background: rgba(232, 0, 61, .1); }
[data-theme="dark"] .cf-alert.is-ok { background: rgba(18, 184, 134, .12); border-color: rgba(18, 184, 134, .4); color: #b7f0cf; }
[data-theme="dark"] .cf-alert.is-bad { background: rgba(232, 0, 61, .14); border-color: rgba(255, 179, 204, .3); color: var(--accent-soft); }

/* ── Responsive ── */
@media (max-width: 860px) {
  /* the grid it sticks inside collapses to one column here (see .dl-layout),
     and a sticky aside would then run past the form it sits above */
  .legal-aside { position: static; }
}

@media (max-width: 560px) {
  .cf-btn { flex: 1 1 100%; }
  .cf-alt .dl-btn { flex: 1 1 100%; }
  .cf-actions { gap: 10px; }
}

/* ===== UTILITY ===== */
.hidden { display: none !important; }

