/* Nutrition Tracker — single stylesheet, light + dark via prefers-color-scheme. */

:root {
  color-scheme: light dark;
  --bg: #f1f1ed;
  --surface: #ffffff;
  --text: #0b0b0b;
  --text-2: #52514e;
  --muted: #898781;
  --grid: #e1e0d9;
  --border: rgba(11, 11, 11, 0.10);
  --accent: #d55181;      /* magenta — default accent; a phase re-points it */
  --on-accent: #ffffff;   /* text on accent surfaces */
  --brand: #d55181;       /* the app's own identity — phases NEVER change it */
  --on-brand: #ffffff;
  --protein: #d95926;
  --carbs: #1baf7a;
  --fat: #c98500;
  /* sleep is the phase color too — one primary, no near-miss shades */
  --sleep: var(--accent);
  --response: var(--accent); /* meal-response curve follows the accent */
  --over: #d03b3b;
  --radius: 16px;      /* iOS-style rounding, applies to all cards */
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.05), 0 6px 20px rgba(0, 0, 0, 0.07);
  /* ---- glass ----
     The two cues that actually read as "glass": a LIT top edge and a
     SHADED underside. Both are inset shadows, so they cost nothing —
     no extra compositing layer, no repaint on scroll. See the .tabbar
     rule for why we stop here and don't chase refraction. */
  --glass-lit: rgba(255, 255, 255, 0.60);
  --glass-shade: rgba(11, 11, 11, 0.09);
  --glass-tint: 58%;   /* how much surface survives; the rest is backdrop */
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0d0d0d;
    --surface: #1d1d1c;
    --text: #ffffff;
    --text-2: #c3c2b7;
    --muted: #898781;
    --grid: #2c2c2a;
    --border: rgba(255, 255, 255, 0.10);
    --accent: #e87ba4;
    --on-accent: #22141b;
    --brand: #e87ba4;
    --on-brand: #22141b;
    --protein: #eb6834;
    --carbs: #199e70;
    --fat: #eda100;
    --over: #e66767;
    --shadow: 0 2px 10px rgba(0, 0, 0, 0.35), 0 8px 28px rgba(0, 0, 0, 0.28);
    /* dark glass: the lit edge is a whisper, the underside does the work */
    --glass-lit: rgba(255, 255, 255, 0.16);
    --glass-shade: rgba(0, 0, 0, 0.40);
    --glass-tint: 52%;
  }
}

/* ---- phase palette ----------------------------------------------------
   Each phase owns a primary color; app.js stamps the active phase's key on
   <html data-phase-color>, which re-points --accent (and the meal-response
   curve that matches it) so the WHOLE UI wears that phase's color while its
   days are in view. Curated, not a free picker: contrast stays honest in
   both themes, and green (adherence scale) and red (warnings) are
   deliberately absent so meaning is never overloaded. Keys mirror
   tracker.PHASE_COLORS and the JS PHASE_COLORS map — change all three. */
[data-phase-color="violet"] { --accent: #7c5cd8; --on-accent: #ffffff; }
[data-phase-color="blue"]   { --accent: #2f7fd4; --on-accent: #ffffff; }
[data-phase-color="teal"]   { --accent: #0f938d; --on-accent: #ffffff; }
[data-phase-color="amber"]  { --accent: #b3760c; --on-accent: #ffffff; }
[data-phase-color="slate"]  { --accent: #5b7085; --on-accent: #ffffff; }
/* Utility chrome stays on the brand: a dialog is standard software
   furniture, not a dated feature, so only its phase chips carry phase
   colors. Dates and the day's own surfaces wear the phase. */
#export-dialog { --accent: var(--brand); --on-accent: var(--on-brand); }

@media (prefers-color-scheme: dark) {
  [data-phase-color="violet"] { --accent: #a68ef0; --on-accent: #171233; }
  [data-phase-color="blue"]   { --accent: #6fb0ee; --on-accent: #0b1c2e; }
  [data-phase-color="teal"]   { --accent: #35c2bb; --on-accent: #06201f; }
  [data-phase-color="amber"]  { --accent: #e3a316; --on-accent: #241a05; }
  [data-phase-color="slate"]  { --accent: #93a8bd; --on-accent: #121a22; }
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 15px;
  line-height: 1.45;
}

/* no top bar — pages own their first row edge to edge; the account
   button lives in the tab bar, so nothing has to dodge it */
.container {
  position: relative;
  max-width: 960px;
  margin: 0 auto;
  padding: 20px 16px calc(96px + env(safe-area-inset-bottom));
}

/* floating glass bottom bar: 3 tabs + a detached round action */
.tabbar-wrap {
  /* one size axis for both halves: --tabbar-cur is what they actually
     use, and .compact swaps it. Height/width transitions animate on the
     swap, so the two circles shrink together. */
  --tabbar-h: 56px;
  --tabbar-h-sm: 46px;
  --tabbar-cur: var(--tabbar-h);
  position: fixed;
  left: 50%;
  bottom: calc(11px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  /* the two halves hold the screen's edges; 928px is the container's
     content box (960 max-width less its 16px padding), so the bar lines
     up with the cards above it rather than floating free */
  justify-content: space-between;
  width: calc(100% - 32px);
  max-width: 928px;
  gap: 8px;
  z-index: 60;
}
.tabbar-wrap.compact { --tabbar-cur: var(--tabbar-h-sm); }
/* Glass, approximated — and deliberately stopping short.
   iOS 26's material refracts what is behind it: the compositor samples
   the framebuffer under the view and warps it at the edges. The web
   cannot sample its own backdrop, so that part is simply unavailable and
   faking it costs more than it returns. What DOES carry the look is
   cheap: thinner tint (more backdrop shows through), a lit top edge, a
   shaded underside, and a tighter shadow that sits the pill close to the
   glass rather than floating it in fog.
   Blur went DOWN, not up. Radius is the expensive part of
   backdrop-filter, and the real material is less blurred than classic
   frosted glass anyway — saturation is what sells it. */
.tabbar,
.tabbar-plus {
  background: color-mix(in srgb, var(--surface) var(--glass-tint), transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.9);
  backdrop-filter: blur(14px) saturate(1.9);
  border: 1px solid var(--border);
  box-shadow:
    inset 0 1px 0 var(--glass-lit),
    inset 0 -1px 0 var(--glass-shade),
    0 2px 8px rgba(0, 0, 0, 0.10),
    0 10px 28px rgba(0, 0, 0, 0.16);
}
.tabbar {
  display: flex;
  align-items: center;
  /* holds its natural size where there is room, but gives way rather
     than shoving the + off a narrow screen */
  flex: 0 1 auto;
  min-width: 0;
  gap: 2px;
  height: var(--tabbar-cur);
  box-sizing: border-box;
  /* equal on all four sides so the first and last lozenge sit the same
     distance from the pill's edge as the top one does — with the 1px
     border that is a constant 5px surround */
  padding: 4px;
  border-radius: 999px;
  /* the tabs squeeze to nothing on collapse — clip them rather than let
     them spill past the pill while they go */
  overflow: hidden;
  transition: height 0.32s cubic-bezier(0.32, 0.72, 0, 1),
              width 0.32s cubic-bezier(0.32, 0.72, 0, 1),
              padding 0.32s cubic-bezier(0.32, 0.72, 0, 1);
}
.tabbar a {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  /* four tabs keep the room three had: the bar grows wider rather than
     squeezing the labels together */
  flex: 0 1 auto;
  min-width: 0;
  width: 62px;
  /* 1px less underneath: the label's line-height leaves ~1px of leading
     below the glyphs, so equal padding centres the BOXES and leaves the
     visible ink sitting a pixel high */
  padding: 6px 6px 5px;
  border-radius: 999px;
  text-decoration: none;
  color: var(--muted);
  font-size: 10px;
  font-weight: 550;
  letter-spacing: 0.01em;
  transition: transform 0.12s ease, background 0.16s ease,
              min-width 0.32s cubic-bezier(0.32, 0.72, 0, 1),
              max-width 0.32s cubic-bezier(0.32, 0.72, 0, 1),
              width 0.32s cubic-bezier(0.32, 0.72, 0, 1),
              padding 0.32s cubic-bezier(0.32, 0.72, 0, 1),
              opacity 0.2s ease;
}
.tabbar a svg { width: 19px; height: 19px; }
.tabbar a:hover { color: var(--text); }
.tabbar a:active { transform: scale(0.94); }
/* the selected tab reads as a lozenge sitting ON the glass, not a tint
   painted into it — same lit-edge trick, one layer up */
.tabbar a.active {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 17%, transparent);
  box-shadow:
    inset 0 1px 0 var(--glass-lit),
    0 1px 3px rgba(0, 0, 0, 0.07);
}
.tabbar-plus {
  height: var(--tabbar-cur);
  aspect-ratio: 1 / 1;  /* circle stays 1:1 at the bar's exact height */
  width: auto;
  flex: 0 0 auto;
  box-sizing: border-box;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  cursor: pointer;
  transition: transform 0.12s ease,
              height 0.32s cubic-bezier(0.32, 0.72, 0, 1);
}
.tabbar-plus svg {
  width: 20px;
  height: 20px;
  transition: width 0.32s ease, height 0.32s ease;
}
.compact .tabbar-plus svg { width: 18px; height: 18px; }
.tabbar-plus:active { transform: scale(0.92); }

/* ---- collapsed: four tabs squeeze into the one you are on ----
   The bar contracts to the + button's exact circle. Everything here is
   width/height/opacity on elements that already exist — no clone, no
   re-render, so the active tab genuinely morphs into the button rather
   than being swapped for one. */
.tabbar-wrap.collapsed .tabbar {
  width: var(--tabbar-cur);
  padding: 0;
  gap: 0;   /* the squeezed-out tabs must not keep their gaps either */
  cursor: pointer;
}
.tabbar-wrap.collapsed .tabbar a:not(.active) {
  min-width: 0;
  max-width: 0;
  padding-left: 0;
  padding-right: 0;
  opacity: 0;
  pointer-events: none;
}
.tabbar-wrap.collapsed .tabbar a.active {
  min-width: 0;
  flex: 0 0 auto;   /* own the whole circle instead of shrinking inside it */
  /* 100%, not --tabbar-cur: the bar's 1px border makes its content box
     2px narrower than its height, and a --tabbar-cur-wide anchor would
     overflow it and carry the icon a pixel to the right */
  width: 100%;
  height: 100%;
  padding: 0;   /* the asymmetric label padding must not tilt the icon */
  gap: 0;
  justify-content: center;
  /* the glass circle IS the button now — a lozenge inside it would read
     as a ring around a ring */
  background: none;
  box-shadow: none;
}
.tabbar-wrap.collapsed .tabbar a.active svg { width: 19px; height: 19px; }
/* the label folds away vertically: the anchor is a column */
.tabbar a span {
  display: block;
  max-height: 14px;
  overflow: hidden;
  transition: max-height 0.26s ease, opacity 0.18s ease;
}
.tabbar-wrap.collapsed .tabbar a span { max-height: 0; opacity: 0; }

/* on a 320px screen the four tabs and the + cannot both keep their
   natural size — buy the room back from the margins before squeezing
   the labels, and "Analytics" still fits */
@media (max-width: 360px) {
  .tabbar-wrap { width: calc(100% - 20px); gap: 6px; }
  .tabbar a { padding: 6px 3px 5px; }
}

@media (prefers-reduced-motion: reduce) {
  .tabbar, .tabbar a, .tabbar a span, .tabbar-plus, .tabbar-plus svg {
    transition: none;
  }
}

/* ---- headers ---- */
.day-header {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
  margin-bottom: 26px;
}
.day-header h1 { font-size: 22px; margin: 0; font-weight: 650; }
.day-nav {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
}
/* the day title stays for screen readers and for days picked from the
   calendar that lie outside the strip */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
/* shown only when the viewed day is outside the strip */
.day-nav h1 {
  /* Visible only for a date the strip cannot reach (?date= outside its
     window), where it is the ONLY thing naming the day — so it keeps
     its full width and the strip yields instead. Without the explicit
     flex it lost the negotiation to the strip's enormous scroll width,
     was squeezed to a few pixels, and its nowrap text then spilled off
     the viewport and scrolled the whole page sideways. */
  flex: 0 0 auto;
  font-size: 15px;
  font-weight: 650;
  white-space: nowrap;
  margin: 0 2px 0 0;
  color: var(--accent);
}
.date-strip {
  display: flex;
  min-width: 0;
  gap: 6px;
  overflow-x: auto;
  /* No scroll-behavior here: centring the strip is positioning, not an
     animation, and a CSS-forced smooth scroll turns a plain scrollLeft
     assignment into an animation that never lands if the page is not
     compositing. Smooth is asked for explicitly, per call. */
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  padding: 2px 2px 6px;
  margin: -2px -2px -6px;
  scrollbar-width: none;
}
.date-strip::-webkit-scrollbar { display: none; }
.date-cell {
  flex: 0 0 auto;
  scroll-snap-align: center;
  width: 40px;
  padding: 6px 0 7px;
  border: none;
  border-radius: 14px;
  background: transparent;
  color: var(--text-2);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.date-cell em {
  font-style: normal;
  font-size: 10px;
  font-weight: 650;
  text-transform: uppercase;
  opacity: 0.75;
}
.date-cell b {
  font-size: 15px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}
.date-cell:hover { background: var(--grid); }
/* days that have not happened yet: reachable, but visibly ahead */
.date-cell.future { opacity: 0.42; }
.date-cell.future.on { opacity: 1; }
/* today gets a mark, the viewed day gets the phase colour */
.date-cell.today b { position: relative; }
.date-cell.today b::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -5px;
  width: 4px;
  height: 4px;
  margin-left: -2px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.55;
}
.date-cell.on {
  background: var(--accent);
  color: var(--on-accent);
}
.date-cell.on:hover { background: var(--accent); }
.date-cell.on b::after { opacity: 0.8; }
.day-tools {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
/* the running goal phase, named at the top of the screen */
.phase-banner {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
  padding: 6px 14px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
  font-size: 14px;
}
.phase-banner b { font-weight: 650; letter-spacing: 0.01em; }
/* header actions: soft elevated pills, icon in the phase color */
.day-actions { display: flex; gap: 8px; }
.pill-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 15px 8px 12px;
  border: none;
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow);
  font: inherit;
  font-size: 13.5px;
  font-weight: 550;
  cursor: pointer;
  transition: transform 0.12s ease, background 0.15s ease;
}
.pill-btn svg { width: 17px; height: 17px; color: var(--accent); }
.pill-btn:hover { background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }
.pill-btn:active { transform: scale(0.96); }
@media (max-width: 420px) {
  .pill-btn { padding: 8px 12px 8px 10px; font-size: 13px; }
}
.phase-banner small {
  color: var(--text-2);
  font-size: 12.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* history/analytics keep a simple side-by-side header */
.day-header.row {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
}

/* ---- buttons & inputs ---- */
.btn {
  font: inherit;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  padding: 7px 14px;
  border-radius: 8px;
  cursor: pointer;
}
.btn:hover { background: var(--grid); }
.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 550;
}
.btn.primary:hover { filter: brightness(1.08); background: var(--accent); }
.btn.ghost { background: transparent; }
.btn.ghost:hover { background: var(--grid); }
.btn.icon {
  width: 34px;
  height: 34px;
  padding: 0;
  font-size: 18px;
  line-height: 1;
  border-radius: 50%;
}
.btn.small { padding: 3px 9px; font-size: 13px; border-radius: 6px; text-decoration: none; }

.live-hint {
  font-size: 12px;
  color: var(--muted);
  border: 1px solid var(--grid);
  border-radius: 20px;
  padding: 3px 10px;
}

.head-tools { display: flex; align-items: center; gap: 10px; }

/* day-state toggle chips, one row per group.
   The group label is what keeps fourteen chips readable — without it the
   row is a wall of words with no axis to hang them on. */
.state-chips { display: flex; flex-direction: column; gap: 7px; }
/* a grid, not a wrapping flex row: the label owns a fixed first column
   so chips that wrap stay under the chips, not under the label */
.state-group {
  display: grid;
  grid-template-columns: 62px 1fr;
  align-items: start;
  gap: 6px;
}
.state-group-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.state-group-label {
  padding-top: 5px;   /* meets the first chip's text, not its top edge */
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}
.state-chip {
  font: inherit;
  font-size: 12.5px;
  padding: 4px 11px;
  border-radius: 999px;
  border: 1px dashed var(--border);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.state-chip:hover { border-style: solid; color: var(--text); }
.state-chip:disabled { opacity: 0.5; cursor: default; }
.state-chip.on {
  border-style: solid;
  font-weight: 550;
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
/* Deliberately NO colour per group. The palette above keeps one primary
   and rejects near-miss shades, and four new hues here would be exactly
   that — while saying nothing the group label does not already say.
   The label organises; the accent means "on". */

.state-badge {
  font-size: 10.5px;
  text-transform: capitalize;
  padding: 1px 7px;
  border-radius: 999px;
  margin-left: 6px;
  background: var(--grid);
  color: var(--text-2);
}
.state-badge.sick { color: var(--over); }
.state-badge.refill { color: var(--fat); }
.state-badge.rest { color: var(--sleep); }

/* sleep card */
.sleep-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
.sleep-times { font-size: 13px; color: var(--text-2); }
.sleep-duration { font-size: 22px; font-weight: 650; margin: 2px 0; }
.sleep-duration small { font-size: 13px; color: var(--muted); font-weight: 450; }
.sleep-bar { flex: 1; max-width: 420px; }
/* the per-session breakdown, shown only when a day has more than one */
.sleep-sessions .log-kcal em {
  font-style: normal;
  font-weight: 450;
  color: var(--muted);
}
.sleep-sessions {
  margin-top: 12px;
  padding-top: 4px;
  border-top: 1px solid var(--border);
}
.sleep-target-label { margin-top: 12px; max-width: 220px; }
.view-toggle {
  display: flex;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}
.toggle-btn {
  display: flex;
  align-items: center;
  padding: 5px 9px;
  background: transparent;
  border: none;
  color: var(--muted);
  cursor: pointer;
}
.toggle-btn:hover { color: var(--text); }
.toggle-btn.active { background: var(--grid); color: var(--text); }

select, input {
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 7px 10px;
}
input:focus, select:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ---- macro cards ---- */
/* mobile-first top cards: accumulated headline, intake vs output, macros */
.top-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  margin-bottom: 20px;
}
@media (min-width: 860px) {
  .top-cards {
    grid-template-columns: 1fr 1fr;
    grid-template-areas: "acc io" "macros macros";
  }
  #acc-card { grid-area: acc; }
  #io-card { grid-area: io; }
  #macros-card { grid-area: macros; }
}
.acc-thin { padding: 10px 16px 12px; }
.acc-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
.acc-io { font-size: 12.5px; color: var(--muted); }
.io-balance { font-weight: 650; font-size: 14px; }
/* on-track is a brand moment (accent), off-track a warning (red);
   green is reserved for the semantic adherence scale */
.io-balance.good { color: var(--accent); }
.io-balance.bad { color: var(--over); }
.macro-card {
  background: var(--surface);
  border-radius: var(--radius);
  padding: 14px 16px;
  box-shadow: var(--shadow);
}
.macro-card .macro-name {
  font-size: 13px;
  color: var(--text-2);
  font-weight: 550;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  display: flex;
  justify-content: space-between;
}
.macro-card .macro-value {
  font-size: 24px;
  font-weight: 650;
  margin: 4px 0 2px;
}
.macro-card .macro-value small { font-size: 14px; color: var(--muted); font-weight: 450; }
.macro-card .macro-remaining { font-size: 13px; color: var(--text-2); }
.macro-card .macro-remaining.over { color: var(--over); font-weight: 550; }
.bar {
  height: 6px;
  border-radius: 4px;
  background: var(--grid);
  margin-top: 10px;
  overflow: hidden;
}
.bar > span {
  display: block;
  height: 100%;
  border-radius: 4px;
  background: var(--accent);
  transition: width 0.25s ease;
}
.bar > span.over { background: var(--over); }

/* ---- panels & tables ---- */
.panel {
  background: var(--surface);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 20px;
  box-shadow: var(--shadow);
}
.panel h2 { font-size: 16px; margin: 0 0 12px; font-weight: 650; }
.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}
.panel-head h2 { margin: 0; }

.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
th, td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--grid);
  white-space: nowrap;
}
th {
  font-size: 12px;
  color: var(--muted);
  font-weight: 550;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.wrap { white-space: normal; min-width: 200px; }
tbody tr:hover { background: color-mix(in srgb, var(--grid) 45%, transparent); }
tbody tr:last-child td { border-bottom: none; }
td .food-name { font-weight: 550; }
td .food-notes { color: var(--muted); font-size: 12.5px; }
.meal-tag {
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 20px;
  background: var(--grid);
  color: var(--text-2);
  text-transform: capitalize;
}
.row-actions { display: flex; gap: 4px; justify-content: flex-end; }
tfoot td { font-weight: 650; border-top: 2px solid var(--grid); border-bottom: none; }

.empty { color: var(--muted); text-align: center; padding: 18px 0; margin: 0; }

/* ---- dialogs ---- */
dialog {
  border: none;
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  padding: 20px;
  width: min(520px, calc(100vw - 32px));
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.25);
}
dialog::backdrop { background: rgba(0, 0, 0, 0.4); }
dialog h3 { margin: 0 0 14px; font-size: 17px; }
.hint { color: var(--muted); font-size: 13px; margin: -8px 0 12px; }
.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 12px;
}
.form-grid label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: var(--text-2);
  font-weight: 500;
}
.form-grid label.wide { grid-column: 1 / -1; }
.form-grid input, .form-grid select, .form-grid textarea {
  width: 100%;
  min-width: 0;
}
/* The base look lives on `select, input`, which predates any textarea in
   a dialog. Repeated here rather than widened there: the chat and profile
   textareas are deliberately styled differently and would inherit it. */
.form-grid textarea {
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 7px 10px;
  resize: vertical;
  line-height: 1.35;
}
.form-grid textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.form-grid-3 { grid-template-columns: 1fr 1fr 1fr; }

/* sectioned profile/targets dialog */
.dialog-tall {
  max-height: min(85vh, 780px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* sections separate by shade, not outlines (same language as the cards) */
.dialog-block {
  background: color-mix(in srgb, var(--text) 4%, var(--surface));
  border-radius: 12px;
  padding: 14px 16px 16px;
  margin-bottom: 12px;
}
@media (prefers-color-scheme: dark) {
  .dialog-block {
    background: color-mix(in srgb, #ffffff 5%, var(--surface));
  }
}
.dialog-section {
  margin: 0 0 10px;
  font-size: 12px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
}
.stacked-label {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 13px;
  color: var(--text-2);
  font-weight: 500;
}
.dialog-block .hint { margin: 8px 0 0; }

/* BMR -> TDEE -> suggestion flow */
.calc-flow {
  display: flex;
  align-items: stretch;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
}
.calc-tile {
  flex: 1 1 90px;
  min-width: 90px;
  border-radius: 12px;
  padding: 9px 12px;
  background: color-mix(in srgb, var(--text) 5%, transparent);
}
.calc-tile.accent {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, var(--bg));
}
.calc-label {
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 600;
}
.calc-value { font-size: 19px; font-weight: 650; margin: 1px 0; }
.calc-tile.accent .calc-value { color: var(--accent); }
.calc-sub { font-size: 11.5px; color: var(--muted); }
.calc-arrow {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  min-width: 44px;
}
.calc-arrow span { font-size: 15px; line-height: 1; }
.calc-arrow em {
  font-style: normal;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}
.macro-chips {
  flex-basis: 100%;
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  color: var(--text-2);
  border: 1px solid var(--grid);
  border-radius: 999px;
  padding: 3px 11px;
  font-variant-numeric: tabular-nums;
}
.chip i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
}

.tune-head { display: flex; gap: 8px; margin-top: 10px; }
#goal-tune { margin-top: 10px; }

/* accumulated / balance coloring */
.macro-value.acc-good { color: var(--accent); }
.macro-value.acc-bad { color: var(--over); }
.acc-since { font-size: 11px; text-transform: none; letter-spacing: 0; font-weight: 450; }
.acc-macros {
  font-size: 12px;
  color: var(--muted);
  margin-top: 6px;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  font-variant-numeric: tabular-nums;
}
.acc-empty { padding: 6px 0; text-align: left; font-size: 13px; }

/* slim accumulated-balance bars with numbers inside */
.acc-bars { display: flex; flex-direction: column; gap: 5px; margin-top: 10px; }
.acc-bar {
  position: relative;
  height: 21px;
  background: var(--grid);
  border-radius: 11px;
  overflow: hidden;
}
/* a component of the line above it, not a peer — thinner on purpose, so
   the three macros read as the structure and this as a detail of one */
.acc-bar.sub {
  height: 15px;
  border-radius: 8px;
}
.acc-bar.sub span { font-size: 10.5px; }
.acc-bar i {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: 11px;
}
.acc-bar.sub i { border-radius: 8px; }
.acc-bar span {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 0 9px;
  font-size: 11px;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* A macro row that movement has extended: the plan, a gap, then the
   earned room. Two tracks sized by flex-grow in proportion to their real
   amounts, so a 20 g earned stretch beside a 130 g plan looks like 20 g.
   The label sits ABOVE both, spanning the row, so the text reads
   continuously across the gap the way the fill does. */
.acc-bar-row {
  position: relative;
  display: flex;
  align-items: stretch;
  /* no gap: plan and earned are joined by a neck instead, and the over
     section sets its own margin */
  gap: 0;
  height: 21px;
}
/* over is not joined: it is not more room, and a neck would claim it
   was. A plain 2px step, matching the plan-earned distance exactly. */
.acc-bar-row .acc-bar.over { margin-left: 2px; }

/* The neck between plan and earned. Half the bar's height — thicker than
   a hairline, thinner than the tracks — so the pair reads as one vessel
   with a waist rather than two bars pushed together. Square-ish ends,
   because a rounded neck would read as a third pill. */
.bar-link {
  /* 6px wide against a -2px margin each side, so it CONTRIBUTES exactly
     2px of layout — the same step as plan-to-over. The neck covers the
     gap rather than widening it: it spans the 2px plus 2px into each
     neighbour, and the distance between the tracks stays 2px. */
  flex: 0 0 6px;
  align-self: center;
  /* square: any radius here reads as a third pill sitting between two
     others, which is exactly what made the first attempt look like a
     block dropped in the gap */
  border-radius: 0;
  margin: 0 -2px;
  position: relative;
  z-index: 1;   /* over BOTH tracks: the neck's job is to hide the two
                   facing outlines it crosses, which it can only do from
                   in front */
  /* Top and bottom only — those two edges continue the outline across
     the gap. No side borders: the neck opens into both tracks.
     10px tall because that is where the pills' rounded caps actually sit
     2px in from their ends (r=10.25 after the inset stroke, dx=9, so
     y=±4.9), which is what makes the strokes meet instead of stepping. */
  height: 10px;
  border-top: 1.5px solid color-mix(in srgb, var(--accent) 48%, transparent);
  border-bottom: 1.5px solid color-mix(in srgb, var(--accent) 48%, transparent);
  /* OPAQUE: layered over the card so the tint reads the same as a track
     while still hiding the outlines beneath it. A translucent neck would
     let the two facing strokes show straight through and the whole
     effect would collapse. */
  background:
    linear-gradient(color-mix(in srgb, var(--accent) 16%, transparent) 0 100%),
    var(--surface);
  transition: background 0.35s ease;
}
/* filled only when the plan track is full: that is the moment intake
   really is running through into the earned room */
.bar-link.on { background: var(--accent); }
.acc-bar-row .acc-bar { height: 100%; }
/* ---- sectioned macro row: plan | earned | over -------------------------
   The energy card's grammar, one row at a time. OUTLINE marks the base
   you have to cover; a flat tint marks room that movement earned; amber
   -> red marks what went past both. Structure carries the colour, so
   only the third section is ever a judgement.

   Order matters here. `.acc-bar i` above sets the fill box; these rules
   set what it is PAINTED with and must come after it, and none of them
   may set a background on `.acc-bar` itself without excluding the fill —
   that source-order trap has bitten this stylesheet twice. */
.acc-bar-row .acc-bar.plan {
  /* the sentence lives in here, so it may not be squeezed below reading
     width however far past the plan the day went */
  min-width: 96px;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  box-shadow: inset 0 0 0 1.5px
    color-mix(in srgb, var(--accent) 48%, transparent);
}
.acc-bar-row .acc-bar.earned {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  /* outlined like the plan track: with the neck bridging top and bottom,
     the two outlines and the neck's read as ONE continuous stroke around
     the whole vessel rather than two separate pills */
  box-shadow: inset 0 0 0 1.5px
    color-mix(in srgb, var(--accent) 48%, transparent);
  min-width: 26px;
}
.acc-bar-row .acc-bar.over {
  background: color-mix(in srgb, var(--over) 18%, transparent);
  min-width: 42px;   /* enough for "+120" */
}
/* eaten is eaten: solid, in both of the first two sections */
.acc-bar-row .acc-bar.plan i,
.acc-bar-row .acc-bar.earned i { background: var(--accent); }
/* the third one is always full — it only exists because it was passed */
.acc-bar-row .acc-bar.over i {
  background: linear-gradient(90deg,
    hsl(38 82% 52%), var(--over));
}

/* The duplicated sentence, clipped to the filled width and wearing the
   colour meant to sit on the accent. Same box, same padding, same
   layout as the original underneath it — only the colour differs, so
   the two halves line up glyph for glyph. */
.acc-bar-row .bar-on {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 0 9px;
  font-size: 11px;
  color: var(--on-accent);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
}
.acc-bar-row .bar-on b { font-weight: 650; color: var(--on-accent); }
.acc-bar-row .bar-on em {
  font-style: normal; margin-left: auto; color: var(--on-accent);
}

/* Each of the trailing sections names its own amount, so the plan
   track's sentence never has to stretch across a gap to explain them.
   Scoped to .acc-bar, NOT to .earned: the over section carries a label
   too, and while this rule was earned-only that label had no positioning
   and sat behind the absolutely-placed fill, invisible. */
.acc-bar-row .acc-bar u {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10.5px;
  font-weight: 600;
  text-decoration: none;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  pointer-events: none;
}
/* The earned amount is a footnote to the plan beside it, not a peer:
   smaller and lighter than the row's own sentence so it reads as "and a
   bit more" rather than competing for the glance. Placed AFTER the base
   rule above so these win on source order — the trap this stylesheet
   keeps setting for itself. */
.acc-bar-row .acc-bar.earned u {
  font-size: 10px;
  font-weight: 500;
  color: var(--muted);
}
/* centred, so past half-filled it sits on solid colour and has to flip —
   softened, because full-strength on-accent was the strongest text in
   the card and it is the least important number in the row */
.acc-bar-row .acc-bar.earned u.on {
  color: color-mix(in srgb, var(--on-accent) 72%, transparent);
}
/* the over section is always full, so its label is always on the gradient */
.acc-bar-row .acc-bar.over u { color: #fff; }
.acc-bar span b { font-weight: 650; color: var(--text); }
.acc-bar span em { font-style: normal; margin-left: auto; color: var(--text); }

.row-editable { cursor: pointer; }
.row-editable:hover .food-name { text-decoration: underline; text-underline-offset: 2px; }
.suggest-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}
.suggest-row .dialog-section { margin-bottom: 0; }

.goal-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0;
}
.goal-label { font-weight: 550; }
.seg {
  display: flex;
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: hidden;
  background: var(--bg);
}
.seg button {
  font: inherit;
  font-size: 13.5px;
  padding: 6px 18px;
  border: none;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.seg button:hover { color: var(--text); }
.seg button.active {
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 550;
}
.dialog-actions {
  position: sticky;
  bottom: -20px;
  background: var(--surface);
  padding: 12px 0 4px;
  margin-top: 4px;
}

@media (max-width: 560px) {
  .form-grid-3 { grid-template-columns: 1fr 1fr; }
}
.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 16px;
}

/* ---- analytics ---- */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin-bottom: 20px;
}
.stat-tile {
  background: var(--surface);
  border-radius: var(--radius);
  padding: 14px 16px;
  box-shadow: var(--shadow);
}
.stat-tile .stat-label { font-size: 12.5px; color: var(--text-2); font-weight: 550; }
.stat-tile .stat-value { font-size: 22px; font-weight: 650; margin-top: 2px; }
.stat-tile .stat-sub { font-size: 12.5px; color: var(--muted); }

.chart { position: relative; }
.chart svg { display: block; width: 100%; height: auto; }
.chart .bar-rect { cursor: pointer; }
.chart-tooltip {
  position: absolute;
  pointer-events: none;
  background: var(--text);
  color: var(--bg);
  font-size: 12.5px;
  padding: 6px 9px;
  border-radius: 7px;
  white-space: nowrap;
  transform: translate(-50%, calc(-100% - 8px));
  z-index: 5;
}

.comparison { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.comparison .comp-col {
  background: color-mix(in srgb, var(--text) 5%, transparent);
  border-radius: 12px;
  padding: 12px 14px;
}
.comparison .comp-col h4 { margin: 0 0 8px; font-size: 13.5px; color: var(--text-2); }
.comparison .comp-col .big { font-size: 20px; font-weight: 650; }
.comparison .comp-col ul { list-style: none; margin: 8px 0 0; padding: 0; font-size: 13.5px; color: var(--text-2); }
.comparison .comp-col li { display: flex; justify-content: space-between; padding: 2px 0; }

/* ---- misc ---- */
.toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--text);
  color: var(--bg);
  padding: 9px 18px;
  border-radius: 10px;
  font-size: 14px;
  z-index: 100;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
}
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

@media (max-width: 560px) {
  .form-grid { grid-template-columns: 1fr; }
  .comparison { grid-template-columns: 1fr; }
  .day-nav h1 { min-width: 0; font-size: 18px; }
}

/* ---- assistant chat (the "+" button) ---- */
.assistant-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(0, 0, 0, 0.35);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 12px 12px calc(12px + env(safe-area-inset-bottom));
}
.assistant-panel {
  width: 100%;
  max-width: 560px;
  height: min(78vh, 640px);
  display: flex;
  flex-direction: column;
  border-radius: 22px;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  -webkit-backdrop-filter: blur(24px) saturate(1.5);
  backdrop-filter: blur(24px) saturate(1.5);
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.35);
  overflow: hidden;
}
.assistant-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 14px 10px;
  border-bottom: 1px solid var(--border);
}
.assistant-title { font-weight: 650; }
.assistant-daychip {
  font-size: 12.5px;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-radius: 999px;
  padding: 2px 10px;
}
.assistant-head-spacer { flex: 1; }
.assistant-iconbtn {
  background: none;
  border: none;
  color: var(--muted);
  width: 32px;
  height: 32px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.assistant-iconbtn:hover { color: var(--text); background: color-mix(in srgb, var(--text) 7%, transparent); }
.assistant-iconbtn svg { width: 18px; height: 18px; }
.assistant-msgs {
  flex: 1;
  overflow-y: auto;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.assistant-msg {
  max-width: 86%;
  padding: 9px 13px;
  border-radius: 16px;
  font-size: 14px;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
.assistant-msg.user {
  align-self: flex-end;
  background: var(--accent);
  color: var(--on-accent);
  border-bottom-right-radius: 6px;
}
.assistant-msg.model {
  align-self: flex-start;
  background: color-mix(in srgb, var(--text) 7%, transparent);
  border-bottom-left-radius: 6px;
}
.assistant-msg.notice {
  align-self: center;
  max-width: 96%;
  background: none;
  border: 1px dashed var(--border);
  color: var(--muted);
  font-size: 13px;
  text-align: center;
}
.assistant-msg.error {
  align-self: flex-start;
  background: color-mix(in srgb, var(--over) 12%, transparent);
  color: var(--over);
  font-size: 13.5px;
}
.assistant-typing {
  align-self: flex-start;
  color: var(--muted);
  font-size: 13px;
  padding: 4px 13px;
}
.assistant-typing::after {
  content: "…";
  animation: assistant-dots 1.2s steps(4, end) infinite;
}
@keyframes assistant-dots {
  0% { content: ""; } 25% { content: "."; }
  50% { content: ".."; } 75% { content: "..."; }
}

/* proposal card */
.proposal {
  align-self: stretch;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: color-mix(in srgb, var(--surface) 70%, transparent);
  padding: 12px 14px;
  font-size: 13.5px;
}
.proposal h4 {
  margin: 0 0 8px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}
.proposal-section { margin-bottom: 8px; }
.proposal-section:last-of-type { margin-bottom: 0; }
.proposal-row {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 5px 0;
}
.proposal-row input[type="checkbox"] {
  accent-color: var(--accent);
  margin-top: 3px;
  flex-shrink: 0;
}
.proposal-row .p-body { flex: 1; min-width: 0; }
.proposal-row .p-name { font-weight: 550; }
.proposal-row .p-meta { color: var(--muted); font-size: 12.5px; }
.proposal-row .p-assume {
  color: var(--muted);
  font-size: 12px;
  font-style: italic;
}
.proposal-row .p-nums {
  text-align: right;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}
.proposal-row .p-nums .p-kcal { font-weight: 600; }
.proposal-row .p-nums .p-macros { color: var(--muted); font-size: 12px; }
.proposal-row.off .p-body, .proposal-row.off .p-nums { opacity: 0.4; }
.proposal-totals {
  border-top: 1px solid var(--border);
  margin-top: 10px;
  padding-top: 9px;
  color: var(--text-2);
  font-size: 13px;
  display: flex;
  justify-content: space-between;
  font-variant-numeric: tabular-nums;
}
.proposal-warn { color: var(--fat); font-size: 12.5px; margin-top: 6px; }
.proposal-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}
.proposal-actions button {
  flex: 1;
  border-radius: 10px;
  padding: 8px 0;
  font-size: 14px;
  cursor: pointer;
  border: 1px solid var(--border);
  background: none;
  color: var(--text-2);
}
.proposal-actions .p-accept {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}
.proposal-actions button:disabled { opacity: 0.5; cursor: default; }
.proposal.resolved .proposal-actions { display: none; }
.proposal .proposal-outcome {
  margin-top: 10px;
  font-weight: 600;
  font-size: 13px;
  color: var(--carbs);
}
.proposal .proposal-outcome.declined { color: var(--muted); }

.assistant-inputrow {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom) / 2);
  border-top: 1px solid var(--border);
}
.assistant-inputrow textarea {
  flex: 1;
  resize: none;
  max-height: 120px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  padding: 9px 13px;
  line-height: 1.35;
}
.assistant-inputrow textarea:focus { outline: 2px solid color-mix(in srgb, var(--accent) 45%, transparent); }
.assistant-send {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: none;
  background: var(--accent);
  color: var(--on-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
}
.assistant-send:disabled { opacity: 0.45; cursor: default; }
.assistant-send svg { width: 18px; height: 18px; }

@media (min-width: 700px) {
  .assistant-overlay { align-items: center; }
  .assistant-panel { height: min(72vh, 680px); }
}

/* ---- row delete buttons + meal response indexes ---- */
th.del-col, td.del-col { width: 34px; text-align: center; padding-left: 2px; padding-right: 6px; }
.row-del {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: none;
  color: var(--muted);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  transition: all 0.12s ease;
}
.row-del:hover { color: var(--over); border-color: var(--over); }
.row-del.armed {
  background: var(--over);
  border-color: var(--over);
  color: #fff;
  transform: scale(1.08);
}
.row-del:disabled { opacity: 0.4; cursor: default; }

.response-meals {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}
.response-meal-chip {
  display: flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 6px 4px 12px;
  font-size: 13px;
  background: color-mix(in srgb, var(--surface) 70%, transparent);
}
.response-meal-chip .rm-time { color: var(--muted); font-size: 12px; }
.response-meal-chip .rm-name { font-weight: 550; }
.response-meal-chip .rm-idx { font-weight: 700; font-variant-numeric: tabular-nums; }
.response-meal-chip .rm-band { color: var(--muted); font-size: 12px; }

.proposal-mri {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.proposal-mri-chip {
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 10px;
  font-size: 12px;
  color: var(--text-2);
}

/* ---- sleep extras (quality / wake-up feeling) ---- */
.sleep-extras { margin: 2px 0 2px 33px; }
.extra-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 6px;
}
.extra-label {
  font-size: 11.5px;
  color: var(--muted);
  min-width: 62px;
}
.extra-chip {
  border: 1px solid var(--border);
  background: none;
  color: var(--text-2);
  border-radius: 999px;
  padding: 2px 10px;
  font-size: 12px;
  cursor: pointer;
  transition: all 0.1s ease;
}
.extra-chip:hover { border-color: var(--accent); }
.extra-chip.sel {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}
.extra-chip:disabled { opacity: 0.55; cursor: default; }
.extra-chip:disabled:not(.sel) { opacity: 0.3; }
.sleep-extras-line {
  margin-top: 4px;
  font-size: 12.5px;
  color: var(--sleep);
  font-weight: 550;
  text-transform: capitalize;
}

/* ---- thick single-track bars (intake-vs-output, accumulated pace) ---- */
.thick-bar {
  position: relative;
  height: 34px;
  background: var(--grid);
  border-radius: 999px;
  overflow: hidden;
  margin-top: 10px;
}
.thick-bar i {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: 999px;
  transition: width 0.35s ease;
}
.thick-bar .tb-mark {
  position: absolute;
  top: 5px;
  bottom: 5px;
  width: 3px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--text) 55%, transparent);
}
.thick-bar span {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  padding: 0 14px;
  font-size: 13.5px;
  color: var(--text);
}
.thick-bar span b { font-weight: 650; }
.thick-bar span em { font-style: normal; margin-left: auto; color: var(--text-2); }
.tb-caption {
  display: flex;
  justify-content: space-between;
  /* the two halves must never touch when they happen to fill the row —
     10px is the widest gap that still lets the block count and the goal
     window share one line on a 375px screen */
  gap: 6px 10px;
  flex-wrap: wrap;
  margin-top: 6px;
  font-size: 12px;
  color: var(--muted);
}
.tb-caption > span:last-child { text-align: right; }

/* profile page */
.profile-head { text-align: center; margin: 6px 0 22px; }
.profile-avatar {
  width: 76px;
  height: 76px;
  border-radius: 50%;
  margin: 0 auto 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--accent);
}
.profile-avatar svg { width: 40px; height: 40px; }
.profile-head h1 { margin: 0; font-size: 22px; }
.profile-sub { margin: 4px 0 0; color: var(--muted); font-size: 13.5px; }

.profile-card { padding: 0; overflow: hidden; margin-bottom: 12px; }
.profile-row {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 15px 18px;
  background: none;
  border: none;
  font: inherit;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  text-decoration: none;
}
.profile-row:hover:not(:disabled) { background: color-mix(in srgb, var(--text) 4%, transparent); }
.profile-row:disabled { cursor: default; opacity: 0.55; }
.profile-row-icon { font-size: 20px; width: 28px; text-align: center; }
.profile-row-text { flex: 1; display: flex; flex-direction: column; gap: 1px; }
.profile-row-text b { font-weight: 600; }
.profile-row-text small { color: var(--muted); font-size: 12.5px; }
.profile-chevron {
  color: var(--muted);
  font-size: 20px;
  transition: transform 0.15s ease;
}
.profile-card.open .profile-chevron { transform: rotate(90deg); }
.profile-body { padding: 4px 18px 16px; }
.profile-body textarea {
  width: 100%;
  resize: vertical;
  min-height: 110px;
  font: inherit;
  font-size: 14px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg);
  color: var(--text);
}
.profile-body-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
}
.profile-body-actions .hint { margin: 0; }
.export-quick { display: flex; gap: 8px; margin: 10px 0; }
.export-quick .btn { flex: 1; }
.export-range {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 4px;
}
.export-range label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-2);
}
.export-range input[type="date"] {
  font: inherit;
  font-size: 13px;
  padding: 5px 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
}

/* ---- playful fat blocks (accumulated balance) ---- */
.fat-blocks {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 12px;
}
.fat-block {
  /* square: a block is one unit of mass, and reads as one whether the
     grid is a handful of 100 g chunks or a field of 10 g ones */
  width: 24px;
  height: 24px;
  border-radius: 6px;
  background: linear-gradient(160deg,
    color-mix(in srgb, var(--bc) 92%, #ffffff),
    var(--bc) 55%,
    color-mix(in srgb, var(--bc) 78%, #000000));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35),
              0 1px 2px rgba(0, 0, 0, 0.18);
  animation: fat-block-pop 0.35s ease backwards;
  animation-delay: var(--d, 0ms);
  transition: transform 0.12s ease;
}
.fat-block:hover { transform: translateY(-2px) scale(1.08); }
.fat-block.partial {
  position: relative;
  background: none;
  border: 1.5px solid color-mix(in srgb, var(--bc) 70%, transparent);
  box-shadow: none;
  overflow: hidden;
  animation: none;
}
/* The grid reads left to right, so a block fills left to right too —
   the same direction the wavefront crosses the row. Countdown and fill
   already did this through their own anchoring; this brings the plain
   leading block (banking and maintain drift) in line, so every block on
   this card moves the same way. The energy card's segments stay
   bottom-up: those are one day's food filling a vessel, not a row being
   crossed. */
.fat-block.partial::after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: var(--fill, 0%);
  background: color-mix(in srgb, var(--bc) 45%, transparent);
  border-radius: 4px 0 0 4px;
  transition: width 0.4s ease;
}
/* Countdown mode shows the WHOLE goal from day one and takes it apart:
   solid blocks are the mass still to lose, and a burned one is left as a
   faint ghost of itself. The grid literally dissolves as the cut works —
   the inverted reading the owner asked for. (Growing runs the same
   classes the other way: ghosts are what is still to gain.) */
/* Today's share of the countdown: burned by the clock and by logged
   activity, but only ASSUMED — eating gives it straight back. Drawn with
   the same solid outline as the block currently filling, so today reads
   as one continuous, still-moving stretch; settled days stay dashed. */
/* Four states, one story, reading left to right: a solid block is mass
   the day has no claim on; a half-lit one is what TODAY expects to burn
   and has not burned yet; an empty outline is what today has burned
   already — an estimate, so eating gives it straight back; and a dashed
   ghost is a day that has closed and cannot come back. */
.fat-block.estimate {
  background: color-mix(in srgb, var(--bc) 50%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--bc) 70%, transparent);
  box-shadow: none;
  animation: none;
}
.fat-block.assumed,
.fat-block.draining,
.fat-block.to-gain,
.fat-block.filling {
  background: none;
  border: 1.5px solid color-mix(in srgb, var(--bc) 70%, transparent);
  box-shadow: none;
  animation: none;
}
/* Growing runs the same band the other way. A cut states today's estimate
   at half strength and empties it to nothing; a bulk states it empty and
   fills it to half strength, because a deficit arrives on its own and a
   surplus has to be eaten. Half-lit therefore means "today's, provisional"
   in both, and solid means settled in both. */
.fat-block.assumed-fill {
  background: color-mix(in srgb, var(--bc) 50%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--bc) 70%, transparent);
  box-shadow: none;
  animation: none;
}
/* The edge. The burn crosses the grid left to right, so it crosses this
   block the same way: what is left of it holds the right-hand side and
   retreats, still wearing the state it is being taken from. */
.fat-block.draining::after {
  top: 0;
  bottom: 0;
  left: auto;
  right: 0;
  width: var(--fill, 0%);
  height: auto;
  border-radius: 0 4px 4px 0;
  transition: width 0.4s ease;
  background: linear-gradient(160deg,
    color-mix(in srgb, var(--bc) 92%, #ffffff),
    var(--bc) 55%,
    color-mix(in srgb, var(--bc) 78%, #000000));
}
.fat-block.draining.est::after {
  background: color-mix(in srgb, var(--bc) 50%, transparent);
}
/* the block being gained right now: fills from the left, in the same
   half strength the finished ones of today wear */
.fat-block.filling::after {
  top: 0;
  bottom: 0;
  left: 0;
  right: auto;
  width: var(--fill, 0%);
  height: auto;
  border-radius: 4px 0 0 4px;
  transition: width 0.4s ease;
  background: color-mix(in srgb, var(--bc) 50%, transparent);
}

.fat-block.cleared {
  background: none;
  border: 1.5px dashed color-mix(in srgb, var(--bc) 40%, transparent);
  box-shadow: none;
  opacity: 0.5;
  animation: none;
}

/* Counting in energy instead of body mass: many more, much smaller
   blocks, so the same balance reads as a fine-grained field rather than
   a handful of chunks. */
.fat-blocks.compact { gap: 3px; }
.fat-blocks.compact .fat-block {
  width: 10px;
  height: 10px;
  border-radius: 3px;
}
.fat-blocks.compact .fat-block.partial,
.fat-blocks.compact .fat-block.cleared,
.fat-blocks.compact .fat-block.estimate,
.fat-blocks.compact .fat-block.to-gain,
.fat-blocks.compact .fat-block.assumed { border-width: 1px; }
.fat-blocks.compact .fat-block.partial::after { border-radius: 2px 0 0 2px; }
.fat-blocks.compact .fat-block.draining::after { border-radius: 0 2px 2px 0; }
.fat-blocks.compact .fat-block.filling::after { border-radius: 2px 0 0 2px; }

/* the block-unit toggle sits beside the info button, carrying both the
   mass and its energy so the equivalence is never a mystery */
.unit-toggle {
  flex: 0 0 auto;
  margin-left: auto;
  display: flex;
  align-items: baseline;
  gap: 5px;
  padding: 2px 9px;
  border: none;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  font: 650 10.5px/1.6 inherit;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background-color 0.12s ease;
}
.unit-toggle em {
  font-style: normal;
  font-weight: 550;
  opacity: 0.72;
}
.unit-toggle:hover { background: color-mix(in srgb, var(--accent) 22%, transparent); }
/* the info button follows the toggle, so the title keeps the left edge */
.macro-card .macro-name > span:first-child { margin-right: 0; }

@keyframes fat-block-pop {
  from { opacity: 0; transform: scale(0.4); }
  60% { transform: scale(1.12); }
  to { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .fat-block { animation: none; }
}
.fat-blocks.static .fat-block { animation: none; }
.fat-blocks.static .fat-block.partial::after { transition: none; }

/* ---- login (access gate) ---- */
.login-wrap {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 24px;
}
.login-icon { border-radius: 20px; box-shadow: var(--shadow); }
.login-wrap h1 { margin: 0; font-size: 20px; }
.login-tagline {
  margin: -4px 0 4px;
  text-align: center;
  color: var(--text-2);
  font-size: 15px;
  max-width: 300px;
}
.login-form { display: flex; gap: 8px; width: min(320px, 100%); }
.login-form input {
  flex: 1;
  font: inherit;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text);
}
.login-error { color: var(--over); font-size: 13.5px; margin: 0; }
.login-note {
  color: var(--text-2);
  font-size: 14px;
  text-align: center;
  max-width: 320px;
  margin: 0;
}
.profile-email { margin: 2px 0 0; color: var(--accent); font-size: 13.5px; }
.profile-card form { margin: 0; }

/* phase choice spotlight: the mandatory "Apply as" block pulses when the
   user tries to save without deciding */
#phase-block {
  border-radius: 12px;
  transition: outline-color 0.25s ease;
  outline: 2px solid transparent;
  outline-offset: 6px;
}
#phase-block.attention {
  outline-color: var(--accent);
  animation: phase-pulse 0.55s ease 2;
}
@keyframes phase-pulse {
  0%, 100% { transform: none; }
  35% { transform: translateX(-4px); }
  70% { transform: translateX(4px); }
}

/* ---- phase color picker (targets dialog) ---- */
.swatches { display: flex; gap: 8px; flex-wrap: wrap; }
.swatch {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid transparent;
  background: var(--pc);
  cursor: pointer;
  padding: 0;
  outline-offset: 2px;
  transition: transform 0.12s ease;
}
.swatch:hover { transform: scale(1.12); }
.swatch.active {
  border-color: var(--surface);
  outline: 2px solid var(--pc);
}

/* ---- phase chips (export dialog, analytics legend) ---- */
.export-phases { display: flex; flex-wrap: wrap; gap: 8px; }
.phase-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 13px;
  border: none;
  border-radius: 999px;
  background: color-mix(in srgb, var(--pc) 14%, transparent);
  color: var(--text);
  font: inherit;
  font-size: 13.5px;
  font-weight: 550;
  cursor: pointer;
}
.phase-chip i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--pc);
  flex: none;
}
.phase-chip small { color: var(--muted); font-weight: 400; }
.phase-chip:hover { background: color-mix(in srgb, var(--pc) 24%, transparent); }
.phase-chip.static { cursor: default; }
.phase-legend {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: -6px 0 14px;
}
.phase-legend small { color: var(--muted); font-size: 13px; }
.export-range { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
.export-range label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: var(--text-2);
}
.export-quick { display: flex; gap: 8px; flex-wrap: wrap; }
.export-range-btn { margin-top: 10px; }

/* ---- history: one collapsible card per phase ---- */
.phase-card { padding: 0; overflow: hidden; }
.phase-card-head {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas: "title chevron" "meta chevron";
  align-items: center;
  gap: 2px 10px;
  width: 100%;
  padding: 14px 18px;
  background: none;
  border: none;
  border-left: 4px solid var(--pc, var(--accent));
  font: inherit;
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
.phase-card-head:hover {
  background: color-mix(in srgb, var(--pc, var(--accent)) 7%, transparent);
}
.phase-card-title {
  grid-area: title;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 650;
  color: var(--pc, var(--accent));
}
.phase-card-title i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--pc, var(--accent));
}
.phase-card-meta {
  grid-area: meta;
  color: var(--muted);
  font-size: 12.5px;
  min-width: 0;
}
.phase-card-chevron {
  grid-area: chevron;
  color: var(--muted);
  font-size: 22px;
  line-height: 1;
  transition: transform 0.18s ease;
}
.phase-card.open .phase-card-chevron { transform: rotate(90deg); }
.phase-card-body { display: none; padding: 0 4px 6px; }
.phase-card.open .phase-card-body { display: block; }
@media (max-width: 560px) {
  .phase-card-head { padding: 13px 14px; }
}

/* analytics phase picker: chips that are the range */
.phase-select {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: -4px 0 10px;
}
.phase-select .phase-chip { opacity: 0.55; }
.phase-select .phase-chip.on {
  opacity: 1;
  background: color-mix(in srgb, var(--pc) 20%, transparent);
  box-shadow: inset 0 0 0 1.5px var(--pc);
}
.analytics-range { margin: 0 0 16px; }

/* phases timeline (profile page) */
.phase-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 2px;
  border-bottom: 1px solid var(--border);
}
.phase-item:last-child { border-bottom: none; }
.phase-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: none;
  margin-right: 2px;
}
.phase-item-main { display: flex; flex-direction: column; flex: 1; }
.phase-item-main small { color: var(--muted); font-size: 12.5px; }
.phase-item.current b { color: var(--accent); }
.phase-res { text-align: right; font-weight: 600; font-size: 14px; }
.phase-res small {
  display: block;
  color: var(--muted);
  font-weight: 400;
  font-size: 12px;
}

/* ---- PWA install hint (browser only, floats above the tab bar) ---- */
.pwa-hint {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(80px + env(safe-area-inset-bottom));
  z-index: 59;
  display: flex;
  align-items: center;
  gap: 10px;
  width: max-content;
  max-width: min(94vw, 460px);
  padding: 9px 10px 9px 14px;
  border-radius: 14px;
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  -webkit-backdrop-filter: blur(20px) saturate(1.5);
  backdrop-filter: blur(20px) saturate(1.5);
  border: 1px solid var(--border);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.28);
  font-size: 13.5px;
}
.pwa-hint-text { color: var(--text-2); }
.pwa-hint-text b { color: var(--text); font-weight: 600; }
.pwa-share-icon {
  width: 14px;
  height: 14px;
  vertical-align: -2px;
  color: var(--accent);
}
.pwa-hint-install { padding: 5px 12px; border-radius: 10px; font-size: 13px; }
.pwa-hint-close {
  border: none;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 14px;
  line-height: 1;
  padding: 6px;
  border-radius: 8px;
  cursor: pointer;
}
.pwa-hint-close:hover { background: var(--grid); color: var(--text); }

/* ---- M7: earned windows, goal window line, live marker ---- */
.earned-tag {
  font-size: 11px;
  font-weight: 650;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  padding: 2px 7px;
  border-radius: 999px;
}
/* "live" marker on the current day's accumulated figures. Deliberately
   plain text, not a pill: the row has to stay on one line. */
.live-tag {
  font-style: normal;
  font-weight: 650;
  margin-left: 4px;
  color: var(--accent);
}
/* The goal window's own line: where the amount-and-time goal stands and
   what it now asks. Advisory only — a warning tint, never a block. */
.goal-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  margin-top: 10px;
  padding: 7px 10px;
  border-radius: 10px;
  font-size: 12px;
  color: var(--text-2);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.goal-line b { color: var(--text); font-weight: 650; }
.goal-line.warn {
  background: color-mix(in srgb, var(--over) 10%, transparent);
  color: var(--text);
}
.goal-line.done { background: var(--grid); }

.p-earned {
  font-size: 11px;
  font-weight: 650;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  padding: 1px 6px;
  border-radius: 999px;
  margin-right: 6px;
}
.dialog-note {
  margin: 10px 0 -2px;
  font-size: 12px;
  color: var(--text-2);
}

/* Live status of an amount-and-time goal inside the targets dialog:
   the plan fields say what was asked for, this says where it stands. */
.goal-status {
  margin: 10px 0 0;
  padding: 8px 11px;
  border-radius: 10px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--text-2);
  background: color-mix(in srgb, var(--accent) 9%, transparent);
}
.goal-status b { color: var(--text); font-weight: 650; }
.goal-status.warn {
  background: color-mix(in srgb, var(--over) 11%, transparent);
  color: var(--text);
}

.row-earned {
  display: inline-block;
  margin-left: 7px;
  padding: 0 6px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 650;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  vertical-align: 1px;
}

.goal-line.actionable { cursor: pointer; }
.goal-line.actionable:active { transform: scale(0.995); }
.goal-line.actionable::after {
  content: "›";
  margin-left: auto;
  font-size: 15px;
  opacity: 0.55;
}

/* Build marker at the foot of the profile page — deliberately quiet, but
   the fastest way to confirm which version a device is actually running. */
.app-version {
  margin: 18px 0 4px;
  text-align: center;
  font-size: 11.5px;
  color: var(--text-2);
  opacity: 0.75;
  font-variant-numeric: tabular-nums;
}

/* ---- energy card: the day's allowance as segments you fill ----
   Deliberately not the accumulated card's blocks: those are a body-mass
   ledger in fixed 50 g units, these are one day's food and stretch to
   fit whatever the allowance is. Flat, thin, one row. */
.kcal-meter {
  display: flex;
  gap: 3px;
  margin-top: 12px;
  height: 26px;
}
.kcal-seg {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  border-radius: 4px;
  background: var(--grid);
  overflow: hidden;
  transition: background-color 0.25s ease;
}
/* Three zones read left to right, each with its own empty state, so the
   shape of the day is legible before a single calorie is logged:
     PLANNED — outlined: what the plan allows
     EARNED  — tinted: room today's movement added on top
     BURN    — bare: past the allowance, burned rather than eaten */
.kcal-seg.plan {
  background: color-mix(in srgb, var(--accent) 34%, transparent);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 62%, transparent);
}
.kcal-seg.earned { background: color-mix(in srgb, var(--accent) 34%, transparent); }
.kcal-seg.burn { background: color-mix(in srgb, var(--grid) 70%, transparent); }
/* Eaten is eaten: full colour, no transparency. The translucent states
   are the ones that have not happened yet — planned and earned room —
   so "spent" and "available" never look like shades of the same thing. */
.kcal-seg.on { background: var(--sc); }
/* eaten past the plan: no transparency, so the escalation reads clearly */
/* an eaten segment is solid whichever zone it sits in */
.kcal-seg.plan.on { box-shadow: none; }
/* the segment currently being eaten into fills from the bottom */
.kcal-seg[data-part]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--p, 0%);
  background: var(--sc);
  transition: height 0.35s ease;
}
.kcal-line {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-top: 9px;
  font-size: 13px;
  color: var(--text-2);
}
.kcal-line b { font-size: 17px; font-weight: 700; color: var(--text); }
.kcal-line em { font-style: normal; margin-left: auto; color: var(--text); }
.kcal-line em.over { color: var(--over); }

/* ---- skeletons ----
   Shape while waiting, not a spinner. Purely decorative: no layout
   thrash, no JS, and the first real render replaces them wholesale.
   A sweep runs only while something is genuinely pending — waiting is
   the one moment an animation earns its keep. */
.skel {
  display: block;
  height: 12px;
  border-radius: 6px;
  background: linear-gradient(100deg,
    var(--grid) 30%,
    color-mix(in srgb, var(--text-2) 12%, var(--grid)) 48%,
    var(--grid) 66%);
  background-size: 260% 100%;
  animation: skel-sweep 1.5s ease-in-out infinite;
}
.skel-head {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 14px;
}
.skel-head .skel { height: 11px; }
.skel-lg { height: 26px; border-radius: 8px; }
.skel-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 16px;
}
.skel-blk { width: 24px; height: 24px; border-radius: 6px; }
.skel-meter { height: 26px; border-radius: 4px; margin-top: 12px; }
.skel-bar { height: 18px; border-radius: 9px; margin-top: 5px; }
@keyframes skel-sweep {
  from { background-position: 140% 0; }
  to   { background-position: -40% 0; }
}
/* A day that takes a moment to arrive gets the same sweep back, rather
   than the previous day's numbers sitting there looking current. */
#top-cards.loading { opacity: 0.55; transition: opacity 0.18s ease; }
@media (prefers-reduced-motion: reduce) {
  .skel { animation: none; }
}

/* ---- card info button + shared explainer dialog ---- */
.card-info {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin-left: 8px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: color-mix(in srgb, var(--text-2) 18%, transparent);
  color: var(--text-2);
  font: 650 11px/1 ui-serif, Georgia, serif;
  font-style: italic;
  cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease;
}
.card-info:hover {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  color: var(--accent);
}
/* the header is a flex row; keep the title left and the rest right */
.macro-card .macro-name { align-items: center; }
.macro-card .macro-name > span:first-child { margin-right: auto; }

.info-dialog { position: relative; padding-top: 22px; }
.info-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 50%;
  background: var(--grid);
  color: var(--text-2);
  font-size: 19px;
  line-height: 1;
  cursor: pointer;
}
.info-close:hover { color: var(--text); }
.info-dialog h3 { padding-right: 34px; }
.info-body { font-size: 13.5px; line-height: 1.55; color: var(--text-2); }
.info-body p { margin: 12px 0 0; }
.info-body b { color: var(--text); font-weight: 650; }
.info-rows { margin: 0; display: flex; flex-direction: column; gap: 5px; }
.info-rows > div {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  font-variant-numeric: tabular-nums;
}
.info-rows dt { margin: 0; }
.info-rows dd { margin: 0; color: var(--text); font-weight: 600; }
.info-rows .info-sum {
  margin-top: 3px;
  padding-top: 7px;
  border-top: 1px solid var(--border);
}
.info-legend {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 14px;
}
/* DIRECT children only: a nested <span> wrapping the description text
   would otherwise become a flex container itself and split its own words
   into columns */
.info-legend > span { display: flex; align-items: flex-start; gap: 9px; }
.info-legend i {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  margin-top: 2px;
  border-radius: 4px;
}
.info-legend .lg-plan { background: color-mix(in srgb, var(--accent) 13%, transparent); }
.info-legend .lg-eaten { background: var(--accent); }
.info-legend .lg-over { background: hsl(38 82% 52%); }
.info-legend .lg-warn { background: hsl(28 80% 52%); }
/* the micros card's over state is the real over colour, not the macro
   card's amber staging — the swatch has to match what it describes */
.info-legend .lg-limit { background: var(--over); }
/* the macro row's three sections, drawn as they actually appear */
.info-legend .lg-plan-out {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  box-shadow: inset 0 0 0 1.5px
    color-mix(in srgb, var(--accent) 48%, transparent);
}
.info-legend .lg-over {
  background: linear-gradient(90deg, hsl(38 82% 52%), var(--over));
}
.info-legend .lg-band {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-left: 1px dashed color-mix(in srgb, var(--accent) 70%, transparent);
  border-radius: 0 4px 4px 0;
}

/* block-state swatches for the accumulated card's explainer */
.info-legend .lg-block-full {
  border-radius: 4px;
  background: linear-gradient(160deg,
    color-mix(in srgb, var(--accent) 92%, #ffffff),
    var(--accent) 55%,
    color-mix(in srgb, var(--accent) 78%, #000000));
}
.info-legend .lg-block-estimate {
  background: color-mix(in srgb, var(--accent) 50%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--accent) 70%, transparent);
}
.info-legend .lg-block-assumed {
  background: none;
  border: 1.5px solid color-mix(in srgb, var(--accent) 70%, transparent);
}
.info-legend .lg-block-cleared {
  background: none;
  border: 1.5px dashed color-mix(in srgb, var(--accent) 40%, transparent);
  opacity: 0.6;
}
/* the value row: number left, live marker right */
.acc-row { align-items: baseline; }

/* The goal window's line moved into the footer row, opposite the block
   count: same information, one row instead of a band across the card.
   Down here it is text, not a filled bar — a warning still reads as one
   through colour, without spending a full-width block on it. */
.tb-caption .goal-line {
  margin-top: 0;
  padding: 0;
  gap: 4px;
  background: none;
  font-size: inherit;
  justify-content: flex-end;
  text-align: right;
}
.tb-caption .goal-line.warn,
.tb-caption .goal-line.warn b { color: var(--over); }
.tb-caption .goal-line.done { background: none; }
.tb-caption .goal-line.actionable::after { margin-left: 2px; font-size: 14px; }

/* zone swatches for the energy explainer */
.info-legend .lg-zone-plan {
  background: color-mix(in srgb, var(--accent) 34%, transparent);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 62%, transparent);
}
.info-legend .lg-zone-earned { background: color-mix(in srgb, var(--accent) 34%, transparent); }
/* the burn swatch is "bare track" — outlined so it reads the same on the
   dialog's lighter surface as it does on the card */
.info-legend .lg-zone-burn {
  background: none;
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--text-2) 34%, transparent);
}

/* ---- zone rail ----
   A dimension bracket hung under the meter — the drawing convention for
   "this span is that thing", which beats a row of labels nobody reads.
   Each bracket is placed with the meter's own geometry, so it measures
   exactly the segments it sits under. */
.zone-rail {
  position: relative;
  height: 25px;
  margin-top: 5px;
}
.zone {
  position: absolute;
  top: 0;
  height: 6px;
  border: 1.5px solid;
  border-top: none;
  border-radius: 0 0 4px 4px;
}
.zone.z-plan { border-color: color-mix(in srgb, var(--accent) 62%, transparent); }
.zone.z-earned { border-color: color-mix(in srgb, var(--accent) 34%, transparent); }
.zone.z-burn { border-color: color-mix(in srgb, var(--text-2) 30%, transparent); }
/* the name hangs from the middle of its span and ignores the span width */
.zone b {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: baseline;
  gap: 4px;
  white-space: nowrap;
  font-size: 10px;
  font-weight: 650;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-2);
}
.zone b em {
  font-style: normal;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.zone.z-plan b em { color: var(--accent); }

/* the "over" zone: eaten past everything you burned today */
/* :not(.on) matters — this rule sits after `.kcal-seg.on` and would
   otherwise win on source order, leaving an EATEN over-segment wearing
   the faint "not yet" tint instead of solid red. */
.kcal-seg.over:not(.on) { background: color-mix(in srgb, var(--over) 18%, transparent); }
/* Growing, the card keeps the cut's grammar with the roles swapped: the
   OUTLINE marks the base you have to cover, the flat tint marks what is
   added on top. Cutting that is planned (outlined) then earned (flat);
   growing it is tdee (outlined) then the surplus (flat). Both are food
   you plan to eat, both fill to solid once eaten.
   :not(.on) for the same reason as the over rule above — these sit after
   `.kcal-seg.on` at equal specificity, and without the guard source
   order wins and an EATEN segment draws as unspent. */
.kcal-seg.tdee:not(.on) {
  background: color-mix(in srgb, var(--sc) 34%, transparent);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--sc) 62%, transparent);
}
.kcal-seg.surplus:not(.on) {
  background: color-mix(in srgb, var(--sc) 34%, transparent);
}
.zone.z-tdee { border-color: color-mix(in srgb, var(--text-2) 45%, transparent); }
.zone.z-tdee b { color: var(--text-2); }
.zone.z-tdee b em { color: var(--text-2); }
.info-legend .lg-zone-tdee {
  background: color-mix(in srgb, var(--text-2) 55%, transparent);
  border-radius: 3px;
}
.zone.z-over { border-color: color-mix(in srgb, var(--over) 60%, transparent); }
.zone.z-over b em { color: var(--over); }

/* ---- the info dialog's sums ----
   Two relationships instead of eight loose figures: the numbers explain
   each other, which a list of rows never does. */
.eq {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 6px;
  margin-top: 12px;
  padding: 11px 12px;
  border-radius: 12px;
  background: var(--grid);
}
.eq-t {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.eq-t b {
  font-size: 16px;
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.eq-t em {
  font-style: normal;
  font-size: 10px;
  font-weight: 650;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-2);
  white-space: nowrap;
}
.eq-sum b { color: var(--accent); }
.eq-op {
  padding-bottom: 12px;
  font-size: 13px;
  color: var(--text-2);
}

/* the live/full-day switch is a control, so it looks like one */
/* The balance and the mode it is being read in are one statement, so
   they are one control: the number, and a slow pulse saying it is still
   moving. Two people asked in a row why this figure differed from the
   day card's — it was never the arithmetic, it was that "live" looked
   like a label instead of a switch. */
.acc-live {
  /* hugs its own statement instead of stretching the card: the figure,
     what it is in, and whether it is still moving are one phrase, and a
     full-width panel made the "live" end read as a separate widget */
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  width: auto;
  max-width: 100%;
  margin: 2px 0 0;
  padding: 6px 12px;
  border: none;
  border-radius: 14px;
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 0.2s ease;
}
.acc-live:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.acc-live.off { background: color-mix(in srgb, var(--text-2) 8%, transparent); }
.live-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  user-select: none;
}
.acc-live.off .live-tag { color: var(--text-2); }
.live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  animation: live-pulse 2.4s ease-in-out infinite;
}
.acc-live.off .live-dot { animation: none; opacity: 0.5; }
@keyframes live-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.25; transform: scale(0.8); }
}
@media (prefers-reduced-motion: reduce) {
  .live-dot { animation: none; }
}


/* ---- iOS: stop the focus zoom at its cause ----
   Safari zooms the page whenever a field with a computed font-size under
   16px takes focus, and does NOT zoom back out when it blurs — which is
   why opening the "+" chat on an iPhone left the whole layout enlarged
   and had to be pinched back by hand. Every field in the app was under
   16px (the chat textarea at 14, dialog inputs and selects at 13), so
   every one of them triggered it.

   Fixed at the cause rather than with `user-scalable=no`: that would
   also stop the zoom, and would also stop the user pinching to read
   anything, which is a worse trade. Scoped to touch pointers, so desktop
   keeps its denser forms and phones get larger tap targets they wanted
   anyway. The explicit selectors are repeated because they set their own
   size and would otherwise win on specificity. */
@media (hover: none) and (pointer: coarse) {
  input,
  select,
  textarea,
  .assistant-inputrow textarea,
  .profile-body textarea,
  .export-range input[type="date"] { font-size: 16px; }
}

/* ---- Body response ----------------------------------------------------
   A DECK, not a card. Each signal is a whole panel — own title, own
   status, own info button — and the deck slides sideways. They answer
   different questions, and a shared frame kept forcing them to share a
   header that could only describe one of them at a time.

   All cards wear the phase primary: one is on screen at a time and its
   title names it, so colour has no work left to do, and the stylesheet
   keeps one primary with no near-miss shades. */
.body-deck { margin-bottom: 20px; }
/* The track breaks OUT to the screen edges (negative margin) and pulls
   its content back in with equal padding. Two things fall out of that:
   the cards still line up with every other panel on the page, and a
   swipe shows the page background in the gutter instead of two rounded
   corners butting together — which is what made the deck feel glued.
   The 16px must match the page's own side padding; if that ever changes,
   this changes with it. */
.body-track {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  margin-inline: -16px;
  padding-inline: 16px;
  scroll-padding-inline: 16px;
  overflow-x: auto;
  /* every card is padded to the tallest in JS, so the track is one
     stable height and swiping never makes the page jump */
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.body-track::-webkit-scrollbar { display: none; }
.body-slide {
  flex: 0 0 100%;
  min-width: 0;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
/* the panel's own bottom margin would break the snap rhythm — the deck
   owns the spacing below the whole stack instead */
.body-card { margin-bottom: 0; }

.body-title { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
/* the family name, so a card that says "Fuel" still says what it is part of */
.body-kicker {
  font-size: 9.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.body-card h2 { margin: 0; }
.body-note {
  margin: 0 0 10px;
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1.4;
}
.body-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 10px;
}
/* Small on purpose. At 11.5px in a pill with 10px of padding, three of
   these filled the card and read as buttons — they are STATUS, not
   controls, and nothing here is tappable. */
.body-chip {
  font-size: 10px;
  padding: 3px 7px;
  border-radius: 999px;
  background: var(--grid);
  color: var(--text-2);
  white-space: nowrap;
}
/* the one chip that is a call to action, so the one that is filled */
.body-chip.open {
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}
.body-svg { display: block; width: 100%; height: auto; }

.body-dots {
  display: flex;
  justify-content: center;
  gap: 7px;
  margin-top: 10px;
}
.body-dot {
  width: 7px;
  height: 7px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--grid);
  cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease;
}
.body-dot.on { background: var(--accent); transform: scale(1.25); }

/* readiness card — the one with no curve */
.rd-score {
  font-size: 44px;
  font-weight: 700;
  line-height: 1;
  color: var(--accent);
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 2px 0 14px;
}
.rd-score.low { color: var(--over); }
.rd-score small {
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
  text-transform: capitalize;
}
.rd-parts { display: flex; flex-direction: column; }
.rd-part {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
  font-size: 12.5px;
}
.rd-part:last-child { border-bottom: 0; }
.rd-part-label { color: var(--text); font-weight: 550; }
.rd-part-note {
  color: var(--muted);
  font-size: 11.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.rd-part-delta {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  color: var(--text-2);
  flex: 0 0 auto;
}

/* Absent cards are NAMED and the gaps listed on Readiness: a card
   missing because nothing was logged must never be mistaken for a flat
   one — the same rule the micros card follows. */
.body-missing, .body-gap {
  margin-top: 10px;
  font-size: 11px;
  color: var(--muted);
  line-height: 1.4;
}
.body-gap { color: var(--over); opacity: 0.85; }
.body-foot {
  margin-top: 10px;
  padding-top: 9px;
  border-top: 1px solid var(--border);
  font-size: 11px;
  color: var(--muted);
  line-height: 1.4;
}

/* The "wrong day open" notice. Deliberately NOT a proposal: there is
   nothing to accept, and it has to be obvious at a glance that no
   estimate is on offer — the whole reason it exists is that a reply
   asking someone to swipe elsewhere gets ignored while the numbers
   underneath it get accepted. */
.otherday {
  border: 1px solid color-mix(in srgb, var(--over) 40%, transparent);
  background: color-mix(in srgb, var(--over) 9%, transparent);
  border-radius: 14px;
  padding: 12px 14px;
  margin: 4px 0 2px;
}
.otherday-head {
  font-size: 12px;
  font-weight: 650;
  color: var(--over);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 6px;
}
.otherday p {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-2);
}

/* ---- Analytics: by week ------------------------------------------------
   The phase's own 7-day blocks. Tabs select; one panel below reports.
   The headline is the week's ENERGY BALANCE — intake against everything
   burned — because adherence to an aggressive plan says nothing about
   whether the week went well. The hero bar IS that balance: the track is
   output, the fill is intake, so short of the end reads as a deficit and
   past it spills into the over colour. Below it the week is a table, not
   a stack of cards: seven days, then the averages. */
.weeks-panel h2 { margin-bottom: 12px; }
.week-tabs {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  padding-bottom: 4px;
  margin-bottom: 14px;
}
.week-tabs::-webkit-scrollbar { display: none; }
.week-tab {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  min-width: 62px;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: none;
  color: var(--text-2);
  font: inherit;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease,
              transform 0.12s ease;
}
.week-tab:active { transform: scale(0.95); }
.week-tab b { font-size: 13px; font-weight: 650; }
.week-tab small { font-size: 11px; color: var(--muted); }
.week-tab.on {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  color: var(--accent);
}
.week-tab.on small { color: var(--accent); }
.wk-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--grid);
}
/* the dot answers "did this week go the way the phase wants" — the same
   good/bad the Today card's balance uses, not an adherence scale */
.wk-dot.good { background: var(--accent); }
.wk-dot.bad  { background: var(--over); }

/* the panel fades and lifts a little on every switch, so changing tabs
   reads as the same card turning over rather than a silent re-render */
.week-detail.swap { animation: wk-swap 0.28s ease both; }
@keyframes wk-swap {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.wk-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin-bottom: 14px; flex-wrap: wrap;
}
.wk-range { font-size: 14px; font-weight: 600; color: var(--text); }
.wk-days { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
/* the count IS the warning when days are missing — a second "N days not
   logged" beside it said the same thing twice and wrapped the line */
.wk-days.gap { color: var(--over); }
.wk-hero { margin-bottom: 4px; }
.wk-hero-num {
  font-size: 30px; font-weight: 700; line-height: 1.05;
  color: var(--text-2);
}
.wk-hero-num.good { color: var(--accent); }
.wk-hero-num.bad { color: var(--over); }
.wk-hero-num small {
  font-size: 14px; font-weight: 450; color: var(--muted);
}
.wk-hero-word {
  font-size: 12.5px; color: var(--muted); margin: 3px 0 10px;
}
.wk-hero-sub {
  display: flex; justify-content: space-between;
  font-size: 12.5px; color: var(--muted); margin-top: 6px;
}
.wk-hero-sub b { color: var(--text-2); font-weight: 650; }

.wk-bar {
  position: relative;
  height: 10px;
  border-radius: 999px;
  background: var(--grid);
  overflow: hidden;
}
.wk-bar-lg { height: 14px; }
.wk-bar i { position: absolute; top: 0; bottom: 0; display: block; }
.wk-plan   { left: 0; background: color-mix(in srgb, var(--accent) 22%, transparent); }
/* every bar wears the phase primary, macros included: the row is already
   labelled, so a per-macro tint adds a second colour language for no
   information — and the stylesheet's standing rule is one primary, no
   near-miss shades */
.wk-fill   { left: 0; background: var(--accent); border-radius: 999px; }
.wk-over   { background: var(--over); }

/* Two plain tables, not cards of bars. The week's job is to be READ —
   seven days down a column, then the averages — and a stack of decorated
   panels made that harder, not easier. */
.wk-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 18px;
  font-size: 12.5px;
}
.wk-table th {
  text-align: left;
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  padding: 0 0 6px;
  border-bottom: 1px solid var(--border);
}
.wk-table td {
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
  color: var(--text-2);
}
.wk-table td.num, .wk-table th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.wk-table tbody tr:last-child td { border-bottom: 0; }
.wk-table tfoot td {
  border-top: 1.5px solid var(--border);
  border-bottom: 0;
  padding-top: 9px;
  font-weight: 650;
  color: var(--text);
}
/* an unlogged day still burned its baseline, so it keeps its row and its
   negative — dimmed, because the missing intake is the story */
.wk-unlogged td { opacity: 0.5; }
.wk-table .good { color: var(--accent); }
.wk-table .bad { color: var(--over); }
/* the plan comparison, demoted to a third column: still the answer to
   "did I hit my protein", no longer the headline it was never right for */
.wk-week-total { color: var(--muted); font-size: 11.5px; }
.wk-avg td:first-child { color: var(--text-2); }
.wk-avg b { color: var(--text); font-weight: 650; font-size: 13px; }
/* Micros on a week. A nutrient with no estimate is ABSENT from this
   table, never a zero row — the same rule the day card follows, and it
   matters more here: micros began at v0.6.2, so an earlier week has food
   and no estimates, and twenty-four zeroes would report a catastrophic
   week that never happened. Such a week renders no micro table at all. */
.wk-micros { margin-top: 20px; }
.wk-micros thead th:last-child {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
}
.wk-group td {
  padding-top: 12px;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  border-bottom: 0;
}
.wk-limit {
  font-size: 10px;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0 5px;
  margin-left: 4px;
}
.wk-micro-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 10px;
}
.wk-more {
  font: inherit;
  font-size: 12px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--accent);
  cursor: pointer;
}
.wk-more:hover { text-decoration: underline; }
/* coverage is never hidden behind the expand: without it a low row and a
   never-asked row look identical, and they are opposite conclusions */
.wk-micro-gap { font-size: 11px; color: var(--muted); }

@media (max-width: 360px) {
  .wk-table { font-size: 12px; }
  .wk-week-total { font-size: 11px; }
}

/* ---- Micros card ------------------------------------------------------
   Under Macros, short by default. Thin bars because the expanded list is
   24 rows — anything thicker turns the page into a scroll. Coverage sits
   in the header, never hidden behind the expand, because a missing
   estimate and a genuine shortfall look identical without it. */
/* deliberately shorter than Macros when collapsed: it is a glance, not a
   panel. The chevron carries "there is more", so no footer line is spent
   saying it. */
.micros-card { padding-bottom: 14px; }
/* the info button is a SIBLING of the expand button, never inside it —
   nested buttons are invalid, and tapping "i" must not also toggle */
.mi-topline { display: flex; align-items: center; gap: 8px; }
.mi-topline .card-info { flex: 0 0 auto; margin-bottom: 10px; }
.mi-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 0 10px;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.mi-coverage {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--muted);
  font-weight: 500;
}
.mi-chev {
  width: 16px; height: 16px;
  color: var(--muted);
  flex: 0 0 auto;
  transition: transform 0.24s ease;
}
.mi-head[aria-expanded="true"] .mi-chev { transform: rotate(180deg); }

.mi-body { display: flex; flex-direction: column; gap: 7px; }
.mi-group {
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 8px;
}
.mi-group:first-child { margin-top: 0; }

.mi-row {
  display: grid;
  grid-template-columns: 82px 1fr auto 34px;
  align-items: center;
  gap: 8px;
}
.mi-name {
  font-size: 12px;
  color: var(--text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mi-track {
  height: 4px;                 /* thin, as asked — 24 of these must fit */
  border-radius: 999px;
  background: var(--grid);
  overflow: hidden;
}
.mi-fill {
  display: block; height: 100%;
  background: var(--accent);
  border-radius: 999px;
  transition: width 0.4s ease;
}
.mi-fill.over { background: var(--over); }
.mi-val { font-size: 11.5px; font-weight: 600; white-space: nowrap; }
.mi-val em { font-style: normal; font-weight: 450; color: var(--muted); }
.mi-pct {
  font-size: 11px; color: var(--muted); text-align: right;
}
/* not estimated is its own state, visibly different from "you ate none" */
.mi-row.none .mi-name, .mi-row.none .mi-val { color: var(--muted); }
.mi-row.none .mi-track { opacity: 0.45; }

/* worked example inside the info dialog: the same row the card draws,
   shown once with its arithmetic spelled out underneath */
.mi-eg {
  margin: 12px 0;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
.mi-eg-row {
  display: grid;
  grid-template-columns: 82px 1fr auto 34px;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-2);
}
.mi-eg-track {
  height: 4px; border-radius: 999px;
  background: var(--grid); overflow: hidden;
}
.mi-eg-track i {
  display: block; height: 100%;
  background: var(--accent); border-radius: 999px;
}
.mi-eg-num { font-size: 11.5px; font-weight: 600; color: var(--text); }
.mi-eg-num em { font-style: normal; font-weight: 450; color: var(--muted); }
.mi-eg-pct { font-size: 11px; color: var(--muted); text-align: right; }
.mi-eg p { margin: 10px 0 0; font-size: 12.5px; }

/* the un-estimated nutrients, named but not drawn — see renderMicrosCard */
.mi-missing {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--muted);
}
.mi-missing b {
  display: block;
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: 4px;
}
.mi-gap {
  margin-top: 10px;
  padding-top: 9px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--muted);
}
@media (max-width: 360px) {
  .mi-row { grid-template-columns: 70px 1fr auto 30px; gap: 6px; }
  .mi-name { font-size: 11.5px; }
}
.p-micros { color: var(--accent); font-weight: 550; }

/* ---- Food log as a list ------------------------------------------------
   Replaces a ten-column table that could not fit a phone. Two lines per
   entry: name and calories on the first, meal/quantity and macros on the
   second, with the time in a fixed narrow column so every row shares a
   left edge to scan down. Nothing scrolls sideways. */
.log-list { display: flex; flex-direction: column; }
.log-row {
  display: grid;
  grid-template-columns: 42px 1fr;
  align-items: start;
  gap: 8px;
  padding: 9px 0;
  border-bottom: 1px solid var(--border);
}
.log-row:last-child { border-bottom: 0; }
.log-time {
  font-size: 11.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  padding-top: 1px;
}
.log-body { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.log-line {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.log-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  /* ellipsis rather than wrapping: a long food name must not turn one
     entry into three lines and push the rest of the day off screen */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.log-kcal {
  margin-left: auto;
  font-size: 13px;
  font-weight: 650;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}
.log-sub { font-size: 11.5px; color: var(--muted); }
.log-meta {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.log-macros {
  margin-left: auto;
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  color: var(--text-2);
}
/* the unit letters are the quiet half — the number is what is read */
.log-macros em { font-style: normal; color: var(--muted); }
.log-macros i { font-style: normal; color: var(--border); margin: 0 3px; }
.log-note {
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1.35;
  margin-top: 1px;
}
.log-resp {
  font-size: 11.5px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  margin-left: auto;
  padding-right: 8px;
}
.log-resp + .log-macros { margin-left: 0; }
/* a destructive control in the dialog, where the decision is already
   being made — the row keeps its width for the numbers instead */
.btn.danger {
  background: color-mix(in srgb, var(--over) 14%, transparent);
  color: var(--over);
  border: 1px solid color-mix(in srgb, var(--over) 32%, transparent);
  margin-right: auto;
}
.btn.danger.armed { background: var(--over); color: #fff; border-color: var(--over); }
.rm-tap { cursor: pointer; }
.log-total {
  border-top: 1.5px solid var(--border);
  border-bottom: 0;
  margin-top: 2px;
}
.log-total .log-title { font-weight: 700; }
.row-editable.log-row:hover .log-title {
  text-decoration: underline;
  text-underline-offset: 2px;
}
@media (max-width: 360px) {
  .log-row { grid-template-columns: 38px 1fr; gap: 6px; }
  .log-macros, .log-sub { font-size: 11px; }
}

/* An activity's earned macros sit where a food's eaten macros do, and
   must not be mistaken for them: the "+" and the accent say this WIDENS
   the day rather than spending it. */
.log-earned { color: var(--accent); }
.log-earned em { color: color-mix(in srgb, var(--accent) 62%, transparent); }
.log-earned i { color: color-mix(in srgb, var(--accent) 34%, transparent); }
