:root {
  color-scheme: dark;
  --page:        #131515;
  --surface:     rgba(26, 29, 29, 0.7);
  --surface-2:   rgba(35, 40, 39, 0.65);
  --ink:         #e6ece9;
  --ink-2:       #b4c0bc;
  --muted:       #7c8985;
  --gridline:    rgba(91, 154, 139, 0.08);
  --border:      rgba(91, 154, 139, 0.2);
  --border-soft: rgba(233, 236, 233, 0.08);
  --shadow:      0 0 0 1px rgba(0, 0, 0, 0.4) inset, 0 20px 50px -20px rgba(0, 0, 0, 0.7);

  --accent-hrv:      #4ea8ff;
  --accent-rhr:      #ff5f6d;
  --accent-sleep:    #a688ff;
  --accent-activity: #ff9d4d;
  --accent-primary:  #5b9a8b;
  --glow-soft:       rgba(91, 154, 139, 0.16);

  --font-display: "Fraunces", ui-serif, Georgia, serif;
  --font-mono:    "IBM Plex Mono", ui-monospace, monospace;
}

* { box-sizing: border-box; }

/* iOS Safari / the app webview auto-zooms the whole page in when you focus a text field whose
   font-size is under 16px, and never zooms back out. Keep the compact sizes on desktop but force
   every form control to 16px on phone-width screens so focusing an input doesn't hijack the view. */
@media (max-width: 640px) {
  input, select, textarea { font-size: 16px !important; }
}

body {
  margin: 0;
  font-family: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--page);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  position: relative;
}

/* Without this, the `hidden` attribute (used by setCardVisible in shared.js to hide stat cards
   with no data for the current account) silently does nothing on any element whose class also
   sets its own `display` — e.g. .stat-card's `display: flex` — since an author stylesheet rule
   always overrides the browser's own default [hidden] styling, regardless of selector specificity. */
[hidden] { display: none !important; }
/* header must outrank main — they tie at z-index and DOM order would otherwise let main's
   content (e.g. the day-nav row) paint over header dropdowns like the wearable menu, since
   z-index only resolves within a shared stacking context and each of these forms its own. */
header { position: relative; z-index: 2; }
main { position: relative; z-index: 1; }

header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.15rem 2.5rem;
  background: rgba(9, 13, 17, 0.75);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border-soft);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
  gap: 1.5rem;
  flex-wrap: wrap;
}

.header-left {
  display: flex;
  align-items: center;
  gap: 2rem;
  flex-wrap: wrap;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.main-nav { display: flex; gap: 1.5rem; }
.main-nav a {
  font-family: var(--font-display);
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
  padding: 0.3rem 0;
  border-bottom: 2px solid transparent;
}
.main-nav a:hover { color: var(--ink); }
.main-nav a.active {
  color: var(--accent-primary);
  border-bottom-color: var(--accent-primary);
}
/* nav icons are for the mobile bottom tab bar only; desktop keeps the text nav */
.nav-ico { display: none; width: 22px; height: 22px; }

/* ---- mobile app shell: bottom tab bar + safe areas ---- */
@media (max-width: 640px) {
  header {
    position: sticky;
    top: 0;
    padding: 0.6rem 0.9rem;
    padding-top: calc(0.6rem + env(safe-area-inset-top));
    gap: 0.5rem 0.75rem;
    flex-wrap: wrap;
    justify-content: flex-start;
    /* backdrop-filter would trap the position:fixed tab bar inside the header, so drop it here
       and lean on a near-opaque background instead. */
    background: rgba(10, 14, 18, 0.97);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* Two explicit rows: Body Journal + your name on row one, Devices/Settings/Sync on row two.
     header-left and connection-status are two separate boxes in the markup (one static, one
     JS-rendered), so `display: contents` dissolves both into plain wrappers — their children
     become direct children of `header` itself, letting all of them wrap together as one flat
     sequence instead of as two independently-wrapping blocks (which is what produced the
     3+ mismatched rows and trailing dead space in earlier passes).
     Selectors are prefixed with `header` (not just the bare class/id) purely for specificity:
     the unconditional #connection-status/.header-break rules further down this file have equal
     specificity to a bare selector here and come later in the file, so a bare selector would
     silently lose the cascade on mobile even though it's the more specific *intent*. */
  header .header-left, header #connection-status { display: contents; }
  /* Pushes the name (and everything after it, up to the forced break below) to the right on its
     line, so it lands opposite Body Journal — flex-start alone would just pack them together on
     the left. */
  .account-name { margin-left: auto; }
  /* Forces devices/settings/sync onto their own line regardless of how much room row one has left,
     the standard flex line-break trick — zero height so it doesn't add any visible gap. */
  header .header-break { display: block; flex-basis: 100%; height: 0; }

  /* wide content scrolls inside its own box instead of pushing the page sideways */
  table { display: block; overflow-x: auto; white-space: nowrap; -webkit-overflow-scrolling: touch; }
  .chart, .chart svg { max-width: 100%; }

  .main-nav {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 50;
    display: flex;
    gap: 0;
    background: rgba(9, 13, 17, 0.94);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border-top: 1px solid var(--border-soft);
    padding: 6px 0 calc(6px + env(safe-area-inset-bottom));
  }
  .main-nav > a {
    width: 20%;
    flex: 0 0 20%;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 4px 0;
    font-family: var(--font-display);
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    text-transform: none;
    color: var(--muted);
    border-bottom: none;
    white-space: nowrap;
    overflow: hidden;
  }
  .main-nav a.active { color: var(--accent-primary); border-bottom: none; }
  .main-nav .nav-ico { display: block; flex: none; }
  .main-nav .nav-label { display: none; }
  .main-nav a::after { content: attr(data-short); }

  main { padding: 1.25rem 1rem calc(72px + env(safe-area-inset-bottom)); }
}

/* ---- native app: remove browser-isms ---- */
html.is-native, html.is-native body { overscroll-behavior: none; }
html.is-native body { -webkit-tap-highlight-color: transparent; }
html.is-native header,
html.is-native .main-nav { user-select: none; -webkit-user-select: none; }
html.is-native ::-webkit-scrollbar { width: 0; height: 0; }

.brand-mark {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent-hrv), var(--accent-activity));
  box-shadow: 0 0 8px 1px rgba(91, 154, 139, 0.28), 0 0 0 3px rgba(91, 154, 139, 0.08);
}

#connection-status {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  flex-wrap: wrap;
}

/* Only meaningful on mobile, where it's turned into a forced flex line-break — see the media
   query below. On desktop (header-left/connection-status stay real boxes there) it must stay
   fully out of the layout, not just invisible, or it'd eat a slot in the flex row. */
.header-break {
  display: none;
}

#connection-status a,
#connection-status button {
  font-family: var(--font-display);
  letter-spacing: 0.01em;
}
#connection-status a,
#connection-status button#sync-btn {
  background: linear-gradient(160deg, #8fc9bb, var(--accent-primary) 60%);
  color: #0d1a17;
  border: none;
  padding: 0.55rem 1.15rem;
  border-radius: 6px;
  font-weight: 600;
  font-size: 0.92rem;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 0 14px -6px rgba(91, 154, 139, 0.55);
}
#connection-status a:hover,
#connection-status button#sync-btn:hover {
  transform: translateY(-1px);
  filter: brightness(1.05);
  box-shadow: 0 4px 14px -4px rgba(91, 154, 139, 0.45);
}
#connection-status button:disabled {
  opacity: 0.6;
  transform: none;
  cursor: default;
}
#connection-status a.reconnect-link,
#connection-status button.reconnect-link {
  background: none;
  color: var(--muted);
  box-shadow: none;
  padding: 0;
  font-weight: 500;
}
#connection-status a.reconnect-link:hover,
#connection-status button.reconnect-link:hover {
  color: var(--ink-2);
  transform: none;
  box-shadow: none;
}
#connection-status a:focus-visible,
#connection-status button:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 2px;
}

.wearable-menu {
  position: relative;
}
#connection-status button.wearable-menu-btn {
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--border);
  color: var(--ink-2);
  padding: 0.45rem 0.85rem;
  border-radius: 6px;
  font-weight: 500;
  font-size: 0.85rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  box-shadow: none;
}
#connection-status button.wearable-menu-btn:hover {
  color: var(--ink);
  border-color: var(--accent-primary);
  background: rgba(91, 154, 139, 0.06);
  transform: none;
  box-shadow: none;
}
.wearable-menu-btn .caret {
  font-size: 0.7rem;
  opacity: 0.7;
}
.wearable-dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 220px;
  background: var(--surface);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow);
  padding: 0.4rem;
  z-index: 20;
}
/* Settings is the last item in the header row, so the default right-alignment above is correct
   for it. Devices sits earlier in the row (and can end up alone on a wrapped mobile line further
   left than its final desktop position) — right-aligning its dropdown like the default would risk
   pushing part of it off the left edge of narrow screens, the same bug this class of dropdown had
   before. Anchor it to its own left edge instead. */
#devices-dropdown {
  right: auto;
  left: 0;
}
.wearable-dropdown-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.55rem 0.6rem;
  border-radius: 6px;
  font-size: 0.88rem;
}
.wearable-dropdown-row:hover {
  background: var(--surface-2);
}
.wearable-dropdown-row .wearable-name {
  color: var(--ink);
  font-weight: 500;
}
.wearable-dropdown-row .wearable-connected {
  color: var(--accent-primary);
  font-size: 0.78rem;
  font-weight: 600;
}
#connection-status .wearable-dropdown-row a.wearable-connect-link,
#connection-status .wearable-dropdown-row button.wearable-connect-link {
  background: none;
  border: none;
  color: var(--accent-primary);
  padding: 0;
  border-radius: 0;
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: normal;
  text-decoration: none;
  box-shadow: none;
  cursor: pointer;
}
#connection-status .wearable-dropdown-row a.wearable-connect-link:hover,
#connection-status .wearable-dropdown-row button.wearable-connect-link:hover {
  text-decoration: underline;
  transform: none;
  box-shadow: none;
}
#connection-status .wearable-dropdown-row button.wearable-connect-link:disabled {
  opacity: 0.6;
  cursor: default;
  text-decoration: none;
}
.wearable-dropdown-row--action { cursor: pointer; }

/* ---- Account modal ---- */
.detail-close {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.2rem;
}
.detail-close:hover { color: var(--ink); }

.account-panel { max-width: 480px; }
.account-email {
  color: var(--muted);
  font-size: 0.9rem;
  margin: 0 0 1.4rem;
}
.account-danger-zone {
  border: 1px solid rgba(255, 95, 109, 0.3);
  border-radius: 10px;
  padding: 1rem 1.1rem;
}
.account-danger-title {
  color: var(--accent-rhr);
  font-weight: 600;
  font-size: 0.92rem;
  margin-bottom: 0.5rem;
}
.account-danger-copy {
  color: var(--muted);
  font-size: 0.84rem;
  line-height: 1.5;
  margin: 0 0 1rem;
}
.account-delete-btn {
  background: var(--accent-rhr);
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: 0.55rem 1rem;
  font-weight: 600;
  font-size: 0.85rem;
  cursor: pointer;
  font-family: inherit;
}
.account-delete-btn:hover { filter: brightness(1.08); }
.account-delete-btn:disabled { opacity: 0.5; cursor: default; }
.account-cancel-btn {
  background: none;
  border: 1px solid var(--border);
  color: var(--ink-2);
  border-radius: 8px;
  padding: 0.55rem 1rem;
  font-weight: 500;
  font-size: 0.85rem;
  cursor: pointer;
  font-family: inherit;
}
.account-confirm-label {
  display: block;
  font-size: 0.8rem;
  color: var(--muted);
  margin-bottom: 0.4rem;
}
.account-confirm-row {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.account-confirm-row input {
  flex: 1;
  min-width: 120px;
  background: var(--page);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.5rem 0.7rem;
  color: var(--ink);
  font-family: inherit;
  font-size: 0.88rem;
}

.account-name {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.005em;
  color: var(--ink-2);
}
.account-avatar {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  border: 1px solid var(--border);
  object-fit: cover;
}

.login-gate {
  max-width: 420px;
  margin: 4rem auto;
  text-align: center;
  padding: 2.5rem 2rem;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: 16px;
  box-shadow: var(--shadow);
}
.login-gate-mark {
  width: 28px;
  height: 28px;
  margin: 0 auto 1.25rem;
  box-shadow: 0 0 0 8px rgba(91, 154, 139, 0.1);
}
.login-gate h1 { font-size: 1.2rem; margin: 0 0 0.6rem; }
.login-gate p { color: var(--muted); font-size: 0.9rem; line-height: 1.5; margin: 0 0 1.5rem; }
.login-gate-btn {
  display: inline-block;
  background: var(--accent-primary);
  color: var(--page);
  text-decoration: none;
  font-weight: 600;
  font-size: 0.92rem;
  padding: 0.65rem 1.4rem;
  border-radius: 8px;
  box-shadow: 0 1px 2px rgba(91, 154, 139, 0.25);
}
.login-gate-btn:hover { box-shadow: 0 4px 10px rgba(91, 154, 139, 0.3); }
.login-gate-options {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  align-items: center;
}
.login-gate-btn-secondary {
  background: none;
  color: var(--ink-2);
  border: 1px solid var(--border);
  box-shadow: none;
}
.login-gate-btn-secondary:hover {
  color: var(--ink);
  border-color: var(--accent-primary);
  box-shadow: none;
}

.data-disclosure {
  max-width: 460px;
  margin: 1.5rem auto 4rem;
  padding: 1.5rem 1.7rem;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  text-align: left;
}
.data-disclosure-title {
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.8rem;
}
.data-disclosure p {
  color: var(--ink-2);
  font-size: 0.85rem;
  line-height: 1.55;
  margin: 0 0 0.9rem;
}
.data-disclosure p:last-child { margin-bottom: 0; }
.data-disclosure a { color: var(--accent-primary); }
.data-disclosure-modal { padding-top: 0.2rem; }

main {
  max-width: 1280px;
  margin: 0 auto;
  padding: 2rem 2.5rem 3rem;
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.1rem;
  margin-bottom: 1.1rem;
}

/* ---- Overview instrument cluster: Recovery anchors the group as a larger reading,
   the rest sit as quieter secondary tiles beside it. Field notes moved above this cluster
   (see index.html) and this whole group was made deliberately compact -- smaller padding,
   charts, and type than the same classes' defaults elsewhere -- to keep the page from running
   tall, all scoped under .cluster so Sleep/Activity/Nutrition's own use of .card/.chart/
   .stat-value elsewhere is untouched. ---- */
.cluster {
  display: grid;
  grid-template-columns: minmax(220px, 1.1fr) 2fr;
  gap: 0.75rem;
  margin-bottom: 0.9rem;
}
.cluster .card { padding: 0.9rem 1.1rem; }
.cluster-secondary {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.75rem;
}
.cluster-hero { justify-content: center; }
.cluster-hero .stat-label {
  font-size: 0.78rem;
  letter-spacing: 0.06em;
}
.cluster-hero .stat-value {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 2.3rem;
  letter-spacing: -0.01em;
  margin-bottom: 0.3rem;
}
.cluster-hero .chart svg { height: 56px; }
.cluster-secondary .stat-value { font-size: 1.4rem; margin-bottom: 0.3rem; }
.cluster-secondary .chart, .cluster-secondary .chart svg { min-height: 40px; height: 40px; }

@media (max-width: 760px) {
  .cluster { grid-template-columns: 1fr; }
}
/* At phone width, drop the "Recovery is the big hero, everything else is secondary" layout
   entirely and lay out all five cards as one flat 2-column grid instead -- same "N cards, 2 per
   row" treatment every other page's grid already got, rather than Overview being the one page
   that still stacks every metric in its own full-width row. display:contents dissolves
   .cluster-secondary's own wrapper so its four cards become direct grid items of .cluster
   alongside the hero card (the same technique used to rebuild the mobile header earlier).
   Placed after every rule above it touches, not the early media block, to actually win the
   cascade -- learned that the hard way on the header. */
@media (max-width: 640px) {
  .cluster { grid-template-columns: repeat(2, 1fr); gap: 0.6rem; align-items: stretch; }
  .cluster-secondary { display: contents; }
  .cluster-hero { justify-content: normal; }
  .cluster-hero .stat-label { font-size: 0.72rem; letter-spacing: normal; }
  /* Exactly matches .cluster-secondary's own stat-value size/margin below (1.4rem/0.3rem) --
     hero was left at slightly different values (1.35rem/0.4rem) that didn't quite match, so the
     five cards weren't actually uniform despite looking close. */
  .cluster-hero .stat-value { font-size: 1.4rem; margin-bottom: 0.3rem; }
  .cluster-hero .chart svg { height: 40px; }
}

.card {
  background: var(--surface);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-radius: 8px;
  padding: 1.4rem 1.5rem;
  border: 1px solid var(--border-soft);
  box-shadow: var(--shadow);
}
/* .card has no margin of its own, so two stacked directly as siblings (like Calorie Tracker's
   "Your goal" and "Today's macros") touch edge-to-edge with nothing but that 8%-opacity border
   between them -- barely visible as two separate cards. */
.card + .card { margin-top: 1.1rem; }
/* The rule above is an ADJACENT-SIBLING selector, which matches on DOM adjacency regardless of
   how the parent lays things out -- so inside every multi-column metric grid (2 cards per row),
   every card past the first in DOM order is *also* a ".card" preceded by a ".card" and silently
   picks up that same 17.6px top margin. Combined with the grid's own row-stretch, this margin eats
   into that card's stretched box from the top, making every second-in-a-row card visibly shorter
   than its row-mate (root cause of the uneven metric-card-height bug across Sleep/Activity/Body/
   Nutrition). The grid's own `gap` already spaces cards apart in both directions, so this margin
   is redundant there anyway -- reset it to 0 in every such grid. */
.cluster .card,
.cluster-secondary .card,
.grid .card,
#activity-grid .card,
#body-grid .card,
#withings-grid .card {
  margin-top: 0;
}

.card-title {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--ink);
  margin-bottom: 1rem;
}

.stat-card {
  position: relative;
  display: flex;
  flex-direction: column;
  cursor: pointer;
  text-decoration: none;
  color: inherit;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.stat-card:hover, .stat-card:focus-visible {
  transform: translateY(-2px);
  border-color: var(--border);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4) inset, 0 16px 34px -18px rgba(0, 0, 0, 0.7);
  outline: none;
}

/* Overview page only — per-metric tinted stat numbers, matching each card's own accent color. */
#recovery-value { color: var(--accent-hrv); }
#sleepScore-value { color: var(--accent-sleep); }
#caloriesEaten-value { color: #ffc850; }
#caloriesBurned-value { color: var(--accent-activity); }

.stat-label {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 0.5rem;
}

.stat-value {
  font-family: var(--font-mono);
  font-size: 1.9rem;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin-bottom: 0.85rem;
  font-variant-numeric: tabular-nums;
}
.stat-value .unit {
  font-size: 1rem;
  font-weight: 500;
  color: var(--muted);
  margin-left: 0.3rem;
}

.stat-trend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.15rem 0.3rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--muted);
  margin-top: -0.55rem;
  margin-bottom: 0.85rem;
  min-height: 1.1rem;
}
.stat-trend.up-good { color: #006300; }
.stat-trend.down-good { color: var(--accent-rhr); }
.stat-trend .trend-compare { font-weight: 500; color: var(--muted); }
/* Both pieces wrap as whole units (moving down to their own line together) instead of the
   container's flex layout squeezing each into its own narrow column and letting the words
   *inside* wrap independently -- that's what produced text broken mid-phrase ("vs" / "yesterday"
   / "as" / "of" each on their own line) in narrow 2-column cards. */
.stat-trend .trend-delta, .stat-trend .trend-compare { white-space: nowrap; }

.stat-macros {
  display: flex;
  gap: 0.6rem;
  font-size: 0.78rem;
  color: var(--ink-2);
  margin-bottom: 0.85rem;
  font-variant-numeric: tabular-nums;
}
.stat-macros span { color: var(--muted); font-weight: 500; }

.chart { min-height: 70px; margin-top: auto; }
.chart svg { width: 100%; height: 70px; display: block; overflow: visible; }
.chart .empty { color: var(--muted); font-size: 0.85rem; padding-top: 0.5rem; }
.chart .date-range { font-size: 0.75rem; color: var(--muted); margin-top: 0.5rem; }

/* .grid's metric cards, two per row on mobile instead of the one-per-row that its auto-fit
   (minmax(260px,...)) collapses to at phone widths. Originally Sleep & Recovery only; Nutrition's
   Today's macros now reuses this same class deliberately, for the exact same 2-column layout and
   compact sizing rather than its own bespoke version of it. This whole block is placed after every
   base rule it touches (.grid/.card/.stat-value/.chart above) so it doesn't silently lose the
   cascade to them the way an earlier pass on the header did. Slightly smaller padding/type/chart
   height too, so two columns don't feel cramped. */
@media (max-width: 640px) {
  .grid { grid-template-columns: repeat(2, 1fr); gap: 0.6rem; align-items: stretch; }
  /* Sleep's own cards are <a class="card stat-card">, and .stat-card already makes them a flex
     column (chart pinned to the bottom regardless of how many lines the trend text above it
     wraps to). Nutrition's macro cards reuse .grid but are plain <div class="card"> -- no
     .stat-card, since they're not links -- so they never got that, and a card whose trend wrapped
     to 2 lines (e.g. Calories: "1,039 / 3,062 2,023 left") ended up taller than a row-mate whose
     trend fit on one line (Protein). display:flex/column + margin-top:auto here reproduces
     .stat-card's layout behavior for every .grid card, .stat-card or not, so this can't be missed
     again the way it was for Activity/Body's grids (fixed the same way, separately, right before
     this bug surfaced here too). */
  .grid .card { padding: 0.9rem 1rem; display: flex; flex-direction: column; }
  .grid .stat-label { font-size: 0.72rem; }
  .grid .stat-value { font-size: 1.35rem; margin-bottom: 0.4rem; }
  .grid .chart, .grid .chart svg { min-height: 44px; height: 44px; }
  .grid .chart { margin-top: auto; }
}

form { display: flex; flex-wrap: wrap; gap: 0.9rem; align-items: end; margin-bottom: 1.3rem; }
form label {
  display: flex;
  flex-direction: column;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  gap: 0.35rem;
}
form input {
  background: var(--page);
  border: 1px solid var(--border);
  color: var(--ink);
  padding: 0.5rem 0.65rem;
  border-radius: 8px;
  font-size: 0.92rem;
  font-family: inherit;
  min-width: 8rem;
}
form input:focus {
  outline: none;
  border-color: var(--accent-primary);
  box-shadow: 0 0 0 3px rgba(91, 154, 139, 0.15);
}
form button {
  background: var(--accent-primary);
  color: var(--page);
  border: none;
  padding: 0.55rem 1.1rem;
  border-radius: 8px;
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
  box-shadow: 0 1px 2px rgba(91, 154, 139, 0.25);
}
form button:hover { transform: translateY(-1px); box-shadow: 0 4px 10px rgba(91, 154, 139, 0.3); }

#activities-table .empty { color: var(--muted); text-align: center; padding: 1.25rem 0; }

table { width: 100%; border-collapse: collapse; font-size: 0.9rem; font-variant-numeric: tabular-nums; }
th, td { text-align: left; padding: 0.55rem 0.4rem; border-bottom: 1px solid var(--gridline); }
th { color: var(--muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; }
tbody tr:hover { background: var(--page); }
td button {
  background: none;
  border: none;
  color: var(--accent-rhr);
  cursor: pointer;
  font-size: 0.95rem;
  opacity: 0.6;
}
td button:hover { opacity: 1; }

.chat-card {
  margin-bottom: 1.1rem;
  padding: 0.8rem 1.1rem 0.7rem;
}
/* Bleeds past main's own 1rem side padding (see the mobile `main` rule above) so this card
   actually reaches the screen edges and reads as wider than the stat cards below it, which stay
   inset within that padding like everything else on the page -- same negative-margin lever
   .goal-rationale uses elsewhere in this file, just against the page's padding instead of the
   card's own. Flat corners/no side borders on mobile only, so it looks like a deliberate
   full-bleed banner rather than a rounded card clipped by the viewport. */
@media (max-width: 640px) {
  .chat-card {
    margin-left: -1rem;
    margin-right: -1rem;
    border-left: none;
    border-right: none;
    border-radius: 0;
  }
}
.chat-card .card-title {
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
  margin-bottom: 0.35rem;
}
.chat-icon {
  display: inline-block;
  vertical-align: middle;
  margin-right: 0.1rem;
  margin-top: -2px;
}

.chat-messages {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  max-height: 115px;
  overflow-y: auto;
  margin-bottom: 0.5rem;
}
.chat-messages:empty { display: none; }

.chat-bubble {
  max-width: 100%;
  padding: 0.15rem 0;
  font-family: var(--font-display);
  font-size: 0.88rem;
  line-height: 1.42;
  white-space: pre-wrap;
  color: var(--ink-2);
}
.chat-bubble.assistant {
  align-self: flex-start;
  border-left: 2px solid var(--border);
  padding-left: 0.7rem;
}
.chat-bubble.user {
  align-self: flex-end;
  color: var(--ink);
  font-family: var(--sans, "IBM Plex Sans", sans-serif);
  font-size: 0.9rem;
  text-align: right;
}
.chat-bubble.pending { color: var(--muted); font-style: italic; }
.chat-bubble .stat-n {
  font-family: var(--font-mono);
  color: var(--accent-primary);
  font-weight: 600;
}

.chat-form { display: flex; align-items: center; gap: 0.7rem; margin-bottom: 0; border-top: 1px solid var(--border-soft); padding-top: 0.4rem; }
.chat-form input {
  flex: 1;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  color: var(--ink);
  padding: 0.15rem 0 0.25rem;
  border-radius: 0;
  font-size: 0.9rem;
  font-family: inherit;
}
.chat-form input:focus {
  outline: none;
  border-color: var(--accent-primary);
  box-shadow: none;
}
.chat-form button {
  background: none;
  color: var(--accent-primary);
  border: none;
  padding: 0.15rem 0;
  font-weight: 600;
  font-size: 0.88rem;
  cursor: pointer;
}
.chat-form button:hover:not(:disabled) { color: var(--ink); }
.chat-form button:disabled { opacity: 0.6; cursor: default; }

.photo-upload { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.9rem; }
.photo-upload-btn {
  display: inline-block;
  background: var(--page);
  border: 1px solid var(--border);
  color: var(--ink);
  padding: 0.5rem 0.9rem;
  border-radius: 8px;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}
.photo-upload-btn:hover { border-color: var(--accent-primary); }
/* photo-upload-btn is normally a <label> (nutrition.html); reused here as an <a>, which needs
   the link defaults (underline, visited-color) reset that a label never had to worry about. */
.withings-connect-btn { display: inline-block; text-decoration: none; margin-bottom: 1.1rem; }
.withings-connect-btn:visited { color: var(--ink); }
.photo-status { font-size: 0.82rem; color: var(--muted); }

/* Today's macros used to be its own bespoke flex layout (.macro-summary/-item) nested inside one
   outer card -- however much spacing or divider tweaking that got, it still didn't read as
   "separate metrics" the way Activity's grid of individual bordered cards does, since it was
   fundamentally a different structure (one card containing four plain divs, not four actual
   cards). Rebuilt in nutrition.html to reuse .grid + .card directly instead -- the exact same
   structure and classes Sleep & Recovery uses, automatically inheriting identical borders,
   backgrounds, spacing, and the 2-column mobile layout with zero bespoke CSS of its own. */
.trend-over { color: var(--accent-rhr); }

/* ---- Cut/bulk goal ---- */
form select {
  background: var(--page);
  border: 1px solid var(--border);
  color: var(--ink);
  padding: 0.5rem 0.65rem;
  border-radius: 8px;
  font-size: 0.92rem;
  font-family: inherit;
}
form select:focus { outline: none; border-color: var(--accent-primary); box-shadow: 0 0 0 3px rgba(91, 154, 139, 0.15); }

.goal-loading { color: var(--muted); font-size: 0.9rem; margin: 0; }
.goal-form { display: flex; flex-wrap: wrap; gap: 1rem 1.25rem; align-items: flex-end; }
.goal-form label { flex: 0 1 auto; }
.goal-form select, .goal-form input { min-width: 7rem; }
.goal-height-fields { display: inline-flex; gap: 0.5rem; }
.goal-height-fields input { min-width: 0; width: 4rem; }
.goal-form-actions { display: flex; align-items: center; gap: 1rem; flex-basis: 100%; }
.goal-hint { flex-basis: 100%; margin: 0; font-size: 0.8rem; color: var(--muted); line-height: 1.45; }

/* Tighter than the default .card padding -- this card's own content (targets/delta/maintenance
   line/rationale) already carries its own spacing, so the generous 1.4rem/1.5rem card padding on
   top of that was doubling up and leaving too much dead space around a genuinely compact set of
   numbers. .goal-rationale's own negative margin below is matched to this padding, not .card's. */
.goal-card { padding: 1rem 1.1rem; }
.goal-card .card-title { margin-bottom: 0.5rem; }

.goal-summary-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.goal-name { font-family: var(--font-display); font-size: 1.1rem; font-weight: 600; }
.goal-targets { display: flex; gap: 1.75rem; margin: 0.7rem 0 0.6rem; flex-wrap: wrap; }
.goal-target { display: flex; flex-direction: column; gap: 0.2rem; }
.goal-target-value { font-family: var(--font-display); font-size: 1.35rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.goal-target-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
/* Plain flex-wrap left the 4th target (Fat) stranding alone on its own row once 3 didn't fit,
   with a large empty gap beside it rather than filling the row -- an even 2-column grid instead
   means every row is actually full, no matter which target ends up last. */
@media (max-width: 640px) {
  .goal-targets { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.6rem 1.25rem; }
}
.goal-delta { margin: 0 0 0.25rem; font-size: 0.92rem; color: var(--ink-2); }
.goal-delta strong { color: var(--ink); }
.goal-maintenance { margin: 0 0 0.6rem; font-size: 0.82rem; color: var(--muted); }
/* An actual bounded box (background + padding on all sides) instead of a plain left-border
   indent -- tighter line-height, a smaller font, AND a hard max-height with scroll, so this is
   short and wide no matter how long the underlying AI text is (an already-saved goal's rationale
   was written under the old, longer prompt and won't reflect the new shorter one until it's
   rebuilt -- this caps the box regardless, rather than waiting on that).
   Negative margins matching .card's own padding bleed it out to the card's real edges instead of
   sitting inset within the card's content padding like every other element in it -- that's the
   actual lever for "wider," since this box was already the full width .card's padding allows.
   Teal accent border + tinted background borrows the app's one signature accent color
   (--accent-primary, the same teal used on buttons/active nav/etc.) instead of a neutral gray box. */
.goal-rationale {
  display: block;
  margin: 0.5rem -1.1rem 0;
  font-size: 0.78rem;
  line-height: 1.3;
  color: var(--ink-2);
  background: var(--glow-soft);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding: 0.6rem 1.1rem;
  max-height: 4.2rem;
  overflow-y: auto;
}

/* ---- Overview: energy balance card ---- */
.energy-balance-card { display: block; text-decoration: none; color: inherit; }
.energy-balance-main { display: flex; align-items: baseline; gap: 0.7rem; margin-top: 0.6rem; flex-wrap: wrap; }
.energy-balance-value { font-family: var(--font-display); font-size: 2.1rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.energy-balance-value.is-deficit { color: var(--accent-hrv); }
.energy-balance-value.is-surplus { color: var(--accent-activity); }
.energy-balance-caption { font-size: 0.85rem; color: var(--muted); }
.energy-balance-breakdown { margin-top: 0.7rem; font-size: 0.85rem; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.energy-balance-target { color: var(--muted); }

.food-text-entry { margin-bottom: 0.9rem; }
.food-text-row { display: flex; gap: 0.5rem; margin-top: 0.35rem; }
.food-text-row input {
  flex: 1;
  background: var(--page);
  border: 1px solid var(--border);
  color: var(--ink);
  padding: 0.5rem 0.7rem;
  border-radius: 8px;
  font-size: 0.9rem;
  font-family: inherit;
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
}
.food-text-row input:focus {
  outline: none;
  border-color: var(--accent-primary);
  box-shadow: 0 0 0 3px rgba(91, 154, 139, 0.15);
}
.food-text-row button {
  background: var(--accent-primary);
  color: var(--page);
  border: none;
  padding: 0.5rem 0.9rem;
  border-radius: 8px;
  font-weight: 600;
  font-size: 0.85rem;
  cursor: pointer;
  white-space: nowrap;
}
.food-text-row button:disabled { opacity: 0.6; cursor: default; }
/* The button's white-space:nowrap means its text never shrinks, so at phone width it was just
   overflowing past the screen edge instead of fitting next to the input. Stack them instead. */
@media (max-width: 640px) {
  .food-text-row { flex-wrap: wrap; }
  .food-text-row input, .food-text-row button { flex: 1 1 100%; }
}

.photo-estimate {
  background: var(--page);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  padding: 0.75rem 1rem;
  margin-bottom: 0.9rem;
  font-size: 0.88rem;
}
.photo-estimate strong { color: var(--ink); }
.photo-estimate-note { margin: 0.3rem 0 0; color: var(--muted); font-size: 0.8rem; }
.photo-estimate-note--intro { margin: -0.5rem 0 1rem; }
.confidence-badge {
  margin-left: 0.5rem;
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.1rem 0.5rem;
}

.detail-overlay {
  position: fixed;
  inset: 0;
  background: rgba(11, 11, 11, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  z-index: 100;
}
.detail-overlay[hidden] { display: none; }

.detail-panel {
  background: var(--surface);
  border-radius: 16px;
  box-shadow: 0 20px 60px rgba(11, 11, 11, 0.25);
  width: 100%;
  max-width: 820px;
  padding: 1.5rem 1.75rem 1.75rem;
}

.detail-header {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1.1rem;
}
.detail-title { font-size: 1.05rem; font-weight: 600; color: var(--ink); flex: 1; }

.detail-range { display: flex; gap: 0.35rem; }
.detail-range button {
  background: var(--page);
  border: 1px solid var(--border);
  color: var(--ink-2);
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.3rem 0.6rem;
  border-radius: 6px;
  cursor: pointer;
}
.detail-range button.active {
  background: var(--accent-primary);
  border-color: var(--accent-primary);
  color: var(--page);
}

.detail-close {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1rem;
  cursor: pointer;
  padding: 0.2rem 0.4rem;
}
.detail-close:hover { color: var(--ink); }

.detail-chart, .section-chart { position: relative; min-height: 260px; }
.detail-chart svg, .section-chart svg { width: 100%; height: 260px; display: block; overflow: visible; }
.detail-chart .empty, .section-chart .empty { color: var(--muted); font-size: 0.9rem; padding: 2rem 0; text-align: center; }
.detail-chart .grid-line, .section-chart .grid-line { stroke: var(--gridline); stroke-width: 1; }
.detail-chart .axis-label, .section-chart .axis-label { fill: var(--muted); font-size: 10px; font-family: system-ui, sans-serif; }
.detail-chart .crosshair-line, .section-chart .crosshair-line { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 3 3; opacity: 0; pointer-events: none; }
.detail-chart .crosshair-dot, .section-chart .crosshair-dot { opacity: 0; pointer-events: none; }

.detail-tooltip {
  position: absolute;
  pointer-events: none;
  background: var(--ink);
  color: var(--page);
  padding: 0.4rem 0.6rem;
  border-radius: 6px;
  font-size: 0.8rem;
  line-height: 1.4;
  white-space: nowrap;
  transform: translate(-50%, -115%);
  opacity: 0;
  transition: opacity 0.08s ease;
  z-index: 1;
}
.detail-tooltip .tt-value { font-weight: 700; }
.detail-tooltip .tt-date { color: rgba(255, 255, 255, 0.65); }

.food-search { position: relative; margin-bottom: 0.6rem; }
.food-search-hint {
  margin: 0.4rem 0 0;
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--muted);
}
.food-search-label {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.food-search-label input {
  background: var(--page);
  border: 1px solid var(--border);
  color: var(--ink);
  padding: 0.5rem 0.7rem;
  border-radius: 8px;
  font-size: 0.9rem;
  font-family: inherit;
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
}
.food-search-label input:focus {
  outline: none;
  border-color: var(--accent-primary);
  box-shadow: 0 0 0 3px rgba(91, 154, 139, 0.15);
}

.food-search-results {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow);
  margin-top: 0.25rem;
  max-height: 220px;
  overflow-y: auto;
  z-index: 10;
}
.food-search-result {
  padding: 0.5rem 0.75rem;
  font-size: 0.88rem;
  color: var(--ink);
  cursor: pointer;
}
.food-search-result:hover, .food-search-result.active { background: var(--page); }
.food-search-result .macro-hint { color: var(--muted); font-size: 0.78rem; margin-left: 0.4rem; }

.recent-meals-section { margin-bottom: 1.1rem; display: flex; flex-direction: column; gap: 0.4rem; }
.quick-add-label { font-size: 0.8rem; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; }
.recent-meals-section select {
  padding: 0.55rem 0.75rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.9rem;
  max-width: 28rem;
}
.recent-meals-section select:disabled { opacity: 0.6; }

.food-search-weight-row { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; }
.food-search-weight-row input {
  width: 6rem;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink);
  padding: 0.4rem 0.6rem;
  border-radius: 6px;
  font-size: 0.88rem;
}
.food-search-weight-row select {
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink);
  padding: 0.4rem 0.5rem;
  border-radius: 6px;
  font-size: 0.88rem;
}
.food-search-weight-row button {
  background: var(--accent-primary);
  color: var(--page);
  border: none;
  padding: 0.4rem 0.8rem;
  border-radius: 6px;
  font-weight: 600;
  font-size: 0.85rem;
  cursor: pointer;
}

.activities-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
}
.activities-header .card-title { margin-bottom: 0; }

.day-nav { display: flex; align-items: center; gap: 0.6rem; }
.day-nav button {
  background: var(--page);
  border: 1px solid var(--border);
  color: var(--ink-2);
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 6px;
  font-size: 0.75rem;
  cursor: pointer;
}
.day-nav button:hover:not(:disabled) { border-color: var(--accent-primary); color: var(--accent-primary); }
.day-nav button:disabled { opacity: 0.35; cursor: default; }
.day-nav span { font-size: 0.85rem; font-weight: 600; color: var(--ink); min-width: 7rem; text-align: center; }

.page-title {
  font-family: var(--font-display);
  font-size: 1.7rem;
  font-weight: 500;
  letter-spacing: -0.015em;
  margin-bottom: 1.25rem;
}
/* "Sleep & Recovery" was wrapping onto two lines at this size once squeezed next to the
   30d/90d/1y toggle on a phone-width screen -- shrink instead. Placed right after the base rule
   (not in the early mobile media block near the top of this file) so it isn't silently lost to
   this same equal-specificity-but-later-in-source-order rule. */
@media (max-width: 640px) {
  .page-title { font-size: 1.3rem; }
}

.subsection-title {
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin: 2rem 0 1rem;
}

.section-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
}
.section-card-header .card-title { margin-bottom: 0; }

.section-stat-value {
  font-family: var(--font-mono);
  font-size: 1.5rem;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin-bottom: 0.75rem;
  font-variant-numeric: tabular-nums;
}
.section-stat-value .unit { font-size: 0.9rem; font-weight: 500; color: var(--muted); margin-left: 0.3rem; }

.section-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.1rem;
  margin-bottom: 1.1rem;
}
@media (min-width: 900px) {
  .section-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
}

/* Activity's metric grid, two per row on mobile -- same treatment as Sleep & Recovery's .grid.
   Scoped to the #activity-grid id (not the bare .section-grid class, which Body's page also
   uses) so Body's layout is untouched. The "Activities" log table is a separate sibling <section>
   in activity.html, outside this grid entirely, so it already stays full-width on its own row
   below regardless of this change. Placed after every base rule it touches (.section-grid/
   .section-stat-value/.section-chart/.card above), same reasoning as the Sleep page fix. */
@media (max-width: 640px) {
  #activity-grid { grid-template-columns: repeat(2, 1fr); gap: 0.6rem; align-items: stretch; }
  /* Sleep/Overview's grid cards all carry .stat-card, which makes them a flex column so the
     chart can sit at the bottom regardless of how many lines the label above it takes -- these
     cards never got that, so a 2-line label ("Active Zone Minutes") vs. a 1-line one ("Steps")
     left them with genuinely different total content height, and the outer card just sized to
     fit its own content rather than matching its row-mate. display:flex here + margin-top:auto
     on the chart reproduces the same effect without reusing .stat-card itself, since these cards
     aren't clickable links the way Sleep's are. */
  #activity-grid .card { padding: 0.9rem 1rem; display: flex; flex-direction: column; }
  #activity-grid .stat-label { font-size: 0.72rem; }
  #activity-grid .section-stat-value { font-size: 1.25rem; margin-bottom: 0.4rem; }
  /* section-chart's <svg> stretches non-uniformly to fill its box (preserveAspectRatio="none"),
     including its axis-label text, so this is a more conservative cut than Sleep's tiny
     sparklines got -- shrinking harder risked visibly squishing the date/value labels. */
  #activity-grid .section-chart, #activity-grid .section-chart svg { min-height: 150px; height: 150px; }
  #activity-grid .section-chart { margin-top: auto; }
}

/* Body's two metric grids (native Weight/Body fat/BMI, and the separate Withings Weight/Body
   fat), same treatment as Activity's above -- two per row on mobile, scoped to these ids rather
   than the bare .section-grid class (Activity also uses that class). The Profile form and "Log a
   scale reading" form+table are separate sibling <section>s outside both grids, so they already
   stay full-width on their own rows and needed no change. */
@media (max-width: 640px) {
  #body-grid, #withings-grid { grid-template-columns: repeat(2, 1fr); gap: 0.6rem; align-items: stretch; }
  /* Same reasoning as Activity's grid above: these cards lack .stat-card's flex-column behavior,
     so e.g. "Weight (lbs)" (short label) vs. "Body Fat %" ended up different total heights. */
  #body-grid .card, #withings-grid .card { padding: 0.9rem 1rem; display: flex; flex-direction: column; }
  #body-grid .stat-label, #withings-grid .stat-label { font-size: 0.72rem; }
  #body-grid .section-stat-value, #withings-grid .section-stat-value { font-size: 1.25rem; margin-bottom: 0.4rem; }
  #body-grid .section-chart, #body-grid .section-chart svg,
  #withings-grid .section-chart, #withings-grid .section-chart svg { min-height: 150px; height: 150px; }
  #body-grid .section-chart, #withings-grid .section-chart { margin-top: auto; }
}

/* ---- Loading screen ---- */
#loading-screen {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--page);
  transition: opacity 0.35s ease;
}
#loading-screen.loading-screen-hide {
  opacity: 0;
  pointer-events: none;
}
.loading-rig {
  position: relative;
  width: 150px;
  height: 204px;
  perspective: 700px;
}
.loading-shadow {
  position: absolute;
  left: 50%;
  bottom: 2px;
  width: 88px;
  height: 14px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(ellipse at center, var(--border) 0%, transparent 72%);
  animation: loadingShadowPulse 2.6s ease-in-out infinite;
}
.loading-rig canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  will-change: transform;
}
@keyframes loadingShadowPulse {
  0%, 100% { transform: translateX(-50%) scale(1); opacity: 0.85; }
  50%      { transform: translateX(-50%) scale(0.82); opacity: 0.55; }
}
@media (prefers-reduced-motion: reduce) {
  .loading-shadow { animation: none; }
  #loading-screen { transition: none; }
}

.loading-frame {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.4rem;
}
.loading-brand {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.loading-brand .brand-mark { width: 10px; height: 10px; }
.loading-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  color: var(--muted);
  text-transform: uppercase;
  display: flex;
  align-items: center;
  gap: 0.7rem;
}
.loading-eyebrow::before, .loading-eyebrow::after {
  content: "";
  width: 24px;
  height: 1px;
  background: var(--border);
}
.loading-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 210px;
  height: 210px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1px solid var(--border-soft);
  pointer-events: none;
}
.loading-ring::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  background: conic-gradient(from 0deg, transparent 0deg, var(--accent-primary) 24deg, transparent 70deg, transparent 360deg);
  animation: loadingRingSpin 2.4s linear infinite;
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));
}
@keyframes loadingRingSpin { to { transform: rotate(360deg); } }

.loading-log {
  font-family: var(--font-mono);
  font-size: 0.76rem;
  color: var(--ink-2);
  min-height: 1.2em;
  text-align: center;
  letter-spacing: 0.02em;
}
.loading-progress {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  width: 220px;
}
.loading-bar {
  flex: 1;
  height: 2px;
  background: var(--border);
  position: relative;
  overflow: hidden;
}
.loading-bar-fill {
  position: absolute;
  inset: 0;
  width: 0%;
  background: var(--accent-primary);
  box-shadow: 0 0 10px var(--accent-primary);
  transition: width 0.15s linear;
}
.loading-pct {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--ink);
  min-width: 3ch;
  font-variant-numeric: tabular-nums;
}
@media (prefers-reduced-motion: reduce) {
  .loading-ring::before { animation: none; }
}

.legal-page {
  max-width: 720px;
  padding-bottom: 5rem;
}
.legal-page .page-title { margin-bottom: 0.4rem; }
.legal-updated {
  color: var(--muted);
  font-size: 0.85rem;
  margin: 0 0 2rem;
}
.legal-page p {
  color: var(--ink-2);
  line-height: 1.65;
  margin: 0 0 1rem;
}
.legal-page ul {
  color: var(--ink-2);
  line-height: 1.65;
  margin: 0 0 1rem;
  padding-left: 1.4rem;
}
.legal-page li { margin-bottom: 0.4rem; }
.legal-page strong { color: var(--ink); font-weight: 600; }
.legal-page a { color: var(--accent-primary); }
.legal-page h2 {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--ink);
  margin: 2rem 0 0.75rem;
}

.site-footer {
  display: flex;
  justify-content: center;
  gap: 1.5rem;
  padding: 1rem 2.5rem 3rem;
  font-size: 0.8rem;
}
.site-footer a {
  color: var(--muted);
  text-decoration: none;
}
.site-footer a:hover { color: var(--ink-2); }
