/*
 * CozyCasting site styles. One theme, no toggle.
 *
 * Direction: the waterline is the fold. Each page's hero sits above the
 * surface at dawn; scrolling takes you under, and the page keeps darkening as
 * it goes. Everything is CSS, inline SVG and two self-hosted fonts — no
 * third-party requests. Every motion is behind prefers-reduced-motion.
 *
 * Layers, top to bottom of this file: tokens → base → the sky and the water →
 * the shell (header, menus, footer) → shared components (buttons, cards,
 * chips, stat tiles, pager, breadcrumb, toc) → page sections → motion.
 * Pages add sections; they never redefine a component.
 *
 * The --cc-rarity-* tokens and --cc-wood are hand-copied from bot/constants.py
 * (RARITY_COLORS and COLOR_EQUIPMENT). tests/test_palette_css.py fails when the
 * two sides disagree, so drift is a CI failure rather than a visual bug.
 */

/* Same-origin navigations cross-fade instead of flashing white. Cannot live
   inside a media query — it is a top-level at-rule — so its reduced-motion
   escape hatch is the ::view-transition-* block at the end of this file. */
@view-transition { navigation: auto; }

/* Self-hosted, Latin subset, both SIL OFL (api/static/fonts/). The CSP has no
   font-src, so it falls back to default-src 'self' and these just work. */
@font-face {
  font-family: "Fredoka";
  src: url("/fonts/fredoka-latin.woff2") format("woff2");
  font-weight: 300 700;
  font-display: swap;
}

@font-face {
  font-family: "Nunito";
  src: url("/fonts/nunito-latin.woff2") format("woff2");
  font-weight: 200 1000;
  font-display: swap;
}

:root {
  --cc-navy-deep: #0B1D33;   /* just under the surface */
  --cc-navy:      #14304F;   /* panels, cards */
  --cc-sky:       #2C5F8A;   /* accents, rules */
  --cc-wood:      #8B4513;   /* COLOR_EQUIPMENT */
  --cc-wood-light:#B5651D;   /* the warm thing: dawn, ticks */
  --cc-foam:      #E8F1F5;   /* primary text */

  --cc-rarity-common:    #9E9E9E;
  --cc-rarity-uncommon:  #4CAF50;
  --cc-rarity-rare:      #2196F3;
  --cc-rarity-epic:      #9C27B0;
  --cc-rarity-legendary: #FF9800;
  --cc-rarity-mythical:  #E91E63;
  --cc-rarity-unique:    #FFD700;

  --cc-abyss: #040C16;       /* the bottom of the page */
  --cc-muted: #9AB3C7;
  --cc-rule:  rgba(120, 170, 215, 0.16);
  --cc-rule-strong: rgba(120, 170, 215, 0.32);

  /* v2 accents. Sea-glass teal is the call to action (7.9:1 on --cc-abyss as
     text, and the button text on it is the abyss itself); dawn amber is for
     eyebrows and small highlights. */
  --cc-accent:      #5EE0C4;
  --cc-accent-deep: #2BB59A;
  --cc-accent-ink:  #03211C;
  --cc-sun:         #F4B26B;

  --cc-card:        rgba(18, 44, 74, 0.62);
  --cc-card-solid:  #102742;
  --cc-card-hover:  rgba(26, 58, 94, 0.78);
  --cc-glass-edge:  rgba(255, 255, 255, 0.06);

  --font-display: "Fredoka", "Nunito", ui-rounded, system-ui, sans-serif;
  --font-body: "Nunito", ui-rounded, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, Consolas, monospace;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4.5rem;

  --radius-sm: 8px;
  --radius: 14px;
  --radius-lg: 22px;
  --radius-pill: 999px;

  --shadow-1: 0 1px 2px rgba(2, 8, 16, 0.4), 0 4px 14px rgba(2, 8, 16, 0.25);
  --shadow-2: 0 10px 30px rgba(2, 8, 16, 0.45), 0 2px 6px rgba(2, 8, 16, 0.35);

  --rail: 8.5rem;
  --gutter: clamp(1rem, 4vw, 1.5rem);
  --measure: 36rem;
  --page: 74rem;
  --header-h: 4rem;
  --hero-h: clamp(26rem, 80vh, 68rem);
  --surface-h: 8rem;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--cc-abyss);
  color: var(--cc-foam);
  font: 400 1rem/1.65 var(--font-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  /* The water takes whatever height is left over, so a short page never runs
     out of water and shows the dawn under the footer. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 600; letter-spacing: -0.01em; }

a { color: inherit; }

img { max-width: 100%; }

:focus-visible {
  outline: 2px solid var(--cc-accent);
  outline-offset: 3px;
  border-radius: 4px;
}

::selection { background: rgba(94, 224, 196, 0.35); color: #fff; }

.icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  vertical-align: -0.2em;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* =========================================================================
   Above the waterline: a fixed dawn sky behind the hero.
   ========================================================================= */

.sky {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Named, so the dawn does not cross-fade with itself between pages. Both
     this and .wordmark are single elements on every page, which is what a
     view-transition-name requires. .deep and .hero stay unnamed on purpose —
     they differ per page and should cross-fade. */
  view-transition-name: sky;
  background:
    radial-gradient(120% 50% at 50% 74%, rgba(226, 145, 62, 0.60) 0%, rgba(181, 101, 29, 0.16) 42%, transparent 68%),
    linear-gradient(180deg, #08182B 0%, #14304F 46%, #34617F 78%, #618DA8 100%);
}

/* =========================================================================
   Below the waterline. The block owns its own water so the surface can never
   drift out of alignment with it.
   ========================================================================= */

.deep {
  position: relative;
  /* The surface SVG is 200% wide so `swell` can loop by translating -50%. That
     overflow still counts toward the document's scroll width, which `body`'s
     overflow-x: hidden hides on screen but not from anything measuring the
     page — a full-page screenshot comes out half empty on the right.
     `clip`, not `hidden`: hidden on one axis forces the other to `auto` and
     would make this a scroll container, breaking the fixed-attachment ambient
     layers inside it. */
  overflow-x: clip;
  /* The water always reaches the bottom of the viewport: body is a flex column
     at 100vh and this is the growing child, so it fills whatever the header
     and hero leave — whatever height they happen to be. */
  flex: 1;
  margin-top: calc(var(--surface-h) * -1);
  padding-top: var(--surface-h);
  background:
    linear-gradient(180deg,
      var(--cc-navy-deep) 0%,
      #0A1B30 24%,
      #071426 58%,
      var(--cc-abyss) 100%);
  /* After the shorthand, never before: `background` resets the clip. The water
     starts below the surface strip, so the wave is the only seam. */
  background-clip: content-box;
}

/* The god rays and marine snow. A viewport-sized *fixed* layer whose children
   animate only `transform`/`opacity`, so the compositor slides a cached
   texture instead of repainting the whole underwater block every frame (#174:
   the old page-tall, fixed-attachment layers ran the landing page at 9 fps).
   It fades in as you scroll under the surface, so it never paints over the
   dawn sky above the waterline. */
.ambient {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  contain: strict;
  overflow: hidden;
  opacity: 0;
}

/* Browsers without scroll timelines get the layer over the water only. */
@supports not (animation-timeline: scroll()) {
  .ambient { position: absolute; inset: var(--surface-h) 0 0 0; opacity: 1; }
  .ambient > * { max-height: 140vh; }
}

.ambient > * { position: absolute; will-change: transform; backface-visibility: hidden; }

/* Light through the surface, fading out with depth. */
.shafts {
  inset: -10vh -20vw;
  opacity: 0.55;
  background-image:
    linear-gradient(103deg, transparent 43%, rgba(163, 209, 240, 0.13) 47%, transparent 52%),
    linear-gradient(94deg, transparent 21%, rgba(163, 209, 240, 0.09) 26%, transparent 32%),
    linear-gradient(84deg, transparent 65%, rgba(163, 209, 240, 0.10) 70%, transparent 76%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, transparent 70%);
  mask-image: linear-gradient(180deg, #000 0%, transparent 70%);
}

/* Marine snow rising through the beam. Soft-edged, because a hard 1px dot
   moving slower than a pixel per frame visibly snaps from pixel to pixel.
   One large tile of scattered motes of
   mixed size and brightness — smaller, evenly sized tiles read as a grid. The
   layer is one tile taller than the viewport, so a one-tile translate loops
   seamlessly. */
.motes {
  top: 0;
  left: 0;
  right: 0;
  height: calc(100vh + 720px);
  opacity: 0.7;
  background-image:
    radial-gradient(3px 3px at 45.4% 55.7%, rgba(232, 241, 245, 0.65) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2.5px 2.5px at 50.8% 58.4%, rgba(232, 241, 245, 0.65) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2px 2px at 62.5% 78.1%, rgba(232, 241, 245, 0.65) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2px 2px at 31.1% 10.7%, rgba(232, 241, 245, 0.65) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(3.5px 3.5px at 94.6% 64.8%, rgba(232, 241, 245, 0.45) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2px 2px at 61.8% 81.8%, rgba(232, 241, 245, 0.35) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(3.5px 3.5px at 5.4% 86.4%, rgba(232, 241, 245, 0.35) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(3.5px 3.5px at 76.7% 33.3%, rgba(232, 241, 245, 0.45) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(3px 3px at 51.8% 63.5%, rgba(232, 241, 245, 0.35) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2.5px 2.5px at 65.6% 45.9%, rgba(232, 241, 245, 0.65) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2px 2px at 97.8% 97.6%, rgba(232, 241, 245, 0.55) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2.5px 2.5px at 32.3% 24.0%, rgba(232, 241, 245, 0.35) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(3px 3px at 8.7% 75.6%, rgba(232, 241, 245, 0.35) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2px 2px at 83.3% 39.1%, rgba(232, 241, 245, 0.35) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2px 2px at 22.5% 91.0%, rgba(232, 241, 245, 0.65) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(3px 3px at 38.0% 70.1%, rgba(232, 241, 245, 0.35) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2.5px 2.5px at 56.4% 21.1%, rgba(232, 241, 245, 0.55) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(3px 3px at 10.4% 33.9%, rgba(232, 241, 245, 0.35) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2px 2px at 14.9% 69.9%, rgba(232, 241, 245, 0.35) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(3.5px 3.5px at 46.6% 48.7%, rgba(232, 241, 245, 0.45) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(2.5px 2.5px at 45.0% 20.3%, rgba(232, 241, 245, 0.65) 0%, rgba(232, 241, 245, 0) 100%),
    radial-gradient(3px 3px at 63.8% 13.2%, rgba(232, 241, 245, 0.45) 0%, rgba(232, 241, 245, 0) 100%);
  background-size: 720px 720px;
}

/* =========================================================================
   The shell: header, menus, mobile sheet
   ========================================================================= */

.eyebrow {
  margin: 0 0 0.9rem;
  font: 700 0.74rem/1.2 var(--font-body);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cc-sun);
}

/* Sticky, and the one blurred surface on the page: a single header-height
   backdrop-filter is cheap; fifty of them on feed cards were not (#174). */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(8, 24, 43, 0.72);
  -webkit-backdrop-filter: blur(10px) saturate(1.3);
  backdrop-filter: blur(10px) saturate(1.3);
  border-bottom: 1px solid var(--cc-rule);
}

.header-inner {
  display: flex;
  align-items: center;
  gap: 1rem;
  max-width: var(--page);
  height: var(--header-h);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  flex: none;
  font: 600 1.28rem/1 var(--font-display);
  letter-spacing: -0.01em;
  text-decoration: none;
  view-transition-name: wordmark;
}

.wordmark img {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  box-shadow: 0 0 0 2px rgba(244, 178, 107, 0.35);
}

.wordmark b { font-weight: 600; color: var(--cc-sun); }

.site-nav { flex: 1; min-width: 0; }

.site-nav ul {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav-link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.5rem 0.7rem;
  border: 0;
  border-radius: var(--radius-pill);
  background: none;
  color: rgba(232, 241, 245, 0.82);
  font: 600 0.95rem/1 var(--font-body);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease;
}

.nav-link:hover,
.nav-trigger[aria-expanded="true"] { background: rgba(94, 224, 196, 0.1); color: var(--cc-foam); }

.nav-link[aria-current="page"],
.nav-trigger[data-current="true"] { color: var(--cc-foam); background: rgba(94, 224, 196, 0.14); }

.icon-chevron { width: 0.9em; height: 0.9em; opacity: 0.7; transition: transform 150ms ease; }
.nav-trigger[aria-expanded="true"] .icon-chevron { transform: rotate(180deg); }

.icon-ext { width: 0.75em; height: 0.75em; margin-left: 0.25em; opacity: 0.55; vertical-align: 0; }

/* ---- dropdown menus: native popovers, anchored under their trigger ---- */

.menu {
  /* Undo the UA popover centring. */
  inset: auto;
  margin: 0.55rem 0 0;
  position-area: bottom span-right;
  position-try-fallbacks: flip-inline;
  width: 19rem;
  max-width: calc(100vw - 1rem);
  max-height: calc(100vh - var(--header-h) - 2rem);
  overflow: auto;
  padding: 0.5rem;
  color: var(--cc-foam);
  background: #0E2440;
  border: 1px solid var(--cc-rule-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
}

.menu-wide {
  width: min(40rem, calc(100vw - 1rem));
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.menu:not(:popover-open) { display: none; }

.menu-item {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.6rem 0.7rem;
  border-radius: var(--radius-sm);
  text-decoration: none;
  transition: background-color 120ms ease;
}

.menu-item:hover,
.menu-item:focus-visible { background: rgba(94, 224, 196, 0.1); }

.menu-icon {
  width: 2.1rem;
  height: 2.1rem;
  padding: 0.45rem;
  border-radius: 10px;
  color: var(--cc-accent);
  background: rgba(94, 224, 196, 0.1);
  border: 1px solid rgba(94, 224, 196, 0.18);
}

.menu-item b { display: block; font: 600 0.98rem/1.3 var(--font-display); }
.menu-item small { display: block; margin-top: 0.1rem; color: var(--cc-muted); font-size: 0.82rem; line-height: 1.35; }

.header-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-left: auto;
}

.header-support {
  display: inline-grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 50%;
  color: rgba(232, 241, 245, 0.8);
  transition: background-color 150ms ease, color 150ms ease;
}

.header-support:hover { background: rgba(88, 101, 242, 0.25); color: #fff; }
.header-support .icon { width: 1.35rem; height: 1.35rem; }

.icon-button {
  display: inline-grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: 1px solid var(--cc-rule-strong);
  border-radius: 50%;
  background: rgba(20, 48, 79, 0.6);
  color: var(--cc-foam);
  cursor: pointer;
}

.icon-button .icon { width: 1.25rem; height: 1.25rem; }

.menu-toggle { display: none; }

/* ---- the phone menu: a sheet from the right ---- */

.sheet {
  inset: 0 0 0 auto;
  width: min(24rem, 100vw);
  height: 100dvh;
  max-height: none;
  margin: 0;
  padding: 1rem 1.1rem 2rem;
  overflow-y: auto;
  color: var(--cc-foam);
  background: #0B1E36;
  border: 0;
  border-left: 1px solid var(--cc-rule-strong);
  box-shadow: var(--shadow-2);
}

.sheet::backdrop { background: rgba(2, 8, 16, 0.6); }

.sheet-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem; }
.sheet-title { font: 600 1.2rem/1 var(--font-display); }

.sheet-top {
  display: block;
  padding: 0.7rem 0.2rem;
  border-bottom: 1px solid var(--cc-rule);
  font: 600 1.05rem/1.2 var(--font-display);
  text-decoration: none;
}

.sheet-label {
  margin: 1.2rem 0 0.5rem;
  font: 700 0.72rem/1 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cc-sun);
}

.sheet-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.4rem; }

.sheet-grid a {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.5rem;
  border-radius: var(--radius-sm);
  background: rgba(20, 48, 79, 0.5);
  font-weight: 600;
  font-size: 0.92rem;
  text-decoration: none;
}

.sheet-grid .menu-icon { width: 1.8rem; height: 1.8rem; padding: 0.35rem; }

.sheet .button-block { margin-top: 1.5rem; }

@media (max-width: 80rem) {
  .header-actions .header-support { display: none; }
}

@media (max-width: 70rem) {
  .site-nav, .header-actions .header-invite { display: none; }
  .header-actions .menu-toggle { display: inline-grid; }
}

@media (max-width: 30rem) {
  .signin-long { display: none; }
}

@media (max-width: 30rem) {
  .wordmark span { font-size: 1.12rem; }
  .wordmark img { width: 32px; height: 32px; }
}

/* ---- sign-in ----

   The header slot ships empty: nobody who is signed in should watch a Sign-in
   button flash on the way to their own name. While it is empty it holds a
   quiet block roughly the size of the button, so the header does not jump. */

.auth-slot:empty {
  display: inline-block;
  width: 6.5rem;
  height: 2.2rem;
  border-radius: var(--radius-pill);
  background: rgba(44, 95, 138, 0.18);
}

@media (prefers-reduced-motion: no-preference) {
  .auth-slot:empty { animation: auth-pulse 1.6s ease-in-out infinite; }
}

@keyframes auth-pulse {
  50% { opacity: 0.5; }
}

.signin,
.signout {
  display: inline-block;
  padding: 0.5rem 0.95rem;
  border: 1px solid var(--cc-rule-strong);
  border-radius: var(--radius-pill);
  background: rgba(20, 48, 79, 0.6);
  color: var(--cc-foam);
  font: 700 0.85rem/1 var(--font-body);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

.signin:hover,
.signout:hover { border-color: var(--cc-accent); }

.whoami { display: inline-flex; align-items: center; gap: 0.55rem; }
.whoami-name { font-weight: 700; font-size: 0.9rem; max-width: 9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.avatar { width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--cc-rule-strong); }

@media (max-width: 30rem) {
  .whoami-name { display: none; }
  .signout { padding: 0.45rem 0.7rem; }
  .auth-slot:empty { width: 4.5rem; }
}

/* =========================================================================
   Hero — above water
   ========================================================================= */

.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  min-height: calc(var(--hero-h) - var(--header-h));
  max-width: var(--page);
  margin: 0 auto;
  padding: 3rem var(--gutter) calc(var(--surface-h) + 3rem);
}

.hero h1 {
  margin: 0 0 1.2rem;
  font: 600 clamp(2.3rem, 6.2vw, 4.2rem)/1.04 var(--font-display);
  letter-spacing: -0.02em;
  text-shadow: 0 2px 24px rgba(4, 12, 22, 0.45);
  text-wrap: balance;
}

/* A page title band: the dawn, then straight under. */
.hero-short { min-height: 0; padding-top: 3.5rem; padding-bottom: calc(var(--surface-h) + 1.5rem); }

/* No hero at all: a sliver of sky so the wave still has something to break. */
.hero-band { min-height: 0; padding: 0 0 var(--surface-h); }

.lede {
  max-width: var(--measure);
  margin: 0;
  font-size: clamp(1.02rem, 2.4vw, 1.18rem);
  color: #D5E4EE;
  text-wrap: pretty;
}

.cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.8rem;
  margin: 2rem 0 0;
}

.cta-note {
  font: 700 0.76rem/1 var(--font-body);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(232, 241, 245, 0.62);
}

/* =========================================================================
   Components. Pages compose these; they do not redefine them.
   ========================================================================= */

/* ---- buttons ---- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.8rem 1.4rem;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: linear-gradient(180deg, #74EBD1, var(--cc-accent) 45%, var(--cc-accent-deep));
  box-shadow: 0 6px 22px rgba(43, 181, 154, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.35);
  color: var(--cc-accent-ink);
  font: 700 1rem/1.1 var(--font-body);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: transform 160ms ease, box-shadow 160ms ease, filter 160ms ease;
}

.button:hover { filter: brightness(1.06); box-shadow: 0 10px 28px rgba(43, 181, 154, 0.38), inset 0 1px 0 rgba(255, 255, 255, 0.35); }

.button-ghost {
  background: rgba(11, 29, 51, 0.45);
  border-color: var(--cc-rule-strong);
  box-shadow: none;
  color: var(--cc-foam);
}

.button-ghost:hover { background: rgba(44, 95, 138, 0.4); border-color: var(--cc-accent); box-shadow: none; }

.button-small { padding: 0.55rem 1rem; font-size: 0.9rem; }
.button-block { display: flex; width: 100%; }
.button .icon { width: 1.2em; height: 1.2em; }

@media (prefers-reduced-motion: no-preference) {
  .button:hover { transform: translateY(-2px); }
}

/* A button that reads as a link: it changes what is on screen, not where you
   are, so it must be a <button> — but it is not the page's action. */
.link-button {
  padding: 0;
  font: 700 0.8rem/1.2 var(--font-body);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cc-muted);
  text-decoration: none;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--cc-rule-strong);
  cursor: pointer;
}

.link-button:hover { color: var(--cc-foam); border-bottom-color: var(--cc-accent); }

/* ---- cards ---- */

.card {
  position: relative;
  padding: 1.15rem 1.25rem;
  background: var(--cc-card);
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  box-shadow: inset 0 1px 0 var(--cc-glass-edge);
}

.card[data-rarity],
.card.has-rarity { border-left: 3px solid var(--cc-rarity, var(--cc-sky)); }

/* A whole-card link: lifts, and a diagonal light sweeps across on hover. */
.card-link {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  overflow: hidden;
  color: inherit;
  text-decoration: none;
  transition: transform 180ms ease, border-color 180ms ease, background-color 180ms ease;
}

.card-link::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(110deg, transparent 30%, rgba(255, 255, 255, 0.07) 45%, transparent 60%);
  transform: translateX(-110%);
}

.card-link:hover,
.card-link:focus-visible { border-color: var(--cc-rarity, rgba(94, 224, 196, 0.5)); background: var(--cc-card-hover); }

@media (prefers-reduced-motion: no-preference) {
  .card-link:hover,
  .card-link:focus-visible { transform: translateY(-3px); }
  .card-link:hover::after { transform: translateX(110%); transition: transform 700ms ease; }
}

.card-title { margin: 0; font: 600 1.12rem/1.25 var(--font-display); overflow-wrap: anywhere; }
.card-blurb { margin: 0; color: var(--cc-muted); font-size: 0.92rem; line-height: 1.5; }

.grid-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
  gap: 1rem;
}

/* ---- chips ---- */

.chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0; padding: 0; list-style: none; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--cc-rule-strong);
  border-radius: var(--radius-pill);
  background: rgba(20, 48, 79, 0.55);
  color: #CFE0EB;
  font: 700 0.76rem/1.35 var(--font-body);
  white-space: nowrap;
  text-decoration: none;
}

a.chip:hover { border-color: var(--cc-accent); color: var(--cc-foam); }

.chip-rarity {
  color: var(--cc-rarity, var(--cc-muted));
  border-color: color-mix(in srgb, var(--cc-rarity, var(--cc-sky)) 55%, transparent);
  background: color-mix(in srgb, var(--cc-rarity, var(--cc-sky)) 14%, transparent);
  text-transform: capitalize;
}

.chip-accent { color: var(--cc-accent); border-color: rgba(94, 224, 196, 0.4); background: rgba(94, 224, 196, 0.08); }
.chip-sun { color: var(--cc-sun); border-color: rgba(244, 178, 107, 0.4); background: rgba(244, 178, 107, 0.08); }

/* The rarity accent, bound by class rather than by an inline `style` (#98). */
.rarity-common    { --cc-rarity: var(--cc-rarity-common); }
.rarity-uncommon  { --cc-rarity: var(--cc-rarity-uncommon); }
.rarity-rare      { --cc-rarity: var(--cc-rarity-rare); }
.rarity-epic      { --cc-rarity: var(--cc-rarity-epic); }
.rarity-legendary { --cc-rarity: var(--cc-rarity-legendary); }
.rarity-mythical  { --cc-rarity: var(--cc-rarity-mythical); }
.rarity-unique    { --cc-rarity: var(--cc-rarity-unique); }

/* ---- stat tiles ---- */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(10.5rem, 100%), 1fr));
  gap: 0.75rem;
  margin: 0;
}

.stat-tile {
  padding: 0.9rem 1rem;
  background: var(--cc-card);
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
}

.stat-tile dt,
.stat-label {
  font: 700 0.72rem/1.3 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cc-muted);
}

.stat-tile dd,
.stat-value {
  margin: 0.3rem 0 0;
  font: 600 1.3rem/1.2 var(--font-display);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* ---- pager, breadcrumb, toc ---- */

.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.75rem 1.25rem;
  margin-top: 1.75rem;
  min-height: 2.6rem;
}

.pager-label {
  font: 700 0.78rem/1 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cc-muted);
}

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 1rem;
  padding: 0;
  list-style: none;
  font-size: 0.88rem;
  color: var(--cc-muted);
}

.breadcrumb li + li::before { content: "/"; margin-right: 0.4rem; opacity: 0.5; }
.breadcrumb a { text-decoration: none; }
.breadcrumb a:hover { color: var(--cc-foam); text-decoration: underline; }

.toc {
  font-size: 0.9rem;
}

.toc-title {
  margin: 0 0 0.6rem;
  font: 700 0.72rem/1 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cc-sun);
}

.toc ol, .toc ul { margin: 0; padding: 0; list-style: none; }

.toc a {
  display: block;
  padding: 0.3rem 0 0.3rem 0.8rem;
  border-left: 2px solid var(--cc-rule);
  color: var(--cc-muted);
  text-decoration: none;
  line-height: 1.35;
}

.toc a:hover { color: var(--cc-foam); }
.toc a[aria-current="true"] { color: var(--cc-foam); border-left-color: var(--cc-accent); }
.toc .toc-sub a { padding-left: 1.6rem; font-size: 0.85rem; }

/* ---- page layout under the water ---- */

.page {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--page);
  margin: 0 auto;
  padding: 1rem var(--gutter) 5rem;
}

.page-section { padding: 2.5rem 0; scroll-margin-top: calc(var(--header-h) + 1rem); }

.section-head { max-width: 44rem; margin-bottom: 1.75rem; }

.section-title {
  margin: 0 0 0.6rem;
  font: 600 clamp(1.65rem, 3.8vw, 2.35rem)/1.12 var(--font-display);
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.section-blurb {
  max-width: var(--measure);
  margin: 0;
  color: var(--cc-muted);
  font-size: 1.02rem;
  text-wrap: pretty;
}

.empty {
  margin: 0;
  padding: 2.5rem 1.5rem;
  border: 1px dashed var(--cc-rule-strong);
  border-radius: var(--radius);
  text-align: center;
  color: var(--cc-muted);
}

/* Skeleton shimmer for panels waiting on a fetch. */
.skeleton {
  display: block;
  min-height: 1em;
  border-radius: var(--radius-sm);
  background: rgba(44, 95, 138, 0.2);
}

@media (prefers-reduced-motion: no-preference) {
  .skeleton { animation: auth-pulse 1.6s ease-in-out infinite; }
}

/* =========================================================================
   The surface
   ========================================================================= */

.surface {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 2;
  height: var(--surface-h);
}

.surface svg {
  display: block;
  width: 200%;
  height: 100%;
  overflow: visible;
}

/* Front wave is the water itself; the back wave is the swell behind it, and
   the crest is the light catching the top of the surface. */
.wave-front { fill: var(--cc-navy-deep); }
.wave-back { fill: rgba(24, 61, 96, 0.85); }

.crest {
  fill: none;
  stroke: rgba(196, 226, 240, 0.55);
  stroke-width: 2;
  vector-effect: non-scaling-stroke;
}

.waterline-label {
  position: absolute;
  right: var(--gutter);
  bottom: 0.6rem;
  font: 700 0.66rem/1 var(--font-body);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(143, 167, 188, 0.55);
}

/* =========================================================================
   The depth rail and the sections under it
   ========================================================================= */

.column {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  max-width: var(--page-max, 66rem);
  margin: 0 auto;
  padding: 0 var(--gutter) 5rem;
}

.column > section {
  grid-column: 2;
  padding: 3.5rem 0 3.5rem 2.25rem;
  border-left: 1px solid var(--cc-rule);
  /* Anchors land below the wave rather than under it. */
  scroll-margin-top: 2rem;
}


.section-title {
  margin: 0 0 0.75rem;
  font: 600 clamp(1.6rem, 3.6vw, 2.1rem)/1.15 var(--font-display);
  text-wrap: balance;
}

.section-blurb {
  max-width: var(--measure);
  margin: 0;
  color: var(--cc-muted);
  text-wrap: pretty;
}

/* =========================================================================
   Discord mockups (#176). Built from DOM with Discord's dark palette as local
   tokens — never a screenshot of Discord UI. Copy mirrors the bot's embed
   builders; a mockup that disagrees with the bot is the mockup's bug.
   ========================================================================= */

.dc {
  --dc-bg: #313338;
  --dc-embed: #2b2d31;
  --dc-text: #dbdee1;
  --dc-muted: #949ba4;
  --dc-link: #00a8fc;
  --dc-blurple: #5865f2;
  --dc-green: #248046;
  --dc-grey: #4e5058;
  margin: 0;
  overflow: hidden;
  background: var(--dc-bg);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  color: var(--dc-text);
  font: 400 0.95rem/1.375 var(--font-body);
  text-align: left;
}

.dc-bar {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid rgba(0, 0, 0, 0.3);
  color: #f2f3f5;
  font-weight: 700;
  font-size: 0.95rem;
}

.dc-hash { color: var(--dc-muted); font-size: 1.25rem; font-weight: 500; line-height: 1; }

.dc-log { display: flex; flex-direction: column; gap: 1rem; padding: 1rem 1rem 1.1rem; }

.dc-msg { display: flex; gap: 0.85rem; min-width: 0; }

.dc-avatar {
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 50%;
}

.dc-avatar-user {
  display: grid;
  place-items: center;
  background: linear-gradient(135deg, #f0a35e, #d86a4a);
  color: #fff;
  font: 700 1rem/1 var(--font-display);
}

.dc-body { min-width: 0; flex: 1; }
.dc-meta { margin: 0 0 0.15rem; }
.dc-name { color: #f2f3f5; font-weight: 700; }
.dc-bot { color: #7ee0c8; }

.dc-app {
  display: inline-block;
  padding: 0 0.3rem;
  border-radius: 3px;
  background: var(--dc-blurple);
  color: #fff;
  font: 700 0.62rem/1.35rem var(--font-body);
  vertical-align: 0.1em;
}

.dc-time { color: var(--dc-muted); font-size: 0.75rem; margin-left: 0.2rem; }
.dc-text { margin: 0; }

.dc-slash {
  display: inline-block;
  padding: 0 0.3rem;
  border-radius: 4px;
  background: rgba(88, 101, 242, 0.3);
  color: #c9cdfb;
  font-weight: 600;
}

.dc-slash-lg { padding: 0.35rem 0.9rem; font-size: 1.3rem; border-radius: 8px; }

.dc-mention {
  padding: 0 2px;
  border-radius: 3px;
  background: rgba(88, 101, 242, 0.3);
  color: #c9cdfb;
  font-weight: 500;
}

.dc-embed {
  max-width: 32rem;
  margin-top: 0.25rem;
  padding: 0.6rem 1rem 0.9rem 0.9rem;
  background: var(--dc-embed);
  border-left: 4px solid var(--cc-rarity, #1e1f22);
  border-radius: 4px;
  font-size: 0.875rem;
}

.dc-embed-info { --cc-rarity: #3498db; }

.dc-title { margin: 0.25rem 0 0.4rem; color: #f2f3f5; font: 700 1rem/1.3 var(--font-body); }
.dc-desc { margin: 0 0 0.5rem; }
.dc-field { margin-top: 0.5rem; }
.dc-field p { margin: 0; }
.dc-field-name { font-weight: 700; color: #f2f3f5; margin-bottom: 0.1rem !important; }
.dc-emoji { width: 1.2em; height: 1.2em; vertical-align: -0.25em; object-fit: contain; }
.dc-title .dc-emoji { width: 1.35em; height: 1.35em; }

.dc-image {
  display: block;
  width: 100%;
  height: auto;
  margin-top: 0.8rem;
  border-radius: 4px;
  background: #1e1f22;
  object-fit: contain;
}

.dc-footer { margin: 0.6rem 0 0; color: var(--dc-muted); font-size: 0.75rem; }

.dc-buttons { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.5rem; }

.dc-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.3rem 0.9rem;
  border-radius: 4px;
  background: var(--dc-grey);
  color: #fff;
  font-weight: 600;
  font-size: 0.875rem;
  white-space: nowrap;
}

.dc-btn-green { background: var(--dc-green); }
.dc-btn-blurple { background: var(--dc-blurple); }

.dc-caption {
  padding: 0 1rem 0.8rem;
  color: var(--dc-muted);
  font-size: 0.75rem;
  text-align: right;
}

.dc-caption:empty { display: none; }

/* The typing indicator while the demo "waits for a bite". */
.dc-typing { display: inline-flex; gap: 4px; padding: 0.4rem 0; }
.dc-typing span { width: 7px; height: 7px; border-radius: 50%; background: var(--dc-muted); }

.dc-clickable { cursor: pointer; }
.dc-clickable:hover { outline: 1px solid rgba(255, 255, 255, 0.12); }

/* =========================================================================
   The landing page
   ========================================================================= */

.hero-home { padding-top: 3rem; }

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(2rem, 5vw, 4rem);
}

.hero-copy h1 { font-size: clamp(2.4rem, 5.4vw, 4rem); }

.dc-hero { transform: rotate(0.6deg); }
.dc-hero .dc-image { max-height: 12rem; }

code.slash {
  padding: 0.05em 0.4em;
  border-radius: 6px;
  background: rgba(88, 101, 242, 0.28);
  color: #d4d8ff;
  font: 700 0.95em/1 var(--font-body);
}

.live-strip {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  margin: 1.5rem 0 0;
  padding: 0.4rem 0.85rem;
  border: 1px solid var(--cc-rule-strong);
  border-radius: var(--radius-pill);
  background: rgba(8, 24, 43, 0.5);
  font: 700 0.84rem/1.4 var(--font-body);
  font-variant-numeric: tabular-nums;
  color: #CFE0EB;
}

.live-strip[hidden] { display: none; }

.live-strip::before {
  content: "";
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: var(--cc-accent);
  box-shadow: 0 0 0 4px rgba(94, 224, 196, 0.2);
}

/* A depth marker between sections: a real level gate, not decoration. */
.depth {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1rem 0 0;
  color: var(--cc-muted);
  font: 700 0.72rem/1 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.depth span { color: var(--cc-sun); }
.depth::after { content: ""; flex: 1; height: 1px; background: linear-gradient(90deg, var(--cc-rule-strong), transparent); }

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  gap: 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.step { display: flex; flex-direction: column; gap: 0.5rem; padding: 1.4rem; }
.step h3 { margin: 0; font-size: 1.3rem; }
.step p { margin: 0; color: var(--cc-muted); }

.step-no {
  display: grid;
  place-items: center;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  background: rgba(94, 224, 196, 0.12);
  border: 1px solid rgba(94, 224, 196, 0.35);
  color: var(--cc-accent);
  font: 700 1rem/1 var(--font-display);
}

.step-demo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  min-height: 4.5rem;
  margin-top: auto;
  padding: 0.9rem;
  border-radius: var(--radius-sm);
  background: #313338;
  font-size: 0.8rem;
}

.step-demo.dc-buttons .dc-btn { font-size: 0.78rem; padding: 0.3rem 0.6rem; }

.step-demo:has(.journal-meter) { flex-direction: column; align-items: stretch; justify-content: center; }

.journal-meter { height: 0.55rem; border-radius: var(--radius-pill); background: rgba(255, 255, 255, 0.08); overflow: hidden; }
.journal-meter-fill { display: block; width: 78%; height: 100%; background: linear-gradient(90deg, var(--cc-accent-deep), var(--cc-accent)); }
.journal-meter-label { margin: 0.4rem 0 0 !important; color: #b5bac1 !important; font-size: 0.8rem; }

.mock-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.25rem;
  align-items: start;
}

/* ---- the feature bento ---- */

.bento {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1rem;
}

.bento .card-link { padding: 1.3rem; }
.bento-weather, .bento-pets, .bento-wide { grid-column: span 2; }

.bento-icon {
  width: 2.6rem;
  height: 2.6rem;
  padding: 0.55rem;
  margin-bottom: 0.35rem;
  border-radius: 12px;
  color: var(--cc-accent);
  background: rgba(94, 224, 196, 0.1);
  border: 1px solid rgba(94, 224, 196, 0.2);
}

.weather-now-list { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.5rem 0 0; padding: 0; list-style: none; }
.weather-now-list:empty { display: none; }

.pet-strip { display: flex; gap: 0.6rem; margin-top: 0.6rem; }
.pet-strip img { width: 56px; height: 56px; border-radius: 14px; background: rgba(4, 12, 22, 0.45); image-rendering: auto; }

.collection {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 1.5rem;
  margin-top: 1.5rem;
  padding: 1.25rem 1.4rem;
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  background: linear-gradient(120deg, rgba(94, 224, 196, 0.07), rgba(244, 178, 107, 0.05));
}

.collection-count { display: flex; align-items: baseline; gap: 0.6rem; }
.collection-num { font: 600 2.2rem/1 var(--font-display); color: var(--cc-foam); font-variant-numeric: tabular-nums; }
.collection-label { color: var(--cc-muted); font-weight: 700; }
.rarity-chips { flex: 1; }
.collection-note { flex-basis: 100%; margin: 0; color: var(--cc-muted); }
.collection-note b { color: var(--cc-foam); }
.rarity-chips img { width: 16px; height: 16px; }

/* ---- the closing band ---- */

.cta-band {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 1.5rem;
  margin-top: 3rem;
  padding: 2rem;
  border: 1px solid rgba(94, 224, 196, 0.25);
  border-radius: var(--radius-lg);
  background:
    radial-gradient(80% 140% at 0% 0%, rgba(94, 224, 196, 0.14), transparent 60%),
    radial-gradient(60% 120% at 100% 100%, rgba(244, 178, 107, 0.12), transparent 60%),
    var(--cc-card);
}

.cta-band img { width: 96px; height: 96px; border-radius: 50%; }
.cta-band .section-title { margin-bottom: 0.3rem; }
.cta-band .cta { margin: 0; }

@media (max-width: 60rem) {
  .hero-grid { grid-template-columns: minmax(0, 1fr); }
  .dc-hero { transform: none; }
  .mock-pair { grid-template-columns: minmax(0, 1fr); }
  .bento { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cta-band { grid-template-columns: minmax(0, 1fr); text-align: left; }
  .cta-band img { width: 64px; height: 64px; }
}

@media (max-width: 36rem) {
  .bento { grid-template-columns: minmax(0, 1fr); }
  .bento-weather, .bento-pets, .bento-wide { grid-column: auto; }
  .dc-log { padding: 0.8rem 0.7rem; gap: 0.8rem; }
  .dc-msg { gap: 0.6rem; }
  .dc-avatar { width: 34px; height: 34px; }
  .dc-embed { padding: 0.5rem 0.7rem 0.75rem 0.7rem; }
  .cta-band { padding: 1.4rem; }
}

/* =========================================================================
   The encyclopedia (#177–#180)
   ========================================================================= */

.h1-icon {
  width: 0.85em;
  height: 0.85em;
  margin-right: 0.3em;
  padding: 0.12em;
  vertical-align: -0.08em;
  color: var(--cc-accent);
}

.hero .breadcrumb { margin-bottom: 1.25rem; }

/* Item art: a soft tile the picture sits on, tinted by rarity when there is one. */
.art {
  display: inline-grid;
  place-items: center;
  flex: none;
  border-radius: var(--radius);
  background:
    radial-gradient(70% 70% at 50% 35%, color-mix(in srgb, var(--cc-rarity, #5EE0C4) 22%, transparent), transparent 70%),
    rgba(4, 12, 22, 0.45);
  border: 1px solid var(--cc-rule);
}

.art-96 { width: 5.25rem; height: 5.25rem; }
.art-160 { width: 9rem; height: 9rem; border-radius: var(--radius-lg); }
.art img { width: 82%; height: 82%; object-fit: contain; }
.art-emoji { font-size: 2.4rem; line-height: 1; }
.art-160 .art-emoji { font-size: 4rem; }
.art-icon { width: 45%; height: 45%; color: var(--cc-accent); }

/* Pixel art stays crisp when scaled. */
.pixel-art .art img { image-rendering: pixelated; }

.catalog-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
  margin: 0.5rem 0 1rem;
}

.search-box {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1 1 18rem;
  max-width: 28rem;
  padding: 0 0.9rem;
  border: 1px solid var(--cc-rule-strong);
  border-radius: var(--radius-pill);
  background: rgba(8, 24, 43, 0.7);
  color: var(--cc-muted);
}

.search-box:focus-within { border-color: var(--cc-accent); }

.search-box input {
  flex: 1;
  min-width: 0;
  padding: 0.7rem 0;
  border: 0;
  background: none;
  color: var(--cc-foam);
  font: 600 1rem/1.2 var(--font-body);
}

.search-box input:focus { outline: none; }
.search-box input::placeholder { color: var(--cc-muted); }

.catalog-count { margin: 0; color: var(--cc-muted); font-weight: 700; font-size: 0.9rem; }
.catalog-jump { margin-bottom: 0.5rem; }

.catalog-group { padding-top: 2rem; scroll-margin-top: calc(var(--header-h) + 1rem); }
.catalog-group[hidden] { display: none; }

.catalog-group-head { display: flex; align-items: baseline; gap: 0.75rem; margin-bottom: 0.4rem; }
.catalog-group-head h2 { margin: 0; font-size: 1.55rem; }

.catalog-group-count {
  padding: 0.05rem 0.55rem;
  border-radius: var(--radius-pill);
  background: rgba(94, 224, 196, 0.12);
  color: var(--cc-accent);
  font-weight: 800;
  font-size: 0.8rem;
}

.catalog-group > .section-blurb { margin-bottom: 1rem; }

.item-grid { grid-template-columns: repeat(auto-fill, minmax(min(19rem, 100%), 1fr)); }

.item-card {
  flex-direction: row;
  align-items: flex-start;
  gap: 1rem;
}

.item-card[hidden] { display: none; }
.item-card-body { display: flex; flex-direction: column; gap: 0.35rem; min-width: 0; }
.item-card .card-blurb { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }

/* ---- detail pages ---- */

.detail-hero {
  display: flex;
  align-items: center;
  gap: clamp(1.25rem, 4vw, 2.5rem);
}

.detail-hero h1 { margin-bottom: 0.6rem; }
.detail-hero .chips { margin-bottom: 1rem; }
.detail-hero .eyebrow { margin-bottom: 0.5rem; }

.detail .stat-grid { margin-bottom: 1rem; }

.detail-section {
  margin-top: 1.25rem;
  padding: 1.35rem 1.5rem;
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  background: var(--cc-card);
}

.detail-section h2 { margin: 0 0 0.8rem; font-size: 1.25rem; }
.detail-section p { margin: 0 0 0.6rem; }
.detail-section p:last-child { margin-bottom: 0; }
.sub-head { margin: 1rem 0 0.5rem; font-size: 0.95rem; color: var(--cc-muted); }
.sub-head:first-child { margin-top: 0; }

.muted { color: var(--cc-muted); }

.effect-list, .plain-list { margin: 0; padding-left: 1.2rem; display: grid; gap: 0.35rem; }
.effect-list li::marker { color: var(--cc-accent); }
.plain-list a { color: var(--cc-accent); }

.chip-link { cursor: pointer; }
.chip-link:hover { border-color: var(--cc-accent); color: var(--cc-foam); }

.mini-fish-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 0.6rem;
}

.mini-fish {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  padding: 0.6rem 0.4rem 0.7rem;
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius-sm);
  background: rgba(4, 12, 22, 0.35);
  font-size: 0.85rem;
  font-weight: 700;
  text-align: center;
  text-decoration: none;
  transition: border-color 150ms ease, background-color 150ms ease;
}

.mini-fish:hover { border-color: var(--cc-accent); background: rgba(20, 48, 79, 0.6); }
.mini-fish img { width: 100%; max-width: 7.5rem; height: auto; aspect-ratio: 500 / 222; object-fit: contain; }
.more-note { margin-top: 0.8rem !important; color: var(--cc-muted); }
.more-note a { color: var(--cc-accent); }

.table-wrap { overflow-x: auto; }

.data-table { width: 100%; border-collapse: collapse; font-size: 0.93rem; }
.data-table th, .data-table td { padding: 0.6rem 0.75rem; border-bottom: 1px solid var(--cc-rule); text-align: left; vertical-align: top; }
.data-table th { color: var(--cc-muted); font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase; }
.data-table td:first-child { font-weight: 800; color: var(--cc-sun); white-space: nowrap; }
.data-table td:nth-child(2) { white-space: nowrap; font-variant-numeric: tabular-nums; }

.back { margin: 2rem 0 1rem; }

.prev-next { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.prev-next .card-link { padding: 0.9rem 1.1rem; }
.prev-next small { color: var(--cc-muted); font-weight: 700; font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase; }
.prev-next .next { text-align: right; align-items: flex-end; }

/* ---- the hub ---- */

.hub-grid { grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr)); }
.hub-card { gap: 0.5rem; padding: 1.3rem; }
.hub-card .chip { align-self: flex-start; margin-top: auto; }

/* ---- weather ---- */

.weather-legend {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
  gap: 0.75rem;
  margin: 1rem 0 0;
  padding: 0;
  list-style: none;
}

.weather-card { display: grid; grid-template-columns: auto 1fr; column-gap: 0.75rem; row-gap: 0.15rem; align-items: center; }
.weather-card > span:last-child, .weather-card small { grid-column: 2; color: var(--cc-muted); font-size: 0.9rem; }
.weather-emoji { grid-row: span 2; font-size: 1.8rem; line-height: 1; }

.hidden-note { margin-top: 2rem; }

@media (max-width: 40rem) {
  .detail-hero { flex-direction: column; align-items: flex-start; }
  .art-160 { width: 7rem; height: 7rem; }
  .detail-section { padding: 1.1rem; }
  .prev-next { grid-template-columns: 1fr; }
  .prev-next .next { text-align: left; align-items: flex-start; }
  .item-card { gap: 0.8rem; }
  .art-96 { width: 4.25rem; height: 4.25rem; }
}

/* =========================================================================
   The Fish Encyclopedia (#181) and a species page
   ========================================================================= */

.fish-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.75rem;
  margin: 0.5rem 0 1rem;
  padding: 1rem;
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  background: var(--cc-card);
}

.fish-filters .search-box { flex: 2 1 16rem; max-width: none; }

.field { display: flex; flex-direction: column; gap: 0.3rem; flex: 1 1 9.5rem; min-width: 0; }
.field > span { font-size: 0.72rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: var(--cc-muted); }

.field select,
.filters select {
  width: 100%;
  padding: 0.6rem 2rem 0.6rem 0.8rem;
  border: 1px solid var(--cc-rule-strong);
  border-radius: var(--radius-sm);
  background-color: rgba(8, 24, 43, 0.8);
  background-image: linear-gradient(45deg, transparent 50%, #9AB3C7 50%), linear-gradient(135deg, #9AB3C7 50%, transparent 50%);
  background-position: calc(100% - 17px) 55%, calc(100% - 12px) 55%;
  background-size: 5px 5px;
  background-repeat: no-repeat;
  color: var(--cc-foam);
  font: 700 0.92rem/1.2 var(--font-body);
  appearance: none;
}

.field select:focus { border-color: var(--cc-accent); outline: none; }

/* With JS the form updates as you change it; the button is for no-JS. */
.fish-filters .button { flex: none; }

.fish-grid { grid-template-columns: repeat(auto-fill, minmax(min(13.5rem, 100%), 1fr)); margin-top: 0.75rem; }

.fish-card { align-items: stretch; gap: 0.4rem; padding: 0.8rem 0.8rem 1rem; }

.fish-card-art {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 8;
  margin-bottom: 0.3rem;
  border-radius: var(--radius-sm);
  background:
    radial-gradient(80% 90% at 50% 60%, rgba(94, 224, 196, 0.12), transparent 70%),
    rgba(4, 12, 22, 0.45);
}

.fish-card-art img { width: 90%; height: auto; aspect-ratio: 500 / 222; object-fit: contain; }
.fish-card .card-title { font-size: 1.02rem; }
.fish-card-size { color: var(--cc-muted); font-size: 0.85rem; font-weight: 700; font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: no-preference) {
  .fish-card:hover .fish-card-art img { transform: scale(1.06) translateY(-2px); transition: transform 300ms ease; }
}

.fish-footnote { margin: 1.5rem 0 0; color: var(--cc-muted); text-align: center; }
.fish-footnote a { color: var(--cc-accent); }

.a-to-z {
  margin-top: 2.5rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  background: var(--cc-card);
}

.a-to-z[hidden] { display: none; }
.a-to-z summary { cursor: pointer; font: 600 1.15rem/1.4 var(--font-display); }
.fish-jump { display: flex; flex-wrap: wrap; gap: 0.2rem 0.6rem; font-weight: 800; }
.fish-jump a { color: var(--cc-muted); text-decoration: none; }
.fish-jump a:hover { color: var(--cc-foam); }
.a-to-z .fish-jump { margin-top: 1rem; }
.a-to-z h3 { margin: 1.5rem 0 0.5rem; color: var(--cc-sun); scroll-margin-top: calc(var(--header-h) + 1rem); }
.a-to-z ul { margin: 0; padding: 0; list-style: none; columns: 13rem; column-gap: 2rem; }
.a-to-z li { break-inside: avoid; padding: 0.12rem 0; }
.a-to-z li a { text-decoration: none; }
.a-to-z li a:hover { color: var(--cc-accent); }

.fish-card-art img, .species-hero-art, .mini-fish img, .dc-image, .catch-full { image-rendering: pixelated; }

.species-hero { display: flex; align-items: center; gap: clamp(1.25rem, 4vw, 3rem); }

.species-hero-art {
  flex: 0 1 26rem;
  width: 100%;
  max-width: 26rem;
  height: auto;
  padding: 1rem;
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius-lg);
  background: radial-gradient(70% 80% at 50% 55%, rgba(94, 224, 196, 0.14), transparent 70%), rgba(4, 12, 22, 0.4);
}

.species-hero .share-name { margin: 0 0 0.6rem; font: 600 clamp(2.1rem, 5vw, 3.4rem)/1.05 var(--font-display); }
.species-hero .chips { margin-bottom: 1rem; }

.species-detail #live-measures:not([hidden]) {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
  gap: 0.75rem;
  margin: 0 0 1rem;
  border: 0;
}

.species-detail #live-measures .dialog-measure {
  flex-direction: column;
  padding: 0.9rem 1rem;
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  background: var(--cc-card);
}

.species-detail #live-measures .dialog-measure > span:last-child { font: 600 1.3rem/1.2 var(--font-display); }
.where-list .muted { font-size: 0.85rem; }
.species-detail .cta { justify-content: center; margin-top: 2.5rem; }

@media (max-width: 44rem) {
  .species-hero { flex-direction: column; align-items: flex-start; }
  .fish-filters { padding: 0.8rem; }
  .field { flex-basis: calc(50% - 0.5rem); }
  .fish-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem; }
  .fish-card { padding: 0.6rem 0.6rem 0.8rem; }
  .fish-card .chips { display: none; }
}

/* =========================================================================
   The wiki (#182): 3 columns at ≥ 1100 px, TOC hidden below, sidebar a
   <details> on phones. `.prose` styles the rendered Markdown.
   ========================================================================= */

.hero-wiki .lede { margin-bottom: 0.5rem; }

.wiki-search { position: relative; max-width: 36rem; margin-top: 1.5rem; }
.wiki-search .search-box { max-width: none; background: rgba(8, 24, 43, 0.85); }
.wiki-search .search-box input { padding: 0.9rem 0; font-size: 1.05rem; }

.wiki-results {
  position: absolute;
  z-index: 20;
  top: calc(100% + 0.4rem);
  left: 0;
  right: 0;
  max-height: 24rem;
  overflow-y: auto;
  margin: 0;
  padding: 0.35rem;
  list-style: none;
  background: #0E2440;
  border: 1px solid var(--cc-rule-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
}

.wiki-results[hidden] { display: none; }

.wiki-hit {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.1rem 0.75rem;
  padding: 0.6rem 0.75rem;
  border-radius: var(--radius-sm);
  text-decoration: none;
}

.wiki-hit small { color: var(--cc-sun); font-weight: 800; font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase; }
.wiki-hit span { grid-column: 1 / -1; color: var(--cc-muted); font-size: 0.86rem; }
[role="option"][aria-selected="true"] .wiki-hit,
.wiki-hit:hover { background: rgba(94, 224, 196, 0.12); }
.wiki-hit-empty { padding: 0.6rem 0.75rem; color: var(--cc-muted); }

.wiki {
  display: grid;
  grid-template-columns: 14rem minmax(0, 1fr);
  gap: 2.5rem;
  align-items: start;
}

.wiki-article { grid-template-columns: 13rem minmax(0, 1fr) 14rem; }

.wiki-sidebar {
  position: sticky;
  top: calc(var(--header-h) + 1.25rem);
  padding: 0.6rem;
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  background: var(--cc-card);
}

.wiki-sidebar summary {
  padding: 0.35rem 0.5rem;
  cursor: pointer;
  font: 700 0.74rem/1.2 var(--font-body);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cc-sun);
}

.wiki-sidebar nav { display: grid; gap: 0.1rem; margin-top: 0.3rem; }

.wiki-side-link {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.45rem 0.6rem;
  border-radius: var(--radius-sm);
  color: #CFE0EB;
  font-weight: 700;
  font-size: 0.92rem;
  text-decoration: none;
}

.wiki-side-link span { margin-left: auto; color: var(--cc-muted); font-size: 0.8rem; font-variant-numeric: tabular-nums; }
.wiki-side-link .icon { color: var(--cc-accent); }
.wiki-side-link:hover { background: rgba(94, 224, 196, 0.08); color: var(--cc-foam); }
.wiki-side-link[aria-current="page"] { background: rgba(94, 224, 196, 0.14); color: var(--cc-foam); }
.wiki-side-all { margin-top: 0.4rem; border-top: 1px solid var(--cc-rule); border-radius: 0; color: var(--cc-muted); }

.wiki-cat { padding-bottom: 2rem; scroll-margin-top: calc(var(--header-h) + 1rem); }
.wiki-cat h2 { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 1rem; font-size: 1.5rem; }
.wiki-cat-icon { width: 1.1em; height: 1.1em; color: var(--cc-accent); }

.wiki-grid { grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr)); }
.wiki-card { gap: 0.4rem; }

.wiki-toc { position: sticky; top: calc(var(--header-h) + 1.25rem); max-height: calc(100vh - var(--header-h) - 2.5rem); overflow-y: auto; }

.wiki-body { min-width: 0; }
.wiki-related { margin-top: 3rem; }
.wiki-related h2 { font-size: 1.35rem; }
.wiki-body .prev-next { margin-top: 2rem; }

/* ---- rendered Markdown ---- */

.prose { font-size: 1.04rem; line-height: 1.75; color: #DCE8EF; }
.prose > :first-child { margin-top: 0; }
.prose h2 { margin: 2.6rem 0 0.8rem; font-size: 1.6rem; line-height: 1.2; scroll-margin-top: calc(var(--header-h) + 1rem); }
.prose h3 { margin: 2rem 0 0.6rem; font-size: 1.25rem; scroll-margin-top: calc(var(--header-h) + 1rem); }
.prose h4 { margin: 1.5rem 0 0.4rem; font-size: 1.05rem; }
.prose p, .prose ul, .prose ol, .prose dl, .prose table { margin: 0 0 1.1rem; }
.prose ul, .prose ol { padding-left: 1.4rem; }
.prose li { margin: 0.25rem 0; }
.prose li::marker { color: var(--cc-accent); }
.prose a { color: var(--cc-accent); text-underline-offset: 3px; }
.prose a:hover { color: #9CF1DE; }
.prose strong { color: var(--cc-foam); }
.prose hr { margin: 2.5rem 0; border: 0; border-top: 1px solid var(--cc-rule); }

.prose .headerlink { margin-left: 0.4rem; color: var(--cc-muted); text-decoration: none; opacity: 0; transition: opacity 120ms ease; }
.prose h2:hover .headerlink, .prose h3:hover .headerlink, .prose .headerlink:focus { opacity: 1; }

.prose code {
  padding: 0.12em 0.4em;
  border-radius: 6px;
  background: rgba(88, 101, 242, 0.2);
  color: #D4D8FF;
  font: 700 0.9em/1 var(--font-body);
}

.prose pre { overflow-x: auto; padding: 1rem; border-radius: var(--radius-sm); background: rgba(4, 12, 22, 0.7); border: 1px solid var(--cc-rule); }
.prose pre code { padding: 0; background: none; font: 400 0.9rem/1.6 var(--font-mono); color: var(--cc-foam); }

.prose table { display: block; width: 100%; overflow-x: auto; border-collapse: collapse; font-size: 0.95rem; }
.prose th, .prose td { padding: 0.55rem 0.8rem; border-bottom: 1px solid var(--cc-rule); text-align: left; vertical-align: top; }
.prose th { color: var(--cc-muted); font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; }
.prose tbody tr:hover { background: rgba(94, 224, 196, 0.04); }

.prose dt { font-weight: 800; color: var(--cc-foam); }
.prose dd { margin: 0 0 0.8rem 1rem; }

.prose img { height: auto; border-radius: var(--radius-sm); border: 1px solid var(--cc-rule); }

.prose .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr)); gap: 1rem; margin: 1rem 0 1.5rem; }
.prose figure { margin: 0; padding: 0.75rem; border: 1px solid var(--cc-rule); border-radius: var(--radius); background: var(--cc-card); }
.prose figure p { margin: 0; }
.prose figcaption { margin-top: 0.5rem; color: var(--cc-muted); font-size: 0.88rem; line-height: 1.5; }

/* Admonitions map onto the card component, one accent per kind. */
.prose .admonition,
.prose details {
  margin: 1.25rem 0;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--cc-rule);
  border-left: 4px solid var(--note, var(--cc-sky));
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--note, var(--cc-sky)) 9%, var(--cc-card-solid));
}

.prose .admonition > :last-child, .prose details > :last-child { margin-bottom: 0; }
.prose .admonition-title, .prose details summary { margin: 0 0 0.4rem; font-weight: 800; color: var(--note, var(--cc-foam)); }
.prose details summary { cursor: pointer; }
.prose .tip { --note: var(--cc-accent); }
.prose .note, .prose .info { --note: #7CC4F5; }
.prose .warning, .prose .danger { --note: var(--cc-sun); }

@media (max-width: 68.75rem) {
  .wiki-article { grid-template-columns: 13rem minmax(0, 1fr); }
  .wiki-toc { display: none; }
}

@media (max-width: 48rem) {
  .wiki, .wiki-article { grid-template-columns: minmax(0, 1fr); gap: 1.25rem; }
  .wiki-sidebar { position: static; }
  .prose { font-size: 1rem; }
  .prose h2 { font-size: 1.4rem; }
}

/* =========================================================================
   Global stats — aggregates, never anybody in particular
   ========================================================================= */

.stats { margin-top: 2rem; }

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1rem;
}

.tile {
  padding: 1.1rem 1.2rem;
  background: var(--cc-card);
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  box-shadow: inset 0 1px 0 var(--cc-glass-edge);
}

.tile-value {
  font: 600 clamp(1.6rem, 3.4vw, 2.2rem)/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  color: var(--cc-foam);
}

.tile-label {
  margin-top: 0.45rem;
  font: 700 0.72rem/1.3 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cc-muted);
}

.panel-title {
  margin: 2.5rem 0 0.9rem;
  font: 700 0.8rem/1 var(--font-body);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cc-sun);
}

/* One stacked bar. Colour is never the only carrier — the legend has numbers. */
.rarity-bar {
  display: flex;
  height: 0.85rem;
  overflow: hidden;
  border-radius: 999px;
  border: 1px solid var(--cc-rule);
}

/* A sliver stays visible: Unique is ~0.01% of every catch. */
.rarity-seg { min-width: 4px; }

.rarity-legend {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0.4rem 1.5rem;
  margin: 1rem 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.85rem;
}

.rarity-legend li {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.swatch {
  width: 0.7rem;
  height: 0.7rem;
  flex: none;
  border-radius: 2px;
}

.rarity-name { text-transform: capitalize; }

.rarity-count {
  margin-left: auto;
  font-family: var(--font-body);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-size: 0.78rem;
  color: var(--cc-muted);
}

/* Three record cards where one used to sit. The scroller is the carousel —
   scroll-snap and a real overflow container, so a swipe on a phone and a
   keyboard's arrow keys both work without a line of JS behind them. stats.js
   only ever calls scrollTo. */
/* No width cap. This was a single small tile when it was one "biggest fish",
   and 26rem left two thirds of the column empty next to stat tiles that span
   all of it. A record card is now as wide as they are. */

.record-track {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* The cards lift on hover and the focus ring sits outside them, so the
     scroller needs room to show both without clipping. */
  padding: 3px;
  scrollbar-width: none;
}

.record-track::-webkit-scrollbar {
  display: none;
}

.record-track > * {
  flex: 0 0 100%;
  scroll-snap-align: start;
}

.record-title {
  margin-bottom: 0.35rem;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cc-muted);
}

.record-dots {
  display: flex;
  gap: 0.4rem;
  justify-content: center;
  margin-top: 0.6rem;
}

.record-dot {
  width: 0.5rem;
  height: 0.5rem;
  padding: 0;
  border: 1px solid var(--cc-rule);
  border-radius: 50%;
  background: none;
  cursor: pointer;
}

.record-dot[aria-current="true"] {
  background: var(--cc-sky);
  border-color: var(--cc-sky);
}

.biggest {
  display: flex;
  gap: 1rem;
  padding: 1rem;
  background: var(--cc-card);
  border: 1px solid var(--cc-rule);
  border-left: 3px solid var(--cc-rarity, var(--cc-sky));
  border-radius: var(--radius);
}

/* The tile is a <button> when there is a catch behind it, so it needs the same
   control reset `.catch` carries — otherwise it inherits button chrome and
   centred UA text and stops looking like a card. */
button.biggest {
  width: 100%;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 120ms ease, transform 120ms ease;
}

button.biggest:hover,
button.biggest:focus-visible {
  border-color: var(--cc-rarity, var(--cc-sky));
  transform: translateY(-3px);
}

/* The file's own rule: "every motion is behind prefers-reduced-motion". The
   feed cards predate that and lift on hover regardless; both are covered here
   now rather than only the newer one. */
@media (prefers-reduced-motion: reduce) {
  button.biggest,
  .catch {
    transition: none;
  }
  button.biggest:hover,
  button.biggest:focus-visible,
  .catch:hover,
  .catch:focus-visible {
    transform: none;
  }
}

.biggest img {
  width: 76px;
  height: 76px;
  flex: none;
  object-fit: contain;
  border-radius: 6px;
  background: rgba(4, 12, 22, 0.5);
}

/* `min-width: 0` so a long species name wraps instead of widening the flex
   item past the card; `flex: 1` so the rule above the measures runs the full
   width of the card rather than stopping at the end of the longest line. */
.record-body {
  flex: 1;
  min-width: 0;
}

/* One record at a time, filling the column. The fish carries the extra space —
   scaling the type instead would just make a landing-page card shout. */
@media (min-width: 46rem) {
  .biggest {
    gap: 1.75rem;
    padding: 1.5rem;
    align-items: center;
  }

  .biggest img {
    width: 148px;
    height: 148px;
  }

  .record-title {
    margin-bottom: 0.5rem;
  }

  /* Scoped to the card: `.catch-measure` is also the feed's and the dialog's,
     and neither of those grew. */
  .biggest .catch-measure {
    font-size: 0.88rem;
  }

  .biggest .catch-name {
    font-size: 1.45rem;
  }
}

.stats-empty {
  margin: 0;
  padding: 2rem 1.5rem;
  border: 1px dashed var(--cc-rule);
  border-radius: var(--radius);
  text-align: center;
  color: var(--cc-muted);
}

/* =========================================================================
   Feed — a catch is a record slip
   ========================================================================= */

.feed {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 2rem;
}

.feed-empty {
  flex: 1 1 100%;
  margin: 0;
  padding: 2.75rem 1.5rem;
  border: 1px dashed var(--cc-rule);
  border-radius: var(--radius);
  text-align: center;
  color: var(--cc-muted);
}

/* A <button>, because a card opens the catch dialog — keyboard and Enter/Space
   come with the element rather than from a role and a keydown handler. */
.catch {
  flex: 1 1 19rem;
  min-width: 0;
  display: flex;
  gap: 1rem;
  padding: 1rem;
  text-align: left;
  color: inherit;
  font: inherit;
  cursor: pointer;
  background: var(--cc-card);
  border: 1px solid var(--cc-rule);
  border-left: 3px solid var(--cc-rarity, var(--cc-sky));
  border-radius: var(--radius);
  backdrop-filter: blur(2px);
  transition: transform 200ms ease, border-color 200ms ease;
}

/* Rarity-coloured, matching the left border the card already carries — and
   matching `button.biggest`, which is the same kind of object. It used to hover to
   a flat --cc-sky while the biggest-fish tile lit up in its rarity colour, so
   two identical-looking cards behaved differently for no reason. */
.catch:hover,
.catch:focus-visible {
  transform: translateY(-3px);
  border-color: var(--cc-rarity, var(--cc-sky));
}

/* The dialog's big copy of the same render: the fish is the point, so it gets
   the full width and its natural aspect rather than the card's 76px thumb. */
.catch-full {
  display: block;
  width: 100%;
  aspect-ratio: 500 / 222;
  object-fit: contain;
  margin-bottom: 1rem;
  border-radius: var(--radius);
  background: rgba(4, 12, 22, 0.5);
}

.catch img {
  width: 76px;
  height: 76px;
  flex: none;
  object-fit: contain;
  border-radius: 6px;
  background: rgba(4, 12, 22, 0.5);
}

.catch-body { min-width: 0; }

.catch-rarity {
  font: 700 0.68rem/1 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cc-rarity, var(--cc-muted));
}

.catch-name {
  margin-top: 0.35rem;
  font: 600 1.2rem/1.25 var(--font-display);
  overflow-wrap: anywhere;
}

.catch-species { color: var(--cc-muted); font-size: 0.86rem; }

/* The species name in a catch dialog links to its page. catchdialog.js swaps
   the text for an anchor once the slug resolves, so this styles a link that is
   not in the markup — inheriting the colour keeps the line looking the same
   until you hover it. */
.catch-species a,
.dialog-name a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--cc-rule);
}

.catch-species a:hover,
.catch-species a:focus-visible,
.dialog-name a:hover,
.dialog-name a:focus-visible { color: var(--cc-foam); border-bottom-color: var(--cc-sky); }

.catch-measure {
  display: flex;
  gap: 1.25rem;
  margin-top: 0.7rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--cc-rule);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
}

.catch-meta { color: var(--cc-muted); font-size: 0.82rem; overflow-wrap: anywhere; }


/* =========================================================================
   Species journal (/journal.html)

   One canonical image per species, written by scripts/render_species_art.py.
   An undiscovered species is that same image darkened — there is no second
   asset set, so art can never fall out of sync with itself.
   ========================================================================= */

/* The journal leads straight into the map, so it needs far less sky than the
   landing page's hero does. */

/* No lede under it, and the page below is dense — less sky than the pages
   that open with a paragraph. */
.hero-dash { padding-bottom: calc(var(--surface-h) + 0.5rem); }

/* ---------- the map: 27 locations, all on screen ----------
   Replaces a pair of selects. Everything is visible, one click deep, and each
   entry carries its own progress — which is the number people came for. */

.chart { margin-top: 1rem; }

.chart-controls { margin: 1.5rem 0 0; }


.chart-region { border-bottom: 1px solid var(--cc-rule); }

.chart-region-head {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 0.85rem 0;
  cursor: pointer;
  list-style: none;
}

/* Safari draws its own triangle through ::-webkit-details-marker. */
.chart-region-head::-webkit-details-marker { display: none; }

/* The disclosure chevron, rotated by the open state rather than swapped. */
.chart-region-head::before {
  content: "";
  width: 0.42rem;
  height: 0.42rem;
  flex: none;
  border-right: 1.5px solid var(--cc-wood-light);
  border-bottom: 1.5px solid var(--cc-wood-light);
  transform: rotate(-45deg);
  transition: transform 160ms ease-out;
}

.chart-region[open] > .chart-region-head::before { transform: rotate(45deg); }

.chart-region-name {
  font: 700 0.78rem/1.4 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cc-sun);
}

.chart-region-count {
  margin-left: auto;
  font: 700 0.78rem/1.4 var(--font-body);
  font-variant-numeric: tabular-nums;
  color: var(--cc-muted);
}

.waters {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
  gap: 0.5rem;
  padding: 0.2rem 0 1.1rem;
}

.water {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 0.2rem 0.6rem;
  padding: 0.6rem 0.75rem;
  font: inherit;
  color: var(--cc-foam);
  text-align: left;
  background: rgba(11, 29, 51, 0.55);
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color 140ms ease-out, background-color 140ms ease-out;
}

.water:hover { background: rgba(20, 48, 79, 0.8); border-color: var(--cc-sky); }

/* The selected water is the one the grid below is showing. */
.water.selected {
  background: rgba(20, 48, 79, 0.95);
  border-color: var(--cc-wood-light);
}

.water-lv {
  font: 700 0.62rem/1 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cc-sun);
}

.water-name {
  grid-column: 2;
  grid-row: 1;
  font-size: 0.9rem;
  overflow-wrap: anywhere;
}

.water-meter {
  grid-column: 1 / -1;
  height: 3px;
  margin-top: 0.45rem;
  overflow: hidden;
  background: rgba(4, 12, 22, 0.6);
  border-radius: 999px;
}

.water-meter-fill {
  display: block;
  height: 100%;
  background: var(--cc-sky);
}

/* Complete. The one place on the page that earns the warm colour. */
.water.done .water-meter-fill { background: var(--cc-wood-light); }
.water.done .water-count { color: var(--cc-wood-light); }

.water-count {
  grid-column: 1 / -1;
  font: 700 0.68rem/1 var(--font-body);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  color: var(--cc-muted);
}

/* The Contest Pier's schedule reuses the journal's water tiles, but a posted
   timetable is not a control: no pointer, no hover response, nothing to click.
   `today` gets the warm accent — the one thing on the page worth finding. */
.water-static { cursor: default; }
.water-static:hover { background: rgba(11, 29, 51, 0.55); border-color: var(--cc-rule); }

.water.today,
.water-static.today:hover {
  background: rgba(20, 48, 79, 0.95);
  border-color: var(--cc-wood-light);
}

.water.today .water-lv { color: var(--cc-foam); }

.dex-progress {
  margin: 1.5rem 0 0;
  font: 700 0.8rem/1 var(--font-body);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cc-muted);
}

.dex-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin-top: 2rem;
}

.dex-empty {
  flex: 1 1 100%;
  margin: 0;
  padding: 2.75rem 1.5rem;
  border: 1px dashed var(--cc-rule);
  border-radius: var(--radius);
  text-align: center;
  color: var(--cc-muted);
}

.dex-empty .button { margin-top: 1.25rem; }

/* A record slip, the same shape as a catch on the landing page. Relative
   because the hover state lifts it over its neighbours. */
.dex-card {
  position: relative;
  flex: 1 1 9.5rem;
  max-width: 13rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  padding: 0.9rem 0.7rem 1rem;
  font: inherit;
  color: inherit;
  text-align: center;
  background: var(--cc-card);
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  cursor: pointer;
  transition: transform 160ms ease-out, border-color 160ms ease-out;
  /* No content-visibility: auto here, tempting as it is on a 460-card grid.
     It brings paint containment along even while the card is on screen, which
     clips `.dex-tip` — the tip is positioned entirely above the card's box, so
     the hover and focus affordance simply stops being drawn. Verified in
     Chromium: an absolutely-positioned child sticking out of a
     content-visibility: auto parent is not painted or hit-testable.
     The reveal animations deliberately land on the sections, not on these
     cards, so the grid pays no animation cost that would need offsetting. */
}

.dex-card img {
  width: 100%;
  max-width: 8.5rem;
  aspect-ratio: 500 / 222;
  object-fit: contain;
  /* A species surfacing out of the dark when you sign in, rather than
     snapping into colour. Still one image and one filter. */
  transition: filter 320ms ease-out;
}

/* The silhouette. One image, one filter — never a second file. */
.dex-card.undiscovered img { filter: brightness(0) opacity(0.55); }

.dex-card.undiscovered {
  color: var(--cc-muted);
  cursor: pointer;
}

.dex-eyebrow-line {
  font: 700 0.66rem/1 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cc-sun);
}

.dex-card.undiscovered .dex-eyebrow-line { color: rgba(143, 167, 188, 0.7); }

.dex-name {
  font: 600 1rem/1.25 var(--font-display);
  overflow-wrap: anywhere;
}

/* The hover panel. Hidden rather than absent so it needs no JS to appear, and
   pointer-events: none keeps it from stealing the hover that shows it. */
.dex-tip {
  position: absolute;
  bottom: calc(100% + 0.4rem);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: max-content;
  padding: 0.5rem 0.7rem;
  background: rgba(4, 12, 22, 0.95);
  border: 1px solid var(--cc-rule);
  border-radius: 6px;
  font: 700 0.72rem/1.4 var(--font-body);
  font-variant-numeric: tabular-nums;
  color: var(--cc-foam);
  opacity: 0;
  pointer-events: none;
  transition: opacity 160ms ease-out;
}

.dex-card:hover,
.dex-card:focus-visible { border-color: var(--cc-sky); z-index: 3; }

.dex-card:hover .dex-tip,
.dex-card:focus-visible .dex-tip { opacity: 1; }

/* A silhouette has nothing to grow into and nothing to say, so it does
   neither. */
@media (prefers-reduced-motion: no-preference) {
  .dex-card:not(.undiscovered):hover,
  .dex-card:not(.undiscovered):focus-visible { transform: scale(1.12); }
}

/* Reduced motion still needs the "this one is under the cursor" signal —
   it just gets it from an outline instead of from movement. */
@media (prefers-reduced-motion: reduce) {
  .dex-card, .dex-card img { transition: none; }
  .dex-card:hover,
  .dex-card:focus-visible { outline: 2px solid var(--cc-wood-light); outline-offset: 2px; }
}

/* ---------- the species dialog ---------- */

.species-dialog {
  width: min(34rem, calc(100vw - 2rem));
  padding: 1.5rem;
  color: var(--cc-foam);
  background: linear-gradient(160deg, #14304F, #0B1D33);
  border: 1px solid var(--cc-sky);
  border-radius: var(--radius);
}

.species-dialog::backdrop { background: rgba(4, 12, 22, 0.72); }

/* The dialog opens like something. `display` is not animatable, so this needs
   allow-discrete plus @starting-style — the platform answer, no JS.
   The whole set is guarded: unguarded, a reduced-motion user with no
   transition support for the discrete properties gets an invisible dialog. */
@media (prefers-reduced-motion: no-preference) {
  .species-dialog {
    opacity: 0;
    transform: translateY(0.75rem) scale(0.98);
    transition: opacity 180ms ease, transform 180ms ease,
                overlay 180ms allow-discrete, display 180ms allow-discrete;
  }
  .species-dialog[open] { opacity: 1; transform: none; }
  @starting-style {
    .species-dialog[open] { opacity: 0; transform: translateY(0.75rem) scale(0.98); }
  }
  .species-dialog::backdrop {
    transition: background-color 180ms ease,
                overlay 180ms allow-discrete, display 180ms allow-discrete;
  }
}

.dialog-close-form { float: right; margin: -0.5rem -0.5rem 0 0; }

.dialog-close {
  padding: 0.3rem 0.55rem;
  color: var(--cc-muted);
  background: none;
  border: 1px solid var(--cc-rule);
  border-radius: 6px;
  font: 0.9rem/1 var(--font-mono);
  cursor: pointer;
}

.dialog-close:hover { color: var(--cc-foam); border-color: var(--cc-sky); }

.dialog-head {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.dialog-head img {
  width: 8rem;
  flex: none;
  aspect-ratio: 500 / 222;
  object-fit: contain;
}

.dialog-head img.silhouette { filter: brightness(0) opacity(0.55); }

.dialog-name { margin: 0.2rem 0 0; font: 600 1.5rem/1.2 var(--font-display); }

.dialog-measures {
  margin-top: 1.25rem;
  border-top: 1px solid var(--cc-rule);
}

.dialog-measure {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--cc-rule);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.84rem;
  font-variant-numeric: tabular-nums;
}

.dialog-measure-label { color: var(--cc-muted); letter-spacing: 0.06em; text-transform: uppercase; font-size: 0.72rem; }

.dialog-label {
  margin: 1.15rem 0 0.3rem;
  font: 700 0.72rem/1 var(--font-body);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cc-sun);
}

.dialog-list { margin: 0; text-transform: capitalize; }

.dialog-locations {
  margin: 0;
  padding-left: 1.1rem;
  color: var(--cc-muted);
  font-size: 0.9rem;
}

.dialog-note,
.dialog-loading { margin: 1.25rem 0 0; color: var(--cc-muted); font-size: 0.9rem; }

/* =========================================================================
   The logbook (/dashboard.html) — one player's own numbers

   Reuses the tiles, the rarity bar and the catch card the other two pages
   already have; everything below is what those did not already cover.
   ========================================================================= */

/* The dashboard is wider than the reading pages: it is figures in columns,
   not prose, so the 66rem measure buys nothing and costs a column. */
.column-wide { --page-max: 84rem; }

.tile-hint {
  margin-top: 0.35rem;
  font-size: 0.8rem;
  color: var(--cc-muted);
}

/* Label/value pairs — equipment, and anything else that is a fact rather than
   a figure. */
.facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.35rem 1.25rem;
  margin: 0;
}

.facts dt {
  font: 700 0.72rem/1.6 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cc-muted);
}

.facts dd { margin: 0; }

/* A count on the left, a filter on the right. */
.panel-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
}

.panel-search {
  min-width: 0;
  width: 9rem;
  padding: 0.35rem 0.6rem;
  color: var(--cc-foam);
  background: var(--cc-navy);
  border: 1px solid var(--cc-rule);
  border-radius: 6px;
  font: 400 0.85rem/1 var(--font-body);
}

.panel-search:focus-visible { outline: 2px solid var(--cc-sky); outline-offset: 1px; }

/* A list too long to own the page — it gets a few rows' worth of height and
   scrolls inside itself. `overscroll-behavior` so reaching the bottom does not
   then start scrolling the page out from under the cursor. */
.scroll-box {
  max-height: clamp(18rem, 42vh, 28rem);
  margin-top: 0.85rem;
  padding: 0.15rem 0.5rem 0.15rem 0.15rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--cc-sky) transparent;
}

.scroll-box:focus-visible { outline: 2px solid var(--cc-sky); outline-offset: 2px; }

/* A section whose contents are several small panels rather than one run of
   prose — the breakdown's chart and three lists, which stacked to about a
   screen and a half on their own. */
.panel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 1.5rem 2.75rem;
  align-items: start;
  margin-top: 1.5rem;
}

.panel-grid .panel-title { margin-top: 0; }
.panel-wide { grid-column: 1 / -1; }

/* ---------- the 90-day sparkline: one path, no library ---------- */

.spark-wrap { margin-top: 0; }

.spark {
  display: block;
  width: 100%;
  height: 5.5rem;
  overflow: visible;
}

.spark-line {
  fill: none;
  stroke: var(--cc-wood-light);
  stroke-width: 2;
  vector-effect: non-scaling-stroke;
}

.spark-fill { fill: rgba(181, 101, 29, 0.16); stroke: none; }

.spark-caption {
  margin: 0.6rem 0 0;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.8rem;
  color: var(--cc-muted);
}

/* ---------- ranked lists ---------- */

.bars {
  display: grid;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.bars li {
  display: grid;
  grid-template-columns: minmax(6rem, 12rem) 1fr max-content;
  align-items: center;
  gap: 0.9rem;
  font-size: 0.9rem;
}

.bars-label { overflow-wrap: anywhere; }

.bars-meter {
  height: 0.5rem;
  border-radius: 999px;
  background: rgba(44, 95, 138, 0.25);
  overflow: hidden;
}

.bars-fill {
  display: block;
  height: 100%;
  background: var(--cc-sky);
}

.bars-count {
  font-family: var(--font-body);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  color: var(--cc-muted);
}

/* ---------- catch history ---------- */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin: 1.5rem 0 0;
  font: 700 0.72rem/1 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cc-muted);
}

.filters label {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.filters select {
  padding: 0.45rem 0.6rem;
  color: var(--cc-foam);
  background: var(--cc-navy);
  border: 1px solid var(--cc-rule);
  border-radius: 6px;
  font: 400 0.85rem/1 var(--font-body);
  letter-spacing: normal;
  text-transform: none;
}

/* The same card the feed uses, in a grid. */
.catch-list {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 1.5rem;
}

.catch-tag {
  margin-top: 0.4rem;
  font: 700 0.68rem/1 var(--font-body);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cc-sun);
}

.catch-more {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.5rem;
}

.catch-status { font-size: 0.85rem; color: var(--cc-muted); }

/* ---------- gear and achievements ---------- */

.gear-list,
.achievements {
  display: grid;
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Wide rows rather than small cards: at 15rem a badge got two columns and
   every one of them wrapped. A row is never narrower than the sentence it has
   to hold, so a second column only appears when there is room for two. */
.achievements {
  grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr));
  gap: 0.4rem;
}

.gear-list li {
  display: grid;
  grid-template-columns: minmax(5rem, max-content) minmax(8rem, 1fr) 2fr;
  gap: 1rem;
  padding: 0.7rem 0.9rem;
  background: rgba(20, 48, 79, 0.5);
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  font-size: 0.9rem;
}

.gear-slot {
  font: 700 0.7rem/1.6 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cc-muted);
}

.gear-bonuses { color: var(--cc-muted); font-size: 0.85rem; }

.achievement {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 0.7rem;
  padding: 0.5rem 0.7rem;
  background: rgba(20, 48, 79, 0.4);
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  /* Unearned reads as unearned without colour being the only signal — it has
     no earned date, and it is inside a fold labelled "still to earn". */
  opacity: 0.55;
}

.achievement.earned {
  opacity: 1;
  border-left: 2px solid var(--cc-wood-light);
}

/* Name and meta share the first line, the description has the second. The
   meta is `max-content` and never wraps, so the name is what gives way when
   the row is narrow. */
.achievement-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) max-content;
  align-items: baseline;
  column-gap: 0.75rem;
}

.achievement-emoji { font-size: 1.15rem; line-height: 1.4; }

.achievement-name {
  font-weight: 600;
  font-size: 0.95rem;
  /* The one thing here of unbounded length — a long name shortens rather than
     pushing the date off the row. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.achievement-desc {
  grid-column: 1 / -1;
  color: var(--cc-muted);
  font-size: 0.82rem;
  line-height: 1.35;
}

.achievement-meta {
  white-space: nowrap;
  /* 1.4, not 1: at a line-height of 1 a wrapped meta line sat on top of the
     card's own padding. It cannot wrap any more, but nothing here should
     depend on that. */
  font: 700 0.68rem/1.4 var(--font-body);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cc-muted);
}

/* ---------- Special Waters (#79) ----------
   Not part of the depth progression the rest of the map describes, so it does
   not borrow the rail's language: a warm rule instead of a level, and a plain
   sentence saying it is worth nothing toward completion. */

.dex-note {
  max-width: var(--measure);
  margin: 0.75rem 0 0;
  padding-left: 0.9rem;
  border-left: 2px solid var(--cc-wood-light);
  color: var(--cc-muted);
  font-size: 0.88rem;
}

/* Set apart in the map too: it sits below the three real regions and is not a
   rung on the same ladder, so it does not inherit their progression colour. */
.chart-region[data-region="special"] { border-top: 1px solid var(--cc-rule); }

.chart-region[data-region="special"] .chart-region-name { color: var(--cc-foam); }

.chart-region[data-region="special"] .water-lv { color: var(--cc-muted); }

/* =========================================================================
   Hall of Champions (the champions tab of /leaderboard.html)

   A settled record, so it reads as a ledger: one line per period, dates on
   the left, the count on the right, nothing between them competing for it.
   ========================================================================= */

.champions {
  display: grid;
  gap: 0.4rem;
  margin: 1.75rem 0 2.5rem;
  padding: 0;
  list-style: none;
  counter-reset: none;
}

.champion {
  display: grid;
  grid-template-columns: minmax(10rem, 15rem) 1fr max-content;
  align-items: baseline;
  gap: 1rem;
  padding: 0.75rem 1rem;
  background: rgba(20, 48, 79, 0.4);
  border: 1px solid var(--cc-rule);
  border-left: 2px solid transparent;
  border-radius: var(--radius);
}

/* The most recent settled period, which is what most visitors came for. */
.champion-latest {
  background: var(--cc-card);
  border-left-color: var(--cc-wood-light);
}

.champion-when {
  font: 700 0.74rem/1.5 var(--font-body);
  letter-spacing: 0.06em;
  color: var(--cc-muted);
}

.champion-who {
  font: 600 1.15rem/1.3 var(--font-display);
  overflow-wrap: anywhere;
}

/* A period nobody won is a real state, not a gap — it just says so quietly. */
.champion-nobody {
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-style: italic;
  color: var(--cc-muted);
}

.champion-count {
  font-family: var(--font-body);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-size: 0.84rem;
  color: var(--cc-muted);
}

@media (max-width: 34rem) {
  .champion {
    grid-template-columns: 1fr max-content;
  }
  .champion-when { grid-column: 1 / -1; }

  /* The rank stays inline: unlike a date range it fits beside the name, and
     giving it its own row would waste a line per entry on the narrowest
     screens — where the board is longest. */
  .board .champion { grid-template-columns: 3rem 1fr max-content; }
  .board .champion-when { grid-column: auto; }
}

/* =========================================================================
   Live standings (/leaderboard.html)

   Borrows the champions ledger wholesale — one line per angler — and adds a
   rank column plus a stacked filter block, because this page has two controls
   where that one has a single pair of radios.
   ========================================================================= */

.filters-stacked {
  flex-direction: column;
  align-items: flex-start;
  gap: 1.25rem;
}

.filters fieldset {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  border: 0;
}

.filters legend {
  padding: 0;
  color: var(--cc-muted);
}

/* Eight raw radio dots read as a form to fill in; this is navigation between
   eight views of one board. So the dots go and the labels become the control —
   the real <input> stays for keyboard and screen readers, just not painted.
   `appearance: none` rather than `display: none`, which would take the radio
   out of the tab order entirely. */
.board-filters label {
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--cc-rule);
  border-radius: 999px;
  color: var(--cc-muted);
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}

.board-filters input {
  appearance: none;
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.board-filters label:hover { color: var(--cc-foam); border-color: var(--cc-sky); }

/* The selected view. Filled rather than merely tinted, so which one is active
   survives being read at a glance or in greyscale. */
.board-filters label:has(:checked) {
  color: var(--cc-foam);
  background: rgba(44, 95, 138, 0.5);
  border-color: var(--cc-sky);
}

/* The outline follows the hidden input, so keyboard focus is still visible. */
.board-filters label:focus-within {
  outline: 2px solid var(--cc-wood-light);
  outline-offset: 2px;
}

/* A window control the ranking cannot honour. Greyed rather than removed —
   it comes back when you pick a category that supports it, and a control that
   appears and disappears is harder to read than one that dims. */
.filters fieldset.is-disabled { opacity: 0.4; }
.filters fieldset.is-disabled label { cursor: not-allowed; }

/* The champions ledger sizes its first column for a date range ("1–7 Jun
   2026"). A rank is four characters, so inheriting that width strands the name
   in the middle of an empty row. */
.board .champion { grid-template-columns: 4.5rem 1fr max-content; }

/* Monospace so the ranks form a column rather than a ragged edge — #1 and #100
   are different widths. */
.champion .rank {
  font-variant-numeric: tabular-nums;
  color: var(--cc-sun);
}

/* ---- tabs: the live board and the champions record share one page ----

   Same chip language as .board-filters, because this is also navigation
   between views of one thing. Real <button>s, so keyboard focus comes free;
   leaderboard.js toggles aria-selected and the hash together. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 1.5rem 0 0;
}

.tab {
  padding: 0.35rem 0.7rem;
  font: 700 0.72rem/1 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cc-muted);
  background: none;
  border: 1px solid var(--cc-rule);
  border-radius: 999px;
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}

.tab:hover { color: var(--cc-foam); border-color: var(--cc-sky); }

/* Filled rather than merely tinted, matching the selected filter chip — the
   active view must survive a glance or greyscale. */
.tab[aria-selected="true"] {
  color: var(--cc-foam);
  background: rgba(44, 95, 138, 0.5);
  border-color: var(--cc-sky);
}

.tab:focus-visible {
  outline: 2px solid var(--cc-wood-light);
  outline-offset: 2px;
}

.tab-panel[hidden] { display: none; }

/* =========================================================================
   A shared catch (/c/{short_code})

   Reached from a Discord unfurl, so it is one fish and one call to action —
   no rail, no sections, nothing else to read.
   ========================================================================= */

/* A band of sky, not the full dawn: the reader came from a Discord link to see
   one fish. `.deep` reads `--surface-h` for its own negative margin as well as
   the wave's height, so overriding it here keeps the two in step. */
.deep-share { --surface-h: 5rem; }

/* Deeper than `--surface-h`, or the wave that `.deep` pulls up over this hero
   would cut across the eyebrow. */
.hero-share { min-height: 0; padding-bottom: 8rem; }


.share {
  position: relative;
  z-index: 1;
  max-width: 32rem;
  margin: 0 auto;
  padding: 2rem var(--gutter) 4rem;
  text-align: center;
}

/* `.cta` is a flex row, which ignores the centring above. */
.share .cta {
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
}

.share .catch-full { margin: 1rem auto; }

.share-name {
  margin: 0.3rem 0 0;
  font: 600 clamp(1.7rem, 5vw, 2.4rem)/1.15 var(--font-display);
  text-wrap: balance;
}

.share-angler { margin: 0.5rem 0 0; color: var(--cc-muted); }

.share .dialog-measures {
  margin: 2rem 0;
  text-align: left;
}

/* /fish/{slug} splits its measures in two: the server renders size and weight,
   and /species-page.js appends the catch counts into a second block, because
   the served page has to be byte-identical for every visitor. They are one
   table to the reader, so the second block drops the gap and the rule that
   would otherwise double the line above it. */
.share .dialog-measures + .dialog-measures {
  margin-top: 0;
  border-top: 0;
}

.share .catch-rarity { justify-content: center; }

/* The species page borrows this layout, and a bulleted list inside a centred
   block leaves its markers stranded out in the margin. */
.share .dialog-locations { display: inline-block; text-align: left; }

/* The share link inside the catch dialog. A button, because it is the one
   action on an otherwise read-only panel. */
.dialog-share {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  margin-top: 1.5rem;
  text-align: center;
}

.dialog-share-note {
  font: 400 0.72rem/1.4 var(--font-body);
  letter-spacing: normal;
  text-transform: none;
  opacity: 0.75;
}

/* =========================================================================
   Journal search

   A combobox over an in-memory index. The panel is absolutely positioned so
   opening it does not push the 27-location map down the page.
   ========================================================================= */


.dex-search {
  position: relative;
  max-width: var(--measure);
  margin: 1.5rem 0 0;
}

.dex-search input {
  width: 100%;
  padding: 0.65rem 0.85rem;
  color: var(--cc-foam);
  background: var(--cc-navy);
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius);
  font: 400 0.95rem/1.4 var(--font-body);
}

.dex-search input::placeholder { color: var(--cc-muted); }
.dex-search input:focus { border-color: var(--cc-sky); }

.dex-search-count {
  margin: 0.4rem 0 0;
  font: 700 0.68rem/1 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cc-muted);
  min-height: 0.7rem;
}

.dex-search-results {
  position: absolute;
  z-index: 4;
  top: 100%;
  left: 0;
  right: 0;
  max-height: 20rem;
  overflow-y: auto;
  margin: 0.35rem 0 0;
  padding: 0.3rem;
  list-style: none;
  background: var(--cc-navy-deep);
  border: 1px solid var(--cc-sky);
  border-radius: var(--radius);
  box-shadow: 0 18px 40px rgba(4, 12, 22, 0.55);
}

.dex-search-hit {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding: 0.5rem 0.65rem;
  border-radius: 6px;
  cursor: pointer;
}

/* Hover and keyboard selection are the same state — there is one cursor
   through this list however you drive it. */
.dex-search-hit:hover,
.dex-search-hit.active { background: rgba(44, 95, 138, 0.4); }

.dex-search-label { overflow-wrap: anywhere; }

.dex-search-hint {
  flex: none;
  font: 700 0.68rem/1.4 var(--font-body);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cc-muted);
}

.dex-search-empty {
  padding: 0.5rem 0.65rem;
  color: var(--cc-muted);
  font-size: 0.86rem;
}

/* =========================================================================
   Footer — the seabed. Generated from NAV_GROUPS, so it always agrees with
   the nav.
   ========================================================================= */

.site-footer {
  position: relative;
  z-index: 1;
  margin-top: auto;
  border-top: 1px solid var(--cc-rule);
  background: linear-gradient(180deg, rgba(4, 12, 22, 0), rgba(4, 12, 22, 0.85) 30%);
  padding: 3.5rem var(--gutter) 2.5rem;
  color: var(--cc-muted);
}

.footer-inner {
  display: grid;
  grid-template-columns: minmax(16rem, 1.6fr) repeat(4, minmax(0, 1fr));
  gap: 2rem;
  max-width: var(--page);
  margin: 0 auto;
}

.footer-brand p { max-width: 22rem; margin: 1rem 0 0; font-size: 0.95rem; }
.footer-brand .wordmark { color: var(--cc-foam); }
.footer-cta { display: flex; flex-wrap: wrap; gap: 0.6rem; }

.footer-col h2 {
  margin: 0.4rem 0 0.9rem;
  font: 700 0.74rem/1 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cc-foam);
}

.footer-col ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 0.45rem; }
.footer-col a { font-size: 0.93rem; text-decoration: none; }
.footer-col a:hover { color: var(--cc-foam); text-decoration: underline; }

.fine-print {
  max-width: var(--page);
  margin: 2.5rem auto 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--cc-rule);
  font-size: 0.82rem;
  text-align: center;
}

@media (max-width: 60rem) {
  .footer-inner { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .footer-brand { grid-column: 1 / -1; }
}

/* =========================================================================
   Narrow screens: the rail folds into an eyebrow
   ========================================================================= */

@media (max-width: 46rem) {
  :root { --hero-h: clamp(22rem, 76vh, 40rem); }

  .column { grid-template-columns: minmax(0, 1fr); }

  .column > section,
  .dash-grid > section {
    grid-column: 1;
    border-left: 0;
    padding: 0.75rem 0 3rem;
  }

  .dash-grid:not([hidden]) { padding-left: 0; border-left: 0; }

  :root { --surface-h: 5rem; }
  .waterline-label { display: none; }

  .waters { grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); }
}

/* =========================================================================
   Motion. All of it optional, none of it load-bearing.
   ========================================================================= */

@media (prefers-reduced-motion: no-preference) {
  /* The surface is alive: the swell slides behind the water at its own pace. */
  .wave-front, .crest { animation: swell 16s linear infinite; }
  .wave-back { animation: swell 25s linear infinite reverse; }

  /* Light through the water, breathing. */
  .shafts { animation: sway 19s ease-in-out infinite alternate; }

  /* Marine snow rising. */
  .motes { animation: drift 45s linear infinite; }

  /* The Discord demo: messages arrive, dots type, buttons nudge once. */
  .dc-enter { animation: rise 420ms cubic-bezier(0.2, 0.7, 0.3, 1) both; }
  .dc-typing span { animation: typing 1.2s ease-in-out infinite; }
  .dc-typing span:nth-child(2) { animation-delay: 150ms; }
  .dc-typing span:nth-child(3) { animation-delay: 300ms; }
  .dc-pulse .dc-btn { animation: nudge 700ms ease 500ms 1 both; }
  .dc-pulse .dc-btn:nth-child(2) { animation-delay: 600ms; }
  .dc-pulse .dc-btn:nth-child(3) { animation-delay: 700ms; }

  /* A catch that arrived while you watched. */
  .catch-new { animation: rise 600ms cubic-bezier(0.2, 0.7, 0.3, 1) both; }

  /* The hero surfaces once on load. */
  .hero > * { animation: rise 700ms cubic-bezier(0.2, 0.7, 0.3, 1) backwards; }
  .hero > :nth-child(2) { animation-delay: 80ms; }
  .hero > :nth-child(3) { animation-delay: 160ms; }
  .hero > :nth-child(4) { animation-delay: 240ms; }

  /* Anchor jumps in the journal glide. Inside the guard, because unguarded
     smooth scrolling is a known accessibility problem. */
  html { scroll-behavior: smooth; }

  /* The rarest two get a slow breath on the share page — the page a stranger
     meets the site on. The other five stay still; a shimmer on a Common is
     noise. Keyed off --cc-rarity, which catch.py already sets. */
  .share.rarity-mythical .catch-full,
  .share.rarity-unique .catch-full { animation: rarity-glow 4.5s ease-in-out infinite alternate; }

  /* Scroll-driven, so the descent is something the page does rather than
     something the copy claims. Behind @supports rather than relying on the
     fill: without a timeline these run on the document timeline at 0s and
     land on their end state, which is right for `rise` (visible) and wrong
     for `sink` (a permanently dimmed sky). */
  @supports (animation-timeline: view()) {
    .column > section,
    .dash-grid > section,
    .feed .catch,
    .tile {
      animation: rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 40%;
    }

    /* The sky recedes and dims over the first viewport of scroll. scroll(root)
       binds to the document scroller specifically, so `.deep`'s overflow-x:
       clip is never in the running. */
    .sky {
      animation: sink linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 100vh;
    }
  }
}

/* The ambient layer surfaces as you go under the waterline. Under reduced
   motion the layer never moves, so it simply shows over the water only. */
@media (prefers-reduced-motion: reduce) {
  .ambient { position: absolute; inset: var(--surface-h) 0 0 0; opacity: 1; }
  .ambient > * { max-height: 140vh; }
}

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
  .ambient {
    animation: surface-in linear both;
    animation-timeline: scroll(root block);
    animation-range: 0 var(--ambient-in, 70vh);
  }
  }
}

/* @view-transition is a top-level at-rule and cannot be nested in a media
   query, so the reduced-motion opt-out is on the pseudo-elements instead. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none; }
}

@keyframes swell {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

@keyframes sway {
  from { transform: translateX(-3%) skewX(-1.5deg); opacity: 0.35; }
  to   { transform: translateX(3%) skewX(1.5deg); opacity: 0.6; }
}

@keyframes drift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, -720px, 0); }
}

@keyframes typing {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-4px); opacity: 1; }
}

@keyframes nudge {
  0%, 100% { transform: none; }
  50% { transform: translateY(-3px) scale(1.04); }
}

@keyframes surface-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes rise {
  from { opacity: 0; transform: translateY(1rem); }
  to   { opacity: 1; transform: none; }
}

@keyframes sink {
  from { transform: translateY(0)     scale(1);    opacity: 1;    }
  to   { transform: translateY(-4rem) scale(1.06); opacity: 0.55; }
}

@keyframes rarity-glow {
  from { box-shadow: 0 0 0 1px transparent; }
  to   { box-shadow: 0 0 2.5rem -0.5rem var(--cc-rarity, transparent); }
}

/* =========================================================================
   Player pages on the new design (#183): dashboard, journal, leaderboard,
   Contest Pier, and the shared catch page. The page JS is unchanged in what
   it fetches and renders; this is its new layout.
   ========================================================================= */

/* A section on its own glass panel. */
.panel-card {
  margin-top: 1.25rem;
  padding: clamp(1.1rem, 3vw, 1.75rem);
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius-lg);
  background: var(--cc-card);
  box-shadow: inset 0 1px 0 var(--cc-glass-edge);
  scroll-margin-top: calc(var(--header-h) + 4rem);
}

.panel-card > .eyebrow:first-child { margin-top: 0; }

/* ---- the gate ---- */

.gate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  max-width: 34rem;
  margin: 1rem auto;
  padding: 2.25rem 1.75rem;
  border: 1px solid rgba(94, 224, 196, 0.25);
  border-radius: var(--radius-lg);
  background:
    radial-gradient(80% 90% at 50% 0%, rgba(94, 224, 196, 0.12), transparent 70%),
    var(--cc-card);
  text-align: center;
}

.gate[hidden] { display: none; }
.gate-art { width: 88px; height: 88px; border-radius: 50%; box-shadow: 0 0 0 3px rgba(244, 178, 107, 0.3); }
.gate-title { margin: 0.4rem 0 0; font-size: 1.6rem; }
.gate-blurb { margin: 0 0 0.6rem; color: var(--cc-muted); }

/* ---- skeletons while a gated page decides ---- */

.dash-loading { display: grid; gap: 1rem; }
.dash:has(#gate:not([hidden])) .dash-loading,
.dash:has(#dash:not([hidden])) .dash-loading,
.journal:not(.locked) .journal-loading,
.journal:has(#gate:not([hidden])) .journal-loading { display: none; }

.skeleton-tile { display: grid; gap: 0.6rem; }
.skeleton-tile .skeleton { height: 1.8rem; }
.skeleton-tile .skeleton-short { height: 0.8rem; width: 60%; }
.skeleton-block { height: 12rem; border-radius: var(--radius); }

/* The journal ships locked: the gate (or the loader) is the only visible child
   until journal.js confirms an entitled session. */
.locked > :not(.gate):not(.dash-loading) { display: none; }

/* ---- the dashboard ---- */

.dash-grid:not([hidden]) {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.25rem;
  align-items: start;
}

.dash-grid > section {
  grid-column: 1 / -1;
  margin: 0;
  padding: clamp(1.1rem, 3vw, 1.75rem);
  border: 1px solid var(--cc-rule);
  border-radius: var(--radius-lg);
  background: var(--cc-card);
  box-shadow: inset 0 1px 0 var(--cc-glass-edge);
  scroll-margin-top: calc(var(--header-h) + 4.5rem);
}

#sec-gear { grid-column: 1; }
#sec-achievements { grid-column: 2; }

/* Section navigation: sticky pills under the header, not a fixed side rail. */
.rail-nav {
  position: sticky;
  top: calc(var(--header-h) + 0.6rem);
  z-index: 10;
  grid-column: 1 / -1;
  display: flex;
  gap: 0.35rem;
  overflow-x: auto;
  width: max-content;
  max-width: 100%;
  margin: 0 auto;
  padding: 0.35rem;
  border: 1px solid var(--cc-rule-strong);
  border-radius: var(--radius-pill);
  background: rgba(8, 24, 43, 0.85);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  scrollbar-width: none;
}

.rail-nav a {
  flex: none;
  padding: 0.45rem 0.9rem;
  border-radius: var(--radius-pill);
  color: var(--cc-muted);
  font-weight: 800;
  font-size: 0.88rem;
  text-decoration: none;
  white-space: nowrap;
}

.rail-nav a:hover { color: var(--cc-foam); }
.rail-nav a[aria-current="true"] { background: rgba(94, 224, 196, 0.16); color: var(--cc-foam); }

@media (max-width: 60rem) {
  .dash-grid:not([hidden]) { grid-template-columns: minmax(0, 1fr); }
  #sec-gear, #sec-achievements { grid-column: 1; }
}

.tiles { grid-template-columns: repeat(auto-fill, minmax(min(11rem, 100%), 1fr)); }
.tile-value { font-weight: 600; }
.tile-hint { color: var(--cc-muted); }

/* Catch cards in the history and the feed. */
.catch {
  border-radius: var(--radius);
  background: var(--cc-card);
  box-shadow: inset 0 1px 0 var(--cc-glass-edge);
}

.catch img { border-radius: var(--radius-sm); image-rendering: pixelated; }

/* The rarest two get one shimmer as they arrive — never a loop across a grid. */
@media (prefers-reduced-motion: no-preference) {
  .catch.rarity-mythical::after,
  .catch.rarity-unique::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    background: linear-gradient(110deg, transparent 35%, color-mix(in srgb, var(--cc-rarity) 30%, transparent) 50%, transparent 65%);
    transform: translateX(-100%);
    animation: shimmer 1.4s ease 300ms 1 both;
  }
}

.catch { position: relative; overflow: hidden; }

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

/* ---- the journal ---- */

.journal .chart-section,
.journal .dex-section { margin-top: 1.25rem; }

.depth-chip {
  display: inline-flex;
  gap: 0.5rem;
  margin: 1.5rem 0 0;
  padding: 0.3rem 0.8rem;
  border: 1px solid rgba(244, 178, 107, 0.35);
  border-radius: var(--radius-pill);
  font-weight: 800;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.depth-chip .lv { color: var(--cc-sun); }
.depth-chip .place { color: var(--cc-muted); }

.journal .locked-hidden { display: none; }

.water { position: relative; border-radius: var(--radius); padding-right: 3rem; }

/* Progress ring: a conic gradient on --pct, set from JS. The count beside it
   carries the number. */
.water-ring {
  position: absolute;
  top: 50%;
  right: 0.7rem;
  width: 1.9rem;
  height: 1.9rem;
  margin-top: -0.95rem;
  border-radius: 50%;
  background:
    radial-gradient(closest-side, var(--cc-card-solid) 68%, transparent 70%),
    conic-gradient(var(--cc-accent) calc(var(--pct, 0) * 1%), rgba(255, 255, 255, 0.08) 0);
}

.water.done .water-ring {
  background:
    radial-gradient(closest-side, var(--cc-card-solid) 68%, transparent 70%),
    conic-gradient(var(--cc-sun) 100%, transparent 0);
}

.water.selected { border-color: var(--cc-accent); background: rgba(94, 224, 196, 0.1); }
.water-meter { display: none; }

.dex-card {
  border-radius: var(--radius);
  background: var(--cc-card);
}

.dex-card img { image-rendering: pixelated; }

.dex-search { max-width: 36rem; }
.dex-search input { border-radius: var(--radius-pill); padding: 0.75rem 1.1rem; background: rgba(8, 24, 43, 0.8); }

/* ---- leaderboard and pier ---- */

.leaderboard-page .panel-card,
.contest-page .panel-card { margin-top: 1rem; }

.podium {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: end;
  gap: 0.75rem;
  max-width: 40rem;
  margin: 1.5rem auto 1.75rem;
  padding: 0;
  list-style: none;
  text-align: center;
}

.podium-step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 1rem 0.6rem 0.9rem;
  border: 1px solid var(--cc-rule-strong);
  border-radius: var(--radius) var(--radius) var(--radius-sm) var(--radius-sm);
  background: linear-gradient(180deg, rgba(26, 58, 94, 0.9), rgba(12, 31, 54, 0.9));
  min-width: 0;
}

.podium-1 { min-height: 11rem; border-color: rgba(255, 215, 0, 0.5); background: linear-gradient(180deg, rgba(255, 215, 0, 0.16), rgba(12, 31, 54, 0.95)); }
.podium-2 { min-height: 9rem; }
.podium-3 { min-height: 7.5rem; }
.podium-medal { font-size: 1.9rem; line-height: 1; }
.podium-name { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 600 1.05rem/1.3 var(--font-display); }
.podium-score { color: var(--cc-muted); font-weight: 800; font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.podium-rank { margin-top: auto; color: var(--cc-sun); font-weight: 900; }

@media (prefers-reduced-motion: no-preference) {
  .podium-step { animation: rise 500ms cubic-bezier(0.2, 0.7, 0.3, 1) both; }
  .podium-1 { animation-delay: 120ms; }
  .podium-3 { animation-delay: 220ms; }
}

.champion { border-radius: var(--radius); background: rgba(20, 48, 79, 0.45); }
.champion-latest { background: rgba(94, 224, 196, 0.08); border-left-color: var(--cc-accent); }

.board-filters label:has(:checked),
.tab[aria-selected="true"] { background: rgba(94, 224, 196, 0.16); border-color: var(--cc-accent); }

/* ---- the shared catch page ---- */

.share {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  grid-template-areas:
    "img rarity"
    "img name"
    "img species"
    "img angler"
    "img sponsor"
    "img measures"
    "cta cta";
  column-gap: 2rem;
  align-items: start;
  max-width: 62rem;
  margin: 1.5rem auto 0;
  padding: 1.75rem;
  border: 1px solid var(--cc-rule);
  border-left: 4px solid var(--cc-rarity, var(--cc-sky));
  border-radius: var(--radius-lg);
  background: var(--cc-card);
  text-align: left;
}

.share .catch-full { grid-area: img; margin: 0; border-radius: var(--radius); }
.share .catch-rarity { grid-area: rarity; justify-content: flex-start; margin: 0; }
.share .share-name { grid-area: name; }
.share > .catch-species:not(:last-of-type) { grid-area: species; }
.share .share-angler { grid-area: angler; }
.share .dialog-measures { grid-area: measures; margin: 1rem 0 0; }
.share .cta { grid-area: cta; flex-direction: row; justify-content: center; margin-top: 2rem; }

@media (max-width: 44rem) {
  .share {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "img" "rarity" "name" "species" "angler" "sponsor" "measures" "cta";
    padding: 1.1rem;
  }
  .share .catch-full { margin-bottom: 1rem; }
  .share .cta { flex-direction: column; }
}

/* The catch dialog grows from the card that opened it (--from-x/--from-y are
   set by catchdialog.js; without them it rises from the centre). */
@media (prefers-reduced-motion: no-preference) {
  .species-dialog.from-origin {
    transform-origin: var(--from-x, 50%) var(--from-y, 50%);
  }
  @starting-style {
    .species-dialog.from-origin[open] { opacity: 0; transform: scale(0.6); }
  }
}


.leaderboard-page .tabs { margin: 0.75rem 0 1.5rem; }
.leaderboard-page .filters { margin-bottom: 1rem; }

/* ---- phone pass (#8) ---- */

/* Names wrap between words, never inside one ("Appalac-hian"). */
.water-name { overflow-wrap: break-word; word-break: normal; hyphens: auto; }

@media (max-width: 32rem) {
  /* One water per row: two columns left ~9 characters per line. */
  .waters { grid-template-columns: minmax(0, 1fr); }
  .water { grid-template-columns: auto 1fr; }

  /* Two tiles abreast rather than one very tall column. */
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem; }
  .tile { padding: 0.85rem 0.9rem; }
  .tile-value { font-size: 1.45rem; }

  /* Level tables: the description drops under level and cost. */
  .data-table thead { display: none; }
  .data-table tr {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.2rem 0.9rem;
    padding: 0.7rem 0;
    border-bottom: 1px solid var(--cc-rule);
  }
  .data-table td { padding: 0; border: 0; }
  .data-table td:nth-child(3) { grid-column: 1 / -1; color: #CFE0EB; }

  .rail-nav { width: 100%; }
  .gate { padding: 1.75rem 1.1rem; }
  .podium { gap: 0.4rem; }
  .podium-step { padding: 0.8rem 0.3rem; }
  .podium-name { font-size: 0.9rem; }
}
