/*
 * dashboard.css - all styling for index.html's media-kit dashboard.
 * Light, pink-based theme (page background pink, cards off-white, text
 * near-black) - hand-written, no framework. Color roles are defined once
 * as custom properties below and referenced by name throughout. Chart/
 * data colors (--series-*, --good, --bad) are the dataviz skill's
 * validated LIGHT-mode palette, kept separate from the decorative
 * --brand-gradient used for chrome (buttons, avatar ring, tab indicator).
 */

:root {
  color-scheme: light;
  --page-plane:     #f6c9d8;
  --surface-1:      #fdf6f3;
  --surface-2:      #fbe6ee;
  --text-primary:   #1a1214;
  --text-secondary: #5c4750;
  --text-muted:     #8f7a82;
  --gridline:       #f0d9e2;
  --baseline:       #d9a7ba;
  --border:         rgba(26, 18, 20, 0.12);
  /* Chart/data colors are the dataviz skill's validated LIGHT-mode
     palette (not a recolor - a light background needs different
     lightness/contrast than the dark-mode set this started from). */
  --series-1:       #2a78d6;
  --series-1-wash:  rgba(42, 120, 214, 0.10);
  --series-2:       #eb6834;
  --series-3:       #1baf7a;
  --good:           #006300;
  --bad:            #c0392b;

  /* Decorative brand accent only - the pink/rose theme. Never used for
     chart marks/data encoding, which stay on the validated dataviz
     palette above (--series-*). Reserved for chrome: avatar ring,
     primary CTA, tab indicator, a couple of accent borders. */
  --brand-gradient: linear-gradient(135deg, #ffb6c9 0%, #ec6a9c 50%, #c94277 100%);
  --brand-glow:     rgba(217, 79, 138, 0.14);

  /* Elevation - every card-like surface uses these two steps (resting /
     hover) instead of relying on the 1px border alone. A flat outlined box
     reads as a wireframe; a soft shadow gives the page actual depth, which
     is most of what separates "styled HTML" from "professional dashboard"
     at a glance. Kept deliberately subtle - ink-tinted, not black, so it
     stays warm on the pink page rather than looking like a generic UI kit. */
  --shadow-resting: 0 1px 2px rgba(26, 18, 20, 0.05), 0 1px 4px rgba(26, 18, 20, 0.04);
  --shadow-hover:   0 8px 20px rgba(26, 18, 20, 0.10), 0 2px 6px rgba(26, 18, 20, 0.06);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--page-plane);
  color: var(--text-primary);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
}

body {
  /* Faint rose glow behind the masthead for depth - subtle enough not to
     compete with any content. */
  background-image: radial-gradient(ellipse 900px 400px at 50% -10%, var(--brand-glow), transparent 70%);
  background-repeat: no-repeat;
}

.page {
  max-width: 1040px;
  margin: 0 auto;
  padding: 32px 20px 64px;
}

/* ---------- Masthead ---------- */

.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.masthead-identity {
  display: flex;
  align-items: center;
  gap: 16px;
}

.masthead-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.print-only-heading,
.print-only-value {
  /* Only rendered in the printed/PDF output - see the @media print block.
     On screen the tab nav already labels each panel (print-only-heading),
     and the Time range select already shows its own value as a live
     control (print-only-value), so these would be redundant duplicates. */
  display: none;
}

.avatar-ring {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  flex-shrink: 0;
  padding: 3px;
  background: var(--brand-gradient);
}

.avatar {
  width: 100%;
  height: 100%;
  display: block;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-1);
  border: 2px solid var(--page-plane);
}

.masthead h1 {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.subtitle {
  margin: 2px 0 0;
  color: var(--text-secondary);
  font-size: 14px;
}

.btn-outline {
  color: var(--text-primary);
  text-decoration: none;
  font-size: 13px;
  font-weight: 600;
  padding: 8px 14px;
  border-radius: 8px;
  border: 1px solid var(--border);
  white-space: nowrap;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.btn-outline:hover { background: rgba(26, 18, 20, 0.05); border-color: rgba(26, 18, 20, 0.2); }

.btn-gradient {
  display: inline-block;
  color: #ffffff;
  text-decoration: none;
  font-size: 13px;
  font-weight: 700;
  padding: 9px 16px;
  border-radius: 8px;
  border: none;
  white-space: nowrap;
  background: var(--brand-gradient);
  background-size: 150% 150%;
  background-position: 0% 50%;
  transition: background-position 0.3s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

.btn-gradient:hover {
  background-position: 100% 50%;
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(201, 66, 119, 0.35);
}

/* The two timestamp lines (#asOf, #pageUpdatedAt) are a paired, related
   fact - "data pulled at X, page deployed at Y" - so they sit close to
   each other, with the larger gap reserved for after the pair, before the
   unrelated explainer card below. Equal margins on both (as if they were
   three independent blocks) would visually flatten that relationship.
   #pageUpdatedAt starts [hidden] and only appears once its API lookup
   succeeds (see dashboard.js), so #asOf defaults to carrying the full gap
   itself and only tightens up when the second line will actually render -
   otherwise a failed lookup would leave #asOf's tight 2px margin with
   nothing after it to provide the space before the explainer card. */
.asof {
  margin: 16px 0 22px;
  color: var(--text-muted);
  font-size: 12px;
}

#asOf:has(+ #pageUpdatedAt:not([hidden])) {
  margin-bottom: 2px;
}

#pageUpdatedAt {
  margin-bottom: 22px;
}

/* ---------- Explainer ---------- */

.explainer {
  position: relative;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px 20px 16px 23px;
  margin-bottom: 24px;
  overflow: hidden;
  box-shadow: var(--shadow-resting);
}

.explainer::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--brand-gradient);
}

.explainer h2 {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}

.explainer p {
  margin: 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-secondary);
  max-width: 68ch;
}

.explainer strong {
  color: var(--text-primary);
  font-weight: 600;
}

/* ---------- Filters ---------- */

/* Sits directly above the block of content it actually scopes (the
   filtered KPI row + both trend charts), rather than floating above
   everything on the page - the point-in-time KPI tiles above it never
   change with this control, so it shouldn't look like it governs them. */
.section-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin: 4px 0 16px;
}

.section-heading h2 {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
}

.posts-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
}

.filter-field {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-secondary);
  font-weight: 600;
}

.filter-field select {
  appearance: none;
  background: var(--surface-1);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 7px 30px 7px 12px;
  font-size: 13px;
  font-family: inherit;
  font-weight: 600;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%235c4750' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}

.filter-field select:hover { border-color: var(--series-1); }
.filter-field select:focus-visible { outline: 2px solid var(--series-1); outline-offset: 1px; }

/* ---------- KPI row ---------- */

.kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
  margin-bottom: 32px;
}

.stat-tile {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px 18px;
  box-shadow: var(--shadow-resting);
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.stat-tile:hover {
  border-color: rgba(26, 18, 20, 0.16);
  box-shadow: var(--shadow-hover);
  transform: translateY(-2px);
}

.stat-label {
  margin: 0 0 8px;
  font-size: 12px;
  color: var(--text-secondary);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.stat-value {
  font-size: 28px;
  font-weight: 600;
  line-height: 1.1;
  font-variant-numeric: proportional-nums;
}

.stat-delta {
  margin-top: 6px;
  font-size: 12px;
  font-weight: 600;
}

.stat-delta.up { color: var(--good); }
.stat-delta.down { color: var(--bad); }
.stat-delta.flat { color: var(--text-muted); }

/* ---------- Cards & charts ---------- */

.chart-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  gap: 16px;
  margin-bottom: 24px;
}

/* A lone .card used as its own section (the Audience tab's age/gender
   chart, the Collabs tab's rate card) has no bottom margin of its own,
   and neither did .chart-grid have a top margin - so a chart-grid
   immediately following one of these sat flush against it with a literal
   0px gap ("Top countries"/"Top cities" and "What's included"/"Good to
   know" reading as touching the box above). Scoped to this exact
   adjacency (not a blanket .chart-grid margin-top) so it doesn't also
   add unwanted extra space after a .kpi-row, which already supplies its
   own gap before the chart-grid on the Overview tab. */
.card + .chart-grid {
  margin-top: 24px;
}

.card {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 20px;
  box-shadow: var(--shadow-resting);
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.card:hover {
  border-color: rgba(26, 18, 20, 0.16);
  box-shadow: var(--shadow-hover);
  transform: translateY(-2px);
}

.card-head h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
}

.card-sub {
  margin: 2px 0 16px;
  font-size: 12px;
  color: var(--text-muted);
}

.chart-wrap {
  position: relative;
  width: 100%;
  /* The SVG scales responsively via viewBox rather than overflowing its
     container, so there's no wide content that needs horizontal scroll -
     overflow must stay visible or the hover tooltip clips at the edges. */
  overflow: visible;
}

.chart-wrap svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}

.chart-empty {
  color: var(--text-muted);
  font-size: 13px;
  margin: 24px 0;
}

.chart-tooltip {
  position: absolute;
  pointer-events: none;
  background: #ffffff;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 12px;
  line-height: 1.5;
  white-space: nowrap;
  box-shadow: 0 4px 12px rgba(26, 18, 20, 0.15);
  transform: translate(-50%, -110%);
  opacity: 0;
  transition: opacity 0.1s ease;
  z-index: 5;
}

.chart-tooltip.visible { opacity: 1; }

.chart-tooltip .tt-value {
  color: var(--text-primary);
  font-weight: 700;
}

.chart-tooltip .tt-label {
  color: var(--text-secondary);
}

/* ---------- Posts ---------- */

.posts-card { margin-bottom: 8px; }

.posts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 12px;
}

.post-card {
  display: block;
  color: inherit;
  text-decoration: none;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px;
  box-shadow: var(--shadow-resting);
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.post-card:hover {
  border-color: rgba(214, 41, 118, 0.5);
  box-shadow: var(--shadow-hover);
  transform: translateY(-2px);
}

.post-type {
  /* A solid badge rather than gradient-clipped text - at 10px, a
     transparent-fill gradient anti-aliases a little soft and reads more
     "influencer aesthetic" than "business document." A filled pill is the
     standard professional-dashboard affordance for a repeated metadata
     tag; the gradient stays reserved for hero content (.rate-price). */
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #ffffff;
  background: linear-gradient(90deg, #ec6a9c, #a83368);
  padding: 3px 8px;
  border-radius: 999px;
  margin-bottom: 8px;
}

.post-caption {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0 0 12px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.post-stats {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--text-muted);
  border-top: 1px solid var(--border);
  padding-top: 10px;
}

.post-stats strong {
  color: var(--text-primary);
  font-weight: 600;
}

.post-engagement {
  color: var(--good);
  font-weight: 700;
}

/* ---------- Tabs ---------- */

.tab-nav {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 24px;
}

.tab-btn {
  appearance: none;
  background: var(--surface-1);
  border: 2px solid var(--border);
  border-radius: 12px;
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  padding: 12px 22px;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}

.tab-btn:hover {
  color: var(--text-primary);
  border-color: #c94277;
  transform: translateY(-1px);
}

.tab-btn[aria-selected="true"] {
  color: #ffffff;
  border-color: transparent;
  background: var(--brand-gradient);
  box-shadow: 0 3px 10px rgba(201, 66, 119, 0.3);
}

/* Fade the panel in on activation - setupTabs() toggles this class
   alongside the hidden attribute. */
.tab-panel-enter {
  animation: tabFadeIn 0.2s ease;
}

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

/* ---------- Legend ---------- */

.chart-legend {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 12px;
  font-size: 12px;
  color: var(--text-secondary);
}

.chart-legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
}

.chart-legend-swatch {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  flex-shrink: 0;
}

/* ---------- Rank list (top cities) ---------- */

.rank-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.rank-item {
  display: grid;
  grid-template-columns: 20px 1fr;
  align-items: center;
  gap: 10px;
  font-size: 13px;
}

.rank-item .rank-index {
  color: var(--text-muted);
  font-weight: 700;
  font-size: 12px;
}

.rank-item .rank-bar-track {
  position: relative;
  height: 6px;
  background: var(--gridline);
  border-radius: 3px;
  overflow: hidden;
}

.rank-item .rank-bar-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  background: var(--series-1);
  border-radius: 3px;
}

.rank-item .rank-label-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 4px;
}

.rank-item .rank-name {
  color: var(--text-primary);
}

.rank-item .rank-value {
  color: var(--text-secondary);
  font-weight: 600;
  white-space: nowrap;
}

.rank-item .rank-main {
  display: flex;
  flex-direction: column;
}

/* ---------- Collabs (rate card, contact) ---------- */

.rate-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px;
}

.rate-item {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
  box-shadow: var(--shadow-resting);
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.rate-item:hover {
  border-color: rgba(214, 41, 118, 0.4);
  box-shadow: var(--shadow-hover);
  transform: translateY(-2px);
}

.rate-label {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.rate-price {
  margin: 0 0 8px;
  font-size: 22px;
  font-weight: 800;
  /* A dedicated (not --brand-gradient) gradient here - the brand gradient's
     pale pink starting stop reads too low-contrast for actual pricing
     text against the surface-2 background, so this skips straight to
     saturated rose/plum tones instead. */
  background: linear-gradient(90deg, #ec6a9c, #a83368);
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.rate-desc {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-muted);
}

.plain-list {
  margin: 0;
  padding: 0 0 0 18px;
  font-size: 13px;
  line-height: 1.8;
  color: var(--text-secondary);
}

.plain-list li::marker { color: var(--series-1); }

.contact-row {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

.contact-note {
  margin: 0;
  font-size: 12px;
  color: var(--text-muted);
}

/* ---------- Footer ---------- */

.page-footer {
  margin-top: 40px;
  color: var(--text-muted);
  font-size: 12px;
  border-top: 1px solid var(--border);
  padding-top: 16px;
}

.page-footer a { color: var(--text-secondary); }

@media (max-width: 480px) {
  .stat-value { font-size: 22px; }
}

/* ---------- Print / PDF export ----------
   Triggered by the "Download PDF" button (window.print() in dashboard.js).
   The goal is one complete document covering all three tabs, not just
   whichever tab happened to be open on screen - so every panel's [hidden]
   is forced visible here regardless of the tab JS state, with a page break
   between each so they read as separate sections rather than one run-on
   page.

   This is a genuinely separate visual treatment from the on-screen theme,
   not the same page with a background swap: the saturated pink page-plane
   looks like the brand on a backlit screen, but as large flat printed
   fills (especially the empty leftover space below shorter sections) it
   reads as unfinished/unprofessional rather than on-brand. Print swaps the
   surface roles to white/near-white (keeping the pink only as small
   accents - the explainer's left bar, rate-price/post-type text, the
   print-only headings) and lands in landscape with substantially tighter
   type and spacing throughout, aiming to fit each section in as few pages
   as its actual content allows rather than the screen's touch-friendly
   sizing. */
@media print {
  * {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
    /* Screen-only depth cue - shadows print as muddy smudges rather than
       rendering as light/elevation, so the border alone carries definition
       between surfaces on paper. */
    box-shadow: none !important;
  }

  @page {
    size: A4 landscape;
    margin: 1cm;
  }

  :root {
    /* Neutral print surfaces - see the block comment above for why this
       isn't just the screen theme with color-adjust forced on. */
    --page-plane: #ffffff;
    --surface-1: #ffffff;
    --surface-2: #fdf1f5;
    --border: rgba(26, 18, 20, 0.16);
  }

  body {
    background-image: none;
  }

  .page {
    max-width: 100%;
    padding: 0;
    font-size: 11px;
  }

  /* Anything only meaningful as an on-screen interaction - the PDF trigger
     itself, the clickable tab nav (all panels print together, so tabs have
     nothing left to switch), a hover-only tooltip that has no business
     appearing on a static page, and the footer's GitHub source link (not
     useful to a brand recipient). */
  .no-print,
  .tab-nav,
  .chart-tooltip,
  .page-footer {
    display: none !important;
  }

  .print-only-value {
    display: block;
    font-size: 10px;
    font-weight: 600;
    color: var(--text-secondary);
  }

  /* Override the hidden attribute setupTabs() uses to show only the active
     tab on screen - print always shows all three. */
  #tabOverview,
  #tabAudience,
  #tabCollabs {
    display: block !important;
  }

  #tabAudience,
  #tabCollabs {
    break-before: page;
  }

  .print-only-heading {
    display: block;
    font-size: 15px;
    font-weight: 800;
    margin: 0 0 8px;
  }

  /* ---- Global compaction - every role below gets a much smaller,
     denser treatment than its touch-friendly screen sizing. ---- */

  .masthead h1 { font-size: 17px; }
  .subtitle { font-size: 11px; }
  .asof { margin: 6px 0 10px; font-size: 10px; }

  .explainer { padding: 8px 10px 8px 13px; margin-bottom: 10px; }
  .explainer h2 { font-size: 10px; margin-bottom: 3px; }
  .explainer p { font-size: 10px; line-height: 1.4; }

  .section-heading { margin: 4px 0 8px; }
  .section-heading h2 { font-size: 12px; }

  .kpi-row { gap: 6px; margin-bottom: 10px; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); }
  .stat-tile { padding: 7px 9px; border-radius: 6px; }
  .stat-label { font-size: 8px; margin-bottom: 3px; }
  .stat-value { font-size: 16px; }
  .stat-delta { font-size: 8px; margin-top: 2px; }

  .chart-grid { gap: 8px; margin-bottom: 8px; }
  .card { padding: 10px 12px; border-radius: 8px; }
  .card-head h2 { font-size: 11px; }
  .card-sub { font-size: 8px; margin: 1px 0 6px; }

  .posts-card { margin-bottom: 0; }
  .posts-grid { grid-template-columns: repeat(4, 1fr); gap: 6px; }
  .post-card { padding: 7px 9px; }
  .post-type { font-size: 8px; margin-bottom: 3px; }
  .post-caption {
    font-size: 9px;
    margin-bottom: 6px;
    -webkit-line-clamp: 1;
  }
  .post-stats { font-size: 8px; padding-top: 5px; }

  .rank-list { gap: 4px; }
  .rank-item { font-size: 9px; gap: 6px; }
  .rank-item .rank-index { font-size: 8px; }

  /* The age/gender chart is a lone full-width card on screen, which in
     print's landscape width renders needlessly tall (its aspect ratio is
     fixed via viewBox) and pushes the two rank-list cards below it to a
     second page. Capping its width - height follows automatically since
     the SVG scales by width:100%/height:auto - buys back most of a page. */
  #ageGenderChart svg {
    max-width: 480px;
    margin: 0 auto;
    display: block;
  }

  .rate-grid { gap: 6px; }
  .rate-item { padding: 7px 9px; }
  .rate-label { font-size: 8px; margin-bottom: 3px; }
  .rate-price { font-size: 15px; margin-bottom: 4px; }
  .rate-desc { font-size: 8px; }

  .plain-list { font-size: 9px; line-height: 1.5; }
  .contact-row .btn-outline,
  .contact-row .btn-gradient { padding: 5px 10px; font-size: 9px; }
  .contact-note { font-size: 8px; }

  /* Keep each unit intact rather than splitting across a page boundary. */
  .card,
  .stat-tile,
  .post-card,
  .rate-item,
  .explainer {
    break-inside: avoid;
  }
}
