:root {
  --bg: #0f1115;
  --panel: #171a21;
  --border: #2a2e37;
  --text: #e8eaed;
  --muted: #9aa0aa;
  --accent: #4f9dff;
  --accent-text: #0b0d11;
  --danger: #ff6b6b;
  --good: #35c98c;
  --warn: #e0a52c;
  --bg-alt: #10131a;
  --panel-alt-2: #1c2029;
  --activity-1: #3987e5;
  --activity-2: #d95926;
  --activity-3: #199e70;
  --activity-4: #c98500;
  --activity-5: #d55181;
  --activity-6: #008300;
  --activity-7: #9085e9;
  --activity-8: #e66767;
  --elevation-1: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 3px 1px rgba(0, 0, 0, 0.15);
  --elevation-2: 0 1px 2px rgba(0, 0, 0, 0.3), 0 2px 6px 2px rgba(0, 0, 0, 0.15);
}

:root[data-theme="light"] {
  --bg: #f5f6f8;
  --panel: #ffffff;
  --border: #d8dce3;
  --text: #1a1d23;
  --muted: #5a616e;
  --accent: #2f6fce;
  --accent-text: #ffffff;
  --danger: #d6455a;
  --good: #1f9d68;
  --warn: #b5790a;
  --bg-alt: #eef0f3;
  --panel-alt-2: #ffffff;
  --activity-1: #2a78d6;
  --activity-2: #eb6834;
  --activity-3: #1baf7a;
  --activity-4: #eda100;
  --activity-5: #e87ba4;
  --activity-6: #008300;
  --activity-7: #4a3aa7;
  --activity-8: #e34948;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  background: var(--bg);
  color: var(--text);
}

.demo-banner {
  padding: 8px 20px;
  text-align: center;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--bg);
  background: var(--warn);
}

.view-as-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px 16px;
  padding: 8px 20px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--accent-text);
  background: var(--accent);
}

.view-as-banner[hidden] { display: none; }

.view-as-banner button {
  padding: 3px 12px;
  font: inherit;
  color: inherit;
  background: transparent;
  border: 1px solid currentColor;
  border-radius: 6px;
  cursor: pointer;
}

.view-as-banner button:hover { background: color-mix(in srgb, var(--accent-text) 15%, transparent); }

.load-error-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 10px 20px;
  text-align: center;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--bg);
  background: var(--danger);
}

/* Author styles beat the UA default [hidden] { display: none }, so the
   `display: flex` above would otherwise show this even while hidden. */
.load-error-banner[hidden] { display: none; }

.load-error-banner button {
  flex-shrink: 0;
  padding: 4px 14px;
  border: 1px solid var(--bg);
  border-radius: 999px;
  background: transparent;
  color: var(--bg);
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease;
}

.load-error-banner button:hover { background: color-mix(in srgb, var(--bg) 18%, transparent); }

header {
  padding: 16px 32px;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  column-gap: 16px;
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--panel);
  box-shadow: var(--elevation-1);
}

.header-left {
  display: flex;
  align-items: center;
  gap: 16px;
  min-width: 0;
}

h1 { display: flex; align-items: baseline; gap: 8px; margin: 0; }

/* Logo mark before the wordmark; centered because the h1 aligns on baseline. */
.brand-mark {
  width: 28px;
  height: 28px;
  flex: none;
  align-self: center;
  color: var(--accent);
}

.brand-name {
  font-family: 'Fraunces', serif;
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}

.brand-sub {
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

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

/* Settings panels' status lines (e.g. Google Health's connection, last-sync
   and daily-run lines) read as one grouped block, with room to breathe
   before the buttons below. */
.data-status {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 14px;
}

.menu-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 40;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}

.menu-overlay.open {
  opacity: 1;
  pointer-events: auto;
}

.side-menu {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 280px;
  max-width: 85vw;
  background: var(--panel);
  border-left: 1px solid var(--border);
  box-shadow: var(--elevation-2);
  z-index: 50;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform 0.2s ease;
}

.side-menu.open {
  transform: translateX(0);
}

.side-menu-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-weight: 600;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
}

.menu-close-btn {
  background: none;
  border: none;
  color: var(--text);
  font-size: 1.1rem;
  cursor: pointer;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.15s ease;
}

.menu-close-btn:hover { background: color-mix(in srgb, var(--text) 10%, transparent); }

.settings-actions-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.settings-actions-row button { line-height: 1.2; }

.side-menu-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.side-menu-section-title {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin-bottom: 4px;
}

/* Titled groups within one Settings panel (e.g. Google Health's Connection /
   Sync / History, Display's Appearance / Metrics Page Sections / Chart
   Options), divided by a rule so each area reads on its own. */
.settings-subsection {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.settings-subsection[hidden] { display: none; }

/* A panel saved as a whole (Scoring & Goals): Revert/Save beside its title,
   and each field changed since the last save outlined until then. */
.settings-save-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 8px 12px;
}

.settings-save-bar .hint { margin: 0; }
.settings-save-bar .hint.error { color: var(--danger); }
.settings-save-bar button:disabled { opacity: 0.5; cursor: default; }

.side-menu-field input.is-changed { border-color: var(--accent); }
.side-menu-field input.is-invalid { border-color: var(--danger); }

/* Labelled number fields (Scoring & Goals): label + info tooltip above the
   input, its validation message below. */
/* Fields sit side by side across the panel, stacking on narrow screens. */
.setting-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 16px 20px;
}

@media (max-width: 700px) {
  .setting-grid { grid-template-columns: minmax(0, 1fr); gap: 14px; }
}

/* A label that wraps pushes its input down, so the inputs in a row stay
   level (flex-grow on the label row, aligned to its bottom). */
.setting-row {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.setting-row .side-menu-field input[type="number"] { width: 100%; }

.setting-label-row {
  flex: 1;
  display: flex;
  align-items: flex-end;
  gap: 6px;
  margin-bottom: 6px;
}

.setting-label-row label {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text);
}

/* Info button whose description shows on hover, keyboard focus, or a tap
   (.open - see setupInfoTips() in page-chrome.js). */
.info-tip {
  position: relative;
  display: inline-flex;
}

.info-tip-btn {
  width: 18px;
  height: 18px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  font: italic 600 0.7rem/1 Georgia, serif;
  cursor: help;
}

.info-tip-btn:hover,
.info-tip-btn:focus-visible,
.info-tip.open .info-tip-btn {
  color: var(--accent);
  border-color: var(--accent);
}

.info-tip-text {
  position: absolute;
  bottom: calc(100% + 8px);
  left: -10px;
  z-index: 20;
  width: max-content;
  max-width: min(280px, 75vw);
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel);
  box-shadow: var(--elevation-2);
  color: var(--text);
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.4;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease, visibility 0.12s ease;
}

/* Within a field grid, the tooltip spans its field's column (above the
   label) rather than hanging off the icon, so a right-hand column's can't
   run past the page edge. */
.setting-row .info-tip { position: static; }
.setting-row .info-tip-text {
  left: 0;
  right: 0;
  bottom: calc(100% + 6px);
  width: auto;
  max-width: none;
}

.info-tip:hover .info-tip-text,
.info-tip-btn:focus-visible + .info-tip-text,
.info-tip.open .info-tip-text {
  opacity: 1;
  visibility: visible;
}

/* No rule above a panel's first section when it sits right under the title. */
.panel-header + .settings-subsection {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

.settings-subsection-title {
  margin: 0 0 10px;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.side-menu-checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  color: var(--text);
  cursor: pointer;
  padding: 4px 0;
}

.side-menu-checkbox:hover {
  color: var(--accent);
}

.side-menu-hint {
  font-size: 0.78rem;
  color: var(--muted);
  margin: 0 0 8px;
  line-height: 1.4;
}

/* Extra breathing room when a panel stacks more than one hint/field/status
   group (e.g. Baseline Bands' SD multiplier + SMA acute/baseline days) - the
   first hint right under a .panel-header still reads fine without it. */
.side-menu-status + .side-menu-hint {
  margin-top: 12px;
}

.side-menu-field {
  display: flex;
  gap: 8px;
}

.side-menu-field input[type="number"] {
  width: 80px;
}

.side-menu-field button {
  background: var(--bg-alt);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 0.85rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.side-menu-field button:hover:not(:disabled) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, var(--bg-alt));
}
.side-menu-field button:disabled { opacity: 0.7; cursor: default; }

.side-menu-status { display: block; margin-top: 6px; }
.side-menu-status.error { color: var(--danger); }

.run-script-btn {
  background: var(--accent);
  color: var(--accent-text);
  border: none;
  border-radius: 999px;
  padding: 8px 20px;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: filter 0.15s ease, box-shadow 0.15s ease;
}

.run-script-btn:hover { filter: brightness(1.1); box-shadow: var(--elevation-1); }
.run-script-btn:disabled { opacity: 0.7; cursor: default; box-shadow: none; }

.width-toggle-btn {
  background: none;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 8px 16px;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

/* Settings > Account's "Sign out" is a link (a plain navigation to
   /auth/logout) styled like the buttons next to it. */
a.width-toggle-btn { display: inline-block; text-decoration: none; }

.width-toggle-btn:hover { color: var(--text); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.width-toggle-btn[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }

.theme-toggle-btn {
  background: none;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 8px 16px;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.theme-toggle-btn:hover { color: var(--text); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.theme-toggle-btn[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }

main {
  padding: 24px 32px 64px;
  max-width: 1200px;
  margin: 0 auto;
}

main.full-width {
  max-width: none;
}

.view-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.nav-btn {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 8px 8px 0 0;
  color: var(--muted);
  font-size: 0.95rem;
  font-weight: 500;
  padding: 8px 12px 10px;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.nav-btn:hover { color: var(--text); background: color-mix(in srgb, var(--text) 6%, transparent); }
.nav-btn.active { color: var(--text); border-bottom-color: var(--accent); }

.header-right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}

.log-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--accent);
  color: var(--accent-text);
  border: none;
  border-radius: 999px;
  padding: 8px 16px 8px 12px;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: filter 0.15s ease, box-shadow 0.15s ease;
}

.log-btn:hover { filter: brightness(1.1); box-shadow: var(--elevation-1); }

.bottom-nav { display: none; }

.settings-btn {
  border: 1px solid var(--border);
  border-radius: 50%;
  width: 36px;
  height: 36px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.settings-btn:hover { color: var(--text); background: color-mix(in srgb, var(--text) 8%, transparent); border-color: var(--accent); }
.settings-btn.active { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }

.sync-nav-btn {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
  cursor: pointer;
  transition: filter 0.15s ease, box-shadow 0.15s ease;
}
.sync-nav-btn:hover { color: var(--accent-text); background: var(--accent); filter: brightness(1.1); box-shadow: var(--elevation-1); }
.sync-nav-btn:disabled { opacity: 0.7; cursor: default; box-shadow: none; }
.sync-nav-icon.spinning { animation: loading-spin 0.8s linear infinite; transform-origin: center; }

/* The side-menu carries its own copies of the view tabs and the cards/table
   toggle for small widths (see the max-width: 700px block below); the whole
   section (including its title) is hidden here so it only ever shows in the
   flyout, never alongside the header's. */
#side-menu .side-menu-nav-section {
  display: none;
}

#side-menu .view-nav {
  flex-direction: column;
  gap: 4px;
}

#side-menu .nav-btn {
  width: 100%;
  text-align: left;
  border-bottom: none;
  border-left: 3px solid transparent;
  border-radius: 6px;
  padding: 10px 12px;
}

#side-menu .nav-btn:hover { color: var(--accent); background: var(--bg-alt); }
#side-menu .nav-btn.active {
  color: var(--accent);
  background: var(--bg-alt);
  border-left-color: var(--accent);
}

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


.metrics-table-wrap { max-width: 100%; overflow-x: auto; }

table.metrics-table {
  border-collapse: collapse;
  min-width: 640px;
  width: 100%;
  table-layout: fixed;
  font-size: 0.85rem;
}

table.metrics-table th, table.metrics-table td {
  padding: 9px 12px;
  border: 1px solid var(--border);
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

table.metrics-table th { color: var(--muted); font-weight: 500; }
table.metrics-table th:first-child { width: 36%; text-align: left; }

table.data-table {
  table-layout: auto;
  min-width: 100%;
}

table.data-table th:first-child { width: auto; text-align: left; }
table.data-table th, table.data-table td { white-space: nowrap; }
table.data-table thead th { position: sticky; top: 0; background: var(--panel); }
table.data-table td.cell-good { background: rgba(53, 201, 140, 0.16); }
table.data-table td.cell-bad { background: rgba(255, 107, 107, 0.16); }

.column-picker { position: relative; }

#data-range, .picker-btn { width: 160px; }

.picker-btn {
  background: var(--bg-alt);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px 12px;
  font-size: 0.85rem;
  cursor: pointer;
  white-space: nowrap;
  text-align: left;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.picker-btn:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--bg-alt)); }
.picker-btn[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--bg-alt)); }

.column-picker-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 20;
  background: var(--panel-alt-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px;
  width: max-content;
  min-width: 220px;
  max-width: 320px;
  max-height: 320px;
  overflow-y: auto;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
}

.column-picker-header {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  padding: 2px 4px 8px;
}

.column-picker-actions {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
  padding: 0 4px 8px;
  border-bottom: 1px solid var(--border);
}

.column-picker-actions button {
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--accent);
  cursor: pointer;
  font-size: 0.75rem;
  font-weight: 500;
  padding: 3px 8px;
  margin: -3px -8px;
  transition: background-color 0.15s ease;
}

.column-picker-actions button:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }

.column-picker-list {
  display: flex;
  flex-direction: column;
}

/* Extra .column-picker specificity needed to beat the ".controls label" column-layout rule. */
.column-picker .column-picker-list label {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  padding: 6px 4px;
  border-radius: 4px;
  cursor: pointer;
}

.column-picker-list label:hover { background: var(--bg-alt); }

.column-picker-list label span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 700px) {
  header {
    display: flex;
    justify-content: space-between;
    padding: 16px 12px;
  }

  /* Below this width the tabs (and Settings) live in the bottom nav instead
     of the header - see .bottom-nav below. Compare and Data, which don't fit
     in the bottom nav, stay reachable through the side menu's "More". */
  header .view-nav,
  header .settings-btn {
    display: none;
  }

  /* ...except the sync button, which stays in the header on mobile too -
     there's no bottom-nav equivalent for it. */
  header .sync-nav-btn {
    display: flex;
    width: 40px;
    height: 40px;
  }

  .log-btn {
    width: 40px;
    height: 40px;
    padding: 0;
    justify-content: center;
    border-radius: 50%;
  }

  .log-btn-label {
    display: none;
  }

  #side-menu .side-menu-nav-section {
    display: flex;
  }

  .bottom-nav {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 30;
    background: var(--panel);
    border-top: 1px solid var(--border);
    box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.25);
    padding: 6px max(6px, env(safe-area-inset-right)) calc(6px + env(safe-area-inset-bottom)) max(6px, env(safe-area-inset-left));
  }

  .bottom-nav-btn {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    border-radius: 10px;
    padding: 10px 0;
    color: var(--muted);
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
  }

  .bottom-nav-btn:hover,
  .bottom-nav-btn:active {
    background: color-mix(in srgb, var(--text) 8%, transparent);
  }

  .bottom-nav-btn.active {
    color: var(--accent);
  }

  main {
    max-width: none;
    padding: 16px 12px calc(72px + env(safe-area-inset-bottom));
  }

  .panel {
    padding: 12px;
    border-radius: 8px;
  }

  .cards {
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
  }

  .card {
    padding: 10px 12px;
  }

  /* .card .value/.today-card .value/.today-chart-wrap are NOT overridden
     here - the base (unconditional) .card rules further down the file come
     after this media query block in source order, so at equal specificity
     they'd win over anything placed here regardless of viewport. See the
     second, later @media (max-width: 700px) block (right after those base
     rules) for the actual mobile overrides. */

  table.metrics-table {
    min-width: 0;
    width: max-content;
    table-layout: auto;
  }

  table.metrics-table th, table.metrics-table td {
    overflow: visible;
    text-overflow: clip;
  }

  table.metrics-table th:first-child { width: auto; }

  .chart-wrap {
    height: 60vh;
    min-height: 320px;
  }

  .insight-widget .readiness-header {
    align-items: flex-start;
    gap: 6px;
  }

  .insight-widget .readiness-header .label {
    font-size: 0.85rem;
  }

  /* Every insight card's header value badge - the composite cards' number
     (.header-score) and the other cards' percentage/duration/bpm figure
     (.readiness-score, e.g. Sleep Debt's "6h 23m" or HR Reserve's "12 bpm") -
     shares one font size on mobile so none of them wrap inside the narrow
     two-per-row tiles. .meter-value is left alone: it's always a bare 0-100
     composite number (never a longer unit-bearing string like the ones
     above), so it was never at wrapping risk, and it only ever renders
     inside .meter-block, which is hidden until the card is expanded anyway. */
  .insight-widget .header-score,
  .insight-widget .readiness-score {
    font-size: 1.05rem;
  }

  .insight-widget .readiness-score.calorie-budget-value {
    font-size: 0.9rem;
  }

  .insight-widget .meter-value {
    font-size: 1.3rem;
  }

  /* Every insight card's .readiness-breakdown list (per-metric swatches on
     the flag cards, the mover list on Notable Trends, the per-metric
     composite list on the two Readiness score cards) and its direct-child
     .meta description (the plain-English status line) are both hidden on
     mobile by default to cut card height down to just the header - narrow
     enough, tiled two-per-row, to fit an actual phone width. readiness.js's
     initTapToExpand() marks any card that actually has one or the other as
     .expandable and toggles .expanded on tap, which the rules below reveal/
     hint at - scoped to .insight-widget so this doesn't touch sleep-tile.js's
     unrelated reuse of the same .readiness-breakdown class elsewhere on the
     page, nor the unrelated .meta span nested inside the meter block's "/100"
     figure (the ">" combinator only matches .meta as a direct card child). */
  .insight-widget .readiness-breakdown,
  .insight-widget .readiness-card > .meta {
    display: none;
  }

  .insight-widget .readiness-card.expanded .readiness-breakdown {
    display: flex;
  }

  .insight-widget .readiness-card.expanded > .meta {
    display: block;
  }

  /* The two composite Readiness cards' score bar is the tallest thing in
     their collapsed state (bigger than a header row alone) - hidden by
     default for the same reason as .meta/.readiness-breakdown above, and
     revealed on tap alongside them. Both cards already get .expandable from
     their .meta/.readiness-breakdown content, so no JS change is needed to
     make this collapse/expand. */
  .insight-widget .meter-block {
    display: none;
  }

  .insight-widget .readiness-card.expanded .meter-block {
    display: block;
  }

  .insight-widget .readiness-card.expandable {
    cursor: pointer;
  }

  /* A generated block, so it always renders after the card's real content
     (breakdown included) regardless of where .expandable/.expanded land in
     the DOM - giving a consistent tap affordance without having to inject
     markup per card type. */
  .insight-widget .readiness-card.expandable::after {
    content: '▾ Tap for details';
    display: block;
    margin-top: 8px;
    font-size: 0.72rem;
    color: var(--muted);
  }

  .insight-widget .readiness-card.expandable.expanded::after {
    content: '▴ Tap to collapse';
  }

  /* Daily Briefing is collapsible too, but - unlike every other card above -
     starts expanded rather than collapsed, so its own rules are the inverse:
     .narrative-collapsed hides the prose instead of a plain class revealing
     it. */
  #narrative-insight-wrap .readiness-card {
    cursor: pointer;
  }

  #narrative-insight-wrap .readiness-card.narrative-collapsed .narrative-text {
    display: none;
  }

  #narrative-insight-wrap .readiness-card::after {
    content: '▴ Tap to collapse';
    display: block;
    margin-top: 8px;
    font-size: 0.72rem;
    color: var(--muted);
  }

  #narrative-insight-wrap .readiness-card.narrative-collapsed::after {
    content: '▾ Tap to expand';
  }
}

.view > h2 {
  margin: 0 0 16px;
  font-size: 1.3rem;
}

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--elevation-1);
  padding: 14px 16px;
  /* Grid items default to min-width: auto, letting a child's intrinsic size
     (e.g. the Chart.js sparkline canvas below, which briefly sizes itself
     before its own ResizeObserver settles) force this card - and the whole
     .cards row - wider than the screen. This is what let Today view's cards
     spill past the viewport on mobile specifically after a date change
     (renderTodayMetrics destroys and recreates every canvas). */
  min-width: 0;
}

.card .label { color: var(--muted); font-size: 0.8rem; }
.card .value { font-size: 1.5rem; font-weight: 600; margin: 4px 0; }
.card .value .unit { font-size: 0.65em; font-weight: 500; }
.card .meta { font-size: 0.8rem; color: var(--muted); }
.card .trend-good { color: var(--good); }
.card .trend-bad { color: var(--danger); }

/* Mobile overrides for the rules directly above - deliberately a separate,
   later @media block rather than folded into the big one near the top of
   this file: that earlier block sits *before* the .card rules above in
   source order, so an equal-specificity override placed there loses the
   cascade to these unconditional ones regardless of viewport. Being later
   than them is what makes this one actually win. */
@media (max-width: 700px) {
  .card .value { font-size: 1.2rem; }

  /* Today view's sparkline cards specifically (plain metric cards + the
     Sleep tile, both marked .today-card - see today-view.js/sleep-tile.js)
     run noticeably taller than other mobile cards once you add the chart
     below the value, so they get a further reduction beyond the generic
     .card .value above. (.today-chart-wrap's own mobile override lives in
     a separate block further down, after ITS base rule - see there for why.) */
  .today-card .value { font-size: 1.15rem; }
}

/* The all-time face's trend/avg/min/max lines (today-view.js's
   renderTodayMetricCard, and sleep-tile.js's own all-time face) default to
   zero gap between stacked .meta divs - give each its own breathing room
   instead of sitting flush against the one above it. Scoped to
   .today-card-face-alltime so the daily face's single meta line and
   readiness.js's cards aren't affected. */
.today-card-face-alltime .meta { margin-top: 6px; }

.today-chart-wrap { height: 116px; margin-top: 10px; overflow: hidden; }
.today-chart-wrap canvas { max-width: 100%; }

/* Mobile override for the rule directly above - same reason this can't live
   in the big mobile block near the top of the file (or even the .card .value
   mobile block above): .today-chart-wrap's own unconditional/base rule is
   right here, after both of those in source order, so at equal specificity
   it would win over an override placed earlier regardless of viewport. */
@media (max-width: 700px) {
  .today-chart-wrap { height: 96px; margin-top: 4px; }
}

/* The user-select/touch-callout suppression below stops a tap-and-hold on
   the card's own text (label/value/meta) from triggering the browser's
   native text-selection (Android) or callout (iOS) gesture instead of
   initTodayCardFlip's tap-to-flip. */
.today-card {
  position: relative;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* A Today card with an all-time face (see today-view.js's
   renderTodayMetricCard/initTodayCardFlip) is tappable to flip between its
   day-scoped view and this metric's all-time avg/min/max/trend - but not by
   tapping the chart area (see CHART_REGION_SELECTOR in today-view.js, left
   to its own native touch/hover handling), so the pointer cursor is scoped
   to exclude it too - otherwise the cursor would promise a click that the
   chart area doesn't actually honor. */
.today-card.flippable { cursor: pointer; }
.today-card.flippable .today-chart-wrap,
.today-card.flippable .sleep-tile-donut {
  cursor: default;
}


/* The whole all-time face is one row now, not label/value/trend on top of a
   second stats-vs-donut row below: a 2-column grid, not flex - flex-wrap
   would (and did) drop the donut to its own line below .sleep-tile-summary
   as soon as the row got too narrow to fit both; grid has no such fallback,
   so the donut column stays put to the right and just shrinks instead.
   Column 1 (auto) is exactly .sleep-tile-summary's content width
   (label/value/trend/avg-min-max, stacked); column 2 (1fr) is
   .sleep-tile-donut claiming everything left over, which is what actually
   puts the donut beside the headline number instead of leaving the space to
   its right dead above a skinnier stats-only row. align-items:start (not
   center) keeps .sleep-tile-summary's top line flush with the daily face's
   own label - centering it against the taller donut shifted it down on
   every flip-to-all-time tap, which read as the card jumping under you. */
.sleep-tile-alltime {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 16px 24px;
}

/* Stage-mix donut (sleep-tile.js's stageDonutHtml, rendered by
   today-view.js's renderSleepStageDonut) beside its label+share legend -
   replaces the old plain-text breakdown that used to sit in this same spot.
   justify-content:center centers the donut+legend pair within the grid
   column's full width, so it doesn't just hug the left edge next to
   .sleep-tile-summary. */
.sleep-tile-donut {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
}
/* Chart.js's responsive:true sizes the canvas itself off its parent's
   dimensions (inline styles it sets there beat any width/height put on the
   canvas directly, which is what let an unsized flex child send the chart
   sprawling) - so, same as .today-chart-wrap for the sparklines above, the
   fixed size goes on this wrapper and the canvas just fills it. Bigger than
   before (was 150px) since it now sits beside the full label/value/trend/
   stats column instead of just the stats line, and has the height to match. */
.sleep-tile-donut-chart-wrap {
  width: 170px;
  height: 170px;
  flex-shrink: 0;
}
.sleep-tile-donut-chart-wrap canvas {
  max-width: 100%;
  max-height: 100%;
}

/* Short "Light 32%"-style lines now (the exact minutes are a tap/hover away
   on the donut itself), so the base .readiness-breakdown flex-column layout
   fits without the 2-column grid the longer plain-text version used to need
   - just centered against the donut's height and flush against it instead
   of the base rule's own top margin (meant for sitting below other text). */
.readiness-breakdown.sleep-tile-legend {
  margin-top: 0;
  justify-content: center;
}

.chart-wrap { height: 320px; }

.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--elevation-1);
  padding: 20px;
  margin-bottom: 24px;
}

.panel h2, .panel h3 { margin: 0 0 8px; font-size: 1.1rem; }

.panel-header h3 { margin: 0 0 8px; font-size: 1.1rem; }
.hint { color: var(--muted); font-size: 0.85rem; margin-top: 0; }

.panel-header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

.controls {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: flex-end;
}

.shared-chart-controls {
  justify-content: flex-start;
  margin: -8px 0 16px;
}

.controls label {
  display: flex;
  flex-direction: column;
  font-size: 0.75rem;
  color: var(--muted);
  gap: 4px;
}

.controls label[hidden] {
  display: none;
}

.controls label.checkbox {
  flex-direction: row;
  align-items: center;
  gap: 6px;
}

select, input, textarea {
  background: var(--bg-alt);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 8px;
  font-size: 0.85rem;
  font-family: inherit;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

select:hover, input:hover, textarea:hover { border-color: var(--muted); }

select:focus, input:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

textarea::placeholder,
input::placeholder {
  color: var(--muted);
  opacity: 1;
}

select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239aa0aa' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-position: right 10px center;
  background-repeat: no-repeat;
  padding-right: 32px;
}

:root[data-theme="light"] select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%235a616e' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}

/* The native calendar-picker icon on <input type="date"> renders dark by
   default, which disappears against the dark theme's background - invert it
   there; the light theme already matches the icon's native color. */
input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1);
}

:root[data-theme="light"] input[type="date"]::-webkit-calendar-picker-indicator {
  filter: none;
}

input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  margin: 0;
  padding: 0;
  border: 2px solid var(--muted);
  border-radius: 4px;
  background: transparent;
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

input[type="checkbox"]:hover { border-color: var(--text); }

input[type="checkbox"]:checked {
  background: var(--accent);
  border-color: var(--accent);
}

input[type="checkbox"]:checked::after {
  content: '';
  position: absolute;
  left: 5px;
  top: 1px;
  width: 5px;
  height: 9px;
  border: solid #0b0d11;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

input[type="checkbox"]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent);
}

 #correlation-wrap {
  max-width: 100%;
  overflow-x: auto;
}

table.corr {
  border-collapse: collapse;
  min-width: 100%;
  width: max-content;
  font-size: 0.8rem;
}

table.corr th, table.corr td {
  padding: 8px;
  text-align: center;
  border: 1px solid var(--border);
  white-space: nowrap;
}

table.corr th { color: var(--muted); font-weight: 500; }

.hrv-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 10px;
  font-size: 0.8rem;
  color: var(--muted);
}

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

.hrv-legend-swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
}

.readiness-breakdown {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
  font-size: 0.8rem;
  color: var(--muted);
}

.readiness-breakdown-item {
  display: flex;
  align-items: center;
  gap: 6px;
}

.hrv-metric-control {
  align-items: flex-end;
}

/* --- Today widget grid (see today-widgets.js) -------------------------------
   Ordered flow on a fixed column count: each .widget spans 1-6 columns
   (data-span) and fills left-to-right in layout order, rows stretching to
   their tallest widget. Narrower screens keep the same saved spans but map
   them onto fewer columns - 3 on small desktops, 2 on phones. */
.today-widgets {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 12px;
}

.widget {
  display: flex;
  flex-direction: column;
  min-width: 0;
  grid-column: span 1;
}
.widget[data-span="2"] { grid-column: span 2; }
.widget[data-span="3"] { grid-column: span 3; }
.widget[data-span="4"] { grid-column: span 4; }
.widget[data-span="5"] { grid-column: span 5; }
.widget[data-span="6"] { grid-column: span 6; }

@media (min-width: 701px) and (max-width: 999px) {
  .today-widgets { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .widget[data-span="1"], .widget[data-span="2"] { grid-column: span 1; }
  .widget[data-span="3"], .widget[data-span="4"] { grid-column: span 2; }
  .widget[data-span="5"], .widget[data-span="6"] { grid-column: span 3; }
}

@media (max-width: 700px) {
  .today-widgets { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .widget[data-span="1"], .widget[data-span="2"], .widget[data-span="3"] { grid-column: span 1; }
  .widget[data-span="4"], .widget[data-span="5"], .widget[data-span="6"] { grid-column: span 2; }
}

/* The widget's content wrapper - an insight slot (#*-wrap, filled by
   readiness.js) or a metric card's markup - stretches to the widget's full
   height, and so does the single card inside it. */
.widget-body {
  flex: 1;
  display: flex;
  min-width: 0;
}

.widget-body > .card { flex: 1; }

.insight-widget .readiness-card {
  display: flex;
  flex-direction: column;
}

.widget-grid-empty { grid-column: 1 / -1; }

/* An insight with nothing to show (see readiness.js's renderUnavailable). */
.insight-placeholder-card { border-style: dashed; box-shadow: none; }
.insight-placeholder-msg { margin: 6px 0 0; font-size: 0.8rem; color: var(--muted); }

/* Insights with nothing to report - a placeholder (can't be computed) or a
   quiet all-clear (Illness/Overtraining Watch when not flagged) - collapse
   out of the page in view mode, the widgets after them moving up. In edit
   mode they stay, dimmed and labelled, so they can still be moved/removed. */
.today-widgets:not(.editing) > .widget:has(> .insight-widget > .insight-placeholder-card, > .insight-widget > .insight-quiet) {
  display: none;
}

.today-widgets.editing > .widget:has(> .insight-widget > .insight-placeholder-card, > .insight-widget > .insight-quiet) .widget-body {
  opacity: 0.55;
}

.today-widgets.editing > .widget:has(> .insight-widget > .insight-placeholder-card, > .insight-widget > .insight-quiet) .widget-toolbar-title::after {
  content: ' · hidden today';
  font-style: italic;
}

/* Edit mode. Card content is inert while editing (no flipping, expanding,
   chart tooltips or briefing regenerate) so every tap is a layout action. */
.widget-toolbar { display: none; }

.today-widgets.editing .widget {
  outline: 1px dashed color-mix(in srgb, var(--accent) 60%, transparent);
  outline-offset: 3px;
  border-radius: 10px;
}

.today-widgets.editing .widget.dragging { opacity: 0.5; }

.today-widgets.editing .widget-body { pointer-events: none; }

.today-widgets.editing .widget-toolbar {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 2px 0 6px;
  font-size: 0.75rem;
  color: var(--muted);
}

.widget-toolbar-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.widget-toolbar button {
  min-width: 28px;
  height: 28px;
  padding: 0 6px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-alt);
  color: var(--text);
  font-size: 0.9rem;
  line-height: 1;
  cursor: pointer;
}

.widget-toolbar button:hover:not(:disabled) { border-color: var(--accent); }
.widget-toolbar button:disabled { opacity: 0.4; cursor: default; }
.widget-toolbar .widget-remove-btn:hover { border-color: var(--danger); color: var(--danger); }

/* touch-action: none stops the page from scrolling once a touch-drag starts
   on the handle (see initWidgetDrag). */
.widget-toolbar .widget-drag-handle { cursor: grab; touch-action: none; }
.widget-toolbar .widget-drag-handle:active { cursor: grabbing; }

.widget-width-controls { display: inline-flex; align-items: center; gap: 3px; }
.widget-width-value { min-width: 24px; text-align: center; font-variant-numeric: tabular-nums; }

.today-edit-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
  padding: 10px 12px;
  border: 1px solid color-mix(in srgb, var(--accent) 50%, var(--border));
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 8%, var(--panel));
}

.today-edit-bar[hidden] { display: none; }
.today-edit-bar-label { font-size: 0.85rem; font-weight: 600; }
.today-edit-bar-spacer { flex: 1; }

.day-nav-today-btn.today-edit-done,
.day-nav-today-btn.today-edit-done:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
}

/* Widget drawer - same slide-in-from-the-right treatment as .side-menu,
   pulled open by the edge tab (or the edit bar's "Add widgets"). */
.widget-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 320px;
  max-width: 85vw;
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px;
  overflow-y: auto;
  background: var(--panel);
  border-left: 1px solid var(--border);
  box-shadow: var(--elevation-2);
  transform: translateX(100%);
  visibility: hidden;
  transition: transform 0.2s ease, visibility 0s linear 0.2s;
}

.widget-drawer.open {
  transform: translateX(0);
  visibility: visible;
  transition: transform 0.2s ease;
}

.widget-drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
}

.widget-drawer-header h3 { margin: 0; font-size: 1rem; }
.widget-drawer .hint { margin: 0; }

.widget-drawer-close {
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 50%;
  background: none;
  color: var(--text);
  font-size: 1.2rem;
  cursor: pointer;
}

.widget-drawer-close:hover { background: color-mix(in srgb, var(--text) 10%, transparent); }

.widget-drawer-tab {
  position: fixed;
  top: 50%;
  right: 0;
  z-index: 45;
  transform: translateY(-50%);
  writing-mode: vertical-rl;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 14px 7px;
  border: none;
  border-radius: 10px 0 0 10px;
  background: var(--accent);
  color: var(--accent-text);
  font-size: 0.85rem;
  font-weight: 600;
  box-shadow: var(--elevation-2);
  cursor: pointer;
}

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

.widget-drawer-count:not(:empty) {
  writing-mode: horizontal-tb;
  min-width: 20px;
  padding: 2px 5px;
  border-radius: 999px;
  background: var(--accent-text);
  color: var(--accent);
  font-size: 0.72rem;
  text-align: center;
}

.widget-drawer-list { display: flex; flex-direction: column; gap: 10px; }

.widget-drawer-heading {
  margin: 8px 0 0;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.widget-drawer-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-alt);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease;
}

.widget-drawer-item:hover,
.widget-drawer-item:focus-visible { border-color: var(--accent); }

.widget-drawer-item-title {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.85rem;
  font-weight: 600;
}

.widget-drawer-add { color: var(--accent); font-size: 0.8rem; font-weight: 500; white-space: nowrap; }

.widget-thumb {
  display: block;
  width: 100%;
  height: 84px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
}

.widget-thumb .t-soft { fill: var(--border); }
.widget-thumb .t-strong { fill: var(--muted); }
.widget-thumb .t-accent { fill: var(--accent); }
.widget-thumb .t-accent-mid { fill: color-mix(in srgb, var(--accent) 60%, var(--panel)); }
.widget-thumb .t-accent-soft { fill: color-mix(in srgb, var(--accent) 30%, var(--panel)); }
.widget-thumb .t-good { fill: var(--good); }
.widget-thumb .t-danger { fill: var(--danger); }
.widget-thumb .t-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.narrative-text {
  margin: 8px 0 0;
  line-height: 1.5;
  color: var(--text);
}

.narrative-refresh-btn {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  width: 28px;
  height: 28px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
}

.narrative-refresh-btn:hover:not(:disabled) {
  color: var(--accent);
  background: var(--bg-alt);
}

.narrative-refresh-btn:disabled {
  cursor: default;
  opacity: 0.6;
}

.narrative-refresh-btn.spinning {
  animation: narrative-spin 0.8s linear infinite;
}

@keyframes narrative-spin {
  to { transform: rotate(360deg); }
}

.readiness-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.readiness-header .label {
  color: var(--text);
  font-size: 1.05rem;
  font-weight: 700;
}

.readiness-score { font-size: 2.4rem; font-weight: 700; }

/* Insight widgets can be as narrow as one grid column, so their header wraps
   (value drops below the title) rather than squeezing or overflowing - only
   when it actually doesn't fit on one line. */
.insight-widget .readiness-header {
  flex-wrap: wrap;
  row-gap: 4px;
}

.insight-widget .readiness-header .label {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Calorie Budget's value is a phrase ("1,240 kcal left"), not a bare figure
   like the other cards', so it gets a smaller size to fit alongside the title. */
.readiness-score.calorie-budget-value { font-size: 1.4rem; }

.header-score-group {
  display: flex;
  align-items: baseline;
  gap: 6px;
  flex-shrink: 0;
}

/* Hidden outside mobile - desktop/wide layouts already show this same
   composite number, larger, in the card's .meter-block below the header, so
   showing it here too would just be a redundant echo of the same figure a
   few pixels down. It only earns its place in the header once the mobile
   media query below hides that .meter-block by default to save vertical
   space, at which point this becomes the only place the actual number (not
   just Good/Fair/Low) is visible without tapping the card open. */
.header-score {
  display: none;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Mobile override for the rule directly above - has to come after it in
   source order (same reason as the other late mobile blocks in this file),
   or the unconditional display: none wins at equal specificity. */
@media (max-width: 700px) {
  .header-score { display: inline; }
}

.status-word {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  flex-shrink: 0;
}

.meter-block { margin: 10px 0 2px; }

/* Calorie Budget's intake bar - reuses .meter-track/.meter-fill/.meter-tick,
   but outside .meter-block so it stays visible on collapsed mobile cards. */
.calorie-meter { margin: 10px 0 6px; }

.meter-figures {
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin-bottom: 8px;
}

.meter-value {
  font-size: 1.6rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.meter-track {
  position: relative;
  height: 10px;
  border-radius: 5px;
  overflow: visible;
}

.meter-fill {
  height: 100%;
  border-radius: 5px;
  transition: width 0.2s ease;
}

/* Marks the scoreVar() cutoffs (40/70) the fill color itself changes at,
   same idea as the breakdown swatches - a fixed reference point on the
   track, not data. */
.meter-tick {
  position: absolute;
  top: -3px;
  width: 1px;
  height: 16px;
  background: var(--border);
}

/* --- Day nav (Today / Activity Log) --------------------------------------- */

.day-nav input[type="date"] { width: 160px; }

.day-nav-btn,
.day-nav-today-btn {
  background: var(--bg-alt);
  color: var(--text);
  border: 1px solid var(--border);
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.day-nav-btn {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  font-size: 1rem;
  line-height: 1;
}

.day-nav-today-btn { border-radius: 999px; padding: 6px 14px; font-size: 0.85rem; font-weight: 500; }

.day-nav-btn:hover:not(:disabled),
.day-nav-today-btn:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, var(--bg-alt));
}

.day-nav-btn:disabled { opacity: 0.4; cursor: default; }

/* --- Activity Log ---------------------------------------------------------
   This view swaps between a browse state (card grid + this day's logged
   entries) and an entry form, both inside the same section - see
   activity-log.js. */

.activity-log-day-stat { margin: 0; margin-left: auto; }

#activity-log-browse .panel { margin-bottom: 20px; }
#activity-log-browse .panel:last-child { margin-bottom: 0; }

.activity-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 10px;
}

.activity-card {
  display: flex;
  flex-direction: column;
  gap: 5px;
  text-align: left;
  background: var(--panel-alt-2);
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-left-color: var(--activity-accent, var(--border));
  border-radius: 10px;
  padding: 12px 14px;
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.activity-card:hover, .activity-card:focus-visible {
  border-color: var(--activity-accent, var(--accent));
  box-shadow: var(--elevation-1);
  outline: none;
}
.activity-card .activity-card-name { font-weight: 600; font-size: 0.92rem; }
.activity-card .activity-card-meta { font-size: 0.72rem; color: var(--muted); }

#activity-log-list { display: flex; flex-direction: column; gap: 7px; }

.activity-log-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 11px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
  background: var(--panel-alt-2);
}

.activity-log-entry-btn {
  flex: 1;
  display: flex;
  align-items: baseline;
  gap: 12px;
  background: none;
  border: none;
  color: inherit;
  text-align: left;
  padding: 0;
  min-width: 0;
  font-family: inherit;
  cursor: pointer;
}
.activity-log-entry-btn .time {
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  font-size: 0.78rem;
  width: 66px;
  flex-shrink: 0;
}
.activity-log-entry-btn .activity-name { font-weight: 600; font-size: 0.86rem; flex-shrink: 0; width: 112px; }
.activity-log-entry-btn .value {
  font-size: 0.86rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}

.activity-log-delete-btn {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1rem;
  line-height: 1;
  width: 28px;
  height: 28px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  flex-shrink: 0;
  cursor: pointer;
}
.activity-log-delete-btn:hover { color: var(--danger); background: var(--bg-alt); }

.activity-log-empty { color: var(--muted); font-size: 0.85rem; padding: 14px 4px; text-align: center; }

.activity-log-entry-form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px 20px;
}
/* The form's own `display: grid` above otherwise beats the UA stylesheet's
   [hidden] { display: none } at equal specificity (author styles win ties),
   which left the entry form visible at the page bottom even while hidden. */
.activity-log-entry-form[hidden] { display: none; }
.activity-log-entry-form label {
  display: flex;
  flex-direction: column;
  font-size: 0.8rem;
  color: var(--muted);
  gap: 6px;
}
/* Same specificity issue as the form rule above - this rule's higher
   specificity than a bare [hidden] would otherwise keep a hidden label
   (e.g. a no-value activity's Value field) visible. */
.activity-log-entry-form label[hidden] { display: none; }
.activity-log-full { grid-column: 1 / -1; }
#activity-log-entry-title { margin: 0; }
.activity-log-entry-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.activity-log-lookup-note { font-size: 0.7rem; color: var(--muted); margin-top: 2px; }
.activity-log-textarea { width: 100%; min-height: 110px; resize: vertical; font-family: inherit; }
#activity-log-status.error { color: var(--danger); }

.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(0, 0, 0, 0.5);
}

.modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 61;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--elevation-2);
  padding: 20px;
  width: min(560px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  overflow-y: auto;
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}

.modal-header h3 { margin: 0; font-size: 1.1rem; }

.modal-close-btn {
  background: none;
  border: none;
  border-radius: 50%;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--text);
  font-size: 0.95rem;
  flex-shrink: 0;
}

.modal-close-btn:hover { background: color-mix(in srgb, var(--text) 10%, transparent); }

.activity-log-metrics-table {
  border-collapse: collapse;
  width: 100%;
  font-size: 0.85rem;
}
.activity-log-metrics-table th, .activity-log-metrics-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  text-align: right;
}
.activity-log-metrics-table th:first-child, .activity-log-metrics-table td:first-child {
  text-align: left;
}
.activity-log-metrics-table th { color: var(--muted); font-weight: 500; }

.activity-log-metrics-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  margin-right: 7px;
  vertical-align: middle;
}

.activity-log-metrics-name {
  background: none;
  border: none;
  padding: 0;
  color: inherit;
  font: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-decoration-color: transparent;
  text-underline-offset: 3px;
  transition: text-decoration-color 0.15s ease;
}
.activity-log-metrics-name:hover, .activity-log-metrics-name:focus-visible { text-decoration-color: currentColor; }

#activity-history-list { display: flex; flex-direction: column; gap: 7px; max-height: 60vh; overflow-y: auto; }

@media (max-width: 700px) {
  .activity-log-entry-form { grid-template-columns: 1fr; }
}

/* --- Loading overlay ------------------------------------------------------
   Shown while data is being (re-)pulled in place - initial load, a Run
   Script/metric-picker refresh, or a Baseline Bands save - so a slow Sheets
   round-trip reads as "working" rather than a frozen or broken page, without
   a full browser reload. See page-chrome.js's showLoading()/hideLoading(). */
.loading-overlay {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--bg) 55%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s ease;
}

.loading-overlay.visible {
  opacity: 1;
  visibility: visible;
}

.loading-spinner {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 4px solid var(--border);
  border-top-color: var(--accent);
  animation: loading-spin 0.8s linear infinite;
}

@keyframes loading-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .loading-spinner { animation-duration: 2s; }
}

/* --- Readiness Sandbox ---------------------------------------------------- */

/* Same look as .side-menu-field's own buttons, but usable standalone (e.g.
   the sandbox's "Load Current Live Settings"/"Reset to Actual") - placed
   after .nav-btn's own rules further up the file so these properties also
   win the cascade when combined with .nav-btn. */
.settings-link-btn {
  background: var(--bg-alt);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 0.85rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.settings-link-btn:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--bg-alt)); }

/* Settings page: a left rail of sections beside the one selected panel (see
   setupSettingsNav() in page-chrome.js). The rail sticks below the sticky
   header so it stays reachable on a long section. */
/* Page heading for a view that needs one (Settings), in the brand's serif
   so it reads as the page's title rather than another panel's. */
.view-title {
  margin: 4px 0 20px;
  font-family: 'Fraunces', serif;
  font-size: 1.75rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}

.settings-layout {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}

.settings-nav {
  position: sticky;
  top: 88px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.settings-nav-item {
  display: block;
  padding: 8px 12px;
  border-radius: 8px;
  border-left: 3px solid transparent;
  color: var(--muted);
  font-size: 0.92rem;
  font-weight: 500;
  text-decoration: none;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.settings-nav-item:hover { color: var(--text); background: color-mix(in srgb, var(--text) 6%, transparent); }
.settings-nav-item[aria-current="page"] {
  color: var(--text);
  border-left-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.settings-nav-item[hidden] { display: none; }

.settings-inactive { display: none; }
.settings-back-link { display: none; }

/* Mobile: list-then-detail. With no section chosen the rail fills the page
   as a tappable list; choosing one swaps it for just that panel plus a back
   link to the list. */
@media (max-width: 700px) {
  .settings-layout { display: block; }
  .settings-layout.settings-list-mode .settings-content { display: none; }
  .settings-layout:not(.settings-list-mode) .settings-nav { display: none; }

  .settings-nav {
    position: static;
    gap: 0;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: var(--elevation-1);
    overflow: hidden;
  }

  .settings-nav-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 16px;
    border-left: none;
    border-radius: 0;
    border-bottom: 1px solid var(--border);
    color: var(--text);
    font-size: 1rem;
  }
  .settings-nav-item:last-of-type { border-bottom: none; }
  .settings-nav-item::after { content: '›'; color: var(--muted); font-size: 1.3rem; line-height: 1; }

  .view-title { font-size: 1.5rem; margin-bottom: 14px; }

  .settings-back-link {
    display: inline-block;
    margin-bottom: 12px;
    padding: 6px 0;
    color: var(--accent);
    font-weight: 500;
    text-decoration: none;
  }
}

/* Grid of small labeled number inputs for the sandbox's rolling-window/
   weight/raw-value controls - auto-fits as many columns as the panel width
   allows, same responsive spirit as the dashboard's other card grids. */
.sandbox-controls {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 12px;
}

.sandbox-control-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.sandbox-control-field label {
  font-size: 0.78rem;
  color: var(--muted);
}

.sandbox-control-field input {
  width: 100%;
  box-sizing: border-box;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-alt);
  color: var(--text);
  font-size: 0.9rem;
}

.sandbox-control-field input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Today view's placeholder for an account with no data yet (app.js's
   renderNoDataState) - spans the whole widget grid. */
.no-data-card { grid-column: 1 / -1; }
.no-data-card a { color: var(--accent); }

/* Settings > Activities (activity-settings.js) - a paged table of the user's
   Activity Log activities, edited in a modal. */
table.activities-table { table-layout: auto; min-width: 560px; }
table.activities-table th, table.activities-table td { text-align: left; }
table.activities-table th:first-child { width: auto; }
table.activities-table .is-numeric { text-align: right; }
table.activities-table td.activities-options { white-space: normal; max-width: 360px; }
table.activities-table tr.is-hidden td { color: var(--muted); }
.activities-sort-btn,
.activities-name-btn {
  background: none; border: none; padding: 0; font: inherit; color: inherit; cursor: pointer; text-align: left;
}
.activities-sort-btn { color: var(--muted); font-weight: 500; }
.activities-name-btn { color: var(--accent); font-weight: 500; }
.activities-name-btn:hover, .activities-sort-btn:hover { text-decoration: underline; }
.activities-badge {
  display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 0.75rem;
  border: 1px solid var(--border); color: var(--muted);
}
.activities-pager { display: flex; align-items: center; justify-content: flex-end; gap: 12px; margin-top: 12px; flex-wrap: wrap; }
.activities-pager[hidden] { display: none; }
.activity-form { display: flex; flex-direction: column; gap: 12px; }
.activity-form > label:not(.side-menu-checkbox) { display: flex; flex-direction: column; gap: 4px; font-size: 0.85rem; }
.activity-form textarea { resize: vertical; font-family: inherit; }
.activity-form label[hidden] { display: none; }

/* Setup wizard (onboarding.js) */
.onboarding-modal { width: min(520px, calc(100vw - 32px)); }
.onboarding-modal .modal-header { align-items: baseline; }
.onboarding-step p { line-height: 1.5; margin: 0 0 12px; }
.onboarding-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 18px; }
.onboarding-link-btn { display: inline-block; text-decoration: none; }
.onboarding-choices { display: flex; flex-direction: column; gap: 8px; margin: 4px 0 0; }
.onboarding-choices label { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.onboarding-error { color: var(--danger); }
.onboarding-progress { height: 8px; border-radius: 4px; background: var(--bg-alt); overflow: hidden; margin: 8px 0; }
.onboarding-progress-fill { height: 100%; width: 4%; background: var(--accent); border-radius: 4px; transition: width 0.4s ease; }
.onboarding-progress-fill.indeterminate { animation: onboarding-pulse 1.4s ease-in-out infinite; }
@keyframes onboarding-pulse { 50% { opacity: 0.4; } }
@media (prefers-reduced-motion: reduce) { .onboarding-progress-fill.indeterminate { animation: none; } }
.no-data-card .run-script-btn { margin-top: 10px; align-self: flex-start; }

