/* ═══════════════════════════════════════════════════════════════════════════
   Macro — dark instrument-panel UI.

   Design notes:
   · Surfaces are layered, not flat. Every raised panel gets a visible hairline
     border, a 1px inset highlight along its top edge, and a soft drop shadow —
     that trio is what makes a panel read as physically raised rather than as a
     slightly different shade of the background.
   · Committed to dark only. A half-hearted light mode would be worse than none.
   · Chart and macro hues are validated for the dark surface (OKLCH L 0.48–0.67,
     chroma ≥ 0.1, CVD-separated) rather than picked by eye.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* shadcn's neutral dark theme, resolved from the preset. Every one of these
     is a pure grey — no hue anywhere in the chrome. That absence is the whole
     effect: with nothing tinted, the only colour left on screen is colour that
     means something. */

  /* Surfaces, darkest to lightest */
  --bg: #0a0a0a;
  --surface: #171717;
  --surface-2: var(--surface-2);
  --surface-3: #262626;

  /* Borders are white at low opacity rather than a solid grey, so they hold
     their weight over any surface instead of needing a variant per level. */
  --line: rgba(255, 255, 255, 0.10);
  --line-strong: rgba(255, 255, 255, 0.15);
  --highlight: rgba(255, 255, 255, 0.04);

  /* Ink. shadcn has one muted foreground; this app has two levels, and the
     dimmer one carries the sub-line under every food at 10.5px. Taking
     shadcn's #737373 for it measured 3.78:1 against a card — under the 4.5:1
     floor for text that size. #8f8f8f is 5.55:1 and still reads as secondary. */
  --text: #fafafa;
  --text-2: #a1a1a1;
  --text-3: #8f8f8f;

  /* The accent stays green. shadcn's own preset makes this near-white, and a
     fully monochrome app turned out to read as drained rather than clean — the
     structure was the part worth taking, not the absence of colour.
     Neutral surfaces underneath make the green land harder than it did against
     the old green-black. */
  --accent: #a8e83f;
  --accent-hi: #c6f75e;
  --accent-lo: #6d9c22;
  --accent-ink: #0c1206;

  /* The accent has two jobs and they need different colours once there is a
     light theme. As a filled button it supplies its own background, so it can
     stay bright. As text or an icon on a surface it has to meet 4.5:1 against
     that surface — and lime on white is 1.47:1, which is unreadable. */
  --accent-fg: #a8e83f;
  --accent-wash: rgba(168, 232, 63, 0.08);
  --accent-wash-strong: rgba(168, 232, 63, 0.16);

  /* Macro series — validated against the dark surface for CVD separation.
     Direct-labelled in the UI, so identity never rests on colour alone.
     These are text as well as bar fill: the per-meal 28p/54c/12f figures use
     them, so they answer to the 4.5:1 text floor, not the 3:1 graphics one. */
  --protein: #5296e0;
  --carbs: #bd8626;
  --fat: #d46291;

  --danger: #e5484d;
  --warn: #e08c2e;

  /* Ring gradient, read by JS. Stops live here so the theme owns them rather
     than the script. */
  --ring-1: #6d9c22;
  --ring-2: #a8e83f;
  --ring-3: #c6f75e;
  --ring-cool-1: #2f6b52;
  --ring-cool-2: #4e8a52;
  --ring-cool-3: #6fae66;

  /* Anything painted over content: the tab bar, sheets, the scanner backdrop. */
  --scrim: rgba(10, 10, 10, 0.94);
  --scrim-soft: rgba(10, 10, 10, 0.82);
  --scrim-deep: rgba(5, 5, 5, 0.74);
  --shadow: rgba(0, 0, 0, 0.45);
  --btn-bevel: rgba(255, 255, 255, 0.28);
  --btn-glow: rgba(168, 232, 63, 0.30);
  /* Semantic colours as *text* need to be lighter than the fill versions on a
     dark ground, and darker on a light one. */
  --danger-fg: #ff8b8f;
  --warn-fg: #ffc24a;
  --protein-dim: #2b4b74;
  --carbs-dim: #5c4a1c;

  /* shadcn's --radius is 0.625rem. Its components step around that one value
     rather than growing rounder on bigger elements, which is most of why the
     look reads as tidy. */
  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 14px;

  --ease: cubic-bezier(0.22, 0.8, 0.28, 1);
  --tabs-h: 62px;

  /* Flat. shadcn separates surfaces with a border and a lighter fill, not with
     shadow — stacked glows are what made the old panels feel heavy, and they
     are the first thing to drop when the brief is "clear". */
  --lift: none;
}

/* ─────────────────────────────── light theme ───────────────────────────────
 *
 * Written once and applied from two selectors: the system preference when the
 * person has expressed no choice, and the explicit attribute when they have.
 * The :not() guard is what makes "always dark" stick on a phone set to light —
 * without it the media query would win and the toggle would look broken.
 *
 * Every colour here was measured against its own background rather than
 * darkened by eye. The macro colours and the accent all failed on white at
 * their dark-theme values; the figures are in the commit.
 */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) { color-scheme: light; }
}
:root[data-theme="light"] { color-scheme: light; }

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #fafafa;
    --surface: #ffffff;
    --surface-2: #f4f4f5;
    --surface-3: #e9e9ec;

    --line: rgba(0, 0, 0, 0.12);
    --line-strong: rgba(0, 0, 0, 0.20);
    --highlight: rgba(0, 0, 0, 0.02);

    --text: #0a0a0a;
    --text-2: #52525b;
    --text-3: #6b6b72;

    --accent: #4d7c0f;
    --accent-hi: #5f9613;
    --accent-lo: #7aa82e;
    --accent-ink: #ffffff;
    --accent-fg: #4d7c0f;
    --accent-wash: rgba(77, 124, 15, 0.10);
    --accent-wash-strong: rgba(77, 124, 15, 0.18);

    --protein: #1d6fd0;
    --carbs: #8a5f12;
    --fat: #b03567;

    --danger: #c0272d;
    --warn: #a3620f;

    --ring-1: #7aa82e;
    --ring-2: #5f9613;
    --ring-3: #4d7c0f;
    --ring-cool-1: #b9c9b4;
    --ring-cool-2: #97b48c;
    --ring-cool-3: #7ca26a;

    --scrim: rgba(255, 255, 255, 0.94);
    --scrim-soft: rgba(255, 255, 255, 0.86);
    --scrim-deep: rgba(0, 0, 0, 0.55);
    --shadow: rgba(0, 0, 0, 0.10);
    --btn-bevel: rgba(255, 255, 255, 0.18);
    --btn-glow: rgba(77, 124, 15, 0.28);
    --danger-fg: #c0272d;
    --warn-fg: #a3620f;
    --protein-dim: #b9d3f2;
    --carbs-dim: #e3d3a8;
  }
}

:root[data-theme="light"] {
  --bg: #fafafa;
  --surface: #ffffff;
  --surface-2: #f4f4f5;
  --surface-3: #e9e9ec;

  --line: rgba(0, 0, 0, 0.12);
  --line-strong: rgba(0, 0, 0, 0.20);
  --highlight: rgba(0, 0, 0, 0.02);

  --text: #0a0a0a;
  --text-2: #52525b;
  --text-3: #6b6b72;

  /* The button inverts rather than staying lime. A bright lime block on white
     is loud and its label has nowhere to go; a deep green with white text is
     the same brand colour doing the job the ground now demands. */
  --accent: #4d7c0f;
  --accent-hi: #5f9613;
  --accent-lo: #7aa82e;
  --accent-ink: #ffffff;
  --accent-fg: #4d7c0f;
  --accent-wash: rgba(77, 124, 15, 0.10);
  --accent-wash-strong: rgba(77, 124, 15, 0.18);

  --protein: #1d6fd0;
  --carbs: #8a5f12;
  --fat: #b03567;

  --danger: #c0272d;
  --warn: #a3620f;

  /* The ring fills from cool to saturated. On white that has to run the other
     way — pale to deep — or a "full" ring would be the faintest thing on the
     screen. */
  --ring-1: #7aa82e;
  --ring-2: #5f9613;
  --ring-3: #4d7c0f;
  --ring-cool-1: #b9c9b4;
  --ring-cool-2: #97b48c;
  --ring-cool-3: #7ca26a;

  --scrim: rgba(255, 255, 255, 0.94);
  --scrim-soft: rgba(255, 255, 255, 0.86);
  --scrim-deep: rgba(0, 0, 0, 0.55);
  --shadow: rgba(0, 0, 0, 0.10);
    --btn-bevel: rgba(255, 255, 255, 0.18);
    --btn-glow: rgba(77, 124, 15, 0.28);
    --danger-fg: #c0272d;
    --warn-fg: #a3620f;
    --protein-dim: #b9d3f2;
    --carbs-dim: #e3d3a8;
}

* { box-sizing: border-box; }

/* Elements below set an explicit `display`, which would otherwise beat the
   user-agent rule for [hidden] and leave them stuck on screen. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;

  /* Nothing on this app scrolls sideways, but iOS still lets you drag the page
     horizontally and rubber-band it back. Nothing is overflowing — the page is
     exactly viewport-width — so this is purely the elastic gesture, and it makes
     an installed app feel like a web page. Three rules kill it:
       · overscroll-behavior stops the elastic bounce in both axes
       · touch-action tells the browser only vertical panning and zoom are ours
         to perform (touch *events* still fire, so the hero swipe is unaffected)
       · overflow-x clips anything that ever does escape, rather than letting it
         become a scrollable region */
  /* Refuses the elastic sideways drag, which is what made an installed app feel
     like a web page.

     Deliberately NOT paired with `touch-action: pan-y`. Forbidding horizontal
     panning looks harmless while the page is at 1× — but the moment anything
     zooms the page in, the part now off-screen becomes unreachable, because
     panning to it is exactly what's been banned. A page you can't pan is worse
     than one that wobbles.

     Also deliberately no overflow-x. Clipping looks like a safe third rule, but
     the layout viewport runs a fraction of a pixel wider than the reported
     client width on some devices, so the clip has nothing legitimate to remove
     and shaves that sliver off the right edge instead. */
  overscroll-behavior: none;
}

/* Fills the screen edge to edge, including behind the notch and home indicator,
   so no strip of a different colour shows through while scrolling. */
html { min-height: 100%; }

/* A very slight glow behind the fold stops the page reading as a flat void. */
body {
  min-height: 100dvh;
  padding-bottom: calc(var(--tabs-h) + env(safe-area-inset-bottom));
  background-image: radial-gradient(
    120% 60% at 50% -10%,
    var(--accent-wash),
    transparent 70%
  );
  background-attachment: fixed;
}

h1 { font-size: 21px; margin: 0; letter-spacing: -0.02em; font-weight: 640; }

button, input, select, textarea { font: inherit; color: inherit; font-family: inherit; }

/* iOS zooms the entire page in when you focus a field whose font is under 16px,
   and it does not zoom back out afterwards. That is what was cutting the right
   edge off: the page was left zoomed, so everything past the screen was simply
   gone. Every control is therefore at least 16px — the browser then has no
   reason to zoom at all. */
input, select, textarea { font-size: 16px; }
button { cursor: pointer; -webkit-tap-highlight-color: transparent; }

/* Everything numeric uses lining tabular figures so digits don't jitter as
   values change. */
.num, .ring-center strong, .macro-head b, .entry-kcal, .stat b, .mini > b,
.tdee-head b, .eaten, .preview b, .sheet-row input {
  font-variant-numeric: tabular-nums lining-nums;
}

/* ─────────────────────────────────── layout ─────────────────────────────── */

.view { padding: 0 16px 28px; max-width: 620px; margin: 0 auto; }

.topbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: calc(env(safe-area-inset-top) + 18px) 0 16px;
}

.daylabel { flex: 1; text-align: center; position: relative; }
.daylabel > span {
  display: block;
  font-size: 11px;
  color: var(--text-3);
  margin-top: 3px;
  letter-spacing: 0.03em;
}

#cal-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: none;
  border: 0;
  padding: 2px 4px;
  color: var(--text);
  border-radius: var(--r-sm);
}
#cal-btn svg { color: var(--text-3); transition: color .15s var(--ease); }
#cal-btn:active { background: var(--surface); }
#cal-btn:active svg { color: var(--accent); }

/* The native picker anchors to this input, so it sits under the button rather
   than being visually hidden with display:none (which suppresses showPicker). */
#cal-input {
  position: absolute;
  left: 50%;
  top: 100%;
  width: 1px;
  height: 1px;
  opacity: 0;
  border: 0;
  padding: 0;
  pointer-events: none;
}

.micro-toggle {
  flex: none;
  width: 38px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--text-3);
  border-radius: var(--r-md);
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  transition: color .18s var(--ease), border-color .18s var(--ease), background .18s var(--ease);
}
.micro-toggle[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent-lo);
  background: var(--accent-wash);
}
.micro-toggle:active { transform: scale(.93); }

.daynav {
  width: 40px; height: 40px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text-2);
  border-radius: 50%;
  font-size: 19px;
  line-height: 1;
  display: grid;
  place-items: center;
  box-shadow: var(--lift);
  transition: transform .15s var(--ease), border-color .15s var(--ease);
}
.daynav:active:not(:disabled) { transform: scale(.92); border-color: var(--line-strong); }
.daynav:disabled { opacity: .25; box-shadow: none; }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 18px;
  margin-bottom: 14px;
  box-shadow: var(--lift);
}

.section-title {
  font-size: 11px;
  font-weight: 620;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  color: var(--text-3);
  margin: 22px 4px 10px;
}
.section-title.flush { margin: 0 0 14px; }

.section-row { display: flex; align-items: baseline; justify-content: space-between; }
.section-action {
  background: none;
  border: 0;
  color: var(--text-3);
  font-size: 11px;
  font-weight: 620;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  padding: 22px 4px 10px 12px;
  transition: color .15s var(--ease);
}
.section-action.on { color: var(--accent); }

/* Removal mode: chips gain a × and shed the "tap to log" affordance. */
.chips.editing button {
  border-color: var(--line-strong);
  color: var(--text-2);
  padding-right: 11px;
}
.chips.editing button::after {
  content: "×";
  margin-left: 9px;
  color: var(--danger);
  font-size: 16px;
  line-height: 1;
  vertical-align: -1px;
}
.chips.editing button small { display: none; }

/* ───────────────────────────── hero ring + macros ───────────────────────── */

.rings { display: flex; align-items: center; gap: 20px; }

.ring-wrap { position: relative; width: 124px; height: 124px; flex: none; }

/* Confirmation that a log landed. One short beat — this fires several times a
   day, and anything longer would become something to wait through. */
.ring-wrap.pulse::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  animation: ringPulse .55s var(--ease) forwards;
  pointer-events: none;
}
@keyframes ringPulse {
  from { opacity: .55; transform: scale(.94); }
  to   { opacity: 0;   transform: scale(1.12); }
}
.ring { width: 100%; height: 100%; transform: rotate(-90deg); overflow: visible; }
.ring circle { fill: none; stroke-width: 10; stroke-linecap: round; }
.ring-bg { stroke: var(--surface-3); }
/* A whisper of bloom reads as emissive; more than that reads as a gaming rig. */
.ring-fg {
  stroke: url(#ringGrad);
  stroke-dasharray: 327;
  stroke-dashoffset: 327;
  filter: drop-shadow(0 0 3px var(--accent-wash-strong));
  /* Long enough to read as a sweep, short enough not to delay the number. */
  transition: stroke-dashoffset .75s var(--ease), filter .4s var(--ease);
}
.ring-fg.over { stroke: url(#ringGradOver); filter: drop-shadow(0 0 3px rgba(229, 72, 77, 0.3)); }

.ring-center { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; }
.ring-center strong {
  display: block;
  font-size: 31px;
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1.05;
}
.ring-center span {
  font-size: 10px;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-weight: 600;
}

.macros { flex: 1; min-width: 0; }
.macro + .macro { margin-top: 13px; }
.macro-head { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 6px; }
.macro-head span { color: var(--text-2); font-weight: 500; }
.macro-head b { font-weight: 620; letter-spacing: -0.01em; }

.bar {
  height: 7px;
  background: var(--surface-3);
  border-radius: 99px;
  overflow: hidden;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.4);
}
.bar i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 99px;
  transition: width .6s var(--ease);
}
[data-macro="protein"] .bar i { background: var(--protein); }
[data-macro="carbs"]   .bar i { background: var(--carbs); }
[data-macro="fat"]     .bar i { background: var(--fat); }

/* Hitting a macro target is worth knowing at a glance — especially protein,
   which is the one you're actively trying to reach rather than stay under. */
.macro.met .macro-head b { color: var(--accent); }
.macro.met .bar i { box-shadow: 0 0 8px -2px currentColor; }
.macro.met[data-macro="protein"] .bar i { background: var(--accent); }

/* Full-width summary under both ring and bars, so it never has to wrap inside
   the narrow macro column. */
.eaten {
  margin: 16px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 11.5px;
  color: var(--text-3);
  letter-spacing: 0.01em;
  text-align: center;
}

/* ───────────────────────── swipeable hero (macros ⇄ micros) ─────────────── */

.hero { position: relative; padding-bottom: 30px; touch-action: pan-y; }
.hero-page { animation: heroIn .26s var(--ease); }
@keyframes heroIn {
  from { opacity: 0; transform: translateX(var(--from, 12px)); }
  to   { opacity: 1; transform: none; }
}

.dots {
  position: absolute;
  left: 0; right: 0; bottom: 12px;
  display: flex; justify-content: center; gap: 7px;
}
.dots button {
  width: 7px; height: 7px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--surface-3);
  transition: background .2s var(--ease), width .2s var(--ease);
}
.dots button.on { background: var(--accent); width: 18px; border-radius: 4px; }

.micro-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 14px; }
.micro-head span { font-size: 10px; color: var(--text-3); letter-spacing: .04em; }

.micro-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 14px; }
.micro { min-width: 0; }
.micro-top {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 6px; margin-bottom: 4px;
}
.micro-top span {
  font-size: 11px;
  color: var(--text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.micro-top b { font-size: 11px; font-weight: 620; white-space: nowrap; font-variant-numeric: tabular-nums; }
.micro .bar { height: 4px; }
.micro .bar i { background: var(--accent-lo); }
.micro.hit .bar i { background: var(--accent); }

/* Sodium, sugars and saturated fat are ceilings, not goals. Filling one up is
   not an achievement, so they stay neutral and only colour when exceeded —
   green-and-climbing would say the opposite of what's meant. */
.micro.is-limit .bar i { background: var(--surface-3); }
.micro.over .bar i { background: var(--danger); }
.micro.over .micro-top b { color: var(--danger-fg); }

.micro.none .bar i { background: var(--surface-3); }
.micro.none .micro-top b { color: var(--text-3); font-weight: 400; }

.micro-note { font-size: 11px; color: var(--text-3); margin: 16px 0 0; line-height: 1.5; }
.mini-range { margin-bottom: 14px; }
.mini-range button { padding: 6px; font-size: 11.5px; }

/* Protein spread across the day. Total protein is only half the story. */
.protein-meals { display: flex; gap: 5px; margin-top: 14px; }
.pm { flex: 1; min-width: 0; text-align: center; }
.pm i {
  display: block;
  height: 4px;
  border-radius: 99px;
  background: var(--surface-3);
  margin-bottom: 5px;
}
.pm.ok i { background: var(--protein); }
.pm.low i { background: var(--protein-dim); }
.pm b { display: block; font-size: 11.5px; font-variant-numeric: tabular-nums; font-weight: 620; }
.pm span {
  display: block;
  font-size: 8.5px;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: .06em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Weekly budget */
.week-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.week-head b { font-size: 27px; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.week-head span { font-size: 12px; color: var(--text-3); }
.week-bar { height: 8px; background: var(--surface-3); border-radius: 99px; overflow: hidden; margin: 12px 0 10px; }
.week-bar i { display: block; height: 100%; border-radius: 99px; background: var(--accent-lo); transition: width .5s var(--ease); }
.week-bar i.over { background: var(--danger); }

/* ──────────────────────── streak pill + supplements bar ─────────────────── */

.strip { display: flex; gap: 10px; margin-bottom: 14px; align-items: stretch; }

.streak-pill {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 0 14px;
  box-shadow: var(--lift);
}
.streak-pill b {
  font-size: 16px;
  font-weight: 680;
  letter-spacing: -.02em;
  transition: color .4s var(--ease);
}
/* Weeks of consistency should look different from a couple of days. */
.streak-pill.warm b { color: var(--accent); }
.streak-pill.hot b { color: var(--warn-fg); }
.streak-pill.hot { border-color: rgba(255, 194, 74, .35); }
.streak-pill span { font-size: 10px; color: var(--text-3); text-transform: uppercase; letter-spacing: .07em; font-weight: 600; }

.supp-bar {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 12px 14px;
  color: var(--text-2);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .07em;
  font-weight: 600;
  box-shadow: var(--lift);
  transition: border-color .18s var(--ease);
}
.supp-bar > span { flex: 1; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.supp-bar b { color: var(--text); font-variant-numeric: tabular-nums; letter-spacing: 0; }
.supp-bar svg { transition: transform .22s var(--ease); flex: none; }
.supp-bar[aria-expanded="true"] { border-color: var(--line-strong); }
.supp-bar[aria-expanded="true"] svg { transform: rotate(180deg); }
.supp-bar.done b { color: var(--accent); }

#supp-chips { margin-bottom: 14px; animation: heroIn .2s var(--ease); }

/* ───────────────────────────── streak + water ───────────────────────────── */

.mini-row { display: grid; grid-template-columns: 1fr 1.55fr; gap: 12px; margin-bottom: 14px; }
.mini {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 14px 16px;
  min-width: 0;
  box-shadow: var(--lift);
}
.mini > b {
  display: block;
  font-size: 21px;
  font-weight: 680;
  letter-spacing: -0.03em;
  line-height: 1.15;
}
.mini > b small { font-size: 10.5px; font-weight: 500; color: var(--text-3); letter-spacing: 0; }
.mini > span {
  font-size: 10px;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-weight: 600;
}

/* ─────────────────────────────── supplements ────────────────────────────── */

.chips.supps button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 15px 8px 11px;
  color: var(--text-2);
}
/* Empty circle → filled tick. The state is carried by the mark and the text
   weight, not by colour alone. */
.chips.supps button::before {
  content: "";
  width: 15px; height: 15px;
  border-radius: 50%;
  border: 1.5px solid var(--line-strong);
  flex: none;
  transition: background .18s var(--ease), border-color .18s var(--ease);
}
.chips.supps button.taken {
  color: var(--text);
  border-color: var(--accent-lo);
  background: var(--accent-wash);
  font-weight: 580;
}
.chips.supps button.taken::before {
  background: var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230c1206' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5l5.5 5.5L20 6.5'/%3E%3C/svg%3E") center / 10px no-repeat;
  border-color: var(--accent);
}

/* Management list in Setup — a × instead of a tick */
.chips.supps.manage button::before { display: none; }
.chips.supps.manage button {
  padding: 8px 13px;
  gap: 9px;
}
.chips.supps.manage button::after {
  content: "×";
  font-size: 16px;
  line-height: 1;
  color: var(--text-3);
}
.chips.supps.manage button:active::after { color: var(--danger); }

/* ──────────────────────────────── composer ──────────────────────────────── */

.composer { padding: 12px; }

.composer textarea {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 10px 12px;
  resize: none;
  outline: none;
  line-height: 1.4;
  min-height: 42px;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.composer textarea:focus {
  border-color: var(--accent-lo);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3), 0 0 0 3px var(--accent-wash);
}
.composer textarea::placeholder { color: var(--text-3); }

.composer { position: relative; }

/* Type-ahead over your own history. A pick here skips the AI entirely.
   Anchored to the bottom of the whole card, so it overlays the quick-add row
   below rather than the Add button — covering the primary action to offer a
   shortcut is a bad trade. Compact rows keep it from swallowing the screen. */
.suggest {
  position: absolute;
  left: 10px; right: 10px; top: calc(100% - 6px);
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  overflow: hidden;
  z-index: 20;
  box-shadow: 0 10px 30px -8px rgba(0, 0, 0, .7);
  animation: heroIn .15s var(--ease);
}
.suggest button {
  display: flex;
  align-items: baseline;
  gap: 9px;
  width: 100%;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--line);
  color: var(--text);
  padding: 8px 12px;
  text-align: left;
  font-size: 13px;
}
.suggest button:last-child { border-bottom: 0; }
.suggest button:active { background: var(--surface-3); }
.suggest button span {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.suggest button small {
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
}
.suggest button b {
  color: var(--accent);
  font-size: 8px;
  letter-spacing: .08em;
  text-transform: uppercase;
  font-weight: 700;
}

.composer-row { display: flex; gap: 7px; margin-top: 8px; align-items: stretch; }

.icon-btn {
  flex: none;
  width: 42px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--text-2);
  border-radius: var(--r-md);
  display: grid;
  place-items: center;
  padding: 0;
  transition: transform .13s var(--ease), border-color .18s var(--ease), color .18s var(--ease);
}
.icon-btn:active { transform: scale(.93); background: var(--surface-3); }
.icon-btn.on { color: var(--accent); border-color: var(--accent-lo); background: var(--accent-wash); }

.composer-row select {
  flex: 1;
  min-width: 0;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 10px 11px;
  outline: none;
  appearance: none;
  color: var(--text-2);
  font-weight: 520;
  /* 16px minimum, or iOS zooms the page when this is tapped. */
  font-size: 16px;
}

.composer-row button[type="submit"] {
  flex: none;
  min-width: 80px;
  padding: 10px 16px;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  color: var(--accent-ink);
  font-weight: 680;
  letter-spacing: -0.01em;
  border: 0;
  border-radius: var(--r-md);
  box-shadow: 0 1px 0 var(--btn-bevel) inset, 0 3px 10px -5px var(--btn-glow);
  transition: transform .13s var(--ease), filter .13s var(--ease);
}
.composer-row button[type="submit"]:active { transform: scale(.96); filter: brightness(.94); }
.composer-row button:disabled { opacity: .45; box-shadow: none; }

.hint { margin: 10px 2px 0; font-size: 12px; color: var(--text-3); min-height: 1em; line-height: 1.45; }
.hint.err { color: var(--danger-fg); }
.hint.queued { color: var(--carbs); }

/* Attached-photo thumbnail */
.shot { position: relative; margin-bottom: 12px; }
.shot img {
  display: block;
  width: 100%;
  max-height: 190px;
  object-fit: cover;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
}
.shot button {
  position: absolute;
  top: 8px; right: 8px;
  width: 30px; height: 30px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 50%;
  background: var(--scrim-soft);
  backdrop-filter: blur(8px);
  color: #fff;
  font-size: 17px;
  line-height: 1;
  display: grid;
  place-items: center;
}

/* ─────────────────────────────── quick chips ────────────────────────────── */

.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chips button {
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: 99px;
  padding: 8px 14px;
  font-size: 13px;
  font-weight: 520;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  box-shadow: var(--lift);
  transition: transform .13s var(--ease), border-color .18s var(--ease);
}
.chips button small {
  color: var(--text-3);
  margin-left: 7px;
  font-variant-numeric: tabular-nums;
}
.chips button:active { transform: scale(.95); border-color: var(--line-strong); }

/* ──────────────────────────────── entries ───────────────────────────────── */

.meal-group { margin-bottom: 16px; }
/* When meals are collapsed this row *is* the interface — it has to carry the
   meal, its calories and its macros at a glance. It was set as an 11px
   micro-label, which is a caption, not a control. */
.meal-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  color: var(--text-2);
  padding: 6px 4px 10px;
}

/* The heading itself is the collapse toggle. */
.meal-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  padding: 2px 0;
  flex: 1;
  min-width: 0;
  text-align: left;
}
.meal-toggle > svg:first-child {
  transition: transform .22s var(--ease);
  flex: none;
  opacity: .55;
  width: 13px;
  height: 13px;
}
/* The meal's own icon — shape carries identity, so no extra colour is needed. */
.meal-toggle > svg:nth-child(2) { flex: none; opacity: .75; color: var(--text-2); }

.meal-title { min-width: 0; flex: 1; }
.meal-name {
  display: block;
  font-size: 15px;
  font-weight: 620;
  color: var(--text);
  /* No capitalize. It existed to titlecase the raw stored key ("breakfast"),
     and the name now arrives already written the way its owner wants it —
     capitalize would turn "pre-workout" into "Pre-Workout" and "iftar" into
     something they did not type. */
  letter-spacing: -0.01em;
  line-height: 1.25;
}
.meal-macros {
  display: flex;
  gap: 9px;
  margin-top: 2px;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}
.meal-macros .m-p { color: var(--protein); }
.meal-macros .m-c { color: var(--carbs); }
.meal-macros .m-f { color: var(--fat); }

.meal-kcal {
  font-size: 15px;
  font-weight: 650;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.meal-group.closed .meal-toggle > svg:first-child { transform: rotate(-90deg); }
.meal-group.closed .meal-items { display: none; }
.meal-items { animation: heroIn .2s var(--ease); }
.meal-group.closed .meal-head { padding-bottom: 6px; }
.meal-head .right { display: flex; align-items: center; gap: 4px; flex: none; }
/* "Save this meal" is a rare action next to a number you read constantly, so
   it stays quiet rather than competing with the calories. */
.meal-head .right > button {
  background: none;
  border: 0;
  color: var(--text-3);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-weight: 620;
  padding: 6px 2px 6px 10px;
  transition: color .15s var(--ease);
}
.meal-head .right > button:active { color: var(--accent); }

/* Foods are children of a meal and should read that way. They were set at the
   same weight as the meal heading — heavier, in fact — which inverted the
   hierarchy and made the list slow to scan. Indented, and a step down in every
   dimension: type, padding, spacing. */
.meal-items { margin-left: 14px; }

.entry {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 9px 12px;
  margin-bottom: 6px;
  /* Flatter than a card. Sitting at full elevation made every food compete
     with its own meal heading for attention. */
  box-shadow: inset 0 1px 0 var(--highlight);
  animation: rise .28s var(--ease) both;
}
@keyframes rise {
  from { opacity: 0; transform: translateY(5px); }
  to   { opacity: 1; transform: none; }
}

/* Deleting collapses the row rather than the list silently shortening, so it's
   clear which entry went. */
.entry.removing {
  height: 0 !important;
  min-height: 0;
  opacity: 0;
  margin-bottom: 0;
  padding-top: 0;
  padding-bottom: 0;
  border-width: 0;
  overflow: hidden;
  transition:
    height .2s var(--ease),
    opacity .15s var(--ease),
    margin .2s var(--ease),
    padding .2s var(--ease);
}

.entry-main { flex: 1; min-width: 0; cursor: pointer; }
.entry-name { font-size: 13.5px; font-weight: 540; letter-spacing: -0.005em; line-height: 1.3; }
.entry-name .low {
  font-size: 9px;
  color: var(--warn);
  border: 1px solid currentColor;
  border-radius: 4px;
  padding: 1px 5px;
  margin-left: 8px;
  vertical-align: 1.5px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-weight: 700;
}
.entry-sub { font-size: 10.5px; color: var(--text-3); margin-top: 2px; letter-spacing: 0.01em; }
/* Same hues as the macro bars above, so the letters key straight to them.
   These clear 5:1 against the card, well past AA for small text. */
.entry-sub .m-p { color: var(--protein); font-weight: 560; }
.entry-sub .m-c { color: var(--carbs);   font-weight: 560; }
.entry-sub .m-f { color: var(--fat);     font-weight: 560; }
/* A food's calories should stay below its meal's total, not match it. */
.entry-kcal { font-weight: 600; white-space: nowrap; letter-spacing: -0.015em; font-size: 13.5px; }
.entry-del {
  background: none;
  border: 0;
  color: var(--text-3);
  font-size: 17px;
  /* The glyph shrank with everything else, but the tap area must not — this
     keeps it at ~45px while the × itself stays small. */
  padding: 14px 4px 14px 10px;
  margin: -14px -2px -14px 0;
  line-height: 1;
  transition: color .15s var(--ease), transform .13s var(--ease);
}
.entry-del:active { color: var(--danger); transform: scale(.85); }

/* Portion scaler, revealed by tapping an entry */
.scaler {
  display: flex;
  gap: 7px;
  padding: 2px 2px 10px;
  margin-top: -4px;
  flex-wrap: wrap;
  align-items: center;
  animation: rise .22s var(--ease) both;
}
.scaler button {
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: var(--r-sm);
  padding: 7px 12px;
  font-size: 13px;
  font-weight: 540;
  font-variant-numeric: tabular-nums;
  transition: transform .13s var(--ease), background .13s var(--ease);
}
.scaler button:active { transform: scale(.93); background: var(--surface-3); }
.scaler input {
  width: 70px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 7px 10px;
  outline: none;
  font-variant-numeric: tabular-nums;
}

.empty {
  color: var(--text-3);
  font-size: 13px;
  text-align: center;
  padding: 30px 16px;
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
  line-height: 1.5;
}

/* ──────────────────────────────── history ──────────────────────────────── */

/* Sits on the page rather than inside a card, so it keeps the card rhythm
   (14px) and reads as a control raised above the background. */
.range {
  display: flex;
  gap: 4px;
  margin-bottom: 14px;
  background: var(--surface-2);
  padding: 4px;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
}
.range button {
  flex: 1;
  background: none;
  border: 0;
  color: var(--text-3);
  border-radius: var(--r-sm);
  padding: 8px;
  font-size: 12.5px;
  font-weight: 560;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.range button.on {
  background: var(--surface-3);
  color: var(--text);
  box-shadow: inset 0 1px 0 var(--highlight), 0 1px 3px rgba(0, 0, 0, 0.4);
}

.stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.stat {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 13px 14px;
}
.stat b {
  display: block;
  font-size: 20px;
  font-weight: 680;
  letter-spacing: -0.03em;
  margin-bottom: 2px;
}
.stat span {
  font-size: 10px;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  line-height: 1.35;
  display: block;
}

/* Bar charts. 2px gaps between bars, 4px rounded data-ends anchored to the
   baseline, recessive target line — the marks carry the data, nothing else. */
.chart { display: flex; align-items: flex-end; gap: 2px; height: 132px; position: relative; }
.chart .target-line {
  position: absolute;
  left: 0; right: 0;
  border-top: 1px dashed var(--accent-lo);
  pointer-events: none;
}
.chart .target-line::after {
  content: "target";
  position: absolute;
  right: 0; top: -14px;
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-lo);
  font-weight: 650;
}
.chart .col {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  height: 100%;
  min-width: 0;
  cursor: pointer;
}
.chart .col i {
  display: block;
  background: var(--surface-3);
  border-radius: 4px 4px 0 0;
  min-height: 3px;
  /* Keeps a 7-day chart from turning into slabs; long ranges shrink naturally. */
  max-width: 34px;
  margin: 0 auto;
  width: 100%;
  transition: filter .15s var(--ease);
}
.chart .col.hit i { background: var(--accent-lo); }
/* Amber, not red. Being a little over target is worth noticing, not an alarm —
   red on most bars reads as failure when you're essentially on plan. */
.chart .col.over i { background: var(--carbs-dim); }
.chart .col.sel i { filter: brightness(1.32); }
.chart.protein .col.hit i { background: var(--protein); }

/* Tap read-out — `title` tooltips never appear on a touch screen. */
.chart-readout {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: 10px;
  font-size: 11.5px;
  color: var(--text-3);
  min-height: 1.2em;
  letter-spacing: 0.01em;
}
.chart-readout b { color: var(--text-2); font-variant-numeric: tabular-nums; font-weight: 600; }

.spark { width: 100%; display: block; overflow: visible; }
.spark .line { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.spark .area { fill: url(#sparkFill); stroke: none; }
/* Raw scale readings sit behind the trend, deliberately quiet — they're the
   input, not the answer. */
.spark .raw { fill: none; stroke: var(--line-strong); stroke-width: 1; stroke-dasharray: 3 3; }
.spark .raw-dot { fill: var(--text-3); opacity: .5; }
.spark .dot { fill: var(--surface); stroke: var(--accent); stroke-width: 2; }
.spark .dot-last { fill: var(--accent); stroke: var(--surface); stroke-width: 2; }
.spark .grid { stroke: var(--line); stroke-width: 1; }
.spark .axis { stroke: var(--line-strong); stroke-width: 1; }
.spark text { fill: var(--text-3); font-size: 9px; font-family: inherit; }
.spark .y-label { text-anchor: end; }
.spark .x-label { text-anchor: middle; }
.chart.weight { display: block; height: auto; }

.weigh label {
  font-size: 10px;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-weight: 620;
}
.weigh-row { display: flex; gap: 9px; margin-top: 10px; }
.weigh-row input {
  flex: 1;
  min-width: 0;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 12px 13px;
  outline: none;
  font-variant-numeric: tabular-nums;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3);
}
.weigh-row input:focus { border-color: var(--accent-lo); }
.weigh-row button {
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  color: var(--text);
  border-radius: var(--r-md);
  padding: 12px 22px;
  font-weight: 560;
  transition: transform .13s var(--ease), background .13s var(--ease);
}
.weigh-row button:active { transform: scale(.95); background: var(--surface-3); }

.ghost {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  color: var(--text);
  border-radius: var(--r-md);
  padding: 13px;
  font-weight: 540;
  transition: transform .13s var(--ease), background .13s var(--ease);
}
.ghost:active { transform: scale(.98); background: var(--surface-3); }

/* ──────────────────────────────── setup ────────────────────────────────── */

#profile-form label {
  display: block;
  font-size: 10px;
  color: var(--text-3);
  margin-bottom: 14px;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-weight: 620;
}
#profile-form input, #profile-form select {
  display: block;
  width: 100%;
  margin-top: 7px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 12px 13px;
  outline: none;
  appearance: none;
  /* 16px minimum, or iOS zooms the page when this is tapped. */
  font-size: 16px;
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  color: var(--text);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.28);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
#profile-form input:focus, #profile-form select:focus {
  border-color: var(--accent-lo);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.28), 0 0 0 3px var(--accent-wash);
}

.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 11px; }

/* ─────────────────── manual targets: the derived carbs tile ─────────────── */

/* Occupies a grid cell like a label+input pair, and has to match their metrics
   exactly or the row goes ragged: 10px caption, 7px gap, then a box with the
   same padding, border and 16px type as a real field. */
.derived { margin-bottom: 14px; }
.derived > span {
  display: block;
  font-size: 10px;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-weight: 620;
}
.derived > b {
  display: block;
  margin-top: 7px;
  /* Reads as an output, not an empty box waiting for input: accent-tinted,
     dashed edge, and no inset shadow suggesting a well you can type into. */
  background: var(--accent-wash);
  border: 1px dashed var(--accent-lo);
  color: var(--accent);
  border-radius: var(--r-md);
  padding: 12px 13px;
  font-size: 16px;
  font-weight: 680;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  text-transform: none;
  transition: color .2s var(--ease), border-color .2s var(--ease),
              background .2s var(--ease);
}
/* Protein and fat alone have eaten the whole calorie budget — there is nothing
   left for carbs, and the tile should say so rather than sit at a calm zero. */
.derived.over > b {
  background: rgba(229, 72, 77, 0.08);
  border-color: var(--danger);
  color: var(--danger);
}

/* ──────────────── manual targets: the calorie budget bar ───────────────── */

.budget {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 14px 13px;
  margin-bottom: 16px;
}
.budget-bar {
  display: flex;
  height: 10px;
  border-radius: 99px;
  overflow: hidden;
  background: var(--surface-3);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .4);
}
.budget-bar i {
  /* Width is set inline from each macro's share of the calories. The transition
     is what makes the split feel connected to the number you just typed. */
  width: 0;
  transition: width .3s var(--ease);
}
.budget-bar .b-p { background: var(--protein); }
.budget-bar .b-c { background: var(--carbs); }
.budget-bar .b-f { background: var(--fat); }
.budget.over .budget-bar .b-f,
.budget.over .budget-bar .b-p { background: var(--danger); }

.budget-legend { margin-top: 12px; display: grid; gap: 7px; }
.budget-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.budget-row .dot {
  width: 8px; height: 8px; border-radius: 99px; flex: none;
  align-self: center;
}
.budget-row .nm { color: var(--text-2); flex: 1; }
.budget-row .g { font-weight: 620; }
/* Secondary to the grams — the conversion, not the target you set. */
.budget-row .kc { color: var(--text-3); min-width: 68px; text-align: right; }
.budget-row .pc { color: var(--text-3); min-width: 34px; text-align: right; }

.budget-note {
  font-size: 11.5px;
  color: var(--text-3);
  line-height: 1.55;
  margin: 11px 0 0;
  padding-top: 11px;
  border-top: 1px solid var(--line);
}
.budget.over .budget-note { color: var(--danger); }

.preview {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 15px 12px;
  margin-bottom: 14px;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  text-align: center;
}
.preview div b {
  display: block;
  font-size: 18px;
  font-weight: 680;
  letter-spacing: -0.03em;
}
.preview div span {
  font-size: 9px;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-weight: 620;
}
.preview .note {
  grid-column: 1 / -1;
  font-size: 11px;
  color: var(--text-3);
  text-align: left;
  margin: 4px 0 0;
  padding-top: 11px;
  border-top: 1px solid var(--line);
}

label.check {
  display: flex !important;
  align-items: center;
  gap: 11px;
  margin-bottom: 14px;
  text-transform: none !important;
  letter-spacing: normal !important;
  font-size: 13.5px !important;
  color: var(--text-2) !important;
  font-weight: 400 !important;
}
/* Needs the #id prefix to outrank `#profile-form input`, which sets
   appearance:none — that blanks the native tick and makes accent-color a no-op,
   so a checked box renders identically to an unchecked one. */
#profile-form label.check input,
label.check input {
  margin: 0 !important;
  appearance: auto !important;
  -webkit-appearance: checkbox !important;
  accent-color: var(--accent);
  width: 18px !important;
  height: 18px;
  flex: none;
  box-shadow: none !important;
  border-radius: 0;
}

#profile-form button[type="submit"] {
  width: 100%;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  color: var(--accent-ink);
  font-weight: 680;
  border: 0;
  border-radius: var(--r-md);
  padding: 14px;
  box-shadow: 0 1px 0 var(--btn-bevel) inset, 0 3px 10px -5px var(--btn-glow);
  transition: transform .13s var(--ease), filter .13s var(--ease);
}
#profile-form button[type="submit"]:active { transform: scale(.98); filter: brightness(.94); }

/* ─────────────────────────── macro split editor ─────────────────────────── */

.presets { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 16px; }
.presets button {
  flex: 1 1 auto;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--text-2);
  border-radius: 99px;
  padding: 8px 12px;
  font-size: 12.5px;
  white-space: nowrap;
}
.presets button.on { border-color: var(--accent-lo); color: var(--accent); background: var(--accent-wash); }
.presets button:active { transform: scale(.96); }

.slider + .slider { margin-top: 14px; }
.slider-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 7px; }
.slider-head span { font-size: 11px; color: var(--text-2); text-transform: uppercase; letter-spacing: .07em; font-weight: 620; }
.slider-head b { font-size: 12.5px; font-variant-numeric: tabular-nums; font-weight: 620; }

/* Track and thumb both need explicit styling — the native control ignores the
   surrounding design system entirely. */
.slider input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 6px;
  border-radius: 99px;
  background: var(--surface-3);
  outline: none;
  padding: 0;
  margin: 0;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .4);
}
.slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--accent);
  border: 3px solid var(--surface);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .5);
  cursor: pointer;
}
.slider input[type="range"]::-moz-range-thumb {
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--accent);
  border: 3px solid var(--surface);
  cursor: pointer;
}
[data-macro="protein"] input[type="range"]::-webkit-slider-thumb { background: var(--protein); }
[data-macro="carbs"]   input[type="range"]::-webkit-slider-thumb { background: var(--carbs); }
[data-macro="fat"]     input[type="range"]::-webkit-slider-thumb { background: var(--fat); }

#split-box { margin-bottom: 14px; }

.fine { font-size: 12px; color: var(--text-3); margin: 0 0 11px; line-height: 1.55; }
.fine:last-child { margin-bottom: 0; }

/* ─────────────────────────────── sign-in gate ──────────────────────────── */

/* Google's button, in Google's colours. Deliberately the one light surface in
   a dark app: a provider button that has been restyled to match reads as a
   fake, and people are right to hesitate before handing over an account. */
.provider {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  /* Literal, not tokens. This button is white with dark text in both themes
     because that is Google's own treatment, and a provider button restyled to
     match the surrounding app reads as a fake — people are right to hesitate
     before handing over an account to one. Tokenising the text colour turned it
     near-white on white in light mode, which is how this comment exists. */
  background: #fff;
  color: #1f1f1f;
  border: 1px solid #dadce0;
  border-radius: var(--r-md);
  padding: 13px;
  font-size: 14.5px;
  font-weight: 560;
  letter-spacing: -0.01em;
  transition: transform .13s var(--ease), filter .13s var(--ease);
}
.provider:active { transform: scale(.985); filter: brightness(.95); }
.provider svg { flex: none; }

/* A rule with the word sitting in it, rather than a bare line — this is a fork
   in the road, not a section break. */
.or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 16px 0;
  color: var(--text-3);
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.or::before, .or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

/* Full width and 44px tall. As inline text these came out at 36px, which is
   under the tap minimum — and "I had an account before" is the only way back in
   for someone whose session has lapsed, so it is a bad one to have to aim at. */
#gate-signin .linkish,
#gate-invite-step .linkish,
#gate-legacy-step .linkish {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 12px 8px;
}
/* Secondary escape hatches: present, but not competing with the way in. */
.linkish.quiet { color: var(--text-3); font-size: 12px; }

.gate-card .gate-sub {
  font-size: 15px;
  font-weight: 560;
  margin: 0 0 6px;
  letter-spacing: -0.01em;
}
.gate-card .gate-sub b { color: var(--accent); font-weight: 620; }
.gate-card .fine { text-align: left; }

/* ────────────────────────── target mode chooser ────────────────────────── */

/* Two tiles, not a segmented strip. This is the main decision on the page and
   the thin toggle gave it the same weight as the 7/30/90 filter on Progress.
   Each tile states what it does, so the choice is readable before it is made
   rather than explained by a caption underneath afterwards. */
.mode-choice {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 16px;
}

.mode-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 3px;
  text-align: left;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 13px 13px 14px;
  /* Room for the tick, so selecting one doesn't reflow the text. */
  padding-right: 34px;
  transition: border-color .16s var(--ease), background .16s var(--ease),
              transform .12s var(--ease);
}
.mode-card:active { transform: scale(.985); }

.mode-title {
  font-size: 14px;
  font-weight: 620;
  color: var(--text-2);
  letter-spacing: -0.01em;
}
.mode-desc {
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--text-3);
}

.mode-card.on {
  border-color: var(--accent-lo);
  background: var(--accent-wash);
}
.mode-card.on .mode-title { color: var(--accent-fg); }
.mode-card.on .mode-desc { color: var(--text-2); }

/* The tick is the only thing that says "this one". Colour alone would leave
   the two tiles nearly identical for anyone who cannot separate the greens. */
.mode-tick {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 16px;
  height: 16px;
  border-radius: 99px;
  border: 1.5px solid var(--line-strong);
  transition: border-color .16s var(--ease), background .16s var(--ease);
}
.mode-card.on .mode-tick {
  border-color: var(--accent-fg);
  background: var(--accent-fg);
}
.mode-card.on .mode-tick::after {
  content: "";
  position: absolute;
  left: 4.5px;
  top: 1.5px;
  width: 4px;
  height: 8px;
  border: solid var(--accent-ink);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* Under about 340px the two tiles squeeze their descriptions into four lines.
   Stacking reads better than wrapping. */
@media (max-width: 360px) {
  .mode-choice { grid-template-columns: 1fr; }
}

/* ──────────────────────────────── admin ───────────────────────────────── */

.admin-list { display: grid; gap: 8px; margin-bottom: 6px; }
.admin-row {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 11px 12px;
  display: flex;
  align-items: center;
  gap: 10px;
}
/* A spent code is history, not a thing to act on. */
.admin-row.spent { opacity: .58; }
.admin-row .body { flex: 1; min-width: 0; }

/* The code is the one thing on the row you need to read character by character,
   so it gets a monospace face where 5 and S can't be confused. */
.code-value {
  background: none;
  border: 0;
  padding: 0;
  color: var(--accent);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 15px;
  font-weight: 620;
  letter-spacing: 0.04em;
  transition: color .15s var(--ease), transform .13s var(--ease);
}
.code-value:active { transform: scale(.97); }
.admin-row.spent .code-value { color: var(--text-2); text-decoration: line-through; }
.code-value.copied { color: var(--accent-hi); }

.admin-meta {
  font-size: 11px;
  color: var(--text-3);
  margin-top: 3px;
  line-height: 1.45;
}
.admin-row .who { font-size: 14px; font-weight: 580; letter-spacing: -0.01em; }
.admin-badge {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-weight: 640;
  color: var(--accent);
  border: 1px solid var(--accent-lo);
  border-radius: 99px;
  padding: 2px 7px;
  margin-left: 7px;
  vertical-align: 1px;
}
/* Same trick as the entry delete: a small glyph with a full-size tap area. */
.admin-del {
  background: none;
  border: 0;
  color: var(--text-3);
  font-size: 17px;
  line-height: 1;
  padding: 14px 4px 14px 12px;
  margin: -14px -2px -14px 0;
  flex: none;
  transition: color .15s var(--ease), transform .13s var(--ease);
}
.admin-del:active { color: var(--danger); transform: scale(.85); }

.admin-row.confirming {
  border-color: var(--danger);
  background: rgba(229, 72, 77, 0.06);
}
.admin-meta.danger { color: var(--danger); margin-top: 0; }
.admin-mini {
  background: var(--surface-3);
  border: 1px solid var(--line-strong);
  color: var(--text);
  border-radius: var(--r-sm);
  /* 14px vertical clears the 44px tap minimum. These commit a permanent
     deletion — a target you can miss is the wrong economy here. */
  padding: 14px 14px;
  font-size: 12.5px;
  font-weight: 560;
  flex: none;
  transition: transform .13s var(--ease);
}
.admin-mini.danger { border-color: var(--danger); color: var(--danger); }
.admin-mini:active { transform: scale(.94); }

/* ───────────────────────────────── tabs ────────────────────────────────── */

.tabs {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  height: calc(var(--tabs-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  display: flex;
  /* Opaque enough that a bright control scrolling underneath doesn't smear its
     colour across the whole bar. */
  background: var(--scrim);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border-top: 1px solid var(--line);
  z-index: 10;
}
.tabs button {
  flex: 1;
  background: none;
  border: 0;
  color: var(--text-3);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  position: relative;
  transition: color .18s var(--ease);
}
.tabs button svg { width: 21px; height: 21px; transition: transform .2s var(--ease); }
.tabs button.on { color: var(--accent); }
.tabs button.on svg { transform: translateY(-1px); }
/* Active marker on the top edge, aligned to the icon */
.tabs button.on::before {
  content: "";
  position: absolute;
  top: 0;
  width: 24px;
  height: 2px;
  border-radius: 0 0 3px 3px;
  background: var(--accent);
}

/* ──────────────────────── scanner + product sheet ──────────────────────── */

#scanner {
  position: fixed; inset: 0; z-index: 60;
  background: #000;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
#scan-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.scan-frame {
  position: relative;
  width: min(78vw, 320px);
  aspect-ratio: 3 / 2;
  border: 2px solid var(--accent);
  border-radius: var(--r-md);
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .6), 0 0 24px var(--accent-wash-strong);
}
.scan-hint {
  position: relative;
  margin-top: 22px;
  color: #fff;
  font-size: 14px;
  text-align: center;
  padding: 0 24px;
  text-shadow: 0 1px 8px rgba(0, 0, 0, .95);
}
.scan-actions {
  position: absolute;
  left: 16px; right: 16px;
  bottom: calc(env(safe-area-inset-bottom) + 24px);
  display: flex; gap: 10px;
}
.scan-actions .ghost { background: var(--scrim); backdrop-filter: blur(10px); }

#code-sheet .sheet-card h2 { margin-bottom: 4px; }
#code-sheet input {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 13px;
  outline: none;
  font-size: 17px;
  letter-spacing: .06em;
  font-variant-numeric: tabular-nums;
  margin: 4px 0 12px;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, .3);
}
#code-sheet input:focus { border-color: var(--accent-lo); }
#code-go {
  width: 100%;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  color: var(--accent-ink);
  font-weight: 680;
  border: 0;
  border-radius: var(--r-md);
  padding: 14px;
  margin-bottom: 9px;
}

/* Old versus new, so a correction is a decision rather than a surprise. */
.diff { display: grid; grid-template-columns: 1fr auto auto; gap: 8px 12px; align-items: baseline; margin: 14px 0 16px; }
.diff-head { font-size: 9px; text-transform: uppercase; letter-spacing: .09em; color: var(--text-3); font-weight: 650; }
.diff span { font-size: 13px; color: var(--text-2); }
.diff b { font-size: 14px; font-variant-numeric: tabular-nums; text-align: right; font-weight: 600; }
.diff b.was { color: var(--text-3); font-weight: 400; }
/* Direction only, never a verdict. Neither number is "better" — the question is
   which is closer to what was eaten, and colouring a rise as a warning would
   quietly argue for one answer. */
.diff b.changed { color: var(--text); }
.diff b.same { color: var(--text-3); font-weight: 400; }
.diff b i { font-style: normal; color: var(--text-3); font-size: 11px; margin-left: 3px; }
.recheck-name { font-size: 13px; color: var(--text); margin: 2px 0 0; }

#recheck-apply {
  width: 100%;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  color: var(--accent-ink);
  font-weight: 680;
  border: 0;
  border-radius: var(--r-md);
  padding: 14px;
  margin-bottom: 9px;
}
#recheck-apply:disabled { opacity: .5; }

#sheet, #code-sheet, #recheck-sheet {
  position: fixed; inset: 0; z-index: 70;
  background: var(--scrim-deep);
  backdrop-filter: blur(6px);
  display: flex; align-items: flex-end; justify-content: center;
  padding: 14px;
  animation: fade .2s var(--ease);
}
@keyframes fade { from { opacity: 0 } to { opacity: 1 } }

.sheet-card {
  width: 100%; max-width: 480px;
  max-height: 88vh; overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 24px;
  padding: 22px;
  padding-bottom: calc(22px + env(safe-area-inset-bottom));
  box-shadow: inset 0 1px 0 var(--highlight), 0 -8px 40px rgba(0, 0, 0, .6);
  animation: sheetUp .3s var(--ease);
}
@keyframes sheetUp { from { transform: translateY(18px); opacity: .6 } to { transform: none; opacity: 1 } }

.sheet-card h2 { margin: 0; font-size: 19px; letter-spacing: -0.02em; font-weight: 660; }
.sheet-brand { margin: 4px 0 12px; font-size: 12px; color: var(--text-3); }
.sheet-card > label {
  display: block;
  font-size: 10px;
  color: var(--text-3);
  margin-bottom: 8px;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-weight: 620;
}

.sheet-row { display: flex; align-items: center; gap: 9px; }
.sheet-row input {
  flex: 1;
  min-width: 0;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 13px;
  outline: none;
  font-size: 18px;
  font-weight: 600;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, .3);
}
.sheet-row input:focus { border-color: var(--accent-lo); }
.sheet-unit { color: var(--text-3); font-size: 14px; width: 26px; }

.sheet-presets { display: flex; gap: 7px; flex-wrap: wrap; margin: 11px 0 15px; }
.sheet-presets button {
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--text-2);
  border-radius: 99px;
  padding: 7px 14px;
  font-size: 12.5px;
  transition: transform .13s var(--ease);
}
.sheet-presets button:active { transform: scale(.95); }
.sheet-presets button.on { border-color: var(--accent-lo); color: var(--accent); background: var(--accent-wash); }

#sheet-add {
  width: 100%;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  color: var(--accent-ink);
  font-weight: 680;
  border: 0;
  border-radius: var(--r-md);
  padding: 14px;
  margin-bottom: 9px;
  box-shadow: 0 1px 0 var(--btn-bevel) inset, 0 4px 14px -4px var(--btn-glow);
  transition: transform .13s var(--ease);
}
#sheet-add:active { transform: scale(.98); }
#sheet-add:disabled { opacity: .45; box-shadow: none; }

/* ─────────────────────────────── adaptive tdee ─────────────────────────── */

.tdee-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; }
.tdee-head b { font-size: 30px; letter-spacing: -0.035em; font-weight: 700; }
.tdee-head span { font-size: 11px; color: var(--text-3); text-transform: uppercase; letter-spacing: 0.07em; font-weight: 600; }
.tdee-delta { font-size: 12.5px; font-weight: 600; margin: 0 0 10px; }
.tdee-delta.up { color: var(--accent); }
.tdee-delta.down { color: var(--warn); }
#tdee-apply {
  width: 100%;
  background: var(--accent-wash);
  border: 1px solid var(--accent-lo);
  color: var(--accent);
  border-radius: var(--r-md);
  padding: 13px;
  margin-top: 14px;
  font-weight: 640;
  transition: transform .13s var(--ease), background .15s var(--ease);
}
#tdee-apply:active { transform: scale(.98); background: var(--accent-wash-strong); }

/* ────────────────────────────── gate + toast ───────────────────────────── */

#gate {
  position: fixed; inset: 0;
  display: grid; place-items: center;
  padding: 24px;
  background: var(--bg);
  background-image: radial-gradient(90% 50% at 50% 30%, var(--accent-wash), transparent 70%);
  z-index: 50;
}
.gate-card { width: 100%; max-width: 320px; text-align: center; }
.gate-card h1 { font-size: 27px; letter-spacing: -0.03em; }
.gate-card p { color: var(--text-3); font-size: 13px; line-height: 1.55; margin: 10px 0 20px; }
.gate-card input {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 14px;
  margin-bottom: 11px;
  outline: none;
  box-shadow: var(--lift);
}
.gate-card input:focus { border-color: var(--accent-lo); }
.gate-card input:disabled { opacity: .55; }
.gate-card button[type="submit"] {
  width: 100%;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  color: var(--accent-ink);
  font-weight: 680;
  border: 0;
  border-radius: var(--r-md);
  padding: 14px;
  box-shadow: 0 1px 0 var(--btn-bevel) inset, 0 3px 10px -5px var(--btn-glow);
}
.gate-card button[type="submit"]:disabled { opacity: .5; }

.linkish {
  background: none;
  border: 0;
  color: var(--text-3);
  font-size: 13px;
  text-decoration: underline;
  text-underline-offset: 3px;
  padding: 16px 8px 0;
  width: 100%;
}
.linkish:active { color: var(--accent); }

.toast-action {
  background: none;
  border: 0;
  color: var(--accent);
  font-weight: 700;
  font-size: 13px;
  margin-left: 14px;
  padding: 0 2px;
  text-transform: uppercase;
  letter-spacing: .06em;
}

#toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--tabs-h) + env(safe-area-inset-bottom) + 16px);
  transform: translateX(-50%);
  background: var(--surface-3);
  border: 1px solid var(--line-strong);
  color: var(--text);
  padding: 11px 18px;
  border-radius: 99px;
  font-size: 13px;
  font-weight: 520;
  z-index: 40;
  max-width: 90vw;
  text-align: center;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .55);
  animation: toastIn .28s var(--ease);
}
@keyframes toastIn {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
}
