/* modern.css
   Web-only visual layer. Loaded after the shared card and app styles so the
   web can evolve independently from the source flashcard design. */

:root {
  --gallery-bg: #f3f6fc;
  --gallery-glow-blue: rgba(113, 145, 214, .11);
  --gallery-glow-rose: rgba(224, 86, 79, .07);
  --modern-surface: rgba(255, 255, 255, .76);
  --modern-surface-strong: rgba(255, 255, 255, .9);
  --modern-border: rgba(116, 131, 156, .14);
  --modern-shadow: 0 1px 2px rgba(35, 45, 65, .04), 0 16px 42px rgba(45, 58, 84, .08);
  --modern-shadow-soft: 0 10px 30px rgba(45, 58, 84, .065);
  --modern-radius: 22px;
  --modern-font: Inter, "SF Pro Text", "Segoe UI Variable", "Segoe UI", system-ui, sans-serif;
  --motion-fast: 140ms;
  --motion-ui: 220ms;
  --motion-art: 280ms;
  --motion-route: 380ms;
  --motion-ease: cubic-bezier(.2, .75, .25, 1);
}

html[data-theme="dark"] {
  --gallery-bg: #222a37;
  --gallery-glow-blue: rgba(99, 132, 201, .17);
  --gallery-glow-rose: rgba(224, 86, 79, .1);
  --modern-surface: rgba(48, 56, 70, .82);
  --modern-surface-strong: rgba(55, 64, 80, .94);
  --modern-border: rgba(205, 216, 234, .12);
  --modern-shadow: 0 1px 2px rgba(8, 13, 22, .14), 0 18px 46px rgba(8, 13, 22, .18);
  --modern-shadow-soft: 0 12px 32px rgba(8, 13, 22, .14);
}

body {
  font-family: var(--modern-font);
}

/* ---------- calm route motion ---------- */
::view-transition-group(root) {
  animation-duration: var(--motion-route);
  animation-timing-function: var(--motion-ease);
}

html[data-route-motion="open"]::view-transition-old(root),
html[data-route-motion="back"]::view-transition-old(root),
html[data-route-motion="random"]::view-transition-old(root) {
  animation-name: route-fade-out;
}

html[data-route-motion="open"]::view-transition-new(root) {
  animation-name: route-open-in;
}

html[data-route-motion="back"]::view-transition-new(root) {
  animation-name: route-back-in;
}

html[data-route-motion="next"]::view-transition-old(root) {
  animation-name: route-next-out;
}

html[data-route-motion="next"]::view-transition-new(root) {
  animation-name: route-next-in;
}

html[data-route-motion="previous"]::view-transition-old(root) {
  animation-name: route-previous-out;
}

html[data-route-motion="previous"]::view-transition-new(root) {
  animation-name: route-previous-in;
}

html[data-route-motion="random"]::view-transition-new(root) {
  animation-name: route-random-in;
}

@keyframes route-fade-out {
  to { opacity: 0; transform: scale(.992); }
}
@keyframes route-open-in {
  from { opacity: 0; transform: translateY(8px) scale(.995); }
}
@keyframes route-back-in {
  from { opacity: 0; transform: translateY(-6px) scale(.996); }
}
@keyframes route-next-out {
  to { opacity: 0; transform: translateX(-14px); }
}
@keyframes route-next-in {
  from { opacity: 0; transform: translateX(18px); }
}
@keyframes route-previous-out {
  to { opacity: 0; transform: translateX(14px); }
}
@keyframes route-previous-in {
  from { opacity: 0; transform: translateX(-18px); }
}
@keyframes route-random-in {
  from { opacity: 0; filter: blur(3px); transform: scale(.994); }
}

/* Gallery background: cool porcelain rather than neutral office grey. */
body:not(:has(.container.detail)) {
  --page-bg: var(--gallery-bg);
  background:
    radial-gradient(900px 520px at 8% -8%, var(--gallery-glow-blue), transparent 68%),
    radial-gradient(760px 460px at 96% 4%, var(--gallery-glow-rose), transparent 66%),
    var(--gallery-bg);
}

body:not(:has(.container.detail))::before {
  background:
    radial-gradient(720px 360px at 50% -160px, rgba(255, 255, 255, .46), transparent 72%),
    linear-gradient(180deg, rgba(255, 255, 255, .18), transparent 280px);
}

html[data-theme="dark"] body:not(:has(.container.detail))::before {
  background: linear-gradient(180deg, rgba(255, 255, 255, .025), transparent 260px);
}

.topbar-inner {
  border-color: var(--modern-border);
  box-shadow: 0 8px 30px rgba(42, 54, 78, .09);
}

.g-card,
.discovery-mon,
.connection-mon {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  min-width: 0;
  min-height: 204px;
  box-sizing: border-box;
  padding: 14px 10px 13px;
  display: flex;
  flex-direction: column;
  align-items: center;
  border: 1px solid color-mix(in srgb, var(--tint) 13%, var(--modern-border));
  border-radius: 20px;
  box-shadow: var(--modern-shadow-soft);
  /* The halo the Discover more cards have always had, now on all three: the
     gallery drew a flat surface and lit up only under the pointer, so the same
     Pokemon looked like two different cards depending on which grid it was in. */
  background:
    radial-gradient(circle at 50% 16%, color-mix(in srgb, var(--tint) 10%, transparent), transparent 48%),
    color-mix(in srgb, var(--modern-surface-strong) 88%, var(--tint) 2%);
  transition: transform .2s cubic-bezier(.2, .75, .25, 1), box-shadow .2s ease, border-color .2s ease;
}

.g-card > *,
.discovery-mon > *,
.connection-mon > * {
  position: relative;
  z-index: 1;
}

/* Hover is asked of the CELL, not of the link inside it. The star is a sibling
   of the link - it has to be, a <button> cannot live inside an <a> - so moving
   the pointer onto the star left the link and the whole card dropped its hover
   while the pointer was still plainly over it. Discover more has no star and no
   wrapper, so it keeps asking itself. */
.g-cell:hover .g-card,
.g-cell:hover .discovery-mon,
.connection-mon:hover {
  transform: none;
  border-color: color-mix(in srgb, var(--tint) 13%, var(--modern-border));
  box-shadow: var(--modern-shadow-soft);
}

.g-img,
.discovery-mon img,
.connection-mon img {
  width: 120px;
  height: 120px;
  margin: 6px 0 7px;
  object-fit: contain;
  filter:
    drop-shadow(0 7px 18px color-mix(in srgb, var(--tint) 18%, transparent))
    drop-shadow(0 3px 6px rgba(21, 28, 40, .12));
  transition: transform .22s cubic-bezier(.2, .75, .25, 1), filter .22s ease;
}

.g-dex,
.discovery-dex,
.connection-dex {
  position: static;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink-faint);
  font-size: calc(10px * var(--ts));
  font-weight: 750;
  letter-spacing: .05em;
  line-height: 1;
}

.g-name,
.discovery-mon strong,
.connection-mon strong {
  margin: 2px 0 7px;
  font-size: calc(13px * var(--ts));
  font-weight: 750;
  line-height: 1.15;
  text-align: center;
}

.g-types,
.discovery-types,
.connection-types {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 5px;
}
/* .detail wins the specificity race against a plain .g-types .type, so the
   Discover more pills need the extra .detail to keep the gallery's geometry. */
.g-types .type,
.discovery-types .type,
.detail .connection-types .type {
  display: inline-flex;
  align-items: center;
  height: 22px;
  box-sizing: border-box;
  margin: 0;
  padding: 0 9px;
  border-radius: 999px;
  font-size: calc(9px * var(--ts));
}

@media (hover: hover) {
  .g-cell:hover .g-card,
  .g-cell:hover .discovery-mon,
  .connection-mon:hover {
    transform: translateY(-4px);
    border-color: color-mix(in srgb, var(--tint) 28%, var(--modern-border));
    box-shadow: 0 18px 38px rgba(38, 49, 72, .13);
  }
  .g-cell:hover .g-img,
  .g-cell:hover .discovery-mon img,
  .connection-mon:hover img {
    transform: translateY(-3px) scale(1.08);
  }
}

@media (hover: hover) and (pointer: fine) {
  .g-card::before,
  .discovery-mon::before,
  .connection-mon::before {
    content: "";
    position: absolute;
    z-index: 0;
    inset: 0;
    pointer-events: none;
    opacity: 0;
    background: radial-gradient(
      190px circle at var(--pointer-x, 50%) var(--pointer-y, 45%),
      color-mix(in srgb, var(--tint) 12%, transparent),
      transparent 68%
    );
    transition: opacity var(--motion-ui) ease;
  }
  .g-card.is-pointer-active::before,
  .discovery-mon.is-pointer-active::before,
  .connection-mon.is-pointer-active::before {
    opacity: 1;
  }
}

/* ---------- Living Pokédex gallery discovery ---------- */
.gallery-discovery {
  display: grid;
  gap: 24px;
  margin-bottom: 34px;
  animation: discovery-in .42s ease both;
}
.gallery-discovery[hidden] { display: none; }
@keyframes discovery-in {
  from { opacity: 0; transform: translateY(8px); }
}

.daily-hero {
  --tint: var(--brand);
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(320px, 1.1fr);
  align-items: center;
  min-height: 340px;
  padding: 42px 52px;
  border-radius: 30px;
  color: #fff;
  background: linear-gradient(
    125deg,
    color-mix(in srgb, var(--tint) 42%, #172438),
    color-mix(in srgb, var(--tint) 64%, #294d4a)
  );
  box-shadow: var(--modern-shadow);
}
.daily-hero::after {
  content: "";
  position: absolute;
  z-index: -1;
  width: 390px;
  height: 390px;
  top: -142px;
  right: -70px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .13);
  box-shadow:
    0 0 0 48px rgba(255, 255, 255, .028),
    0 0 0 96px rgba(255, 255, 255, .018);
}
.daily-copy { position: relative; z-index: 2; }
.discovery-eyebrow {
  display: block;
  font-size: calc(11px * var(--ts));
  font-weight: 750;
  letter-spacing: .1em;
  text-transform: uppercase;
  opacity: .68;
}
.daily-dex {
  display: block;
  margin-top: 16px;
  font-size: calc(12px * var(--ts));
  font-weight: 800;
  opacity: .72;
}
.daily-hero h1 {
  margin: 3px 0 10px;
  font-size: calc(clamp(40px, 5vw, 64px) * var(--ts));
  line-height: .96;
  letter-spacing: -.035em;
}
.daily-hero p {
  max-width: 470px;
  margin: 0;
  color: rgba(255, 255, 255, .72);
  line-height: 1.65;
}
.daily-types,
.discovery-types {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}
.daily-types { margin-top: 16px; }
.daily-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 9px;
  margin-top: 26px;
}
.daily-primary,
.daily-secondary {
  min-height: 42px;
  padding: 0 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  border-radius: 12px;
  font: inherit;
  font-size: calc(13px * var(--ts));
  font-weight: 700;
  cursor: pointer;
}
.daily-primary { background: #fff; color: #233348; }
.daily-secondary {
  border: 1px solid rgba(255, 255, 255, .16);
  background: rgba(255, 255, 255, .1);
  color: #fff;
}
.daily-secondary svg { width: 15px; height: 15px; }
.daily-primary, .daily-secondary { transition: background .12s, border-color .12s; }
@media (hover: hover) {
  /* White on the hero: tint it with the Pokémon's own colour, the same 16% the
     rest of the chrome uses. */
  .daily-primary:hover { background: color-mix(in srgb, var(--tint, var(--brand)) 16%, #fff); }
  /* Translucent white on artwork: a tint would vanish, so it firms up instead. */
  .daily-secondary:hover {
    background: rgba(255, 255, 255, .2);
    border-color: rgba(255, 255, 255, .3);
  }
  html[data-theme="dark"] .daily-primary:hover {
    background: color-mix(in srgb, var(--tint, var(--brand)) 22%, rgba(241, 245, 251, .14));
  }
  html[data-theme="dark"] .daily-secondary:hover {
    background: rgba(8, 14, 24, .3);
    border-color: rgba(255, 255, 255, .2);
  }
}
.daily-art {
  display: grid;
  place-items: center;
  min-width: 0;
}
.daily-art img {
  width: min(100%, 330px);
  max-height: 310px;
  object-fit: contain;
  filter: drop-shadow(0 20px 28px rgba(13, 24, 32, .28));
}
html[data-theme="dark"] .daily-hero {
  color: #f4f7fb;
  border: 1px solid rgba(210, 220, 235, .12);
  background:
    radial-gradient(
      115% 130% at 82% 46%,
      color-mix(in srgb, var(--tint) 24%, transparent) 0%,
      transparent 54%
    ),
    linear-gradient(
      135deg,
      #303849 0%,
      color-mix(in srgb, var(--tint) 12%, #293342) 52%,
      #252d3b 100%
    );
  box-shadow: 0 18px 46px rgba(8, 13, 22, .22);
}
html[data-theme="dark"] .daily-hero::after {
  border-color: rgba(255, 255, 255, .08);
  box-shadow:
    0 0 0 48px rgba(255, 255, 255, .018),
    0 0 0 96px rgba(255, 255, 255, .01);
}
html[data-theme="dark"] .daily-hero p {
  color: #c2cbd8;
}
html[data-theme="dark"] .daily-hero .discovery-eyebrow,
html[data-theme="dark"] .daily-hero .daily-dex {
  color: #dce3ed;
  opacity: .76;
}
html[data-theme="dark"] .daily-art img {
  filter: drop-shadow(0 22px 34px rgba(6, 11, 19, .36));
}
html[data-theme="dark"] .daily-types .type {
  filter: saturate(.82) brightness(.92);
}
html[data-theme="dark"] .daily-primary {
  border: 1px solid rgba(255, 255, 255, .16);
  background: rgba(241, 245, 251, .14);
  color: #f7f9fc;
}
html[data-theme="dark"] .daily-secondary {
  border-color: rgba(255, 255, 255, .1);
  background: rgba(8, 14, 24, .18);
  color: #e7edf5;
}

.discovery-section { min-width: 0; }
.discovery-heading,
.catalog-heading {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 12px;
}
.discovery-heading { margin: 0 2px 10px; }
.discovery-heading h2,
.catalog-heading h2 {
  margin: 0;
  font-size: calc(18px * var(--ts));
  letter-spacing: -.015em;
}
.discovery-heading span,
.catalog-heading span {
  color: var(--ink-faint);
  font-size: calc(12px * var(--ts));
}
.explore-all {
  padding: 5px 0;
  border: 0;
  background: none;
  color: var(--accent, var(--brand));
  font: inherit;
  font-size: calc(12px * var(--ts));
  font-weight: 750;
  cursor: pointer;
}
.editorial-paths {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
.editorial-path-card .editorial-path-art { transition: transform .18s ease; }
.editorial-path-card {
  position: relative;
  overflow: hidden;
  display: grid;
  grid-template-columns: 112px minmax(0, 1fr);
  align-items: center;
  min-height: 142px;
  padding: 14px 16px 14px 10px;
  border: 1px solid var(--modern-border);
  border-radius: 22px;
  background:
    radial-gradient(circle at 14% 18%, color-mix(in srgb, var(--tint) 16%, transparent), transparent 52%),
    var(--modern-surface);
  color: var(--ink);
  box-shadow: var(--modern-shadow-soft);
  transition: transform var(--motion-ui) var(--motion-ease),
    border-color var(--motion-ui) var(--motion-ease);
}
.editorial-path-art {
  position: relative;
  display: block;
  height: 112px;
}
.editorial-path-art img {
  position: absolute;
  width: 76px;
  height: 76px;
  object-fit: contain;
  filter: drop-shadow(0 8px 10px rgba(30, 40, 58, .16));
}
.editorial-path-art img:nth-child(1) { left: 13px; top: 18px; z-index: 3; }
.editorial-path-art img:nth-child(2) { left: -3px; top: -2px; opacity: .68; transform: scale(.68); }
.editorial-path-art img:nth-child(3) { right: -1px; top: 1px; opacity: .62; transform: scale(.62); }
.editorial-path-copy,
.editorial-path-copy > span,
.editorial-path-copy small { display: block; }
.editorial-path-copy strong {
  display: block;
  font-size: calc(14px * var(--ts));
  line-height: 1.25;
}
.editorial-path-copy > span {
  margin-top: 6px;
  color: var(--ink-soft);
  font-size: calc(11px * var(--ts));
  line-height: 1.45;
}
.editorial-path-copy small {
  margin-top: 8px;
  color: var(--ink-faint);
  font-size: calc(10px * var(--ts));
  font-weight: 700;
}
.editorial-loading {
  grid-column: 1 / -1;
  height: 142px;
  border-radius: 22px;
  background: color-mix(in srgb, var(--modern-surface) 82%, var(--modern-border));
}
.active-editorial-path {
  margin-bottom: 14px;
  padding: 24px;
  border: 1px solid var(--modern-border);
  border-radius: 26px;
  background: var(--modern-surface);
  box-shadow: var(--modern-shadow);
  animation: editorial-path-in var(--motion-art) var(--motion-ease) both;
}
@keyframes editorial-path-in {
  from { opacity: 0; transform: translateY(8px) scale(.995); }
}
.active-editorial-heading {
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: 18px;
  margin-bottom: 18px;
}
.active-editorial-heading h3 {
  margin: 5px 0 5px;
  font-size: calc(26px * var(--ts));
  letter-spacing: -.025em;
}
.active-editorial-heading p {
  max-width: 660px;
  margin: 0;
  color: var(--ink-soft);
  font-size: calc(12px * var(--ts));
  line-height: 1.55;
}
.editorial-close {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  border: 1px solid var(--modern-border);
  border-radius: 50%;
  color: var(--ink-soft);
  background: var(--modern-surface-strong);
}
.editorial-close svg { width: 15px; height: 15px; }
.editorial-members {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 10px;
}
.keep-exploring {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 7px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid color-mix(in srgb, var(--modern-border) 60%, transparent);
}
.keep-exploring span {
  margin-right: 4px;
  color: var(--ink-faint);
  font-size: calc(10px * var(--ts));
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.keep-exploring a {
  padding: 7px 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--tint, var(--brand)) 8%, var(--modern-surface));
  color: var(--ink-soft);
  font-size: calc(11px * var(--ts));
  font-weight: 700;
}
.editorial-explore.has-active-path > .discovery-heading,
.editorial-explore.has-active-path > .editorial-paths { display: none; }

.daily-mix {
  display: grid;
  grid-auto-flow: row;
  gap: 10px;
  padding: 2px;
}
/* The reader picks how wide a card should be; the count falls out of it. A
   fixed count is the wrong promise at both ends - eight columns on a laptop
   makes cards the artwork cannot live in, four on an ultrawide leaves the grid
   marooned - and it needed a phone override and a mid-screen cap to stay
   sane. auto-fill needs neither: a 320px phone gets one column, a 2560px
   desktop nine, from the same rule. Today's mix shares it, so both rows keep
   one card width. */
:root { --card-min: 168px; }
:root[data-cardw="narrow"] { --card-min: 132px; }
:root[data-cardw="wide"] { --card-min: 220px; }
.grid {
  grid-template-columns: repeat(auto-fill, minmax(var(--card-min), 1fr));
  gap: 10px;
  padding: 2px;
}
.daily-mix { grid-template-columns: repeat(auto-fill, minmax(var(--card-min), 1fr)); }

/* Fade, no travel. This panel is hydrated after the card is already on screen,
   so something has to mark its arrival or it hard-pops - but the 7px rise it
   used to do was the detail card's flinch happening a second time, one panel
   lower. Opacity alone says "this just arrived" without moving anything the
   reader has settled on. */
@keyframes connections-fade-in {
  from { opacity: 0; }
}

.catalog { scroll-margin-top: 82px; }
.filter-panel:not([hidden]) {
  transform-origin: top center;
  animation: panel-reveal var(--motion-ui) var(--motion-ease) both;
}
.catalog-heading {
  margin-bottom: 12px;
  padding-top: 8px;
}

@keyframes panel-reveal {
  from { opacity: 0; transform: translateY(-5px) scale(.995); }
}

@media (hover: hover) {
  .editorial-path-card:hover {
    transform: translateY(-4px);
    border-color: color-mix(in srgb, var(--tint) 28%, var(--modern-border));
    box-shadow: 0 18px 38px rgba(38, 49, 72, .13);
  }
  .editorial-path-card:hover .editorial-path-art {
    transform: translateY(-2px) scale(1.035);
  }
}

/* ---------- detail navigation ---------- */
.detailbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 44px;
  margin-bottom: 14px;
}

.nav-btn {
  border-color: var(--modern-border);
  background: var(--modern-surface);
  box-shadow: 0 5px 18px rgba(43, 54, 76, .06);
}

/* ---------- modern detail canvas ---------- */
.container.detail {
  max-width: 1360px;
  padding-top: 8px;
}

.detail .card {
  color: var(--ink);
  font-family: var(--modern-font);
  background: transparent;
}

.detail .disclaimer {
  margin-top: 12px;
  padding-top: 14px;
}

.detail-connections-shell {
  width: min(100%, 1280px);
  min-height: 1px;
  margin: 34px auto 0;
}
.explore-connections {
  padding: 26px;
  border: 1px solid var(--modern-border);
  border-radius: 26px;
  background: var(--modern-surface);
  box-shadow: var(--modern-shadow-soft);
  animation: connections-fade-in var(--motion-ui) var(--motion-ease) both;
}
.connections-heading {
  margin-bottom: 14px;
  text-align: center;
}
.connections-heading h2 {
  margin: 0;
  color: var(--accent-ink);
  font-size: calc(14px * var(--ts));
  font-weight: 780;
  letter-spacing: .055em;
  text-transform: uppercase;
}
.connection-group > h3 {
  margin: 0;
  color: var(--accent-ink);
  font-size: calc(12px * var(--ts));
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.connection-group + .connection-group {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid color-mix(in srgb, var(--modern-border) 65%, transparent);
}
.connection-description {
  max-width: 720px;
  margin: 6px 0 13px;
  color: var(--ink-soft);
  font-size: calc(13px * var(--ts));
  line-height: 1.5;
}
.connection-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 10px;
}
/* Colour, ellipsis and the reason line are all this family still owns; the
   box, the artwork, the name and the pills come from the gallery card. */
.connection-mon { color: var(--ink); text-align: center; }
.connection-mon strong {
  overflow: hidden;
  max-width: 100%;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.connection-reason {
  display: -webkit-box;
  margin-top: 8px;
  overflow: hidden;
  color: var(--ink-soft);
  font-size: calc(11px * var(--ts));
  line-height: 1.35;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.connection-collections {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid color-mix(in srgb, var(--modern-border) 65%, transparent);
}
.connection-collections > span {
  color: var(--accent-ink);
  font-size: calc(12px * var(--ts));
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.connection-collection-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 7px;
}
.connection-collections a {
  padding: 7px 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent, var(--brand)) 8%, var(--modern-surface-strong));
  color: var(--ink-soft);
  font-size: calc(10px * var(--ts));
  font-weight: 750;
}


.detail .back-card {
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent !important;
  box-shadow: none;
  overflow: visible;
  /* No entrance animation, deliberately. The header and the Previous/Next bar
     sit outside this element and hold still across a navigation, so a card that
     rose 10px underneath them read as the whole page flinching rather than as
     its contents changing - and every navigation on this page is a content
     change, not an arrival. What moves now is what actually differs: the
     artwork through hero-swap, and the text under it. Putting a rise back here
     is a decision, not a tidy-up; test_hero_swap.mjs pins that. */
}

/* One line that scrolls, matching the Anki card. Without nowrap the shared
   .form-tabs rule wraps instead, and overflow-x below can never fire because
   a wrapping row never overflows - Pikachu's fourteen tabs stacked into five
   rows and read as a blob rather than a control. */
.detail .form-tabs {
  flex-wrap: nowrap;
  /* Never center: on a row that overflows, centering pushes the first tabs off
     the start edge and scrollLeft cannot go negative, so they are unreachable -
     437px of Pikachu's strip at a 431px viewport, and enough to clip BASE even
     on a desktop. The strip is already centred by width:fit-content + margin
     auto whenever the tabs fit, which is what centering was for. */
  justify-content: flex-start;
  box-sizing: border-box;
  width: fit-content;
  max-width: 100%;
  margin: 0 auto 12px;
  padding: 5px;
  border: 1px solid var(--modern-border);
  border-radius: 999px;
  background: var(--modern-surface);
  box-shadow: 0 8px 24px rgba(43, 54, 76, .06);
  overflow-x: auto;
  scrollbar-width: none;
}

.detail .form-tabs::-webkit-scrollbar { display: none; }
.detail .form-tabs:empty { display: none; }

/* Fades on whichever side still has tabs behind it. card.js toggles the
   classes on scroll, so a strip that fits shows neither and keeps its full
   pill. A half-cut label is a hint; this makes it a statement. */
.detail .form-tabs.fade-l {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 24px);
  mask-image: linear-gradient(90deg, transparent 0, #000 24px);
}
.detail .form-tabs.fade-r {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent 100%);
  mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent 100%);
}
.detail .form-tabs.fade-l.fade-r {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
}

.detail .ftab {
  flex: none;            /* scroll the row rather than squeeze the labels */
  min-height: 32px;
  padding: 6px 13px;
  border: 0;
  font-size: calc(12px * var(--ts));
  font-weight: 650;
}

/* Hero is a dedicated product surface instead of the first row of a long card. */
.detail .bc-head {
  --hero-meta-rhythm: 14px;
  position: relative;
  isolation: isolate;
  min-height: 340px;
  /* 12px, matching .bc-body's gap: on narrow the hero is just the first block
     in a vertical stack, so the step down to the next one has to read as the
     same step as every other. Was 18px here against Anki's 14px - the two had
     drifted apart, and neither matched the gap they sat above. */
  margin: 0 0 12px;
  padding: 36px 48px;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--accent) 19%, var(--modern-border));
  border-radius: 30px;
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--accent-soft) 88%, var(--modern-surface-strong)),
    var(--modern-surface-strong)
  );
  box-shadow: var(--modern-shadow);
}

.detail .bc-head > * { position: relative; z-index: 1; }

.detail .back-card[data-w="wide"] .bc-head,
.detail .back-card[data-w="xwide"] .bc-head {
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(320px, 1.1fr);
  column-gap: 42px;
  align-items: center;
  margin-bottom: 28px;
}

.detail .back-card[data-w="wide"] .bc-id,
.detail .back-card[data-w="xwide"] .bc-id {
  grid-column: 1;
  grid-row: 1;
  text-align: left;
}

.detail .back-card[data-w="wide"] .mu-panel,
.detail .back-card[data-w="xwide"] .mu-panel {
  margin-left: 0;
  margin-right: 0;
}

.detail .back-card[data-w="wide"] .bc-head-img,
.detail .back-card[data-w="xwide"] .bc-head-img {
  grid-column: 2;
  grid-row: 1;
  justify-self: center;
  width: 100%;
}

.detail .back-card[data-w="wide"] .bc-head-img .pokemon-image img,
.detail .back-card[data-w="xwide"] .bc-head-img .pokemon-image img {
  width: min(100%, 330px);
  max-height: 300px;
  margin: 0 auto;
  filter: drop-shadow(0 6px 32px color-mix(in srgb, var(--accent) 14%, transparent));
}

.detail .pokemon-header { margin-bottom: 18px; }
.detail .dex-no {
  display: inline-flex;
  align-items: center;
  min-height: 27px;
  margin-bottom: 10px;
  padding: 0 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--modern-surface-strong) 86%, var(--accent) 14%);
  font-size: calc(11px * var(--ts));
  font-weight: 850;
  letter-spacing: .1em;
}

.detail .eng-name {
  margin-bottom: 8px;
  font-size: calc(clamp(34px, 4vw, 52px) * var(--ts));
  line-height: .98;
  letter-spacing: -.035em;
  text-wrap: balance;
}

.detail .jap-box {
  justify-content: flex-start;
  gap: 7px;
  font-size: calc(15px * var(--ts));
}

.detail .species-box {
  justify-content: flex-start;
  margin-top: 9px;
  font-size: calc(14px * var(--ts));
}

.detail .type-row,
.detail .type-box,
.detail .meta-box {
  justify-content: flex-start;
}

.detail .type-row {
  --type-control-height: 28px;
  margin-bottom: var(--hero-meta-rhythm);
}
.detail .type {
  display: inline-flex;
  align-items: center;
  height: var(--type-control-height);
  box-sizing: border-box;
  padding: 0 12px;
  border-radius: 999px;
  /* 11.5px is what the Anki card has always used. The web had drifted half a
     pixel below it for no reason anyone recorded, and it was the only real
     difference between the two type scales. */
  font-size: calc(11.5px * var(--ts));
}
/* On the detail, each type chip opens the matchup table for that type alone, so
   it is a real <button>. Scoped to button.type because the same class dresses
   plain, non-interactive chips in the gallery and the connection lists. */
button.type {
  -webkit-appearance: none;
  appearance: none;
  -webkit-tap-highlight-color: transparent;
  border: 0;
  font-family: inherit;
  cursor: pointer;
  /* Restated because pokedex.css's `.card button { margin: 0 }` reset outranks
     the `margin: 0 3px` on .type - as a <span> the chip kept that spacing, as a
     <button> it lost it, which left the selected ring almost touching the next
     chip. Exactly the original 3px, not more: at that spacing the ring already
     clears its neighbour by 6px, and the row is read far more often with
     nothing selected than with something. */
  margin: 0 3px;
}
/* The ring wears the type's own colour, so the open panel is tied to its source
   by hue as well as by the caret. --type-tone comes from the palette in
   pokedex.css and is already the night value under .nightMode, so this needs no
   dark variant. An outline rather than a box-shadow ring: outline-offset leaves
   the hero background showing through the gap, so no colour has to be matched. */
button.type.on {
  outline: 2px solid var(--type-tone, var(--accent));
  outline-offset: 2px;
}
.detail .mt { border-radius: 999px; }
.detail .mu-toggle {
  width: var(--type-control-height);
  height: var(--type-control-height);
  box-sizing: border-box;
  border-radius: 50%;
  color: var(--accent-ink);
  border-color: color-mix(in srgb, var(--accent) 16%, var(--modern-border));
  background: color-mix(in srgb, var(--accent) 7%, var(--modern-surface-strong));
  box-shadow: 0 3px 10px rgba(40, 48, 64, .07);
  transition: border-color .16s ease, background .16s ease, box-shadow .16s ease;
}
.detail .mu-toggle svg {
  /* 16, not 17: the button is 28px with a 1px border, so the icon sits in a
     26px box and an odd size leaves half a pixel on each side - which the
     renderer then hands to one side, and the shield reads as off-centre
     inside its circle. An even size divides. (The translateY(.5px) that used
     to live here was the same bug on the other axis, patched rather than
     solved; at 16px it would put the half pixel back.) */
  width: 16px;
  height: 16px;
}
.detail .mu-toggle.on {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--accent) 18%, transparent);
}
.detail .meta-box {
  margin-top: 0;
  margin-bottom: 0;
}
.detail .badge-gen,
.detail .badge-region,
.detail .badge-rarity { padding: 5px 10px; font-weight: 720; }

/* Hover for the controls this file restyles. pokedex.css gives every card
   control the same hover, but `.detail .pdx-more` and friends below match with
   equal specificity and land later, so they need saying again here. Same rule:
   border to the tone, background tinted 10%, text untouched. */
@media (hover: hover) {
  .detail .pdx-more:hover,
  .detail .ref-link:hover,
  .detail .shiny-toggle:hover,
  .detail .mu-toggle:hover {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 16%, var(--modern-surface-strong));
  }
  .detail .mu-toggle.on:hover {
    background: color-mix(in srgb, #000 12%, var(--accent));
    border-color: color-mix(in srgb, #000 12%, var(--accent));
  }
  .detail .radar-totals .rt-tap:hover { filter: brightness(0.82); }
  /* Two links that had no hover at all. */
  .connection-collections a:hover {
    background: color-mix(in srgb, var(--accent, var(--brand)) 18%, var(--modern-surface-strong));
  }
  .explore-all:hover { filter: brightness(0.85); }
}

.detail .shiny-toggle {
  position: absolute;
  top: 12px !important;
  right: 12px !important;
  width: 38px;
  height: 38px;
  overflow: visible;
  touch-action: manipulation;
  z-index: 5;
  border-color: color-mix(in srgb, var(--accent) 24%, var(--modern-border));
  background: var(--modern-surface-strong);
  box-shadow: 0 8px 22px rgba(40, 48, 64, .11);
}
.detail .shiny-toggle.on {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 8px 22px color-mix(in srgb, var(--accent) 28%, transparent);
}
.detail .shiny-toggle::before {
  content: "";
  position: absolute;
  inset: -6px;
}

.detail .pokemon-image img.is-changing {
  animation: hero-swap var(--motion-art) var(--motion-ease);
}

/* ---------- bento information grid ---------- */
.detail .bc-body {
  display: grid;
  column-count: initial !important;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "origin"
    "evolution"
    "size"
    "stats"
    "abilities"
    "entries"
    "details"
    "refs";
  gap: 16px;
  align-items: start;
}

.detail .bento-col { display: contents; }
.detail .origins-section { grid-area: origin; }
.detail .evo-section { grid-area: evolution; }
.detail .size-section { grid-area: size; }
.detail .stats-section { grid-area: stats; }
.detail .abil-section { grid-area: abilities; }
.detail .pokedex-section { grid-area: entries; }
.detail .dexdata-section { grid-area: details; }
.detail .ref-section { grid-area: refs; }

.detail .bento-col > * {
  min-width: 0;
  margin: 0 !important;
  padding: 22px;
  break-inside: auto;
  border: 1px solid color-mix(in srgb, var(--accent) 11%, var(--modern-border));
  border-radius: var(--modern-radius);
  background: color-mix(in srgb, var(--modern-surface-strong) 75%, var(--accent-soft));
  box-shadow: var(--modern-shadow-soft);
}

.detail .bento-col > :empty { display: none; }

.detail .origin-label,
.detail .size-title,
.detail .evo-title,
.detail .stats-title,
.detail .abil-title,
.detail .pdx-title,
.detail .dx-title,
.detail .ref-title {
  margin-bottom: 8px;
  color: var(--accent-ink);
  font-size: calc(14px * var(--ts));
  font-weight: 780;
  letter-spacing: .055em;
  text-align: center;
  text-transform: uppercase;
}

/* One leading for every block of prose on the card. Name origin used to sit
   at 1.65 against 1.55 for the entries and the ability text, which reads as an
   accident when the three are stacked in the same column. */
.detail .origin-text {
  font-size: calc(14px * var(--ts));
  line-height: 1.5;
}

.detail .stats-grid,
.detail .dx-grid,
.detail .ref-link {
  border-color: color-mix(in srgb, var(--accent) 16%, var(--modern-border));
  background: color-mix(in srgb, var(--accent-soft) 65%, var(--modern-surface-strong));
}

.detail .origins-section {
  cursor: pointer;
}

.detail .origins-section .origin-block {
  padding: 0;
  border: 0;
}

.detail .origins-section.clamped::after {
  border-radius: 0 0 18px 18px;
  background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--modern-surface-strong) 75%, var(--accent-soft)) 80%);
}

.detail .stats-grid {
  margin: 0;
  border: 0;
  border-radius: 16px;
  background: transparent;
}

.detail .stat-item { padding: 4px 10px; }
.detail .stat-item .label { font-size: calc(11px * var(--ts)); letter-spacing: .07em; }
.detail .stat-item .value {
  white-space: nowrap;
  font-size: calc(clamp(13px, 3.75vw, 15px) * var(--ts));
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

.detail .stats-section {
  display: flex;
  flex-direction: column;
}

.detail .radar > svg { max-width: 300px; }
/* The bars alternative to the hexagon. Six rows on one grid so the labels, the
   rails and the numbers all line up down the column; the rail holds one fill
   normally and two when a comparison is running, which is the case the hexagon
   handles worst. */

/* Five steps on the scale players read a base stat on - drawn in the card's own
   accent at five strengths rather than in five colours of their own. A palette
   from outside fought the tone every card is tinted with: the page said "this
   Pokemon is blue" and the stats answered in purple. Strength says the same
   thing without introducing a second colour system - weak leans toward the rail
   it sits on, strong is the accent at full force.

   The strength arrives as `--w` on each element rather than as a class on the
   row, because in a comparison the two halves of a row are rarely on the same
   step. Both series are ramped; they stay apart by hue, this one in the accent
   and the compared one in its complement.

   Every token in these mixes carries a literal fallback, and the accents are
   read inside each rule rather than hoisted into a variable of their own. A
   color-mix with an undefined argument is not ignored - it is the
   guaranteed-invalid value, and the whole declaration goes with it. It cost two
   rounds: `--accent-contrast` is declared on the card and not at the root, so a
   root-level `--stat-top-2: var(--accent-contrast)` left the compared bars
   transparent and its numbers black; then `--ink-faint` turned out not to exist
   on the Anki card at all, and every stat number there fell back to the body's
   dark navy with no tone in it.

   The strong end has to be lighter than the rail, not merely more of the
   accent. On a dark card the rail and the accent sit at almost the same
   luminance, so mixing one into the other compressed five steps into fourteen
   points of brightness against seventy-three in daylight. Lifting the top of
   the ramp toward white in the dark theme opens it back up; the hue never
   changes. */
.sb-t i.sb-f {
    background: color-mix(in srgb, var(--accent) var(--w, 100%),
                          var(--accent-soft, var(--surface-2)));
}
.sb-t i.sb-f.sb-g {
    background: color-mix(in srgb, var(--accent-contrast) var(--w, 100%),
                          var(--accent-soft, var(--surface-2)));
}
.sb-n { color: color-mix(in srgb, var(--accent) var(--w, 100%), var(--ink-faint, #95a5a6)); }
.sb-n.sb-n2 { color: color-mix(in srgb, var(--accent-contrast) var(--w, 100%), var(--ink-faint, #95a5a6)); }
/* The hexagon has no bars, so its two sets of per-axis numbers carry the ramp. */
.rn { fill: color-mix(in srgb, var(--accent) var(--w, 100%), var(--ink-faint, #95a5a6)); }
.rn2 { fill: color-mix(in srgb, var(--accent-contrast) var(--w, 100%), var(--ink-faint, #95a5a6)); }
html[data-theme="dark"] .sb-t i.sb-f {
    background: color-mix(in srgb, color-mix(in srgb, #fff 22%, var(--accent)) var(--w, 100%),
                          var(--accent-soft, var(--surface-2)));
}
html[data-theme="dark"] .sb-t i.sb-f.sb-g {
    background: color-mix(in srgb, color-mix(in srgb, #fff 22%, var(--accent-contrast)) var(--w, 100%),
                          var(--accent-soft, var(--surface-2)));
}
html[data-theme="dark"] .sb-n { color: color-mix(in srgb, color-mix(in srgb, #fff 22%, var(--accent)) var(--w, 100%), var(--ink-faint, #95a5a6)); }
html[data-theme="dark"] .sb-n.sb-n2 { color: color-mix(in srgb, color-mix(in srgb, #fff 22%, var(--accent-contrast)) var(--w, 100%), var(--ink-faint, #95a5a6)); }
html[data-theme="dark"] .rn { fill: color-mix(in srgb, color-mix(in srgb, #fff 22%, var(--accent)) var(--w, 100%), var(--ink-faint, #95a5a6)); }
html[data-theme="dark"] .rn2 { fill: color-mix(in srgb, color-mix(in srgb, #fff 22%, var(--accent-contrast)) var(--w, 100%), var(--ink-faint, #95a5a6)); }
.sbars { display: grid; gap: 7px; width: 100%; max-width: 300px; margin: 0 auto; }
.sbar {
    display: grid;
    grid-template-columns: 3.6em minmax(0, 1fr) 2.4em;
    align-items: center;
    gap: 9px;
}
.sbars.cmp .sbar { grid-template-columns: 3.6em minmax(0, 1fr) 2.4em 2.4em; }
.sb-k {
    font-size: calc(11px * var(--ts));
    font-weight: 700;
    letter-spacing: .03em;
    color: var(--ink-faint);
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.sb-t {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 3px;
    border-radius: 999px;
    background: var(--accent-soft, var(--surface-2));
}
.sb-t i { display: block; min-width: 3px; height: 8px; border-radius: 999px; }
.sb-n {
    font-size: calc(12.5px * var(--ts));
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.detail .radar-totals { margin-top: 2px; }

.detail .abil,
.detail .pdx-entry {
  position: relative;
  margin-bottom: 0;
  padding: 14px 2px;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}

.detail .abil + .abil::before,
.detail .pdx-entry + .pdx-entry::before {
  content: "";
  position: absolute;
  top: 0;
  right: 4%;
  left: 4%;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent,
    color-mix(in srgb, var(--accent) 16%, var(--modern-border)) 18%,
    color-mix(in srgb, var(--accent) 16%, var(--modern-border)) 82%,
    transparent
  );
}

.detail .abil-en { font-size: calc(15px * var(--ts)); }
.detail .abil-desc,
.detail .pdx-text,
.detail .dx-v { font-size: calc(14px * var(--ts)); line-height: 1.5; }

.detail .pdx-head {
  min-height: 27px;
  margin-bottom: 6px;
}

.detail .pdx-title { margin-bottom: 0; }

.detail .pdx-more {
  min-height: 34px;
  padding: 0 13px;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.detail .dx-grid {
  border: 0;
  border-radius: 16px;
  background: transparent;
}

.detail .dx-item {
  min-height: 39px;
  padding: 9px 0;
  border-color: color-mix(in srgb, var(--accent) 16%, var(--modern-border));
}

.detail .dx-k { font-size: calc(11px * var(--ts)); letter-spacing: .055em; }

.detail .evo {
  min-height: 112px;
}

.detail .evo-node {
  display: grid;
  grid-template-rows: 78px auto;
  align-items: end;
  justify-items: center;
}

.detail .evo-node .ea {
  width: 78px;
  height: 78px;
  object-fit: contain;
  align-self: end;
}
.detail .evo.compact-linear .evo-node {
  grid-template-rows: 70px auto;
  width: 70px;
}
.detail .evo.compact-linear .evo-node .ea {
  width: 70px;
  height: 70px;
}

.detail .ref-links { justify-content: center; }
.detail .ref-link {
  min-height: 36px;
  padding: 0 12px;
  border-radius: 11px;
}

/* Explicit hierarchy at desktop widths. */
.detail .back-card[data-w="wide"] .bc-body {
  display: block;
  column-count: 2 !important;
  column-gap: 18px;
}

.detail .back-card[data-w="wide"] .bento-col {
  display: contents;
}

.detail .back-card[data-w="wide"] .bento-col > * {
  display: inline-block;
  width: 100%;
  margin-bottom: 18px !important;
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
}

.detail .back-card[data-w="xwide"] .bc-body {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr) minmax(0, 1fr);
  grid-template-areas: none;
  gap: 18px;
}

.detail .back-card[data-w="xwide"] .bento-col {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* ---------- loading and motion ---------- */
.detail-skeleton {
  max-width: 1280px;
  margin: 0 auto;
}

.app-status {
  position: fixed;
  z-index: 100;
  left: 50%;
  bottom: max(18px, env(safe-area-inset-bottom));
  width: min(calc(100% - 28px), 420px);
  transform: translateX(-50%);
  pointer-events: none;
}

.app-toast {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  min-height: 46px;
  padding: 10px 12px 10px 16px;
  border: 1px solid var(--modern-border);
  border-radius: 15px;
  color: var(--ink);
  background: color-mix(in srgb, var(--modern-surface-strong) 96%, transparent);
  box-shadow: 0 16px 46px rgba(28, 38, 58, .18);
  backdrop-filter: blur(18px) saturate(1.12);
  opacity: 0;
  transform: translateY(8px) scale(.985);
  transition:
    opacity var(--motion-ui) ease,
    transform var(--motion-ui) var(--motion-ease);
  pointer-events: auto;
}

.app-toast.is-visible {
  opacity: 1;
  transform: none;
}

.app-toast span {
  font-size: calc(13px * var(--ts));
  font-weight: 650;
  line-height: 1.35;
}

.app-toast button {
  min-height: 32px;
  padding: 0 11px;
  border: 0;
  border-radius: 10px;
  background: color-mix(in srgb, var(--brand) 12%, transparent);
  color: var(--brand);
  font: inherit;
  font-size: calc(12px * var(--ts));
  font-weight: 800;
  cursor: pointer;
}

.sk-hero {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  min-height: 340px;
  padding: 40px 48px;
  border: 1px solid var(--modern-border);
  border-radius: 30px;
  background: var(--modern-surface);
  box-shadow: var(--modern-shadow);
}

.sk-copy,
.sk-art,
.sk-grid > span {
  display: block;
  overflow: hidden;
  position: relative;
  background: color-mix(in srgb, var(--line) 64%, var(--surface));
}

.sk-copy { width: 72%; height: 132px; border-radius: 18px; }
.sk-art { justify-self: center; width: 250px; height: 250px; border-radius: 50%; }

.sk-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
  margin-top: 18px;
}

.sk-grid > span { height: 230px; border-radius: var(--modern-radius); }

.sk-copy::after,
.sk-art::after,
.sk-grid > span::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .5), transparent);
  animation: skeleton-shimmer 1.35s infinite;
}

@keyframes hero-swap {
  0% { opacity: .2; transform: translateY(8px) scale(.97); }
  100% { opacity: 1; transform: none; }
}

@keyframes skeleton-shimmer {
  100% { transform: translateX(100%); }
}

@media (max-width: 719px) {
  .gallery-discovery,
  .daily-hero,
  .discovery-section,
  .catalog {
    width: 100%;
    max-width: 100%;
    min-width: 0;
  }
  .gallery-discovery { gap: 20px; margin-bottom: 28px; }
  .daily-hero {
    grid-template-columns: 1fr;
    min-height: 520px;
    padding: 28px 22px 24px;
    border-radius: 24px;
  }
  .daily-copy { z-index: 3; }
  .daily-hero h1 { font-size: calc(clamp(38px, 12vw, 52px) * var(--ts)); }
  .daily-hero p { font-size: calc(13px * var(--ts)); }
  .daily-art {
    align-self: end;
    margin: 10px 0 0;
  }
  .daily-art img {
    width: min(100%, 270px);
    max-height: 250px;
  }
  .daily-actions {
    position: static;
    z-index: 4;
    left: 22px;
    right: 22px;
    bottom: 22px;
    display: grid;
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
    margin-top: 18px;
  }
  /* Two fixed columns, and min-width: 0 lets either go narrower than its own
     label - so a label too long for its column WRAPS rather than overflowing.
     That is deliberate and it is the only safe behaviour here: this is a grid,
     not the flex row above, so `flex-wrap` never applies on a phone and there
     is nothing for a too-wide pair to wrap onto. Adding white-space: nowrap
     looks like the fix and is the opposite - measured across 48 combinations of
     width, text size and language, it spilled the text out of the button in
     five of them and fixed none. Keep the labels short instead; the Vietnamese
     Surprise me was retitled for exactly this reason. */
  .daily-primary,
  .daily-secondary {
    min-width: 0;
    min-height: 40px;
    padding-inline: 12px;
    font-size: calc(11.5px * var(--ts));
  }
  .editorial-paths { grid-template-columns: 1fr; gap: 9px; }
  .editorial-path-card {
    grid-template-columns: 108px minmax(0, 1fr);
    min-height: 126px;
    padding-block: 8px;
  }
  .active-editorial-path {
    margin-inline: 0;
    padding: 18px 12px 14px;
    border-radius: 22px;
  }
  .active-editorial-heading { margin-inline: 4px; }
  .active-editorial-heading h3 { font-size: calc(22px * var(--ts)); }
  .editorial-members { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .keep-exploring { margin-inline: 4px; }
  .discovery-heading,
  .catalog-heading { align-items: baseline; }
  .discovery-heading h2,
  .catalog-heading h2 { font-size: calc(16px * var(--ts)); }
  .discovery-heading span,
  .catalog-heading span { font-size: calc(10px * var(--ts)); }
  .grid,
  .daily-mix { padding-right: 2px; }
  .g-card,
  .discovery-mon,
  .connection-mon {
    min-height: 190px;
    padding: 16px 8px 12px;
  }
  .g-img,
  .discovery-mon img,
  .connection-mon img {
    width: 106px;
    height: 106px;
    margin: 10px 0 8px;
  }
  .container.detail { padding: 2px 12px 24px; }
  .detailbar { margin-bottom: 10px; }
  .detail-connections-shell { margin-top: 26px; }
  .explore-connections {
    padding: 20px 12px 16px;
    border-radius: 22px;
  }
  .connection-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }
  .connections-heading h2 { font-size: calc(13px * var(--ts)); }

  .detail .bc-head {
    display: flex;
    flex-direction: column;
    min-height: 0;
    padding: 24px 18px calc(var(--hero-meta-rhythm) + 1px);
    border-radius: 24px;
  }

  .detail .bc-head-img { order: 1; width: 100%; }
  .detail .bc-id { order: 2; text-align: center; }
  .detail .pokemon-header { margin-bottom: 12px; }
  .detail .meta-box { margin-top: 0; }
  .detail .pokemon-image img {
    width: min(100%, 270px);
    max-height: 270px;
    margin: 0 auto 12px;
    filter: drop-shadow(0 5px 26px color-mix(in srgb, var(--accent) 12%, transparent));
  }
  .detail .shiny-toggle {
    top: 6px !important;
    right: 6px !important;
  }

  .detail .eng-name { font-size: calc(clamp(31px, 10vw, 40px) * var(--ts)); }
  .detail .jap-box,
  .detail .species-box,
  .detail .type-row,
  .detail .type-box,
  .detail .meta-box { justify-content: center; }

  .detail .bc-body { gap: 12px; }
  .detail .bento-col > * { padding: 18px; border-radius: 19px; }
  .detail .stat-item { padding-inline: 6px; }
  .detail .origin-label,
  .detail .size-title,
  .detail .evo-title,
  .detail .stats-title,
  .detail .abil-title,
  .detail .pdx-title,
  .detail .dx-title,
  .detail .ref-title { font-size: calc(13px * var(--ts)); }

  .sk-hero {
    display: flex;
    flex-direction: column-reverse;
    gap: 24px;
    min-height: 480px;
    padding: 30px 20px;
    border-radius: 24px;
  }
  .sk-copy { width: 78%; height: 130px; }
  .sk-art { width: 230px; height: 230px; }
  .sk-grid { grid-template-columns: 1fr; gap: 12px; margin-top: 12px; }
  .sk-grid > span { height: 190px; }
}

@media (max-width: 430px) {
  /* The form strip needs nothing here now: flex-start and the pill radius are
     unconditional above, because the overflow they guard against is not a
     phone-only condition. */
  .detail .bc-head { padding-inline: 14px; }
  .detail .bento-col > * { padding: 16px 14px; }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation-duration: .01ms !important;
    animation-delay: 0ms !important;
  }
  /* Entrance animations off, and nothing travels across the page. */
  .gallery-discovery,
  .active-editorial-path,
  .editorial-path-card,
  .connection-mon,
  .discovery-mon,
  .g-card {
    animation: none !important;
    transform: none !important;
  }
  /* The artwork still answers the pointer: it grows where it stands, which is
     not the kind of motion this setting is asking us to stop. Both families
     named together - listing one and forgetting the other is exactly how the
     catalogue ended up silent while Today's mix kept zooming. */
  .g-cell:hover .g-img,
  .g-cell:hover .discovery-mon img,
  .connection-mon:hover img {
    transform: scale(1.08) !important;
  }
  /* The hero swap is deliberately absent here. It is the one animation that
     carries meaning rather than decoration - it says "this is a different
     Pokemon now" - and the owner wants it seen. Do not add it back without
     asking. */
  /* .detail .back-card is deliberately absent: it has no entrance animation at
     all any more, so an opt-out here would be switching off nothing. See the
     rule itself. */
  .filter-panel:not([hidden]),
  .app-toast,
  .sk-copy::after,
  .sk-art::after,
  .sk-grid > span::after {
    animation: none !important;
  }
  .g-card::before,
  .discovery-mon::before,
  .connection-mon::before { display: none !important; }
  .g-card, .discovery-mon, .connection-mon, .editorial-path-card { transition: none !important; }
  .g-fav:hover { transform: none !important; }
  .g-fav { transition: none !important; }
}

/* Motion, as a setting rather than only as an OS preference. The block above
   answers `prefers-reduced-motion`; this answers the reader who asked for it
   here, on a machine whose OS says nothing. A blanket rule rather than a copy
   of that selector list: two lists drift, and the catalogue once kept zooming
   for a year because one of them had been forgotten.

   The panel writes data-motion="reduced" on <html>. */
:root[data-motion="reduced"] *,
:root[data-motion="reduced"] *::before,
:root[data-motion="reduced"] *::after {
  animation-duration: .01ms !important;
  animation-iteration-count: 1 !important;
  animation-delay: 0ms !important;
  transition-duration: .01ms !important;
  transition-delay: 0ms !important;
}
:root[data-motion="reduced"] {
  scroll-behavior: auto !important;
}
/* No exception here, unlike the media query above. That one keeps the artwork
   growing under the pointer because zooming in place is feedback rather than
   travel - but it is also the most visible movement on the page, and a reader
   who turns this on and sees the catalogue still swelling concludes the setting
   does nothing. An explicit choice gets an obvious answer; the cards still
   respond through their border and shadow, which do not move.

   Every card family is named, not just the catalogue: Today's mix and the
   editorial paths lift on hover through rules of their own, and listing one
   and forgetting the others is exactly how this setting came to look broken. */
:root[data-motion="reduced"] .g-cell:hover .g-card,
:root[data-motion="reduced"] .g-cell:hover .g-img,
:root[data-motion="reduced"] .g-cell:hover .discovery-mon,
:root[data-motion="reduced"] .g-cell:hover .discovery-mon img,
:root[data-motion="reduced"] .connection-mon:hover,
:root[data-motion="reduced"] .connection-mon:hover img,
:root[data-motion="reduced"] .editorial-path-card:hover,
:root[data-motion="reduced"] .editorial-path-card:hover .editorial-path-art,
:root[data-motion="reduced"] .daily-hero:hover,
:root[data-motion="reduced"] .evo-node[href]:hover,
:root[data-motion="reduced"] .g-fav:hover {
  transform: none !important;
}
/* Belt and braces: celebrate() already declines to build a burst under either
   reduced-motion signal, but a page that had the setting switched on mid-flight
   should not finish the one already running. */
:root[data-motion="reduced"] .fav-burst { display: none !important; }
:root[data-motion="reduced"] .g-fav.pop svg,
:root[data-motion="reduced"] .nav-fav.pop svg { animation: none !important; }
/* The glow that follows the pointer across a card. The listener that feeds it
   is bound once at boot and cannot be unbound when the setting changes, so the
   effect is switched off where it is drawn instead - and it comes straight back
   when the setting does. */
:root[data-motion="reduced"] .g-card::before,
:root[data-motion="reduced"] .discovery-mon::before,
:root[data-motion="reduced"] .connection-mon::before,
:root[data-motion="reduced"] .g-card.is-pointer-active::before,
:root[data-motion="reduced"] .discovery-mon.is-pointer-active::before,
:root[data-motion="reduced"] .connection-mon.is-pointer-active::before {
  opacity: 0 !important;
  background: none !important;
}

/* Text size, as one multiplier rather than a list of the handful of elements
   somebody remembered. Every font-size in the three stylesheets is written as
   `calc(Npx * var(--ts))`, so a reader who asks for larger text gets it on the
   gallery, the filters and the footer too - the old rules named eight selectors
   inside .detail and one on the gallery, which is why the grid looked untouched.

   Text only: artwork, grid columns and the settings panel keep their sizes, so
   the page holds its shape. Anyone who wants everything bigger has browser zoom,
   which is a different tool for a different want. */
:root { --ts: 1; }
:root[data-density="small"] { --ts: .9; }
:root[data-density="large"] { --ts: 1.14; }
:root[data-density="xlarge"] { --ts: 1.3; }

/* The padding follows the type a little, or large text sits in a box built for
   smaller text and the card reads as cramped rather than as larger. */
:root[data-density="small"] .detail .bento-col > * { padding: 11px 12px; }
:root[data-density="small"] .detail .bc-head { padding: 12px 12px 10px; }
:root[data-density="small"] .detail .abil { margin-bottom: 8px; }
:root[data-density="small"] .detail .dx-item { padding-block: 5px; }
:root[data-density="small"] .detail .pdx-entry { margin-bottom: 9px; }
:root[data-density="small"] .gallery-discovery { gap: 16px; }
:root[data-density="small"] .g-card,
:root[data-density="small"] .discovery-mon,
:root[data-density="small"] .connection-mon { padding: 8px 8px 10px; }
:root[data-density="large"] .detail .bento-col > * { padding: 18px 16px; }
:root[data-density="xlarge"] .detail .bento-col > * { padding: 20px 17px; }
:root[data-density="large"] .g-card,
:root[data-density="large"] .discovery-mon,
:root[data-density="large"] .connection-mon { padding: 12px 10px 14px; }
:root[data-density="xlarge"] .g-card,
:root[data-density="xlarge"] .discovery-mon,
:root[data-density="xlarge"] .connection-mon { padding: 13px 11px 15px; }
