/* Design system ported directly from the canvas (Keep the Duck Alive.dc.html)
   — same palette, type scale and component shapes, now hand-written CSS
   instead of inline styles inside a .dc.html template. */

@import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@400;600;700;800&family=Instrument+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@500;600&display=swap');

:root {
  --cream: #faf7ef;
  --ink: #22301f;
  --ink-40: rgba(30,40,20,0.4);
  --ink-45: rgba(30,40,20,0.45);
  --ink-50: rgba(30,40,20,0.5);
  --ink-55: rgba(30,40,20,0.55);
  --ink-60: rgba(30,40,20,0.6);
  --ink-65: rgba(30,40,20,0.65);
  --ink-70: rgba(30,40,20,0.7);
  --green: #2f7a3f;
  --green-dark: #1f5c2e;
  --green-bg: #eef6e6;
  --green-border-30: rgba(47,122,63,0.3);
  --green-border-15: rgba(47,122,63,0.15);
  --card-border: rgba(30,40,20,0.1);
  --card-border-soft: rgba(30,40,20,0.08);
  --card-border-06: rgba(30,40,20,0.06);
  --gold-bg-16: rgba(255,196,64,0.16);
  --gold-bg-14: rgba(255,196,64,0.14);
  --gold-border: rgba(184,134,11,0.25);
  --gold-text: #8a6300;
  --gold-text-dark: #b8860b;
  --bar: #22301f;
  --bar-text: #fdf8ec;
  --surface: #ffffff;
  --live-dot: #2fae4a;
  /* Dedicated, always-OPAQUE background for the sticky donate-bar — it used
     to reuse --green-bg, which is a solid hex here but a translucent rgba
     in dark mode (see below), so the scene/page scrolling underneath it
     showed through and made the bar's own text unreadable. */
  --donate-bar-bg: #eef6e6;
  --font-display: 'Bricolage Grotesque', sans-serif;
  --font-body: 'Instrument Sans', sans-serif;
  --font-mono: 'IBM Plex Mono', monospace;
}

/* Dark palette — same roles, inverted values. Applied via [data-theme="dark"]
   on <html> (set by app.js, persisted to localStorage). Every component
   below reads these tokens rather than a hardcoded color, so this block is
   the entire dark-mode implementation — no component rule needs to know
   which theme is active. */
:root[data-theme="dark"] {
  /* v2: pushed much closer to true black per user feedback ("que sea algo
     en negro, mucho mas correlativo con este proyecto") — was a dark forest
     green (#12190f). Still carries a faint green whisper (not literal
     #000) so it stays recognizably "this project" instead of a generic
     black theme, but reads as black at a glance. */
  --cream: #070905;
  --ink: #eaf3e2;
  --ink-40: rgba(234,243,226,0.4);
  --ink-45: rgba(234,243,226,0.45);
  --ink-50: rgba(234,243,226,0.5);
  --ink-55: rgba(234,243,226,0.55);
  --ink-60: rgba(234,243,226,0.6);
  --ink-65: rgba(234,243,226,0.65);
  --ink-70: rgba(234,243,226,0.7);
  --green: #4fbb66;
  --green-dark: #6ccf82;
  --green-bg: rgba(79,187,102,0.14);
  --green-border-30: rgba(79,187,102,0.38);
  --green-border-15: rgba(79,187,102,0.2);
  --card-border: rgba(234,243,226,0.14);
  --card-border-soft: rgba(234,243,226,0.09);
  --card-border-06: rgba(234,243,226,0.07);
  --gold-bg-16: rgba(255,196,64,0.2);
  --gold-bg-14: rgba(255,196,64,0.17);
  --gold-border: rgba(255,196,64,0.35);
  --gold-text: #ffce5c;
  --gold-text-dark: #ffce5c;
  --bar: #000000;
  --bar-text: #eaf3e2;
  --surface: #0e120a;
  --live-dot: #4fe073;
  /* Solid, not translucent — see the light-mode definition's comment. A
     dark, slightly-lifted-off-black green so the sticky bar reads as
     "still the pond" rather than a plain gray toolbar. */
  --donate-bar-bg: #0c130a;
}

* { box-sizing: border-box; }
/* Belt-and-suspenders: several components below set an explicit `display`
   (flex/inline-flex) on the same element that JS toggles via the `hidden`
   attribute. Author CSS always beats the UA stylesheet's default
   `[hidden]{display:none}` at equal specificity, so without this rule a
   "hidden" flex/inline-flex element still renders as an empty box — bit us
   twice already (the biggest-CARE pill and the link-preview chip both
   leaked through empty). One global override fixes the whole class of bug
   instead of patching each component that happens to set `display`. */
[hidden] { display: none !important; }
html { color-scheme: light; }
html[data-theme="dark"] { color-scheme: dark; }
body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--font-body);
  padding-bottom: 48px;
}
a { color: var(--green); text-decoration: none; }
a:hover { color: var(--green-dark); text-decoration: underline; }
/* Was a hardcoded dark rgba — invisible against dark-mode's dark input
   backgrounds (the actual bug behind "no se ve el nombre/compañía en modo
   oscuro"). --ink-40 already has a light-on-dark variant in the dark
   palette above, so this now flips automatically with the theme. */
input::placeholder { color: var(--ink-40); }
button { font-family: inherit; }

@keyframes duckBob0 { 0%,100%{ transform: translateY(0) } 50%{ transform: translateY(var(--bobAmp)) } }
@keyframes duckBob1 { 0%,100%{ transform: translateY(0) } 50%{ transform: translateY(var(--bobAmp)) } }
@keyframes duckBob2 { 0%,100%{ transform: translateY(0) } 50%{ transform: translateY(var(--bobAmp)) } }
@keyframes duckBob3 { 0%,100%{ transform: translateY(0) } 50%{ transform: translateY(var(--bobAmp)) } }
@keyframes eggGlow { 0%,100%{ filter: drop-shadow(0 0 12px rgba(255,214,120,0.4)) } 50%{ filter: drop-shadow(0 0 26px rgba(255,214,120,0.75)) } }
@keyframes drift { 0%{ transform: translateX(0) } 100%{ transform: translateX(30px) } }
@keyframes fall { 0%{ transform: translateY(-10px); opacity:0.8 } 100%{ transform: translateY(140px); opacity:0 } }
@keyframes twinkle { 0%,100%{ opacity:0.3 } 50%{ opacity:0.9 } }
@keyframes rippleMove { 0%,100%{ transform:scale(1); opacity:0.5 } 50%{ transform:scale(1.15); opacity:0.15 } }
@keyframes tickerScroll { 0%{ transform: translateX(0) } 100%{ transform: translateX(-50%) } }
@keyframes liveDotPulse { 0%{ box-shadow: 0 0 0 0 rgba(47,174,74,0.55) } 70%{ box-shadow: 0 0 0 6px rgba(47,174,74,0) } 100%{ box-shadow: 0 0 0 0 rgba(47,174,74,0) } }

.page { min-height: 100vh; }

/* ---- ticker bar ---- */
.ticker-bar {
  background: var(--bar);
  color: var(--bar-text);
  padding: 7px clamp(16px,4vw,40px);
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
  text-align: center;
}
.ticker-bar a { color: var(--bar-text); text-decoration: underline; }
.live-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--live-dot);
  animation: liveDotPulse 1.8s ease-out infinite;
}

/* ---- theme toggle ---- */
.theme-toggle {
  border: 1px solid var(--card-border);
  background: transparent;
  color: var(--ink);
  width: 32px;
  height: 32px;
  border-radius: 999px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  flex-shrink: 0;
}
.theme-toggle:hover { background: var(--card-border-06); }

/* Full-screen wipe transition: a flat-colored panel slides in to cover the
   viewport, the theme swaps underneath it (invisible — same flat color),
   then it slides back out. Dark mode wipes in from the top ("nace arriba");
   light mode wipes in from the bottom ("nace abajo"), per the Dodo Payments
   reference. Driven entirely by two classes app.js toggles — see its
   toggleTheme(). */
.theme-wipe {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  transform: translateY(-100%);
}
.theme-wipe.wipe-to-dark { background: #070905; transform: translateY(-100%); }
.theme-wipe.wipe-to-light { background: #faf7ef; transform: translateY(100%); }
.theme-wipe.wipe-cover { transform: translateY(0); transition: transform 0.5s cubic-bezier(0.65,0,0.35,1); }
.theme-wipe.wipe-exit.wipe-to-dark { transform: translateY(100%); transition: transform 0.5s cubic-bezier(0.65,0,0.35,1); }
.theme-wipe.wipe-exit.wipe-to-light { transform: translateY(-100%); transition: transform 0.5s cubic-bezier(0.65,0,0.35,1); }

/* Caption shown while the overlay fully covers the screen — "KEEP THE DUCK
   ALIVE · Generation N · N ducks alive", filled in from latestGen right as
   the wipe starts (app.js's updateWipeCaption()). Fades in only once the
   cover phase is underway so it never flashes over page content. */
.wipe-caption {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; text-align: center; padding: 0 24px; opacity: 0; transition: opacity 0.35s ease 0.1s;
}
.theme-wipe.wipe-cover .wipe-caption, .theme-wipe.wipe-exit .wipe-caption { opacity: 1; }
.wipe-title { font-family: var(--font-display); font-weight: 800; font-size: clamp(18px,4.5vw,28px); letter-spacing: 0.03em; }
.wipe-sub { font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.04em; opacity: 0.7; }
.theme-wipe.wipe-to-dark .wipe-caption { color: #eaf3e2; }
.theme-wipe.wipe-to-light .wipe-caption { color: #22301f; }

/* ---- header ---- */
.site-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px clamp(16px,4vw,40px);
  border-bottom: 1px solid var(--card-border-soft);
  flex-wrap: wrap;
  gap: 12px;
  background: var(--cream);
}
.brand {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 20px;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 8px;
}
.nav-links { display: flex; gap: 22px; font-size: 14px; color: var(--ink-65); flex-wrap: wrap; }

/* ---- donate bar (sticky — the single amount control lives here) ---- */
.donate-bar {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--donate-bar-bg);
  border-bottom: 1px solid var(--green-border-15);
  padding: 10px clamp(16px,4vw,40px);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 13px;
}
.grace-badge { white-space: nowrap; }

/* No pill, no border — just the number, sized up, with quiet +/- controls
   on either side (outbid.lol's "Claim #25 for $2,122" treatment: the price
   itself IS the control, nothing boxes it in). A live rank-preview caption
   sits directly under it. */
.stepper-wrap { display: flex; flex-direction: column; align-items: center; gap: 1px; }
.stepper {
  display: flex;
  align-items: baseline;
  gap: 4px;
}
.stepper button {
  width: 22px;
  height: 22px;
  border-radius: 999px;
  border: none;
  background: transparent;
  color: var(--green);
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
  line-height: 1;
  transition: background 0.15s ease;
}
.stepper button:hover { background: var(--green-border-15); }
.stepper .amt-wrap { display: flex; align-items: baseline; gap: 1px; font-family: var(--font-mono); }
.stepper .amt-wrap span { font-size: 13px; color: var(--ink-50); font-weight: 600; }
.stepper input {
  /* No fixed px width — a $1000+ amount was getting clipped inside the old
     66px box. app.js's setAmount() keeps this in sync with the digit
     count (in `ch` units, which map 1:1 to this monospace font's digit
     width), so the control grows/shrinks with the number instead of
     cutting it off. */
  width: 2ch;
  min-width: 1ch;
  max-width: 6ch;
  border: none;
  background: transparent;
  font-family: var(--font-mono);
  font-size: 19px;
  font-weight: 700;
  color: var(--ink);
  text-align: center;
  padding: 0;
}
.stepper input:focus { outline: none; }
.stepper input::-webkit-outer-spin-button, .stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stepper input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.rank-preview {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-45);
  letter-spacing: 0.02em;
  white-space: nowrap;
  min-height: 13px;
}
.rank-preview strong { color: var(--green); font-weight: 700; }

.pill-input {
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid var(--green-border-30);
  background: var(--surface);
  color: var(--ink);
  font-size: 12px;
  font-family: var(--font-body);
}
.pill-input.name { width: 180px; }
.pill-input.company { width: 170px; }
select.pill-input { -webkit-appearance: none; appearance: none; background-image: linear-gradient(45deg,transparent 50%,var(--ink-50) 50%),linear-gradient(135deg,var(--ink-50) 50%,transparent 50%); background-position: calc(100% - 14px) center, calc(100% - 9px) center; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 26px; }

/* Sub-flow that opens under the sticky top bar only when it's relevant
   (currently: reason === 'pond_keeper') — "que se abra la pestaña de
   subflujos si corresponde". Kept out of `hidden` (which is display:none,
   can't transition) so it can slide/fade open instead of popping in. */
.bar-subflow {
  flex-basis: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  padding-top: 0;
  transition: max-height 0.3s ease, opacity 0.25s ease, padding-top 0.3s ease;
}
.bar-subflow.open { max-height: 64px; opacity: 1; padding-top: 8px; border-top: 1px dashed var(--green-border-15); margin-top: 2px; }
/* The business-type dropdown's options panel is position:absolute and
   needs to render OUTSIDE this box's bounds — but overflow:hidden (needed
   above so the max-height open/close actually clips/animates) was cutting
   it off entirely, so the panel opened but showed no visible options ever.
   app.js adds this class only once the open transition has fully finished
   (and removes it before the close transition starts), so it clips while
   animating but lets the dropdown escape once fully open. */
.bar-subflow.open.settled { overflow: visible; }
.bar-subflow .icon-dropdown { width: 220px; margin-top: 0; }
.bar-subflow .icon-dropdown-btn { padding: 6px 12px; border-radius: 999px; font-size: 12px; }

.btn-give {
  padding: 6px 16px;
  border-radius: 999px;
  background: var(--green);
  color: #fff;
  font-weight: 700;
  font-size: 12px;
  border: none;
  cursor: pointer;
  white-space: nowrap;
}
.btn-give:disabled { opacity: 0.5; cursor: default; }
.btn-give:hover:not(:disabled) { background: var(--green-dark); }

/* ---- hero ---- */
.hero { max-width: 680px; margin: 36px auto 0; padding: 0 24px; text-align: center; }
.headline { font-family: var(--font-display); font-size: clamp(24px,5vw,34px); font-weight: 800; line-height: 1.15; }
.gen-label { font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.1em; color: var(--ink-50); margin-top: 14px; }
.tagline { font-family: var(--font-display); font-size: 15px; letter-spacing: 0.06em; color: #5a7a4a; text-transform: uppercase; margin-top: 6px; }
.lede { font-size: 14px; color: var(--ink-60); margin-top: 16px; line-height: 1.6; }
.biggest-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 18px;
  background: var(--gold-bg-16);
  border: 1px solid var(--gold-border);
  border-radius: 999px;
  padding: 8px 18px;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--gold-text);
}

/* ---- latest activity bar ---- */
.activity-bar {
  margin: 8px clamp(12px,4vw,40px) 0;
  background: rgba(34,48,31,0.05);
  border-radius: 12px;
  padding: 9px 18px;
  text-align: center;
  font-size: 13px;
  color: var(--ink);
}
.activity-bar .label { opacity: 0.5; margin-right: 6px; }

/* ---- scene + keepers grid ---- */
.main-grid {
  display: grid;
  /* min(360px,100%) instead of a bare 360px — a phone narrower than 360px
     (older/small Android, iPhone SE 1st gen) would otherwise force this
     column wider than the viewport and cause horizontal scroll. */
  grid-template-columns: repeat(auto-fit, minmax(min(360px,100%),1fr));
  gap: 20px;
  margin: 16px clamp(12px,4vw,40px) 0;
  align-items: start;
}
.scene-col {}
.scene {
  position: relative;
  border-radius: 28px;
  overflow: hidden;
  height: min(60vh, 640px);
  min-height: 340px;
  box-shadow: 0 24px 60px rgba(50,60,30,0.18);
}
.scene-bg { position: absolute; inset: 0; transition: background 1.4s ease; }
.scene-badge {
  position: absolute; top: 20px; left: 20px;
  background: rgba(20,30,15,0.4); color: #fff;
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.08em;
  padding: 8px 14px; border-radius: 999px;
}
.scene-flock {
  position: absolute; top: 20px; right: 20px;
  background: rgba(20,30,15,0.4); border-radius: 12px; padding: 8px 16px; text-align: center; color: #fff;
}
.scene-flock .lbl { font-size: 10px; letter-spacing: 0.1em; opacity: 0.7; }
.scene-flock .val { font-family: var(--font-mono); font-size: 18px; font-weight: 600; }
.scene-sub {
  position: absolute; top: 78px; left: 20px; color: #fff; font-size: 13px; max-width: 280px;
  text-shadow: 0 1px 4px rgba(0,0,0,0.55), 0 1px 12px rgba(0,0,0,0.4);
}
.scene-hatch { position: absolute; top: 104px; left: 20px; color: #fff; font-size: 12px; opacity: 0.75; text-shadow: 0 1px 4px rgba(0,0,0,0.55); }
.scene-danger {
  /* top was 112px, right under #scene-hatch's 104px line — fine while the
     two were mutually exclusive (pre-countdown-rework), but now the
     countdown box can render alongside the hatch-progress/next-duck line
     (egg state shows both; so does thriving/hungry now that every alive
     state gets a grace countdown, not just danger ones) — pushed down to
     clear it. */
  position: absolute; top: 138px; left: 20px; color: #fff; font-family: var(--font-mono); font-size: 13px;
  background: rgba(0,0,0,0.3); padding: 6px 12px; border-radius: 8px;
}
/* The egg itself, during the 'rebirth' scene — was missing entirely before
   (just background gradient + text, no actual egg graphic). Reuses the
   eggGlow keyframe already defined for the memorial's dead-state egg. */
.scene-egg-icon {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: clamp(64px,14vw,110px); animation: eggGlow 2.4s ease-in-out infinite; pointer-events: none;
}
.scene-dead {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; text-align: center; padding: 0 40px;
}
.scene-dead .egg { font-size: 48px; animation: eggGlow 2.4s ease-in-out infinite; }
.scene-dead .title { font-family: var(--font-display); font-size: 19px; font-weight: 700; color: #fff; }
.scene-dead p { margin: 0; font-size: 13px; }
.scene-ticker { margin-top: 12px; background: var(--bar); border-radius: 14px; padding: 13px 22px; text-align: center; }
.scene-ticker span { color: var(--bar-text); font-size: 14px; }

.duck { position: absolute; }
.duck-shadow { position: absolute; }
.duck-bob { }
.duck-body-outer { position: relative; width: 44px; height: 36px; }

.sun-el { position: absolute; top: 36px; right: 60px; width: 70px; height: 70px; border-radius: 50%; background: #ffe27a; box-shadow: 0 0 50px 18px rgba(255,226,122,0.55); }
.cloud-el { position: absolute; border-radius: 40px; background: rgba(255,255,255,0.75); animation: drift 9s ease-in-out infinite alternate; }
.rain-el { position: absolute; top: 20px; width: 2px; height: 16px; background: rgba(230,240,255,0.5); animation: fall 1.1s linear infinite; }
.star-el { position: absolute; background: #fff; border-radius: 50%; animation: twinkle 2.4s ease-in-out infinite; }
.dawn-el { position: absolute; bottom: 120px; left: 0; right: 0; height: 200px; background: radial-gradient(ellipse at center, rgba(255,240,200,0.55), transparent 70%); }
.pond-el { position: absolute; bottom: 0; left: 8%; right: 8%; height: 120px; border-radius: 50% 50% 45% 45%/30% 30% 70% 70%; background: radial-gradient(ellipse at 40% 30%, rgba(210,240,250,0.55), rgba(120,180,200,0.35) 60%, rgba(70,130,155,0.25) 100%); }
.ripple-el { position: absolute; border-radius: 50%; background: rgba(255,255,255,0.25); animation: rippleMove 4s ease-in-out infinite; }

/* ---- card shell (keepers / all-time / feed / pond-keepers / memorial) ---- */
.card {
  background: var(--surface);
  border: 1px solid var(--card-border);
  border-radius: 20px;
  padding: 24px;
}
/* Right column of .main-grid: the keepers ranking, with the small
   shareable-moment card on ITS OWN right lateral side ("en el lateral
   derecho de this generation keepers... que ocupe menos de la mitad del
   espacio") — was its own full-width section further down the page, then
   briefly stacked below the keepers card, which wasn't what was asked for.
   Below the 860px breakpoint there isn't room to split this column again
   (it's already down to one ~360px main-grid cell on phones), so it stays
   stacked there; past that width it sits beside keepers at ~40% of its
   width — comfortably under half. */
.keepers-col { display: flex; flex-direction: column; gap: 16px; }
@media (min-width: 860px) {
  .keepers-col { display: grid; grid-template-columns: 1fr minmax(210px,0.4fr); align-items: start; }
}
/* Matches .scene's own height formula exactly ("debe ocupar todo el
   espacio de altura igual que el de los patos") — same min(60vh,640px)
   computed independently gives the same pixel height as the scene box
   beside it, since .main-grid doesn't stretch its columns to match
   (align-items:start). The list itself still scrolls internally
   (.keepers-list's overflow-y:auto) once it outgrows this. */
.keepers-card { height: min(60vh,640px); min-height: 340px; display: flex; flex-direction: column; box-shadow: 0 6px 24px rgba(50,60,30,0.06); box-sizing: border-box; }
.card-title { font-family: var(--font-display); font-weight: 700; font-size: 16px; }
.card-title.sm { font-size: 13px; }
.card-sub { font-size: 12px; color: var(--ink-50); margin-top: 2px; }
.keepers-list { flex: 1; overflow-y: auto; margin-top: 14px; padding-right: 14px; box-sizing: border-box; }
.share-mini { display: flex; flex-direction: column; padding: 18px; box-sizing: border-box; }
/* Back to a short list (up to 3) instead of one card — see app.js's
   buildShareCandidates() for how the set is picked. */
.share-mini-list { display: flex; flex-direction: column; gap: 14px; margin-top: 10px; }
.share-mini-item { display: flex; flex-direction: column; gap: 4px; padding-bottom: 14px; border-bottom: 1px dashed var(--card-border); }
.share-mini-item:last-child { padding-bottom: 0; border-bottom: none; }
.share-mini-headline { font-weight: 700; font-size: 13px; }
.share-mini-copy { font-size: 11px; color: var(--ink-60); }
.share-mini-item .share-btn { margin-top: 2px; padding: 6px 14px; font-size: 12px; align-self: stretch; text-align: center; }
/* Column, not row — in the narrow share-mini card, a row + wrap still forces
   each button to its own intrinsic content width (flex items don't shrink
   below min-content by default), which was wrapping "Share / Copy link"
   across two lines and looking cramped/broken. Full-width stacked buttons
   read cleanly at any column width. */
.share-mini-btns { display: flex; flex-direction: column; gap: 6px; }
.share-copy-btn { margin-top: 2px; background: none; border: 1px solid var(--card-border); color: var(--ink); border-radius: 999px; padding: 6px 14px; font-size: 12px; cursor: pointer; white-space: nowrap; }

.rank-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 8px; border-radius: 10px; margin-bottom: 4px; flex-wrap: wrap; }
.rank-row.top3 { background: var(--gold-bg-14); }
.rank-row .left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.rank-row .rank { font-family: var(--font-mono); font-size: 12px; font-weight: 700; width: 22px; flex-shrink: 0; color: var(--ink-40); }
.rank-row.top3 .rank { color: var(--gold-text-dark); }
.rank-row .name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-row .badge { font-size: 11px; color: var(--ink-55); background: var(--card-border-06); padding: 2px 7px; border-radius: 999px; white-space: nowrap; }
.rank-row .right { display: flex; align-items: center; gap: 10px; flex-shrink: 0; white-space: nowrap; }
.rank-row .time { font-size: 11px; color: var(--ink-40); }
.rank-row .care { font-family: var(--font-mono); font-weight: 600; color: var(--green); font-size: 13px; }

.section-card { margin: 28px clamp(12px,4vw,40px) 0; }
.section-head { display: flex; justify-content: space-between; align-items: baseline; flex-wrap: wrap; gap: 10px; }
.section-title { font-family: var(--font-display); font-weight: 700; font-size: 18px; }

.toast {
  position: fixed; top: 24px; left: 50%; transform: translateX(-50%);
  background: var(--bar); color: var(--bar-text); padding: 16px 26px; border-radius: 16px;
  font-size: 13px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); z-index: 100; text-align: center; max-width: 90vw;
}
.toast .t-title { font-family: var(--font-display); font-weight: 700; font-size: 15px; }
.toast .t-sub { opacity: 0.7; margin-top: 4px; }
.toast .t-note { opacity: 0.55; margin-top: 4px; font-style: italic; }

/* ---- icon dropdown ---- */
.icon-dropdown { position: relative; width: 100%; margin-top: 10px; }
.icon-dropdown-btn {
  width: 100%; text-align: left; display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; border-radius: 10px; border: 1px solid var(--green-border-30);
  background: var(--green-bg); color: var(--ink); font-size: 14px; cursor: pointer; font-family: var(--font-body);
}
.icon-dropdown-btn .chev { margin-left: auto; opacity: 0.5; font-size: 11px; }
.icon-dropdown-btn.placeholder { color: var(--ink-50); }
.icon-dropdown-panel {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 60;
  background: var(--surface); border: 1px solid var(--card-border); border-radius: 14px;
  box-shadow: 0 16px 40px rgba(30,40,20,0.18); max-height: 320px; overflow-y: auto; padding: 6px;
}
.icon-dropdown-group { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-40); padding: 10px 12px 4px; }
.icon-dropdown-row {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 8px; cursor: pointer; font-size: 13.5px;
}
.icon-dropdown-row:hover, .icon-dropdown-row.active { background: var(--green-bg); }
.icon-dropdown-row .ic { font-size: 15px; width: 20px; text-align: center; }

/* ---- pond keepers ---- */
.filter-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.filter-chip { padding: 6px 14px; border-radius: 999px; border: 1px solid var(--card-border); font-size: 12px; cursor: pointer; background: transparent; color: var(--ink); }
.filter-chip.selected { background: var(--bar); color: var(--bar-text); border-color: var(--bar); }
.empty-note { font-size: 13px; color: var(--ink-50); padding: 20px 0; text-align: center; }

/* ---- traffic chart (frontend/stats.html) ---- */
.traffic-chart-wrap { position: relative; }
.traffic-svg { width: 100%; height: 260px; display: block; overflow: visible; }
.traffic-svg .gridline { stroke: var(--card-border); stroke-width: 1; }
.traffic-svg .axis-label { font-family: var(--font-mono); font-size: 10px; fill: var(--ink-45); }
.traffic-svg .area { fill: var(--green); fill-opacity: 0.1; }
.traffic-svg .line { fill: none; stroke: var(--green); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.traffic-svg .end-dot { fill: var(--green); stroke: var(--surface); stroke-width: 2; }
.traffic-svg .end-label { font-family: var(--font-mono); font-size: 11px; font-weight: 700; fill: var(--ink); }
.traffic-svg .crosshair { stroke: var(--ink-40); stroke-width: 1; pointer-events: none; }
.traffic-svg .hover-dot { fill: var(--green); stroke: var(--surface); stroke-width: 2; pointer-events: none; }
.traffic-svg .hit-layer { fill: transparent; cursor: crosshair; }
.traffic-tooltip {
  position: absolute; pointer-events: none; background: var(--bar); color: var(--bar-text);
  font-size: 12px; padding: 6px 10px; border-radius: 8px; transform: translate(-50%, -120%);
  white-space: nowrap; z-index: 5;
}
.traffic-tooltip .val { font-weight: 700; }
.traffic-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.traffic-table th, .traffic-table td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--card-border-soft); font-variant-numeric: tabular-nums; }
.traffic-table th { color: var(--ink-50); font-weight: 600; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; }

/* ---- dashboard ---- */
.stat-chips { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
.stat-chip { background: var(--surface); border: 1px solid var(--card-border); border-radius: 12px; padding: 10px 16px; }
.stat-chip .lbl { font-size: 11px; opacity: 0.55; }
.stat-chip .val { font-weight: 700; }
.feed-list { margin-top: 16px; background: var(--surface); border: 1px solid var(--card-border); border-radius: 16px; padding: 8px 20px; }
.feed-list .row { display: flex; justify-content: space-between; align-items: center; padding: 14px 0; border-bottom: 1px solid var(--card-border-soft); gap: 12px; flex-wrap: wrap; }
.feed-list .row:last-child { border-bottom: none; }
.feed-list .left { display: flex; align-items: center; gap: 10px; }
.feed-list .idx { font-family: var(--font-mono); font-size: 12px; opacity: 0.4; }
.feed-list .right { display: flex; align-items: center; gap: 16px; }
.feed-list .time { font-size: 12px; color: var(--ink-40); }
.feed-list .care { font-family: var(--font-mono); font-weight: 600; color: var(--green); }
.see-more-btn { background: none; border: 1px solid var(--card-border); color: var(--ink); border-radius: 999px; padding: 6px 14px; font-size: 13px; cursor: pointer; }

/* ---- shareable moment (now the .share-mini card in .keepers-col above) ---- */
.share-btn { margin-top: 4px; background: none; border: 1px solid var(--card-border); color: var(--ink); border-radius: 999px; padding: 8px 16px; font-size: 13px; cursor: pointer; align-self: flex-start; }

/* ---- memorial ---- */
.memorial-grid { display: flex; gap: 20px; flex-wrap: wrap; margin-top: 20px; }
.memorial-card { flex: 1 1 380px; min-width: 320px; background: var(--surface); border: 1px solid var(--card-border); border-radius: 16px; padding: 22px; }
.memorial-card .m-head { display: flex; justify-content: space-between; gap: 10px; }
.memorial-card .m-title { font-weight: 700; }
.memorial-card .m-days { font-family: var(--font-mono); font-size: 13px; opacity: 0.55; white-space: nowrap; }
.memorial-card .m-meta { font-size: 12px; color: var(--ink-50); margin-top: 6px; }
.memorial-card .m-narrative { font-size: 13px; font-style: italic; color: var(--ink-70); margin-top: 10px; }
.memorial-card .m-stats { font-size: 13px; color: var(--ink-60); margin-top: 12px; }
.memorial-card .m-closer { font-size: 13px; color: var(--ink-55); margin-top: 12px; border-top: 1px solid var(--card-border-soft); padding-top: 12px; }

/* ---- about / laws ---- */
.prose-section { max-width: 640px; margin: 56px auto 0; padding: 0 24px; }
.prose-section.centered { text-align: center; }
.prose-eyebrow { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em; color: var(--ink-50); }
.prose-title { font-family: var(--font-display); font-size: 22px; font-weight: 800; margin-top: 8px; }
.prose-title.sm { font-size: 16px; }
.prose-body { font-size: 14px; color: var(--ink-70); margin-top: 14px; line-height: 1.8; }
.prose-body.tight { line-height: 1.6; }

/* ---- footer ---- */
.footer-links { margin: 56px 40px 12px; text-align: center; font-size: 13px; display: flex; justify-content: center; gap: 18px; flex-wrap: wrap; }
.footer-credit { margin: 0 40px 24px; text-align: center; font-size: 12px; color: var(--ink-45); }

/* ---- reasons page ---- */
.reasons-ticker { margin: 28px clamp(12px,4vw,40px) 0; background: var(--bar); border-radius: 14px; padding: 12px 0; overflow: hidden; }
.reasons-ticker-inner { white-space: nowrap; font-size: 13px; color: var(--bar-text); animation: tickerScroll 22s linear infinite; display: inline-block; }
.reasons-ticker-inner span.sep { opacity: 0.5; margin: 0 24px; }
.reasons-ticker-inner span.item { margin: 0 24px; }
.reasons-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px,1fr)); gap: 20px; margin: 20px clamp(12px,4vw,40px) 0; }
.reason-card { background: var(--surface); border: 1px solid var(--card-border); border-radius: 18px; padding: 22px; display: flex; flex-direction: column; }
.reason-card .icon { font-size: 24px; }
.reason-card .title { font-family: var(--font-display); font-weight: 700; font-size: 16px; margin-top: 8px; }
.reason-card .desc { font-size: 12px; color: var(--ink-55); margin-top: 4px; line-height: 1.5; }
.reason-card .top3-label { font-size: 11px; color: var(--ink-40); margin-top: 10px; font-family: var(--font-mono); }
.reason-card .top3-row { display: flex; justify-content: space-between; align-items: center; padding: 7px 0; border-bottom: 1px solid var(--card-border-06); gap: 8px; }
.reason-card .top3-row:last-child { border-bottom: none; }
.reason-card .top3-rank { font-family: var(--font-mono); font-size: 11px; color: var(--ink-40); width: 16px; }
.reason-card .top3-name { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.reason-card .top3-amt { font-family: var(--font-mono); font-size: 12px; color: var(--green); font-weight: 600; flex-shrink: 0; }

@media (max-width: 640px) {
  .pill-input.name, .pill-input.company { width: 120px; }
  .donate-bar { padding: 10px 14px; }
}

/* ---- dev fast-forward panel (testing only — see app.js's initDevPanel) ----
   Only ever shown when the page is loaded with ?dev=1. Deliberately styled
   as an out-of-the-way, unmistakably "debug" control — small, muted,
   pinned to a corner — never part of the real visitor experience. */
.dev-panel {
  position: fixed; bottom: 14px; right: 14px; z-index: 200;
  display: flex; flex-direction: column; align-items: stretch; gap: 6px;
  background: rgba(0,0,0,0.82); border: 1px solid rgba(255,255,255,0.15);
  border-radius: 14px; padding: 8px 10px; font-size: 11px; color: #fff;
  font-family: var(--font-mono);
}
.dev-panel-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.dev-panel span { opacity: 0.6; }
.dev-panel button {
  border: 1px solid rgba(255,255,255,0.25); background: transparent; color: #fff;
  border-radius: 999px; padding: 4px 9px; font-size: 11px; cursor: pointer; font-family: var(--font-mono); white-space: nowrap;
}
.dev-panel button:hover { background: rgba(255,255,255,0.15); }
.dev-panel button.danger { border-color: rgba(255,100,100,0.55); color: #ff9797; }
.dev-panel button.danger:hover { background: rgba(255,80,80,0.18); }
/* "counts as real" — visually distinct from the always-hidden "test donate"
   next to it, since the two look similar but do very different things. */
.dev-panel button.real { border-color: rgba(120,220,140,0.6); color: #9be6ac; }
.dev-panel button.real:hover { background: rgba(120,220,140,0.18); }
