/* xylery shared styles */

:root {
  --bg: #f5f0e6;
  --text: #2d3a2d;
  --muted: #5f6b5f;
  --accent: #5a7a5a;
  --accent-dark: #3e5b3e;
  --accent-light: #e8efe8;
  --accent-contrast: #fff;
  --card: #fffdf8;
  --card-elevated: #ffffff;
  --border: #e3ddd0;
  --shadow-rgb: 45, 58, 45;
  --shadow: 0 2px 12px rgba(var(--shadow-rgb), 0.08);
  --radius: 12px;
  --radius-sm: 8px;
  --max-width: 800px;
  --frosted-bg: rgba(245, 240, 230, 0.75);
  --frosted-blur: 12px;
  --glass-bg: rgba(245, 240, 230, 0.82);
  --topbar-height: 64px;

  --banner-start: #6b8c6b;
  --banner-end: #3e5b3e;
  --banner-text: #fff;
  --banner-overlay: rgba(0, 0, 0, 0.35);
  --banner-shadow: rgba(0, 0, 0, 0.45);

  --danger: #c0392b;
  --danger-dark: #a93226;
  --danger-border: #922b21;
  --danger-bg: #fff0f0;
  --danger-border-light: #e0b0b0;
  --danger-text: #8b2e2e;

  --admin: #3b82f6;
  --admin-dark: #2563eb;

  --modal-backdrop: rgba(45, 58, 45, 0.5);

  --overlay-bg: rgba(245, 240, 230, 0.92);

  --form-error-bg: #fff3cd;
  --form-error-border: #e3ddd0;
  --form-error-text: #a33;

  --toast-success-rgb: 230, 242, 230;
  --toast-success-border-rgb: 90, 122, 90;
  --toast-success-text: #2f4a2f;
  --toast-info-rgb: 250, 248, 244;
  --toast-info-border-rgb: 160, 152, 135;
  --toast-info-text: var(--text);
  --toast-warning-rgb: 255, 250, 230;
  --toast-warning-border-rgb: 204, 170, 60;
  --toast-warning-text: #5c4b00;
  --toast-error-rgb: 255, 235, 235;
  --toast-error-border-rgb: 200, 90, 90;
  --toast-error-text: #600;
}

[data-theme="dark"] {
  --bg: #1a1f1a;
  --text: #e6ebe6;
  --muted: #9aa89a;
  --accent: #7fa37f;
  --accent-dark: #9bc09b;
  --accent-light: #2a382a;
  --accent-contrast: #1a1f1a;
  --card: #232a23;
  --card-elevated: #2b332b;
  --border: #3b473b;
  --shadow-rgb: 0, 0, 0;
  --frosted-bg: rgba(30, 35, 30, 0.75);
  --glass-bg: rgba(25, 30, 25, 0.82);

  --banner-start: #4e6b4e;
  --banner-end: #263a26;
  --banner-text: #f0f5f0;
  --banner-overlay: rgba(0, 0, 0, 0.45);

  --danger-bg: #3d1f1f;
  --danger-border-light: #7a3a3a;
  --danger-text: #ffb3b3;

  --modal-backdrop: rgba(0, 0, 0, 0.65);

  --overlay-bg: rgba(26, 31, 26, 0.92);

  --form-error-bg: #3d3420;
  --form-error-text: #e6c8a0;

  --toast-success-rgb: 40, 70, 40;
  --toast-success-text: #d0eed0;
  --toast-info-rgb: 60, 65, 60;
  --toast-info-text: var(--text);
  --toast-warning-rgb: 90, 75, 30;
  --toast-warning-text: #f0e6b8;
  --toast-error-rgb: 80, 35, 35;
  --toast-error-text: #ffd6d6;
}

* {
  box-sizing: border-box;
}

html {
  /* Smooth scrolling is applied only to same-page anchor clicks via
     website/js/smooth-scroll.js; the document default is instant so that
     POST→302→GET fragment landings are not interrupted by layout shifts. */
  scroll-padding-top: 78px;
}

/* XY224 — WCAG 2.5.8 target-size (44×44 CSS px floor). Controls sized at
   2.4rem (38.4px) get their floor via min-dims so glyphs stay unchanged.
   Inline links inside a sentence are exempt per the dossier decision. */
.taskbar-toggle,
.taskbar-close,
.bug-report-button,
.auth-button {
  min-width: 2.75rem;
  min-height: 2.75rem;
}

/* XY224 — central high-contrast :focus-visible (WCAG 1.4.11 / 2.4.7).
   Specific :focus-visible rules (e.g. .genus-link) win on specificity;
   input :focus styling is intentionally untouched. */
:focus-visible {
  outline: 2px solid var(--accent-dark);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  min-height: 100vh;
  overflow-wrap: break-word;
  cursor: url('../images/leaf-cursor.svg?v=4') 10 4, auto;
}

a,
button,
[role="button"] {
  cursor: url('../images/leaf-cursor.svg?v=4') 10 4, pointer;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* Sticky header */
.topbar {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--frosted-bg);
  backdrop-filter: blur(var(--frosted-blur));
  -webkit-backdrop-filter: blur(var(--frosted-blur));
  border-bottom: 1px solid var(--border);
  padding: 0.75rem 1.25rem;
}

.topbar-inner {
  max-width: var(--max-width);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.logo {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: -0.02em;
}

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

.logo a:hover {
  text-decoration: none;
}

.auth-button {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 600;
  font-size: 0.9rem;
  padding: 0.5rem 1rem;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: var(--card);
  color: var(--accent-dark);
  transition: background 0.15s ease, color 0.15s ease;
  margin-left: auto;
}

.auth-button:hover {
  background: var(--accent);
  color: var(--accent-contrast);
  text-decoration: none;
}

.auth-button-icon {
  display: none;
}

/* Header compaction (≤640px, XY49): the auth pill becomes a square icon button
   matching .bug-report-button. New breakpoint — the 480px block further down
   stays hero/buttons only. The SVGs use stroke="currentColor", so the existing
   .auth-button:hover color flip to #fff covers the icon too. */
@media (max-width: 640px) {
  .auth-button-label {
    display: none;
  }

  .auth-button-icon {
    display: block;
  }

  .auth-button {
    width: 2.4rem;
    height: 2.4rem;
    padding: 0;
    justify-content: center;
    border-radius: var(--radius-sm);
    color: var(--accent); /* icon stroke (currentColor) matches the bug icon's #5a7a5a */
  }
}

.bug-report-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  padding: 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--card);
  color: var(--accent-dark);
  transition: background 0.15s ease, color 0.15s ease;
}

.bug-report-button svg {
  transition: stroke 0.15s ease;
}

.bug-report-button:hover {
  background: var(--accent);
  color: var(--accent-contrast);
  text-decoration: none;
}

.bug-report-button:hover svg {
  stroke: var(--accent-contrast) !important;
}

/* Theme toggle: exactly one icon visible; viewport swaps the system icon (XY157, XY170). */
/* All theme icons are hidden by default; .theme-icon-live marks the active state. */
.theme-toggle svg[data-icon] {
  display: none;
}

.theme-toggle svg.theme-icon-live {
  display: block;
}

/* Within the live system pair, swap monitor vs. smartphone by viewport width. */
.theme-toggle svg.theme-icon-live.theme-icon-mobile {
  display: none;
}

@media (max-width: 640px) {
  .theme-toggle svg.theme-icon-live.theme-icon-desktop {
    display: none;
  }
  .theme-toggle svg.theme-icon-live.theme-icon-mobile {
    display: inline;
  }
}

.feed-pager-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  padding: 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--card);
  color: var(--accent);
  transition: background 0.15s ease, color 0.15s ease;
}

.feed-pager-button svg {
  transition: stroke 0.15s ease;
}

.feed-pager-button:hover {
  background: var(--accent);
  color: var(--accent-contrast);
  text-decoration: none;
}

.feed-pager-button:hover svg {
  stroke: var(--accent-contrast);
}

.taskbar-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  padding: 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--card);
  color: var(--accent-dark);
  transition: background 0.15s ease, color 0.15s ease;
}

.taskbar-toggle svg {
  transition: stroke 0.15s ease;
}

.taskbar-toggle:hover {
  background: var(--accent);
  color: var(--accent-contrast);
  text-decoration: none;
}

.taskbar-toggle:hover svg {
  stroke: var(--accent-contrast) !important;
}

/* Layout containers */
.container {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 2rem 1.25rem;
}

.section {
  padding: 2.5rem 0;
}

.section.alt {
  background: var(--card);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

/* Hero */
.hero {
  text-align: center;
  padding: 5rem 1.25rem 3.5rem;
}

.hero .logo-mark {
  font-size: 3.5rem;
  font-weight: 800;
  color: var(--accent);
  margin: 0 0 0.75rem;
  letter-spacing: -0.04em;
  line-height: 1.1;
}

.hero .tagline {
  font-size: 1.25rem;
  color: var(--muted);
  max-width: 560px;
  margin: 0 auto 1.5rem;
}

.lead {
  font-size: 1.15rem;
  color: var(--muted);
  max-width: 640px;
  margin: 0 auto 2rem;
}

/* Buttons */
.actions {
  display: flex;
  gap: 0.75rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 1.5rem;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 600;
  padding: 0.75rem 1.25rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
  cursor: pointer;
  /* XY224: 44px hit-area floor */
  min-height: 44px;
}

.btn:hover {
  transform: translateY(-1px);
  text-decoration: none;
}

.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
}

.btn-primary:hover {
  background: var(--accent-dark);
}

.btn-secondary {
  background: var(--card);
  color: var(--text);
  border-color: var(--border);
}

.btn-secondary:hover {
  background: var(--accent-light);
  border-color: var(--accent);
}

.btn-danger {
  background: var(--danger);
  color: var(--accent-contrast);
  border-color: var(--danger-dark);
}

.btn-danger:hover {
  background: var(--danger-dark);
  border-color: var(--danger-border);
}

.btn-admin {
  background: var(--admin);
  color: var(--accent-contrast);
}

.btn-admin:hover {
  background: var(--admin-dark);
}

/* Typography */
h1, h2, h3 {
  margin: 0 0 0.75rem;
  line-height: 1.25;
  color: var(--text);
}

h1 {
  font-size: 2rem;
}

/* Genus link inside species h1 (XY174) */
.genus-link {
  color: inherit;
  text-decoration: none;
}

.genus-link:hover,
.genus-link:focus {
  text-decoration: underline;
}

.genus-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.pronunciation {
  display: inline-block;
  font-size: 0.7em;
  font-weight: 400;
  color: var(--muted);
  margin-left: 0.35em;
  user-select: none;
  white-space: nowrap;
}

h2 {
  font-size: 1.5rem;
}

h3 {
  font-size: 1.15rem;
}

p {
  margin: 0 0 1rem;
  color: var(--muted);
}

p:last-child {
  margin-bottom: 0;
}

/* Cards */
.card {
  background: var(--card-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  box-shadow: var(--shadow);
  margin-bottom: 1.25rem;
}

.card h2,
.card h3 {
  margin-top: 0;
}

/* Banner (plant page) */
.banner {
  position: relative;
  width: 100%;
  min-height: 220px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 2rem 1.25rem;
  background: linear-gradient(135deg, var(--banner-start) 0%, var(--banner-end) 100%);
  color: var(--banner-text);
  text-align: center;
}

.banner::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--banner-overlay);
  pointer-events: none;
}

.banner-content {
  position: relative;
  z-index: 1;
  max-width: var(--max-width);
  width: 100%;
  margin: 0 auto;
}

.banner-nickname {
  font-size: 2.25rem;
  font-weight: 800;
  margin: 0 0 0.25rem;
  color: var(--banner-text);
  text-shadow: 0 2px 6px var(--banner-shadow);
}

.banner-owner {
  font-size: 0.95rem;
  color: var(--banner-text);
  text-shadow: 0 1px 3px var(--banner-shadow);
  opacity: 0.95;
}

/* Plant hero banner: extend the gradient up behind the sticky glass topbar (XY72).
   XY219: the plant's banner_color arrives as --banner-tint (inline in
   plant_detail.html) and tints the base gradient; the decorative pattern lives
   on ::after, ABOVE the .banner::before contrast veil (which would swallow
   it) but below .banner-content (z-index 1), so hero-text contrast survives. */
.banner--hero {
  margin-top: calc(-1 * var(--topbar-height));
  padding-top: calc(2rem + var(--topbar-height));
  background: linear-gradient(135deg, var(--banner-tint, var(--banner-start)) 0%, var(--banner-end) 100%);
}

.banner--hero::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 100% 80% at 85% 5%, rgba(255, 255, 255, 0.14), transparent 55%),
    radial-gradient(ellipse 80% 60% at 10% 95%, rgba(255, 255, 255, 0.09), transparent 55%),
    radial-gradient(circle 300px at 55% 40%, rgba(255, 255, 255, 0.06), transparent 70%),
    repeating-linear-gradient(112deg, rgba(255, 255, 255, 0.08) 0 2px, transparent 2px 26px),
    repeating-linear-gradient(22deg, rgba(255, 255, 255, 0.05) 0 1px, transparent 1px 34px);
}

/* Site-wide prompt banners (care-share XY215, cover picker XY218) sit above
   {% block content %}; the plant hero's negative margin (XY72) would otherwise
   pull itself up OVER the banner's lower row and eat its clicks. */
.care-share-banner,
.cover-pick-banner {
  position: relative;
  z-index: 1;
}

/* Plant / species info grids */
.info-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 1rem;
  margin: 1.25rem 0 0;
}

.info-item {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 1rem;
}

.info-item strong {
  display: block;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin-bottom: 0.25rem;
}

.info-item span {
  font-weight: 600;
  color: var(--text);
}

/* Summary block */
.summary {
  font-size: 1.1rem;
  color: var(--text);
  background: var(--accent-light);
  border-left: 4px solid var(--accent);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 1.25rem;
  margin: 1.25rem 0;
}

.summary p {
  color: var(--text);
  margin: 0;
}

.summary a {
  font-weight: 600;
}

/* Like button heart icon (XY73): Lucide SVG at text size; stroke/fill use
   currentColor, so the btn-primary/btn-secondary state colors apply as-is. */
.like-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
  margin-right: 0.25em;
}

/* Lists */
.facts-list {
  list-style: none;
  padding: 0;
  margin: 0.75rem 0 0;
}

.facts-list li {
  position: relative;
  padding-left: 1.5rem;
  margin-bottom: 0.75rem;
  color: var(--muted);
}

.facts-list li::before {
  content: "🌿";
  position: absolute;
  left: 0;
  top: 0.1em;
}

/* References box (XY165, re-marked as a collapsible section in XY187) */
.references-list {
  list-style: none;
  padding: 0;
  margin: 0.75rem 0 0;
}

.references-list li {
  margin-bottom: 0.75rem;
}

.references-list a {
  display: inline-block;
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.reference-source {
  display: block;
  font-size: 0.85rem;
  color: var(--muted);
}

/* Photos */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 0.75rem;
  margin-top: 1rem;
}

.photo {
  aspect-ratio: 4 / 3;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 0.85rem;
}

.photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Photo attribution interaction */
.photo-attribution {
  margin: 0;
  display: flex;
  flex-direction: column;
}

.photo-frame {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.photo-frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.photo-frame:hover .photo-tooltip {
  opacity: 1;
  transform: translateY(0);
}

.photo-tooltip {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 2;
  background: var(--accent-dark);
  color: var(--card-elevated);
  font-weight: 900;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-align: center;
  padding: 0.5rem 0.25rem;
  opacity: 0;
  transform: translateY(100%);
  transition: opacity 0.2s ease, transform 0.2s ease;
  pointer-events: none;
}

.photo-attribution-panel {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  background: var(--card-elevated);
  border: 1px solid var(--border);
  border-top: none;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  margin-top: -1px;
  transition: grid-template-rows 0.3s ease, opacity 0.3s ease;
  font-size: 0.8rem;
  color: var(--text);
}

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

.photo-attribution.is-open {
  position: relative;
  z-index: 10;
}

.photo-attribution-panel.is-open {
  grid-template-rows: 1fr;
  opacity: 1;
  z-index: 11;
}

.photo-attribution-panel .attribution-author {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}

.photo-attribution-panel-inner {
  overflow: hidden;
  min-height: 0;
  padding: 0 0.75rem;
  transition: padding 0.3s ease;
}

.photo-attribution-panel.is-open .photo-attribution-panel-inner {
  padding: 0.75rem;
}

.photo-attribution-panel strong {
  display: block;
  font-size: 0.85rem;
  margin-bottom: 0.4rem;
  color: var(--accent-dark);
}

.photo-attribution-panel p {
  margin: 0.25rem 0;
  line-height: 1.5;
}

.photo-attribution-panel .attribution-author {
  color: var(--text);
}

.photo-attribution-panel .attribution-license {
  color: var(--muted);
}

.photo-attribution-panel .attribution-source a {
  font-weight: 600;
}

.photo-attribution-panel .attribution-note {
  font-size: 0.75rem;
  color: var(--muted);
  margin-top: 0.5rem;
}

@media (prefers-reduced-motion: reduce) {
  .photo-tooltip,
  .photo-attribution-panel,
  .photo-attribution-panel-inner {
    transition: none;
  }
}

/* ==========================================================================
   Layout primitives: wide two-track species page (pilot)
   These classes drive the species-detail redesign. Below 1200px the rail
   collapses back to a normal card in the single column; at 1200px+ it becomes
   a scrapbook photo rail in the right margin with the content column centered.

   Rules worth preserving in any later sitewide rollout:
   - The rail itself NEVER gets `overflow` or `max-height` that clips; per the
     CSS overflow spec, any non-visible overflow axis also computes the other
     axis away from visible, which would amputate the horizontal photo overhang
     AND the expanding attribution panel.
   - Photo frames are capped by `max-height` (not by letting aspect ratios run
     free) so a full rail plus one open panel still fits a ~900px viewport.
   - Attribution panels expand in-flow and share the same tilt as their photo
     so they line up visually; the accordion guarantees at most one open panel
     at a time, which bounds the rail's maximum height.
   ========================================================================== */
body {
  overflow-x: clip;
}

.photo-rail {
  /* Narrow fallback: identical to a .card. */
  background: var(--card-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  box-shadow: var(--shadow);
  margin-bottom: 1.25rem;
}

@media (min-width: 1200px) {
  .layout-wide {
    /* Center the fixed-width content track on the page, with the rail as a
       flex item to its right and an invisible left spacer balancing it out. */
    display: flex;
    justify-content: center;
    gap: 0 3rem;
    align-items: start;
    max-width: none;
    margin: 0;
    padding: 2rem 0;
  }

  /* Invisible left spacer: mirrors the rail width so the content track is
     truly centered in the viewport. */
  .layout-wide::before {
    content: "";
    flex: 0 1 320px;
    min-width: 180px;
  }

  .layout-wide .content-track {
    flex: 0 0 700px;
    min-width: 0;
  }

  .layout-wide .photo-rail {
    flex: 0 1 320px;
    min-width: 180px;
  }

  /* XY191: between 1200px and the rail threshold (1756px, see the media
     query below) the fixed 160px taskbar occupies the left edge of the
     viewport, so the rail cards cannot ride beside the content track — the
     centered row's left edge only clears the taskbar at 1756px. In this
     range the rail wrapper drops to its own flex line as a 700px column
     centered under the content track (it aligns exactly: the track's left
     edge sits at (viewport - 700) / 2 in both the 3-item row and a centered
     700px line), so it stays right of the taskbar at every scroll position.
     Photo rail keeps order 1 so it is collected onto the first line before
     the full-width-ish rail closes it. */
  .layout-wide:has(.left-rail) {
    flex-wrap: wrap;
  }

  .layout-wide .content-track {
    order: 1;
  }

  .layout-wide .photo-rail {
    order: 1;
  }

  .layout-wide .left-rail {
    order: 2;
    flex: 0 0 700px;
    max-width: 100%;
    margin: 0 auto;
  }

  /* XY205: pages without a photo rail (genus pages) have nothing to
     balance the invisible spacer against while the left rail wraps
     below, so the spacer hides and the content track centers alone. */
  .layout-wide:not(:has(.photo-rail))::before {
    display: none;
  }

  /* Keep text line lengths readable in wide cards. */
  .layout-wide .content-track > p,
  .layout-wide .summary p,
  .layout-wide .facts-list li,
  .layout-wide .info-item span {
    max-width: 65ch;
  }

  /* Alternating tinted cards so the content track stops reading as
     identical boxes. */
  .layout-wide .card--tinted {
    background: var(--accent-light);
  }

  /* Care grid becomes a two-column mosaic at wide width. */
  .layout-wide .info-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .layout-wide .info-item--wide {
    grid-column: 1 / -1;
  }

  .photo-rail {
    /* Reset card styles for rail mode. */
    background: transparent;
    border: none;
    border-radius: 0;
    padding: 0;
    box-shadow: none;
    margin-bottom: 0;

    display: flex;
    flex-direction: column;
    gap: 1.25rem;
  }

  .photo-rail h2 {
    font-size: 1rem;
    color: var(--muted);
    margin: 0 0 0.25rem;
  }

  .photo-rail .photo-grid {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    margin-top: 0;
  }

  .photo-rail .photo-attribution,
  .photo-rail .photo {
    width: 100%;
  }

  .photo-rail .photo-frame,
  .photo-rail .photo {
    max-height: 22vh;
    aspect-ratio: auto;
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow);
  }

  .photo-rail .photo-frame img,
  .photo-rail .photo {
    max-height: 22vh;
    width: 100%;
    object-fit: cover;
    display: block;
  }

  /* Scrapbook tilts and gutter overhang.
     Rotate the whole figure (photo + attribution panel together) so the panel
     naturally follows the photo's tilt and stays aligned beneath it. */
  .photo-rail .photo-attribution:nth-child(odd),
  .photo-rail .photo:nth-child(odd) {
    margin-left: -2rem;
    transform: rotate(-2deg);
    transform-origin: top center;
  }

  .photo-rail .photo-attribution:nth-child(even),
  .photo-rail .photo:nth-child(even) {
    margin-left: 0.5rem;
    margin-right: -1rem;
    transform: rotate(1.5deg);
    transform-origin: top center;
  }

  /* Horizontal attribution flyout in the wide rail.
     The panel is absolutely positioned relative to its figure so opening it
     never changes the rail's vertical flow. It slides out from the right of
     the photo by default; JS adds .panel-left when the right side would hit
     the viewport edge, flipping it to slide out from the left instead. */
  .photo-rail .photo-attribution {
    position: relative;
  }

  .photo-rail .photo-attribution-panel {
    position: absolute;
    top: 0;
    left: 100%;
    width: 0;
    opacity: 0;
    margin-top: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--card-elevated);
    box-shadow: var(--shadow);
    transition: width 0.3s ease, opacity 0.3s ease;
    z-index: 5;
    overflow: hidden;
    /* Reset the narrow-layout grid animation. */
    display: block;
    grid-template-rows: none;
  }

  .photo-rail .photo-attribution-panel[hidden] {
    display: none;
  }

  .photo-rail .photo-attribution-panel.panel-left {
    left: auto;
    right: 100%;
  }

  .photo-rail .photo-attribution-panel.is-open {
    width: 280px;
    opacity: 1;
  }

  .photo-rail .photo-attribution-panel-inner {
    padding: 0.75rem;
    transition: none;
  }
}

/* XY220: in the 1200–1435px band the photo rail's 320px basis cannot join
   the content row (at 1280px only ~164px is free after the spacer and the
   track), so flex-wrap parks it on its own line at x=0 — directly under the
   fixed 160px taskbar, half-clipping photos, the species plants rail, and
   the weekly board. Fix: hide the invisible spacer and give the wrapped
   rail the same centered 700px column treatment as .left-rail, so the
   track, photo rail, and left rail stack in ONE 700px column centered at
   (viewport - 700) / 2 (>= 250px, clearing the taskbar). Without the spacer
   hide the lone track would center as a 1068px line and sit 184px right of
   the dropped rail. Scoped to this band only: at 1436px+ the basis fits
   beside the content track again and the rail must keep riding at the top
   right. */
@media (min-width: 1200px) and (max-width: 1435px) {
  .layout-wide:has(.left-rail)::before {
    display: none;
  }

  .layout-wide:has(.left-rail) .photo-rail {
    flex: 0 0 700px;
    max-width: 100%;
    margin: 0 auto;
  }
}

/* XY191: left-rail engagement threshold. The wide row is
   rail 320 + content track 700 + photo rail 320 + two 3rem gaps (96)
   = 1436px, centered via justify-content; its left edge sits at
   (viewport - 1436) / 2 and only clears the fixed 160px taskbar at
   viewport >= 1756px. Below that, the wrap rules in the 1200px query
   stack the rail cards full-width in content order instead. */
@media (min-width: 1756px) {
  /* When a left rail (Common problems / References) is present, it takes
     the place of the invisible spacer so the content track stays optically
     centered between the two rails. */
  .layout-wide:has(.left-rail)::before {
    display: none;
  }

  .layout-wide .left-rail {
    order: -1;
    flex: 0 1 320px;
    min-width: 180px;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    margin: 0;
  }

  .layout-wide .left-rail .card {
    margin-bottom: 0;
  }

  /* XY205: genus pages have no photo rail; the invisible spacer returns
     as the right-hand balancer (after the content track) so the genus
     track sits exactly where the species track does, flanked by the
     left rail and the spacer. */
  .layout-wide:has(.left-rail):not(:has(.photo-rail))::before {
    display: block;
    order: 2;
  }
}

/* ==========================================================================
   Collapsible sections (XY66, XY169, XY175)
   Phone-width pages start with every section collapsed to its title row;
   wider screens keep them open. No-JS fallback is all-open.
   The animation uses interpolate-size to animate height between 0 and auto
   on .section-body; browsers without support fall back to the JS height
   animation in sections.js. All padding lives inside .section-inner.
   The entire header bar is the toggle; it shows a static Lucide ellipsis.
   ========================================================================== */
.collapsible-section {
  interpolate-size: allow-keywords;
}

.card.collapsible-section {
  padding: 0;
}

.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  padding: 1.125rem 1.5rem;
  cursor: pointer;
  user-select: none;
}

.section-header:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: var(--radius);
}

.section-header h2 {
  margin: 0;
  font-size: 1.5rem;
}

.section-header-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.section-toggle-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  color: var(--muted);
  flex-shrink: 0;
}

.section-toggle-icon svg {
  display: block;
  width: 1.25rem;
  height: 1.25rem;
  stroke: currentColor;
}

.section-body {
  height: 0;
  overflow: hidden;
}

/* Transitions are only enabled after JS has applied the initial state; this
   prevents a closing animation from playing while the page is still loading. */
.sections-initialized .section-body {
  transition: height 0.3s ease;
}

.collapsible-section.is-open .section-body,
.collapsible-section:target .section-body {
  height: auto;
}

.section-inner {
  min-height: 0;
  padding: 0 1.5rem 1.5rem;
}

/* Fallback when interpolate-size is unsupported: JS in sections.js drives
   the height with WAAPI, so disable the CSS transition to avoid conflicts. */
.no-interpolate-size .section-body {
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .collapsible-section {
    interpolate-size: auto;
  }

  .section-body {
    transition: none;
  }
}

/* ==========================================================================
   Expandable problem solutions (XY171)
   Each problem with solutions becomes a whole-row button that expands a
   🌿-bulleted list. Problems without solutions render as plain rows. Only one
   problem per card is open at a time (accordion). Uses CSS grid-rows so the
   animation works without measuring heights in JS.
   ========================================================================== */
.problems-list {
  list-style: none;
  padding: 0;
  margin: 0.75rem 0 0;
}

.problem-row {
  position: relative;
  padding-left: 1.5rem;
  margin-bottom: 0.75rem;
  color: var(--muted);
}

.problem-row--plain::before {
  content: "🌿";
  position: absolute;
  left: 0;
}

.problem-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 1rem;
  line-height: 1.5;
  text-align: left;
  cursor: pointer;
}

.problem-toggle-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 1rem;
  height: 1rem;
  color: var(--accent);
  transition: transform 0.2s ease;
}

.problem-row.is-open .problem-toggle-icon {
  transform: rotate(90deg);
}

.problem-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.problem-solutions {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.25s ease;
}

.problem-row.is-open .problem-solutions {
  grid-template-rows: 1fr;
}

.problem-solutions-inner {
  min-height: 0;
  overflow: hidden;
}

.problem-solutions-inner .facts-list {
  margin-top: 0.5rem;
  margin-bottom: 0;
}

@media (prefers-reduced-motion: reduce) {
  .problem-solutions {
    transition: none;
  }

  .problem-toggle-icon {
    transition: none;
  }
}

/* ==========================================================================
   Sitewide taskbar
   Rendered in base.html on every page. Below 1200px it is a full-screen
   overlay sliding in from the left. At 1200px and up it becomes the fixed
   left rail originally piloted on species pages (XY47). The z-index ladder
   is: fixed rail 50 < sticky topbar 100 < mobile overlay 150 < bug modal 200.
   ========================================================================== */
#care-summary,
#fun-facts,
#photos,
#species-in-genus {
  scroll-margin-top: 5rem;
}

/* Mobile overlay (default) */
.taskbar {
  position: fixed;
  inset: 0;
  z-index: 150;
  background: var(--card-elevated);
  padding: 4rem 1.5rem 1.5rem;
  transform: translateX(-100%);
  transition: transform 0.25s ease;
  /* XY238: the nav (.taskbar-nav) is the scroller; the aside itself must not
     scroll or the footer/close would ride with the content. */
  overflow-y: hidden;
  display: flex;
  flex-direction: column;
}

body.taskbar-open {
  overflow: hidden;
}

body.taskbar-open .taskbar {
  transform: translateX(0);
}

.taskbar-close {
  position: absolute;
  top: 0.75rem;
  right: 1.25rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  padding: 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--card);
  color: var(--accent-dark);
  transition: background 0.15s ease, color 0.15s ease;
}

.taskbar-close svg {
  transition: stroke 0.15s ease;
}

.taskbar-close:hover {
  background: var(--accent);
  color: var(--accent-contrast);
  text-decoration: none;
}

.taskbar-close:hover svg {
  stroke: var(--accent-contrast) !important;
}

/* Shared nav styles (both overlay and rail) */
/* XY238: the nav is the scroll container (footer/toolbox/close stay pinned);
   chrome compacted per the dossier — the 44px row floor below is untouched. */
.taskbar-nav {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.taskbar-section + .taskbar-section {
  border-top: 1px solid var(--border);
  padding-top: 0.5rem;
}

.taskbar-section h3 {
  margin: 0 0 0.25rem;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
}

.taskbar-section ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

.taskbar-link {
  display: flex;
  align-items: center;
  padding: 0.25rem 0.5rem;
  border-radius: var(--radius-sm);
  font-size: 1rem;
  color: var(--text);
  /* XY224: 44px hit-area floor */
  min-height: 44px;
}

.taskbar-link:hover {
  background: var(--accent-light);
  color: var(--accent-dark);
  text-decoration: none;
}

.taskbar-link-admin {
  background: var(--admin);
  color: var(--accent-contrast);
}

.taskbar-link-admin:hover {
  background: var(--admin-dark);
  color: var(--accent-contrast);
}

/* Red taskbar variant for the raw-database admin panel (mirrors .btn-danger) */
.taskbar-link-danger {
  background: var(--danger);
  color: var(--accent-contrast);
}

.taskbar-link-danger:hover {
  background: var(--danger-dark);
  color: var(--accent-contrast);
}

@media (min-width: 1200px) {
  .taskbar {
    /* XY238: flex column (nav scrolls, footer pinned) with a bounded box —
       bottom auto + height auto let the rail grow past the viewport, which
       is what made the bottom rows unreachable. */
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 5.5rem;
    bottom: 1rem;
    left: 0;
    width: 10rem;
    max-width: 160px;
    z-index: 50;
    background: var(--frosted-bg);
    backdrop-filter: blur(var(--frosted-blur));
    -webkit-backdrop-filter: blur(var(--frosted-blur));
    border-right: 1px solid var(--border);
    border-radius: 0 var(--radius) var(--radius) 0;
    padding: 1rem;
    transform: none;
    transition: none;
    overflow-y: hidden;
  }

  body.taskbar-open {
    overflow: visible;
  }

  .taskbar-toggle,
  .taskbar-close {
    display: none;
  }

  .taskbar-section h3 {
    font-size: 0.65rem;
  }

  .taskbar-link {
    font-size: 0.85rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .taskbar {
    transition: none;
  }
}

/* ==========================================================================
   Glass header + taskbar (XY71)
   Chromium-only progressive enhancement. The .glass class is added by
   website/js/glass.js only when the browser supports SVG filters as
   backdrop-filter. All other engines keep the frosted fallback above.
   The background is slightly more opaque than the frosted fallback to ensure
   text over plant banners never loses contrast.
   ========================================================================== */
.glass .topbar,
.glass .taskbar {
  background: var(--glass-bg);
  backdrop-filter: blur(var(--frosted-blur)) var(--glass-filter, none);
}

/* ==========================================================================
   Floating glass toasts (XY86)
   Replaces the in-flow Django messages block. Bottom-center so we avoid a
   glass-on-glass stack against the sticky topbar. z-index stays below the
   bug-report modal (200) and above the taskbar overlay (150).
   ========================================================================== */
.toast-stack {
  position: fixed;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%);
  z-index: 180;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 0 1rem;
  pointer-events: none;
  max-width: min(calc(100vw - 2rem), 36rem);
}

.toast {
  pointer-events: auto;
  padding: 0.6rem 1.1rem;
  border-radius: 9999px;
  border: 1px solid var(--border);
  background: var(--frosted-bg);
  backdrop-filter: blur(var(--frosted-blur));
  -webkit-backdrop-filter: blur(var(--frosted-blur));
  color: var(--text);
  box-shadow: var(--shadow);
  font-size: 0.95rem;
  line-height: 1.4;
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.25s ease, transform 0.25s ease;
  cursor: pointer;
  max-width: 100%;
}

.toast.is-dismissed {
  opacity: 0;
  transform: translateY(0.75rem);
}

.toast-success {
  border-color: rgba(var(--toast-success-border-rgb), 0.55);
  background: rgba(var(--toast-success-rgb), 0.65);
  backdrop-filter: blur(var(--frosted-blur));
  -webkit-backdrop-filter: blur(var(--frosted-blur));
  color: var(--toast-success-text);
}

.toast-info {
  border-color: rgba(var(--toast-info-border-rgb), 0.5);
  background: rgba(var(--toast-info-rgb), 0.65);
  backdrop-filter: blur(var(--frosted-blur));
  -webkit-backdrop-filter: blur(var(--frosted-blur));
  color: var(--toast-info-text);
}

.toast-warning {
  border-color: rgba(var(--toast-warning-border-rgb), 0.6);
  background: rgba(var(--toast-warning-rgb), 0.65);
  backdrop-filter: blur(var(--frosted-blur));
  -webkit-backdrop-filter: blur(var(--frosted-blur));
  color: var(--toast-warning-text);
}

.toast-error {
  border-color: rgba(var(--toast-error-border-rgb), 0.55);
  background: rgba(var(--toast-error-rgb), 0.65);
  backdrop-filter: blur(var(--frosted-blur));
  -webkit-backdrop-filter: blur(var(--frosted-blur));
  color: var(--toast-error-text);
}

@media (prefers-reduced-motion: reduce) {
  .toast {
    transition: none;
  }
}

/* Gamification banner */
.collect-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  background: var(--card-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1.25rem;
  box-shadow: var(--shadow);
  margin-bottom: 1.25rem;
}

.collect-banner strong {
  color: var(--text);
}

.collect-banner span {
  color: var(--muted);
  font-size: 0.95rem;
}

.collect-banner .scan-explainer {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.85rem;
  color: var(--muted);
}

.collect-banner .scan-explainer a {
  color: var(--accent);
}

.collect-banner .banner-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* Bond-tier picker (XY178) */
.tier-picker {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 0.75rem;
}

.tier-card {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  cursor: pointer;
}

.tier-card input[type="radio"] {
  margin: 0 0 0.25rem 0;
}

.tier-card strong {
  color: var(--text);
}

.tier-card span {
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.35;
}

.tier-card:has(input[type="radio"]:checked) {
  border-color: var(--accent);
  background: var(--accent-light);
}

/* Footer */
footer {
  text-align: center;
  padding: 2.5rem 1.25rem;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.9rem;
}

footer a {
  color: var(--accent);
}

/* Responsive */
@media (max-width: 480px) {
  .hero .logo-mark {
    font-size: 2.5rem;
  }

  .hero .tagline {
    font-size: 1.05rem;
  }

  .banner-nickname {
    font-size: 1.75rem;
  }

  .actions {
    flex-direction: column;
    align-items: stretch;
  }

  .btn {
    justify-content: center;
  }

  .collect-banner {
    flex-direction: column;
    align-items: flex-start;
  }

  /* Reading pages: prefer stacked, full-width blocks on narrow screens. */
  .info-grid {
    grid-template-columns: 1fr;
  }

  .photo-grid {
    grid-template-columns: 1fr;
  }

  .leaderboard-table th,
  .leaderboard-table td,
  .discovered-plants-table th,
  .discovered-plants-table td {
    padding: 0.5rem 0.75rem;
    font-size: 0.85rem;
  }

  /* Mobile money path: customizer, cart, checkout, orders. */
  .preview-3d {
    flex: 1 1 auto;
    width: 100%;
    min-width: auto;
    height: auto;
    aspect-ratio: 140 / 100;
    min-height: 240px;
  }

  .color-row {
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
  }

  .color-options {
    justify-content: flex-start;
  }

  .color-row .color-hex {
    font-size: 0.9rem;
  }

  .cart-item-main,
  .order-header {
    flex-direction: column;
    gap: 0.75rem;
  }

  .cart-item-main .color-swatch,
  .order-header > span:last-child {
    align-self: flex-start;
  }

  .cart-remove-form {
    display: block;
    width: 100%;
  }

  .cart-remove-form button {
    width: 100%;
  }
}

/* Search box (species list page) */
.search-box {
  margin: 1.5rem 0 2rem;
}

.search-box input {
  width: 100%;
  max-width: 480px;
  padding: 0.75rem 1rem;
  font-size: 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--card);
  color: var(--text);
  font-family: inherit;
}

.search-box input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Plant list (species list page) */
.plant-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.plant-item {
  display: flex;
  flex-direction: column;
  padding: 0.75rem 1rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--text);
  transition: border-color 0.15s;
}

.plant-item:hover {
  border-color: var(--accent);
  text-decoration: none;
}

.plant-item strong {
  color: var(--accent-dark);
}

.plant-item span {
  font-size: 0.9rem;
  color: var(--muted);
}

.plant-item .plant-item-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.classification-badge {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
}

.classification-badge.genus {
  color: var(--accent-dark);
  background: var(--accent-light);
}

.classification-badge.species {
  color: var(--muted);
  background: var(--bg);
}

.no-results {
  color: var(--muted);
  font-style: italic;
  padding: 2rem 0;
  text-align: center;
}

/* Plant edit form */
.form-field {
  margin-bottom: 1.25rem;
}

.form-field label {
  display: block;
  margin-bottom: 0.35rem;
  font-weight: 600;
}

/* XY225: no form control renders under 16px — iOS Safari auto-zooms on
   focus of any <16px field, breaking signup. Component rules may go larger. */
input,
textarea,
select {
  font-size: 1rem;
}

.form-field input[type="text"],
.form-field input[type="email"],
.form-field input[type="password"],
.form-field input[type="number"],
.form-field select,
.form-field textarea {
  width: 100%;
  padding: 0.75rem 1rem;
  font-size: 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--card);
  color: var(--text);
  font-family: inherit;
}

.form-field textarea {
  resize: vertical;
}

.form-field input[type="text"]:focus,
.form-field input[type="email"]:focus,
.form-field input[type="password"]:focus,
.form-field input[type="number"]:focus,
.form-field select:focus,
.form-field textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.color-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.color-options {
  display: flex;
  gap: 0.5rem;
}

.color-swatch-radio {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  cursor: pointer;
  font-size: 0.85rem;
}

.color-swatch-radio input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.color-chip {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-sm);
  border: 2px solid var(--border);
  box-sizing: border-box;
}

.color-swatch-radio input[type="radio"]:checked + .color-chip,
.color-swatch-radio input[type="radio"]:focus + .color-chip {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-light);
}

.color-name {
  color: var(--muted);
  text-transform: capitalize;
}

.color-hex {
  font-family: monospace;
  color: var(--muted);
}

.preview-3d {
  flex: 1 1 320px;
  min-width: 280px;
  max-width: 420px;
  width: 100%;
  height: auto;
  aspect-ratio: 140 / 100;
}

.preview-3d svg {
  display: block;
  width: 100%;
  height: 100%;
}

.toggle-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 400;
  cursor: pointer;
}

.toggle-row input[type="checkbox"] {
  width: 1.15rem;
  height: 1.15rem;
  accent-color: var(--accent);
  cursor: pointer;
}

/* Numbered list (no emoji marker, for How-it-works steps) */
.numbered-list {
  padding-left: 1.5rem;
  list-style: decimal;
  margin: 0.75rem 0 0;
}

.numbered-list li {
  margin-bottom: 0.75rem;
  color: var(--muted);
}

/* Form errors and hints (auth pages) */
.form-errors {
  color: var(--form-error-text);
  font-size: 0.875rem;
  margin: 0.35rem 0 0;
}

.form-errors ul {
  margin: 0;
  padding-left: 1.25rem;
}

.form-hint {
  color: var(--muted);
  font-size: 0.8rem;
  margin: 0.35rem 0 0;
}

/* Topbar nav links (feed, account) */
.topbar-inner {
  gap: 0.75rem;
}

.nav-link {
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--accent-dark);
  padding: 0.5rem 0.75rem;
}

.nav-link:hover {
  text-decoration: underline;
}

.nav-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  padding: 0.1rem 0.35rem;
  margin-left: 0.35rem;
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1;
  color: var(--accent-contrast);
  background: var(--accent);
  border-radius: 999px;
}

/* Activity / notification lists (feed) */
.activity-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.activity-list li {
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--border);
}

.activity-list li:last-child {
  border-bottom: none;
}

.activity-list li.unread {
  background: var(--accent-light);
  margin: 0 -1.25rem;
  padding-left: 1.25rem;
  padding-right: 1.25rem;
}

.activity-time {
  display: block;
  font-size: 0.8rem;
  color: var(--muted);
}

/* User list (followers / following) */
.user-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.user-list li {
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
}

.user-list li:last-child {
  border-bottom: none;
}

/* Leaderboard */
.leaderboard-table {
  width: 100%;
  border-collapse: collapse;
}

.leaderboard-table th,
.leaderboard-table td {
  padding: 0.75rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
}

.leaderboard-table th {
  color: var(--muted);
  font-weight: 600;
  font-size: 0.85rem;
}

.leaderboard-table tr.current-user {
  background: var(--accent-light);
}

/* XY224: 44px hit-area floor for cell links (plant / gardener / species) */
.leaderboard-table td a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.current-user-row {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

/* Plant-centered leaderboard (XY184) */
.leaderboard-welcome {
  color: var(--muted);
}

.leaderboard-plant {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

/* XY211: People-tab Gardener cell — avatar (or placeholder) beside the name. */
.leaderboard-gardener {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.leaderboard-plant-photo {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.leaderboard-plant-photo--placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-light);
  color: var(--accent);
}

.bond-chip {
  display: inline-block;
  padding: 0.1rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.8rem;
  color: var(--muted);
}

/* XY233: opt-in bond glyph on the leaderboard (replaces the Bond column). */
.bond-glyph {
  margin-left: 0.25rem;
  font-size: 0.95rem;
}

/* Scroll containment for tables that can't fit on narrow screens. */
.table-wrap {
  width: 100%;
  overflow-x: auto;
}



/* Comments */
.comment-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.comment-item {
  padding: 1rem 0;
  border-bottom: 1px solid var(--border);
}

.comment-item:last-child {
  border-bottom: none;
}

.comment-meta {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0 0 0.35rem;
  font-size: 0.9rem;
}

.comment-time {
  color: var(--muted);
  font-size: 0.8rem;
}

.comment-text {
  color: var(--text);
  margin: 0 0 0.75rem;
}

.comment-delete-form {
  display: inline;
}

.comment-form {
  margin-top: 1.25rem;
}

.comment-form textarea {
  min-height: 80px;
}

.comment-cooldown-notice {
  margin-top: 1.25rem;
  padding: 1rem;
  background: var(--accent-light);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.comment-cooldown-notice strong {
  color: var(--text);
}

.comment-cooldown-notice p {
  margin: 0.25rem 0 0;
  font-size: 0.9rem;
}

.comment-post-error {
  margin: 0.5rem 0 0;
  color: var(--danger-text);
  font-size: 0.9rem;
}

.btn-small {
  padding: 0.35rem 0.65rem;
  font-size: 0.8rem;
  /* XY224: 44px hit-area floor */
  min-height: 44px;
}

.comment-item.is-deleted {
  color: var(--muted);
  opacity: 0.75;
  background: var(--bg);
  border-radius: var(--radius-sm);
  padding: 0.75rem 1rem;
  margin: 0 -1rem;
}

.comment-removed {
  margin: 0;
  font-style: italic;
  color: var(--muted);
}

.comment-item.comment-pending {
  opacity: 0.75;
  background: var(--bg);
  border-radius: var(--radius-sm);
  padding: 0.75rem 1rem;
  margin: 0 -1rem;
}

.comment-item.comment-pending .comment-text {
  color: var(--muted);
}

.pending-stamp {
  display: inline-block;
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  background: var(--border);
  padding: 0.1rem 0.4rem;
  border-radius: var(--radius-sm);
}

.comment-thread {
  list-style: none;
  margin: 0.75rem 0 0 0;
  padding: 0 0 0 1.25rem;
  border-left: 2px solid var(--border);
}

/* Cap visual indentation at 4 levels. Deeper replies keep the same thread-line offset. */
.comment-item.indent-4 .comment-thread {
  padding-left: 0;
  border-left: none;
}

.comment-body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
}

.comment-actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.reply-toggle {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  font-size: 0.85rem;
  cursor: pointer;
  font-family: inherit;
}

.reply-toggle:hover {
  text-decoration: underline;
}

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  background: var(--accent-light);
  color: var(--accent-dark);
  overflow: hidden;
}

.avatar--small {
  width: 1.5rem;
  height: 1.5rem;
}

.avatar--large {
  width: 4rem;
  height: 4rem;
}

.avatar--placeholder svg {
  width: 60%;
  height: 60%;
}

.about-people-link {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  min-width: 44px;
  min-height: 44px;
  padding: 0.5rem;
}

.profile-header {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

/* XY208: profile plant cards — the whole card navigates to the plant page
   (stretched link), while the nested species link stays clickable above it. */
.plant-card {
  position: relative;
}

.plant-card-title a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.plant-card-nested-link {
  position: relative;
  z-index: 1;
}

.reply-form {
  margin-top: 0.75rem;
  padding: 0.75rem;
  background: var(--bg);
  border-radius: var(--radius-sm);
}

.reply-form textarea {
  min-height: 60px;
}

.owner-badge {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--accent-dark);
  background: var(--accent-light);
  padding: 0.1rem 0.4rem;
  border-radius: var(--radius-sm);
}

.reply-to {
  color: var(--muted);
  font-size: 0.8rem;
}

/* XY196 village chat: scrollable panel, sticky floating composer, lazy-load sentinel */
.village-chat-panel {
  max-height: 60vh;
  overflow-y: auto;
}

.village-chat-composer {
  position: sticky;
  bottom: 0;
  margin-top: 0.75rem;
  padding: 0.75rem 0.25rem 0.25rem;
  background: var(--frosted-bg);
  backdrop-filter: blur(var(--frosted-blur));
  -webkit-backdrop-filter: blur(var(--frosted-blur));
  border-radius: var(--radius-sm);
}

.village-chat-sentinel {
  height: 1px;
}

.indent-1 { margin-left: 0; }
.indent-2 { margin-left: 0; }
.indent-3 { margin-left: 0; }
.indent-4 { margin-left: 0; }

/* Cooldown modal */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--modal-backdrop);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

.modal-backdrop[hidden] {
  display: none;
}

.modal-panel {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  max-width: 440px;
  width: 100%;
  box-shadow: var(--shadow);
}

.modal-panel h3 {
  margin-top: 0;
}

.modal-actions {
  display: flex;
  gap: 0.75rem;
  justify-content: flex-end;
  margin-top: 1.25rem;
}

.modal-panel::backdrop {
  background: var(--modal-backdrop);
}

.bug-report-panel textarea {
  width: 100%;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 1rem;
  resize: vertical;
  margin-top: 1rem;
}

.bug-report-panel textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.bug-report-meta {
  margin-top: 1rem;
}

.bug-report-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
  color: var(--muted);
  cursor: pointer;
}

.bug-report-checkbox input {
  width: 1rem;
  height: 1rem;
  accent-color: var(--accent);
}

.bug-report-message {
  margin-top: 1rem;
}

.bug-report-error {
  color: var(--danger-text);
}

.suggest-edit-toggle {
  display: inline-block;
  margin: 0.25rem 0 0.75rem;
  padding: 0;
  background: transparent;
  border: none;
  color: var(--accent);
  font-size: 0.95rem;
  text-decoration: underline;
  cursor: pointer;
}

.suggest-edit-toggle:hover {
  color: var(--accent-hover, var(--accent));
}

#suggest-edit-form select,
#suggest-edit-form textarea {
  width: 100%;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--card);
  color: var(--text);
  font: inherit;
}

#suggest-edit-form select:focus,
#suggest-edit-form textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

#suggest-edit-form .form-label {
  display: block;
  margin-top: 1rem;
  margin-bottom: 0.25rem;
  font-weight: 500;
}


/* Sign customizer */
.classification-option {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.75rem 1rem;
  margin-bottom: 0.5rem;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

.classification-option:hover {
  background: var(--accent-light);
}

.classification-option.selected {
  border-color: var(--accent);
  background: var(--accent-light);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.color-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.color-options {
  display: flex;
  gap: 0.5rem;
}

.color-swatch-radio {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  cursor: pointer;
  font-size: 0.85rem;
}

.color-swatch-radio input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.color-chip {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-sm);
  border: 2px solid var(--border);
  box-sizing: border-box;
}

.color-swatch-radio input[type="radio"]:checked + .color-chip,
.color-swatch-radio input[type="radio"]:focus + .color-chip {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-light);
}

.color-name {
  color: var(--muted);
  text-transform: capitalize;
}

.color-hex {
  font-family: monospace;
  font-size: 0.95rem;
  color: var(--muted);
}

.preview-wrap canvas {
  display: block;
}

.form-hint {
  margin: 0.5rem 0 0;
  font-size: 0.9rem;
  color: var(--muted);
}

.form-errors {
  margin: 0.75rem 0 0;
  padding: 0.75rem 1rem;
  background: var(--form-error-bg);
  border: 1px solid var(--form-error-border);
  border-radius: var(--radius-sm);
  color: var(--form-error-text);
}

.cart-item h3 {
  margin-top: 0;
}

.cart-list .cart-item + .cart-item {
  margin-top: 1rem;
}

.cart-remove-form {
  display: inline;
}

/* Sign customizer: 3D preview loading sprout.
   Placeholder animation — meant to be replaced by a fuller plant-growing
   animation later. Pure CSS: stem grows, two leaves pop, then it loops. */
.sprout-loader {
  position: relative;
  width: 48px;
  height: 56px;
}

.sprout-stem {
  position: absolute;
  bottom: 0;
  left: 50%;
  width: 4px;
  height: 38px;
  margin-left: -2px;
  background: var(--accent);
  border-radius: 2px;
  transform-origin: bottom center;
  animation: sprout-grow 1.8s ease-in-out infinite;
}

.sprout-leaf {
  position: absolute;
  bottom: 30px;
  left: 50%;
  width: 20px;
  height: 12px;
  background: var(--accent-dark);
  border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
  transform-origin: left center;
  animation: sprout-leaf 1.8s ease-in-out infinite;
}

.sprout-leaf-right {
  border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
  transform-origin: right center;
  margin-left: 2px;
  animation-name: sprout-leaf-right;
}

.sprout-leaf-left {
  margin-left: -22px;
}

@keyframes sprout-grow {
  0%    { transform: scaleY(0); }
  45%   { transform: scaleY(1); }
  80%   { transform: scaleY(1); }
  100%  { transform: scaleY(0); }
}

@keyframes sprout-leaf {
  0%, 30%   { transform: rotate(-35deg) scale(0); }
  60%, 80%  { transform: rotate(-35deg) scale(1); }
  100%      { transform: rotate(-35deg) scale(0); }
}

@keyframes sprout-leaf-right {
  0%, 30%   { transform: rotate(35deg) scale(0); }
  60%, 80%  { transform: rotate(35deg) scale(1); }
  100%      { transform: rotate(35deg) scale(0); }
}

/* ---------------------------------------------------------------------------
   Management page (XY68)
   --------------------------------------------------------------------------- */

.appeal-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.appeal-item {
  border-bottom: 1px solid var(--border);
  padding: 1.25rem 0;
}

.appeal-item:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.appeal-item:first-child {
  padding-top: 0;
}

.appeal-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 0.75rem;
}

.appeal-meta a {
  font-weight: 600;
}

.appeal-comment,
.appeal-reason {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.75rem 1rem;
  margin-bottom: 0.75rem;
}

.appeal-comment p,
.appeal-reason p {
  margin: 0;
  color: var(--text);
}

.appeal-comment .muted,
.appeal-item > .muted {
  font-size: 0.875rem;
}

.appeal-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 0.75rem;
}

.appeal-status {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  margin-left: auto;
}

.appeal-status-upheld {
  background: var(--accent-light);
  color: var(--accent-dark);
}

.appeal-status-rejected {
  background: var(--danger-bg);
  color: var(--danger-text);
}

@media (max-width: 480px) {
  .appeal-actions .btn {
    flex: 1 1 auto;
    justify-content: center;
  }

  .appeal-status {
    margin-left: 0;
    width: 100%;
    text-align: center;
  }
}

.management-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.management-item {
  border-bottom: 1px solid var(--border);
  padding: 1.25rem 0;
}

.management-item:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.management-item:first-child {
  padding-top: 0;
}

.management-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 0.5rem;
}

.management-summary {
  margin-bottom: 0.75rem;
  font-size: 0.875rem;
}

.management-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.management-status {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 700;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--accent-light);
  color: var(--accent-dark);
}

.badge {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 700;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  vertical-align: middle;
  margin-left: 0.5rem;
}

@media (max-width: 480px) {
  .management-actions .btn {
    flex: 1 1 auto;
    justify-content: center;
  }
}

/* Snake easter egg (XY100) */
.snake-game-mount {
  margin-top: 1rem;
}

.snake-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  margin-bottom: 0.75rem;
}

.snake-score {
  font-weight: 600;
  color: var(--accent-dark);
}

.snake-best {
  color: var(--muted);
  font-size: 0.95rem;
}

.snake-controls-hint {
  color: var(--muted);
  font-size: 0.85rem;
}

.snake-canvas-wrap {
  position: relative;
  display: inline-block;
  width: 100%;
  max-width: 480px;
}

.snake-canvas {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  background: var(--card);
  touch-action: none;
}

.snake-overlay {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  border-radius: var(--radius);
  background: var(--overlay-bg);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

.snake-overlay.visible {
  display: flex;
}

.snake-overlay-card {
  text-align: center;
  padding: 1.5rem;
  border-radius: var(--radius);
  background: var(--card);
  box-shadow: var(--shadow);
  max-width: 280px;
}

.snake-overlay-card h2 {
  margin: 0 0 0.75rem;
  color: var(--accent-dark);
}

.snake-final-score,
.snake-final-best {
  margin: 0.25rem 0;
  color: var(--text);
}

.snake-restart {
  margin-top: 1rem;
}

.snake-pause-badge {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: none;
  padding: 0.5rem 1rem;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-contrast);
  font-weight: 600;
  box-shadow: var(--shadow);
  pointer-events: none;
}

.snake-pause-badge.visible {
  display: block;
}

/* Care calendar (XY92) */
.care-calendar-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.care-calendar-header h1 {
  margin: 0;
}

.care-calendar-nav {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.care-calendar-month {
  font-weight: 600;
  font-size: 1.1rem;
  min-width: 10ch;
  text-align: center;
}

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

.calendar-weekday {
  font-size: 0.8rem;
  font-weight: 600;
  text-align: center;
  color: var(--muted);
  padding: 0.5rem 0;
}

.calendar-day {
  min-width: 0;
  min-height: 7rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.calendar-day-other {
  background: transparent;
  border-color: transparent;
}

.calendar-day-today {
  border-color: var(--accent);
  background: var(--accent-light);
}

.calendar-day-number {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
  align-self: flex-start;
}

.calendar-day-today .calendar-day-number {
  color: var(--accent-dark);
}

.calendar-chips {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.calendar-chip {
  min-width: 0;
  font-size: 0.8rem;
  background: var(--card-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.35rem;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.calendar-chip-overdue {
  background: var(--danger-bg);
  border-color: var(--danger-border-light);
}

.calendar-chip a {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.calendar-chip-label {
  color: var(--muted);
  font-size: 0.75rem;
}

.calendar-chip-form {
  margin-top: 0.15rem;
}

.calendar-chip-form input[type="date"] {
  max-width: 100%;
}

.calendar-chip-form .btn {
  padding: 0.15rem 0.4rem;
  font-size: 0.7rem;
}

@media (max-width: 640px) {
  .calendar-grid {
    gap: 0.25rem;
  }

  .calendar-day {
    min-height: 4.5rem;
    padding: 0.25rem;
  }

  .calendar-day-number {
    font-size: 0.75rem;
  }

  .calendar-chip {
    padding: 0.2rem;
    font-size: 0.7rem;
  }

  .calendar-chip-label {
    display: none;
  }

  .calendar-chip-form .btn {
    padding: 0.1rem 0.25rem;
    font-size: 0.6rem;
  }
}

@media (max-width: 480px) {
  .care-calendar-header {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* Plant timeline (XY179): story posts, milestones, care chips */
.timeline-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.timeline-post {
  padding: 1rem 0;
  border-bottom: 1px solid var(--border);
}

.timeline-post:last-child {
  border-bottom: none;
}

.post-meta {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 0.5rem;
  font-size: 0.9rem;
  flex-wrap: wrap;
}

.post-meta time {
  color: var(--muted);
  font-size: 0.8rem;
}

.post-text {
  margin: 0.5rem 0 0;
}

.post-photo {
  margin: 0.5rem 0;
  max-width: 420px;
}

.post--milestone {
  background: var(--accent-light);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 1rem;
  margin: 1rem 0;
}

.post--milestone .post-text {
  font-size: 1.05rem;
}

.milestone-badge {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: #fff;
  background: var(--accent);
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
}

/* XY213: weekly care streak chip (own profile + windowsill headers). */
.streak-chip {
  display: inline-block;
  font-size: 0.78rem;
  font-weight: 700;
  color: #9a3412;
  background: #ffedd5;
  border: 1px solid #fed7aa;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  vertical-align: middle;
}

/* XY229: weekly care grid share artifact — the exact copy source. */
.care-grid {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.95rem;
  line-height: 1.7;
  white-space: pre;
  overflow-x: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.75rem 1rem;
  margin: 0.75rem 0 1rem;
}

/* XY216: scoped rolling-week boards (species Patch pages + village pages). */
.weekly-board-window {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0;
}
.weekly-board-track-title {
  font-size: 0.85rem;
  margin: 1rem 0 0.35rem;
}
.weekly-board-list {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: weekly-board-rank;
}
.weekly-board-list li {
  counter-increment: weekly-board-rank;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--border);
  font-size: 0.95rem;
}
.weekly-board-list li:last-child {
  border-bottom: 0;
}
.weekly-board-list li::before {
  content: counter(weekly-board-rank);
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--muted);
  min-width: 1rem;
}
.weekly-board-list li a {
  font-weight: 600;
}
.weekly-board-meta {
  color: var(--muted);
  font-size: 0.8rem;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.weekly-board-score {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.weekly-board-you {
  margin: 0.6rem 0 0;
  font-size: 0.85rem;
  color: var(--muted);
}

/* XY185 share engine: per-post actions, milestone card head + tier chip,
   xylery mark, and the promo banner action row. */

.post-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin: 0.25rem 0 0.5rem;
}

.milestone-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 0.5rem;
}

.milestone-plant-name {
  font-weight: 700;
  font-size: 1.05rem;
}

.milestone-species {
  color: var(--muted);
  font-style: italic;
  font-size: 0.85rem;
  margin-left: 0.4rem;
}

.tier-chip {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--accent);
  border: 1px solid var(--accent);
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
}

.milestone-xylery-mark {
  display: block;
  text-align: right;
  margin-top: 0.75rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--muted);
  text-transform: lowercase;
}

.banner-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-shrink: 0;
}

/* XY195 shared tab component: plain ?tab= links with server-rendered active
   state, no JS. Any number of tabs; overflow scrolls horizontally instead
   of wrapping. The active link carries .tab--active + aria-current. */

.tabs {
  display: flex;
  flex-wrap: nowrap;
  gap: 0.25rem;
  overflow-x: auto;
  border-bottom: 1px solid var(--border);
  margin: 0.5rem 0 1rem;
}

.tab {
  white-space: nowrap;
  padding: 0.5rem 0.9rem;
  color: var(--muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  /* XY224: 44px hit-area floor */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.tab:hover {
  color: var(--text);
}

.tab--active {
  color: var(--text);
  font-weight: 600;
  border-bottom-color: var(--accent);
}

.post-comments {
  margin-top: 0.75rem;
  padding-left: 0.75rem;
  border-left: 3px solid var(--border);
}

.post-comment-form {
  margin-top: 0.75rem;
}

.post-composer {
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1rem;
}

.composer-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-top: 0.5rem;
}

.composer-kind {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.9rem;
}

.care-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.care-chip {
  background: var(--accent-light);
  border-radius: 999px;
  padding: 0.25rem 0.75rem;
  font-size: 0.85rem;
}

.care-chip summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--accent-dark);
  list-style: none;
}

.care-chip summary::-webkit-details-marker {
  display: none;
}

.care-chip-dates {
  list-style: none;
  margin: 0.5rem 0 0.25rem;
  padding: 0;
  color: var(--muted);
  font-size: 0.8rem;
}

/* The Patch (XY186) — species/genus community sections + /patch listing */
.patch-count {
  font-weight: 600;
  color: var(--accent-dark);
  margin: 0 0 1rem;
}

.patch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.patch-post {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.75rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--text);
  transition: border-color 0.15s;
}

.patch-post:hover {
  border-color: var(--accent);
  text-decoration: none;
}

.patch-post img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: calc(var(--radius-sm) - 2px);
}

.patch-post-byline {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  flex-wrap: wrap;
}

.patch-post-byline strong {
  color: var(--accent-dark);
}

.patch-post-byline span {
  font-size: 0.8rem;
  color: var(--muted);
}

.patch-post-text {
  font-size: 0.9rem;
}

.patch-post time {
  font-size: 0.75rem;
  color: var(--muted);
}

.patch-empty {
  color: var(--muted);
  font-style: italic;
}

.patch-browse {
  font-weight: 600;
}

.patch-plants {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}

.patch-plant-card {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.75rem;
  background: var(--card-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--text);
  transition: border-color 0.15s;
}

.patch-plant-card:hover {
  border-color: var(--accent);
  text-decoration: none;
}

.patch-plant-card img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: calc(var(--radius-sm) - 2px);
}

.patch-plant-card strong {
  color: var(--accent-dark);
}

.patch-plant-card span {
  font-size: 0.8rem;
  color: var(--muted);
}

.patch-pagination {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.patch-pagination-status {
  color: var(--muted);
  font-size: 0.9rem;
}

/* ---------------------------------------------------------------------------
   XY202: butterfly swarm easter egg (theme-toggle hold). The stage spans the
   viewport but passes pointer events through; each insect re-enables them for
   the shoo click. Wing motion is TWO nested scaleY transforms: the outer
   .lepi-wing holds the mode pose (--lepi-pose, transitioned ~0.8 s so
   flight→glide→perch blends never jump) and the inner .lepi-wing-inner runs
   the periodic flutter around scaleY(1) — never rotateY/rotateX/scaleX
   (verified wrong, see XY202 dossier).
   --------------------------------------------------------------------------- */
#lepi-stage {
  position: fixed;
  inset: 0;
  /* Above .topbar (100) and .taskbar (150) so perched insects stay visible
     and clickable on nav chrome; below .toast-stack (180), .modal-backdrop
     (200) and .sidebar-drag-ghost (400). */
  z-index: 160;
  pointer-events: none;
}

.lepi {
  position: absolute;
  top: 0;
  left: 0;
  width: 64px;
  height: 158px;
  /* The box around a perched insect is mostly transparent, and it sits over
     nav links — with pointer-events:auto every click in the 64×158 box
     swallowed the link click AND shooed the insect ("they disappear
     randomly"). Only painted wing/body pixels are clickable (set below). */
  pointer-events: none;
  cursor: pointer;
  will-change: transform;
}

.lepi-layer,
.lepi-wing,
.lepi-wing-inner {
  position: absolute;
  inset: 0;
}

.lepi-layer svg,
.lepi-wing svg {
  display: block;
  width: 100%;
  height: 100%;
  /* The svg ELEMENT hit-tests its whole box even with visiblePainted — set
     it to none so transparent pixels pass clicks through to whatever is
     underneath (nav links under a perched insect), and re-enable
     visiblePainted on the painted shapes only, so clicking the butterfly
     itself still shoos it. */
  pointer-events: none;
}

.lepi-layer svg *,
.lepi-wing svg * {
  pointer-events: visiblePainted;
}

.lepi-wing {
  /* Mode pose: set inline by setMode() in lepi_egg.js, transitioned so a
     mode change sweeps instead of snapping. */
  transform: scaleY(var(--lepi-pose, 1));
  transition: transform 0.8s ease-in-out;
  /* Hinge: pixel-scanned from the rendered SVGs (canvas alpha scan at 4x).
     The wing's bottom margin runs flat at y≈77.5 and its root corner —
     where the wing actually meets the body — sits at y≈77.3 of 158
     (48.9%). The first measured guess, 45.0% (y≈71), pivoted INSIDE the
     wing root, so every flap swung the root off the body: floating above
     it on the upstroke, tucking under it on the downstroke. Only the Y
     matters for scaleY; X is documentary. */
  transform-origin: 77.5% 48.9%;
}

.lepi-wing-inner {
  /* Same hinge height as the outer layer (percentages resolve against the
     identical inset:0 box). */
  transform-origin: 77.5% 48.9%;
}

.lepi--flight .lepi-wing-inner {
  animation: lepi-flap 0.2s ease-in-out infinite alternate;
}

/* With the flight pose (0.45) this sweeps the wingplane −0.32 ↔ +0.95:
   a full upstroke with a shallow below-horizontal downstroke. */
@keyframes lepi-flap {
  from { transform: scaleY(-0.7); }
  to { transform: scaleY(2.1); }
}

.lepi--glide .lepi-wing-inner {
  animation: lepi-glide 10s ease-in-out infinite alternate;
}

/* Glide pose 0.5 × 0.9–1.1 = the original 0.45 ↔ 0.55 slow float. */
@keyframes lepi-glide {
  from { transform: scaleY(0.9); }
  to { transform: scaleY(1.1); }
}

.lepi--perch .lepi-wing-inner {
  animation: lepi-perch 3.333s ease-in-out infinite;
}

/* Perch pose 1 × 1 ↔ 0.90 = the 100% ↔ 90% idle flutter. */
@keyframes lepi-perch {
  0%, 100% { transform: scaleY(1); }
  50% { transform: scaleY(0.90); }
}

/* Button wiggle while the hold is charging. */
#theme-toggle.lepi-flutter {
  animation: lepi-hold-wiggle 0.25s ease-in-out infinite;
}

@keyframes lepi-hold-wiggle {
  0%, 100% { transform: rotate(0deg); }
  25% { transform: rotate(-7deg); }
  75% { transform: rotate(7deg); }
}

/* ==========================================================================
   XY201: Windowsill feed button + taskbar edit mode
   ========================================================================== */

/* XY238: flex column — the nav (.taskbar-nav, defined with the shared nav
   styles above) is the scroll container; the toolbox and footer are pinned
   flex items below it. The rail mode uses this same structure (its media
   query redeclares display: flex). */
.taskbar-footer {
  flex: 0 0 auto;
  padding-top: 0.75rem;
}

/* Edit mode: dashed outlines + grab cursor on draggable destinations. */
.sidebar-editing .taskbar-section ul,
.sidebar-editing .sidebar-toolbox ul {
  min-height: 2rem;
}

.sidebar-editing li[data-sidebar-key] {
  outline: 1px dashed var(--border);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
  cursor: grab;
  touch-action: none;
}

.sidebar-editing li[data-sidebar-key]:active {
  cursor: grabbing;
}

.sidebar-drag-source {
  opacity: 0.4;
}

.sidebar-drag-ghost {
  position: fixed;
  z-index: 400;
  pointer-events: none;
  opacity: 0.85;
  list-style: none;
  margin: 0;
  box-shadow: var(--shadow, 0 4px 16px rgba(0, 0, 0, 0.18));
  border-radius: var(--radius-sm);
  background: var(--card-elevated);
}

li.sidebar-drop-before {
  box-shadow: 0 -2px 0 0 var(--accent);
}

li.sidebar-drop-after {
  box-shadow: 0 2px 0 0 var(--accent);
}

#sidebar-toolbox-list.sidebar-drop-before {
  box-shadow: 0 -2px 0 0 var(--accent);
}

.sidebar-toolbox {
  /* XY238: pinned flex item below the scrolling nav; chrome compacted. */
  flex: 0 0 auto;
  margin-top: 0.5rem;
  border-top: 1px dashed var(--border);
  padding-top: 0.75rem;
}

.sidebar-toolbox ul {
  list-style: none;
  margin: 0.25rem 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-height: 2rem;
  border-radius: var(--radius-sm);
}

.sidebar-toolbox-hint {
  margin: 0;
  font-size: 0.85rem;
}

.sidebar-toolbox--error {
  border-top-color: var(--accent);
  animation: sidebar-toolbox-shake 0.3s ease;
}

@keyframes sidebar-toolbox-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

/* ==========================================================================
   XY203 — Species-page "actual plants" rail
   A scrollable box of real owned plants (photo + nickname, whole card is a
   link to the plant page), sitting under the photos in the species rail.
   Desktop: a vertical scrollbox inside the right rail. Narrow: a grid under
   the photos box. ≤640px: the species photos become a horizontal strip and
   the plants rail is a second horizontal strip beneath them.
   ========================================================================== */

.species-plants {
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

.species-plants h2 {
  font-size: 1rem;
  color: var(--muted);
  margin: 0 0 0.75rem;
}

.species-plant-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 0.75rem;
}

.species-plant-card {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.6rem;
  background: var(--card-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--text);
  transition: border-color 0.15s;
}

.species-plant-card:hover {
  border-color: var(--accent);
  text-decoration: none;
}

.species-plant-card img,
.species-plant-photo {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: calc(var(--radius-sm) - 2px);
  display: block;
}

.species-plant-photo--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  color: var(--muted);
  font-size: 0.8rem;
  overflow: hidden;
}

.species-plant-card strong {
  color: var(--accent-dark);
  font-size: 0.9rem;
}

@media (min-width: 1200px) {
  /* Inside the wide rail the aside's own gap separates the box from the
     photos, so the narrow-mode divider goes away. */
  .photo-rail .species-plants {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
  }

  .photo-rail .species-plant-list {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    max-height: 420px;
    overflow-y: auto;
    padding-right: 0.25rem;
  }
}

@media (max-width: 640px) {
  /* Species photos become a horizontal strip on phones. */
  .photo-rail .photo-grid {
    display: flex;
    overflow-x: auto;
    gap: 0.75rem;
    padding-bottom: 0.25rem;
  }

  .photo-rail .photo-grid > * {
    flex: 0 0 72%;
  }

  /* The plants rail is a second horizontal strip beneath them. */
  .species-plant-list {
    display: flex;
    overflow-x: auto;
    gap: 0.75rem;
    padding-bottom: 0.25rem;
  }

  .species-plant-card {
    flex: 0 0 140px;
  }
}

/* ==========================================================================
   XY207: Windowsill rails — Netflix-style autoscrolling carousels.
   Each rail is a titled horizontal track of media-forward cards. The track
   scrolls natively (wheel/keyboard/drag); the scrollbar is hidden because
   drift is the primary motion and drag the manual affordance. Cards reuse
   the XY203 species-rail visual language, extended with snippet + timestamp.
   ========================================================================== */

.ws-rail {
  margin: 0 0 1.75rem;
}

.ws-rail-title {
  font-size: 1.05rem;
  margin: 0 0 0.6rem;
}

.ws-rail-track {
  overflow-x: auto;
  padding: 0.15rem 0.1rem 0.6rem;
  scrollbar-width: none;
  cursor: grab;
}

/* XY223: the cards live on an inner strip that the drift engine moves with
   transform: translate3d — compositor-only, no layout/paint per frame. The
   outer track stays the native scroll container (wheel/keyboard/drag). */
.ws-rail-track-inner {
  display: flex;
  gap: 0.75rem;
  width: max-content;
  will-change: transform;
}

.ws-rail-track::-webkit-scrollbar {
  display: none;
}

.ws-rail-track.is-dragging {
  cursor: grabbing;
}

.ws-card {
  flex: 0 0 200px;
  display: flex;
  flex-direction: column;
  background: var(--card-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--text);
  overflow: hidden;
  transition: border-color 0.15s, transform 0.15s;
}

.ws-card:hover {
  border-color: var(--accent);
  text-decoration: none;
  transform: translateY(-2px);
}

.ws-card-photo {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
}

.ws-card-photo--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  color: var(--muted);
  font-size: 0.8rem;
  overflow: hidden;
}

.ws-card-body {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.5rem 0.65rem 0.6rem;
  font-size: 0.82rem;
}

.ws-card-title {
  color: var(--accent-dark);
  font-size: 0.9rem;
}

.ws-card-meta,
.ws-card-time {
  font-size: 0.72rem;
}

.ws-card-text {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ==========================================================================
   XY221: Windowsill phone tabs + desktop Explore.
   Below 1200px the windowsill is a rednote-style tabbed waterfall; at 1200px
   and up it keeps the rails stack and gains an untitled Explore section of
   two counter-drifting rows on top. The two layouts are separate markup
   blocks toggled by display — never CSS columns for the waterfall, so DOM
   order (WCAG 2.4.3) is the reading order.
   ========================================================================== */

.ws-phone {
  display: block;
}

.ws-desktop {
  display: none;
}

@media (min-width: 1200px) {
  .ws-phone {
    display: none;
  }

  .ws-desktop {
    display: block;
  }
}

.ws-waterfall {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
}

.ws-waterfall-col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

@media (max-width: 350px) {
  .ws-waterfall {
    flex-direction: column;
  }
}

.ws-card--tile {
  flex: none;
  width: 100%;
}

.ws-explore {
  margin: 0 0 1.75rem;
}

.ws-explore .ws-rail {
  margin: 0 0 0.5rem;
}

.ws-motion-row {
  display: flex;
  justify-content: flex-end;
  margin: -0.25rem 0 0.9rem;
}

/* XY222: WCAG 2.2.2 pause/stop control — visible (never hover-revealed),
   keyboard-operable, 44px minimum hit area. */
.ws-motion-toggle {
  min-height: 44px;
  min-width: 44px;
  padding: 0.45rem 0.9rem;
  font-size: 0.85rem;
  font-weight: 500;
}
