/* ---------- shell ---------- */
#app {
  max-width: 560px;
  margin: 0 auto;
  padding: 0 15px calc(104px + env(safe-area-inset-bottom));
}

/* Plain greeting, no bar — it scrolls away with the content. */
.topbar {
  display: flex; align-items: center; gap: 12px;
  padding: calc(16px + env(safe-area-inset-top)) 2px 12px;
  background: none;
}
.topbar .brand { flex: 1; min-width: 0; }
.brand-logo { flex: none; inline-size: 40px; block-size: 40px; object-fit: contain; }
.onb-logo {
  display: block; inline-size: 72px; block-size: 72px; object-fit: contain;
  margin-bottom: 22px;
}
.topbar h1 {
  font-size: 1.5rem; margin: 0; letter-spacing: -.022em; font-weight: 600;
}
.topbar .sub { font-size: .8rem; color: var(--text-dim); margin-top: 2px; }

/* ---------- glass primitives ---------- */
.glass {
  background: var(--glass);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow);
}

.card {
  background: var(--glass);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow);
  border-radius: var(--r-lg);
  padding: var(--pad-card);
  margin-bottom: 12px;
}
.card.tall { padding: 20px var(--pad-card); }
.card.flush { padding: 0; overflow: hidden; }

/* a small icon action in the card's trailing top corner */
.card.with-corner-end { position: relative; }
/* keep the header text clear of the icon */
.card.with-corner-end > .card-head { padding-inline-end: 44px; }
.icon-corner {
  position: absolute; top: 12px; inset-inline-end: 12px; z-index: 2;
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: var(--glass-sunk); border: 1px solid var(--hairline);
  color: var(--text-dim); transition: .16s var(--ease);
}
.icon-corner:active { transform: scale(.92); }

.icon-corner svg { width: 18px; height: 18px; }

.card > h2, .card-head h2, .section-label {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--text-mute); margin: 0 0 12px; font-weight: 600;
}
html[dir="rtl"] .card > h2,
html[dir="rtl"] .card-head h2,
html[dir="rtl"] .section-label { letter-spacing: 0; }

/* The same small label, over a block that is not a card's first child:
   the eating window's title, "Recent" in the food picker, the meal slot
   over a dish in cook mode, the advice in the week sheet. All four used
   this class after its rule had gone, and fell back to body text — so
   the eating window's title was the only card title at full size. */
.label {
  margin: 0; font-size: .72rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: .09em; color: var(--text-mute);
}
html[dir="rtl"] .label { letter-spacing: 0; }

.card-head {
  display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px;
}
.card-head h2 { margin: 0; flex: 1; }
.card-head .sub { font-size: .74rem; color: var(--text-mute); font-weight: 500; }

/* standalone label above a run of goal cards */
.section-label { display: block; padding: 4px 4px 8px; margin: 0; }

/* ---------- language switch ---------- */
.langtoggle {
  display: flex; gap: 2px; padding: 3px; border-radius: 999px;
  background: var(--glass-sunk);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  border: 1px solid var(--hairline);
}
.langtoggle button {
  min-inline-size: 44px;
  padding: 5px 13px; border-radius: 999px; border: none; background: none;
  font: inherit; font-size: .78rem; font-weight: 600; color: var(--text-dim);
  cursor: pointer; transition: .18s var(--ease);
}
.langtoggle button.on {
  background: var(--glass-strong); color: var(--text);
  box-shadow: inset 0 0 0 1px var(--edge);
}

/* ---------- liver widget ---------- */
.liver-stage {
  position: relative;
  display: grid; place-items: center;
  padding: 6px 0 2px;
  perspective: 900px;
}
.liver-tilt {
  transform-style: preserve-3d;
  animation: liverFloat 9s var(--ease) infinite alternate;
  will-change: transform;
}
@keyframes liverFloat {
  from { transform: rotateY(-13deg) rotateX(7deg) translateY(-3px); }
  to   { transform: rotateY(13deg)  rotateX(-4deg) translateY(4px); }
}
@media (prefers-reduced-motion: reduce) {
  .liver-tilt { animation: none; transform: none; }
}
.liver-stage svg,
.liver-stage img {
  display: block; width: 100%; max-width: 280px; height: auto;
  margin-inline: auto;          /* capped width inside a full-width button */
  overflow: visible;
}
.liver-card-visual {
  position: relative; display: block; inline-size: 100%; block-size: 210px;
  isolation: isolate;
}
.liver-card-fallback {
  position: absolute; inset: 0; display: grid; place-items: center;
  inline-size: 100%; block-size: 100%;
}
.liver-state-art {
  transition: opacity .5s var(--ease);
}
.liver-photo-grade {
  transition: filter .5s var(--ease), transform .5s var(--ease);
}
.liver-card-canvas {
  position: absolute; inset: 0; inline-size: 100%; block-size: 100%;
  display: block; visibility: hidden; pointer-events: none;
}
.liver-stage.is-3d .liver-card-canvas { visibility: visible; }
.liver-stage.is-3d .liver-card-fallback { visibility: hidden; }
.liver-stage .liver-tilt { animation: none; transform: none; will-change: auto; }
@media (prefers-reduced-motion: reduce) {
  .liver-state-art,
  .liver-photo-grade { transition: none; }
}

.liver-glow {
  position: absolute; inset: 12% 16%; z-index: -1;
  border-radius: 50%;
  filter: blur(34px);
  opacity: .55;
}

/* readout: score leading, grade badges stacked trailing */
.liver-readout {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  margin-top: 10px;
}
.liver-score-wrap { flex: 0 0 auto; }
.liver-score-name {
  display: block; margin-bottom: var(--pair-y);
  font-size: .72rem; font-weight: 600; color: var(--text-mute);
}
.liver-score {
  font-size: 2.5rem; font-weight: 600; letter-spacing: -.035em; line-height: 1;
}
/* The denominator is a second word, not a suffix. .liver-score runs at
   −.035em to hold a 40px numeral together, and the small inherited it —
   so "/100" sat hard against the 9 with the tracking of a headline. It
   gets its own spacing back, and a little air in front. */
.liver-score small {
  font-size: .95rem; font-weight: 500; color: var(--text-mute);
  letter-spacing: 0; margin-inline-start: 3px;
}

/* The words go to the far end of the row, where the grade badges used
   to be — not up against the figure, which reads as a caption on it.

   Capped, so they wrap. Given the whole of the space the number leaves
   they would run as one long line across the middle of the card; at
   this width every stage but the shortest breaks over two, which is the
   shape the badges held and the height the row already is. The longest
   still lands on two rather than three, which is what stops the row
   growing.

   The two languages need different numbers, because the same five
   stages are not the same length in them: "Serious — see a specialist,
   improving" measures 229px and the Arabic 176. One width cannot give
   both two lines — at 130 the Arabic stages sit on one, and at the 105
   they need, English breaks its longest over three and the row grows.
   Each gets the width at which only its shortest stage stays on one
   line.

   px, not em: the em here would be the card's 16px rather than the
   label's 13, so 11.5em came out at 184 and the common English stage
   stayed on one line. */
.liver-say {
  flex: 0 1 auto; min-width: 0; max-inline-size: 130px;
  text-align: end;
}
html[dir="rtl"] .liver-say { max-inline-size: 105px; }
.liver-stage-label { font-size: .82rem; color: var(--text-dim); line-height: 1.3; }

/* ---------- goal cards: bento grid ---------- */
.bento {
  display: grid;
  /* minmax(0, …), not 1fr: a bare 1fr will not shrink below the tile's
     content, and on a 320px phone the dial and both steppers are wider
     than half the screen — the grid grew and the whole page scrolled
     sideways by 25px. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 12px;
}

.goal {
  display: flex; align-items: center; gap: 13px;
  padding: var(--pad-card);
  border-radius: var(--r-lg);
  background: var(--glass);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow);
  transition: transform .2s var(--ease);
}
.goal.done { border-color: color-mix(in srgb, var(--c) 45%, transparent); }
.goal:active { transform: scale(.992); }

/* the wide tile spans the grid — used for the 8-cup water goal */
.goal.wide { grid-column: 1 / -1; }

/* compact tiles stack vertically so two fit per row */
.goal.tile {
  flex-direction: column; align-items: stretch; gap: 0;
  padding: 13px;
}
.goal.tile .goal-top {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 7px;
}
.goal.tile .goal-body { flex: 0 0 auto; }
.goal.tile .goal-name { font-size: .87rem; line-height: 1.25; }
.goal.tile .goal-count { font-size: .76rem; }
.goal.tile .goal-actions { gap: 8px; }   /* 8 - 2 - 2 = 4px clear between hit areas */
/* 40 drawn, 44 tapped. A full 44px circle crowds the half-width tile
   next to the dial, so the visible control stays smaller and the hit
   area is padded out to the minimum — the 8px gap leaves 4px clear
   between the minus and the plus, so the two never overlap. */
.goal.tile .stepper { width: 40px; height: 40px; font-size: 1rem; }
.goal.tile .stepper::after { content: ''; position: absolute; inset: -2px; }

.goal-dial { position: relative; flex: 0 0 auto; width: 54px; height: 54px; }
/* Direct child only: this rotation starts the progress ring at twelve
   o'clock, and must not reach the icon nested inside .glyph — which it
   did, quietly rendering every goal icon on its side. */
.goal-dial > svg { transform: rotate(-90deg); display: block; }
html[dir="rtl"] .goal-dial > svg { transform: rotate(-90deg) scaleY(-1); }
.goal-dial .glyph {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 1.15rem; line-height: 1;
}
.goal.tile .goal-dial { width: 44px; height: 44px; }
.goal.tile .goal-dial .glyph { font-size: 1rem; }
/* The smallest phones: the half-width tile is 139px, and 44 + 40 + 8 +
   40 + 26 of padding does not go into it. Everything steps down a size;
   the steppers keep a 44px hit area through their padding. */
@media (max-width: 359px) {
  .goal.tile { padding: 10px; }
  .goal.tile .goal-dial { width: 36px; height: 36px; }
  .goal.tile .goal-dial > svg { width: 36px; height: 36px; }
  .goal.tile .stepper { width: 36px; height: 36px; }
  .goal.tile .stepper::after { inset: -4px; }
}

.goal-body { flex: 1; min-width: 0; }
.goal-name { font-weight: 600; font-size: .96rem; letter-spacing: -.01em; }
.goal-count { font-size: .8rem; color: var(--text-dim); margin-top: 1px; }
.goal-count b { color: var(--ci); font-weight: 600; }

.goal-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* 44px, not 36. These are the most-tapped controls in the app and they
   sat below both Apple's 44pt and Android's 48dp minimum. Padding the
   hit area instead would have overlapped the minus and plus, which is
   worse than a small target: the tap lands on whichever is on top. */
.stepper {
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--glass-sunk); border: 1px solid var(--hairline);
  color: var(--text); font-size: 1.15rem; font-weight: 500; line-height: 1;
  cursor: pointer; transition: .15s var(--ease);
  font-family: var(--font);
}
.stepper { position: relative; }        /* so the padded hit area anchors */
.stepper:active { transform: scale(.9); }
.stepper.plus { background: color-mix(in srgb, var(--c) 13%, transparent); border-color: color-mix(in srgb, var(--c) 40%, transparent); color: var(--ci); font-weight: 600; }
.stepper:disabled { opacity: .32; cursor: default; }

/* A day done twice over, said where the ring cannot say it: the ring
   has been full since the target and has no way to show the second
   lap. In the goal's own colour, because that is what it is counting. */
.goal-times {
  display: inline-block; margin-inline-start: 7px;
  padding: 1px 7px; border-radius: 999px;
  font-size: .72rem; font-weight: 700; letter-spacing: -.01em;
  background: color-mix(in srgb, var(--c) 20%, transparent);
  color: var(--ci);
}
/* Past the ceiling rather than past the target. Amber, not the goal's
   own colour: the badge is still reporting what happened, but it has
   stopped congratulating anybody for it. */
.goal-times.too-much {
  background: color-mix(in srgb, var(--warn) 18%, transparent);
  color: var(--warn-ink);
}
.goal.too-much .goal-arc { stroke: var(--warn); }

/* ---------- things to stay away from ----------
   Tiles rather than a checklist: each carries its reason and a running
   clean streak, so marking one is a small win instead of a chore. */
/* All six visible at once, in two rows. This used to carry .hscroll as
   well, whose display:flex won the cascade and turned it into a rail
   that clipped the third tile mid-word — on the section that carries
   two of the day's points. */
.avoid-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
}
.avoid-tile {
  position: relative;                  /* the run badge pins to its corner */
  /* Its own layer, deliberately.

     The card above it is glass — backdrop-filter: blur(24px) — and
     giving these tiles a positioning context put six new paint layers
     inside that blur. On a software renderer the whole section came
     apart: tile backgrounds drawn at stale offsets, names and examples
     missing entirely, badges and icons piled on top of each other,
     while the layout underneath measured perfectly correct. Promoting
     each tile composites it once and hands the blur a finished picture.

     Cheap here — six small tiles that do not animate — and it costs
     nothing on a GPU that never had the problem. */
  transform: translateZ(0);
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 13px 8px 11px; border-radius: var(--r-md); cursor: pointer;
  background: var(--glass-sunk); border: 1px solid var(--hairline-dim);
  font: inherit; color: var(--text); text-align: center;
  transition: .2s var(--ease);
}
.avoid-tile:active { transform: scale(.96); }
.avoid-tile.on {
  background: color-mix(in srgb, var(--c) 13%, transparent);
  border-color: color-mix(in srgb, var(--c) 40%, transparent);
}

.avoid-tile .shield {
  position: relative; width: 42px; height: 42px; margin-bottom: 3px;
  display: grid; place-items: center; font-size: 1.35rem; line-height: 1;
  border-radius: 14px;
  background: var(--glass); border: 1px solid var(--hairline);
  filter: grayscale(.55) opacity(.75);
  transition: .2s var(--ease);
}
.avoid-tile.on .shield { filter: none; }
.avoid-tile .seal {
  position: absolute; bottom: -4px; inset-inline-end: -4px;
  width: 18px; height: 18px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--c); color: var(--on-bright); font-size: .62rem; font-weight: 600;
  font-style: normal; border: 2px solid var(--glass-strong);
  animation: pop .28s var(--ease);
}
@keyframes pop { from { transform: scale(0); } }

.avoid-name { font-size: .78rem; font-weight: 600; line-height: 1.2; }
/* ---------- the doctor's page, drawn ----------
   Every drawing here sits under the number it is drawn from and carries
   aria-hidden: a screen reader gets the figure, an eye gets the shape,
   and the copy-as-text button is unaffected because none of this is
   text. */
.doc-ladder {
  position: relative; margin: 8px 0 14px; height: 26px;
}
.dl-track {
  height: 8px; border-radius: 99px; overflow: hidden;
  background: var(--sunk-ink); border: 1px solid var(--edge);
}
.dl-track i {
  display: block; height: 100%; border-radius: 99px;
  background: linear-gradient(90deg, var(--accent-2), var(--accent));
}
html[dir="rtl"] .dl-track i { background: linear-gradient(270deg, var(--accent-2), var(--accent)); }
.dl-mark { position: absolute; top: 0; transform: translateX(-50%); }
html[dir="rtl"] .dl-mark { transform: translateX(50%); }
.dl-mark u {
  display: block; width: 2px; height: 12px; margin: -2px auto 0;
  background: var(--text-mute); border-radius: 2px;
}
.dl-mark small {
  display: block; margin-top: 2px; font-size: .6rem; color: var(--text-mute);
  font-weight: 600;
}
.dl-mark.passed u { background: var(--accent); }
.dl-mark.passed small { color: var(--accent-ink); }

/* a lab value, with its threshold notched into the track */
.doc-bar {
  position: relative; height: 6px; border-radius: 99px; margin: 5px 0 12px;
  background: var(--sunk-ink); border: 1px solid var(--edge); overflow: visible;
}
.doc-bar i {
  display: block; height: 100%; border-radius: 99px;
  background: var(--danger);
}
.doc-bar.ok i { background: var(--accent); }
.doc-bar u {
  position: absolute; top: -3px; width: 2px; height: 12px;
  background: var(--text); border-radius: 2px; opacity: .55;
}

/* doses taken, days logged — a share of a whole */
.doc-meter {
  height: 6px; border-radius: 99px; margin: 5px 0 12px; overflow: hidden;
  background: var(--sunk-ink); border: 1px solid var(--edge);
}
.doc-meter i { display: block; height: 100%; background: var(--text-mute); }
.doc-meter.good i { background: var(--accent); }
.doc-meter.thin i { background: var(--warn); }

/* ---------- a day, opened from the history ---------- */
.daysheet .section-label { margin: 16px 0 6px; }
.dayrow {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 0; border-bottom: 1px solid var(--hairline-dim);
}
.dayrow:last-of-type { border-bottom: none; }
.dayrow-id { flex: 1; min-width: 0; }
.dayrow-id b { display: block; font-size: .9rem; font-weight: 600; margin-bottom: var(--pair-y); }
.dayrow-id small { font-size: .74rem; color: var(--text-dim); }
.dayrow-acts { display: flex; gap: 6px; flex: 0 0 auto; }
.dayavoid { display: flex; flex-wrap: wrap; gap: 6px; }

.chip-btn {
  font: inherit; font-size: .78rem; font-weight: 600; cursor: pointer;
  padding: 6px 11px; border-radius: 99px; min-width: 38px;
  background: var(--sunk-ink); border: 1px solid var(--edge); color: var(--text-dim);
}
.chip-btn.on {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border-color: color-mix(in srgb, var(--accent) 42%, transparent);
  color: var(--accent-ink, var(--accent));
}
.chip-btn:active { transform: scale(.96); }

/* the history rows are buttons now, so they must not look like form controls */
button.hist-day {
  display: block; width: 100%; text-align: start; font: inherit; cursor: pointer;
}

/* ---------- the day list ----------
   One block per day, colour-keyed down its leading edge by how the day
   went, so a month of them can be read by scrolling rather than by
   reading every line. */
.hist-list { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.hist-day {
  padding: 11px 13px; border-radius: var(--r-md);
  background: var(--sunk-ink); border: 1px solid var(--edge);
  border-inline-start: 3px solid var(--tone, var(--text-mute));
}
.hist-day.v-great { --tone: var(--accent); }
.hist-day.v-good  { --tone: var(--ds-good, var(--accent-2)); }
.hist-day.v-mixed { --tone: var(--warn); }
.hist-day.v-bad   { --tone: var(--danger); }

.hist-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.hist-date { font-weight: 600; font-size: .92rem; }
.hist-verdict { font-size: .72rem; font-weight: 600; color: var(--tone); }
.hist-stats { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 5px; }
.hist-stat { font-size: .74rem; color: var(--text-dim); }
.hist-stat b { color: var(--text); font-weight: 600; }
.hist-line { margin: 6px 0 0; font-size: .8rem; line-height: 1.45; color: var(--text-dim); }

/* the meals you built yourself, listed so they can be dropped */
.mymeal {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 0; border-bottom: 1px solid var(--hairline-dim);
  background: none; border-inline: 0; border-block-start: 0;
  font: inherit; color: inherit; text-align: start; cursor: pointer;
}
.mymeal:last-child { border-bottom: none; }
.mymeal-id { flex: 1; min-width: 0; }
.mymeal-id b { display: block; font-size: .93rem; font-weight: 600; }
.mymeal-id small { font-size: .74rem; color: var(--text-dim); }
/* A meal of your own the plan will not suggest: said in words, with an
   icon, never by colour alone. */
.mymeal-id small.mymeal-out { display: flex; align-items: center; gap: 4px; margin-top: 2px; color: var(--text-mute); }
.mymeal-out svg { flex: none; }

/* ---------- meds ---------- */
.med {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 0; border-bottom: 1px solid var(--hairline-dim);
}
.med:last-child { border-bottom: none; }
.med-pill {
  flex: 0 0 auto; width: 38px; height: 38px; border-radius: 12px;
  display: grid; place-items: center; font-size: 1.05rem;
  background: color-mix(in srgb, var(--med) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--med) 32%, transparent);
}
.med-body { flex: 1; min-width: 0; }
.med-name { font-weight: 600; font-size: .94rem; overflow-wrap: anywhere; }
.med-dose { overflow-wrap: anywhere; }
.med.taken .med-name { text-decoration: line-through; color: var(--text-dim); }
.med-dose { font-size: .78rem; color: var(--text-dim); }
.med-time {
  font-size: .78rem; font-weight: 600; color: var(--text-dim);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  align-self: flex-start; padding-top: 2px;
}

.med-manage {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 0; border-bottom: 1px solid var(--hairline-dim);
}
.med-manage:last-of-type { border-bottom: none; }
.icon-btn {
  width: 32px; height: 32px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center; cursor: pointer;
  background: var(--glass-sunk); border: 1px solid var(--hairline);
  color: var(--text-dim); font-size: .95rem;
}
.icon-btn.danger { color: var(--danger-ink); }

/* ---------- generic ---------- */

.btn {
  display: block; width: 100%; text-align: center; cursor: pointer;
  border: none; border-radius: 15px; padding: 15px;
  font: inherit; font-weight: 600; font-size: .96rem;
  color: var(--on-accent);
  background: linear-gradient(180deg, var(--btn), var(--btn-2));
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.22);   /* no glow under it: no shadows on buttons */
  transition: transform .16s var(--ease);
}
.btn:active { transform: scale(.985); }
/* The privacy link is an <a>, because it leaves the app for a document.
   Everything else about it is a button. */
a.btn { text-decoration: none; box-sizing: border-box; }
.btn.ghost {
  background: var(--sunk-ink); color: var(--text);
  border: 1px solid var(--edge); box-shadow: none;
}
.btn.danger { background: none; color: var(--danger-ink); border: 1px solid var(--edge); box-shadow: none; }
/* min-height rather than more padding: the meal action row has five of
   these and growing the padding would push the text off centre on the
   ones whose label wraps. 35px and 37px were both under Apple's 44. */
.btn.sm { padding: 11px; font-size: .88rem; border-radius: 13px; min-height: 44px; }

.field { margin-bottom: 12px; }
.field label { display: block; font-size: .78rem; color: var(--text-dim); margin-bottom: 5px; font-weight: 500; }
.field input, .field select, .field textarea {
  width: 100%; padding: 13px; border-radius: 14px;
  border: 1px solid var(--edge);
  background: var(--sunk-ink);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  color: var(--text); font: inherit;
}
.field input:focus, .field select:focus { outline: 2px solid var(--accent); outline-offset: -1px; }

/* The box the message is talking about. Not colour alone — the border
   thickens too, because "the red one" is not an instruction for
   somebody who cannot tell it from the others. */
.field input[aria-invalid="true"] {
  border-color: var(--danger-ink);
  box-shadow: inset 0 0 0 1px var(--danger-ink);
}
.row2 { display: flex; gap: 10px; }
.row2 > * { flex: 1; }

.seg { display: flex; gap: 6px; flex-wrap: wrap; }
.seg button {
  flex: 1; min-width: 84px; min-block-size: 44px; padding: 11px 8px; border-radius: 13px;
  border: 1px solid var(--hairline); background: var(--glass-sunk);
  font: inherit; font-size: .82rem; font-weight: 500; color: var(--text);
  text-align: center; cursor: pointer; transition: .16s var(--ease);
}
.seg button.on {
  background: linear-gradient(180deg, var(--btn), var(--btn-2));
  color: var(--on-accent); border-color: transparent;
}
.seg.stack button { flex: 1 1 100%; text-align: start; }
/* An error thickens the edge (BRAND.md). The glass has no border, so the
   edge is a 2px ring drawn inside, and the glass keeps its float. */
.onb-step .seg[aria-invalid="true"] button {
  box-shadow: inset 0 0 0 2px var(--danger-ink), var(--lg-shadow);
}

.note { font-size: .78rem; color: var(--text-dim); line-height: 1.55; }
.note.warn { color: var(--warn-ink); }

.kv {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 10px 0; border-bottom: 1px solid var(--hairline-dim); font-size: .9rem;
}
.kv:last-child { border-bottom: none; }
.kv span { color: var(--text-dim); }
.kv b { font-weight: 600; white-space: nowrap; }

.badge {
  display: inline-block; font-size: .7rem; font-weight: 600;
  padding: 4px 10px; border-radius: 999px;
  background: var(--accent-wash); color: var(--accent-ink);
}
.badge.gold   { background: color-mix(in srgb, var(--gold) 20%, transparent);   color: var(--gold-ink); }
.badge.warn   { background: color-mix(in srgb, var(--warn) 18%, transparent);   color: var(--warn-ink); }
.badge.danger { background: color-mix(in srgb, var(--danger) 15%, transparent); color: var(--danger-ink); }

.statrow { display: flex; gap: 8px; margin-top: 12px; }
.stat {
  /* min-width: 0 so the three actually share the row. A flex item's
     default minimum is its min-content width, so the calorie tile —
     the only one holding two numbers — pushed itself out to fit
     "1999/1740" and took the width off the other two, and the row
     stopped being three of the same thing. The figure inside is sized
     to fit an equal third instead; see .stat.kcal. */
  flex: 1; min-width: 0;
  border-radius: var(--r-md); padding: 10px 8px; text-align: center;
  background: var(--glass-sunk); border: 1px solid var(--hairline-dim);
}
.stat b { display: block; font-size: 1.1rem; letter-spacing: -.02em; margin-bottom: var(--pair-y); }
/* 11.5px: the section-label size, the smallest style in BRAND.md. */
.stat span { font-size: .72rem; color: var(--text-mute); }

/* Tabular, because the calorie tile counts up all day and a figure that
   changes width as it does makes the row twitch.

   The target used to sit beside it inside the figure. Nine characters
   of tabular numerals do not fit a third of a card — on a fully eaten
   day "1999/1740" overflowed, and shrinking it to fit made one tile's
   headline visibly smaller than the two beside it. The target moved to
   the caption line, which the neighbouring tiles already prove has
   room for a dozen characters, and the figure went back to the size
   the row is drawn at. */
.stat.kcal b { font-variant-numeric: tabular-nums; }

/* Two by two, on Progress. Four across gave each tile 71px on the
   narrowest phone this is built for, which is not a tile — it is a
   figure at .86rem over a caption at .62rem, both of them shrunk to
   fit rather than sized to be read. Two rows of two gives each one
   half the card and lets the number go back to the size it is
   everywhere else. */
.statrow.grid2 {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.statrow.grid2 .stat { min-width: 0; padding: 12px 8px; line-height: 1.3; }
.statrow.grid2 .stat b { font-variant-numeric: tabular-nums; }
.statrow.grid2 .stat span { font-size: .72rem; }

/* The target, beside what has been eaten rather than under it. Same
   shape as the liver score's /100, and the reason the calorie tile fits
   a four-figure fraction in the width of a two-figure one. */
.stat b small {
  font-size: .6em; font-weight: 500; color: var(--text-mute);
  letter-spacing: 0;
}

/* A run of days, at stat size. The corner badge's language rather than
   the badge itself — the bolt, the amber, and the fill once a week is
   up — so a streak reads as one whether it is counted on a goal card,
   an avoid tile, or here. */
.stat.streak b {
  display: flex; align-items: center; justify-content: center; gap: 4px;
  color: var(--streak-ink);
}
.stat.streak b .ico { flex: none; }
/* The badge's actual fill, not a wash of it.
   An 18% tint read as a beige tile with a bolt on it — the same shape
   the old design had, which is not what "the streak looks different
   now" is supposed to mean. This is the pill's own gradient at stat
   size, so the run is the brightest thing in the row, which is the
   point of it. */
.stat.streak.lit {
  background-color: var(--streak-lit);
  background-image: linear-gradient(135deg, var(--streak-lit), var(--streak-hot));
  border-color: color-mix(in srgb, var(--streak-hot) 55%, transparent);
  box-shadow: 0 1px 6px color-mix(in srgb, var(--streak-hot) 35%, transparent);
}
.stat.streak.lit b { color: var(--streak-on-lit); }
/* The label goes dark too, or it sits invisible on the fill. */
/* Was 78% of the ink, which is 3.83:1 at the hot end of the pill it
   sits on. Flat, the same ink reads 5.57 there and 9.34 at the other
   end — and the b above it was already flat, so the two lines now
   match rather than one being quietly faded. */
.stat.streak.lit span { color: var(--streak-on-lit); }

/* Not lit — a best-ever run — still belongs to the family: the ink and
   a hint of the fill, so it does not read as an ordinary number. */
.stat.streak:not(.lit) {
  background-color: color-mix(in srgb, var(--streak-lit) 14%, transparent);
  border-color: color-mix(in srgb, var(--streak-hot) 26%, transparent);
}

/* ---------- the weight track ----------
   Two markers on one axis: where the weight is, and where it is going.
   It replaced the ring, on Today and then on Progress too.

   Everything is positioned with inset-inline-start rather than left,
   so the axis runs start-to-end and reverses wholesale in Arabic — the
   markers count in from the right, and the captions stay over them. The
   two transform overrides below are the same fix .lp-dot needs, for the
   same reason: a translate is in real pixels and does not know which
   way the writing runs. */
/* Every modifier below is prefixed. `.goal` on the target marker was
   the goal-card class from four hundred lines up, and the marker came
   out as a 50px black tile sitting on the bar — this stylesheet has one
   global namespace and a two-letter word like `now` or `top` belongs to
   whoever wrote it first. */
.wt {
  --wt-cap: 40px;              /* the room a two-line caption needs */
  position: relative; padding-block: var(--wt-cap);
}

/* The plus in the card's top corner adds a weigh-in, and it is the one
   fixed thing the travelling caption can run into: near the target the
   marker sits at 88% and the caption centred on it reaches in under the
   button. The whole widget starts below it instead — padding would not
   do, because the captions are anchored to the track and would sit
   inside the padding rather than below it.

   The number is the button's own geometry: 12px down, 34px tall and a
   border, less the card's 17px of padding and the slack between --wt-cap
   and a caption two lines high. That comes to 26; 30 leaves a cushion,
   and a test asserts the two boxes never meet.

   A card head does that job on its own — .with-corner-end already keeps
   44px of it clear for the button — so where there is one the margin
   stands down rather than adding a second gap on top of it. */
.card.with-corner-end .wt { margin-block-start: 30px; }
.card.with-corner-end > .card-head + .wt { margin-block-start: 0; }

/* A pale band with a flat green bar filling it as far as the Now
   marker. Nothing rounds the leading end: it is a reading, and the
   marker is drawn on top of it, so a curve there would put the tick
   and the edge of the green in two slightly different places.

   overflow keeps the bar inside the band's own corners, which is why
   the bar can start flush at the edge and still not square them off. */
.wt-track {
  position: relative; height: 34px; border-radius: 6px; overflow: hidden;
  background: var(--accent-wash);
  border: 1px solid var(--hairline-dim);
  /* Both ends dissolve rather than stopping. The axis has sixteen per
     cent of air past the last thing on it in either direction, so its
     ends are not readings — a hard edge there invites them to be read
     as the start and the finish, which they are not. Symmetric, so it
     needs no RTL variant.

     A two-stop ramp is not a smooth fade. Opacity climbs at a constant
     rate and then stops climbing all at once, and the eye finds that
     corner — the fade reads as a band with an edge on it, which is the
     thing the fade exists to remove. The stops below are smoothstep,
     3t²−2t³, sampled at eighths: it leaves the transparent end and
     arrives at the opaque one with no change of rate at either.

     The ramp itself is on :root, with its width, because the strip of
     targets wears the same one — see the note there. Inside the strip
     this fade stands down, because there the window fades and the bars
     move behind it. */
  -webkit-mask-image: var(--wt-mask);
  mask-image: var(--wt-mask);
}
.wt-bar {
  position: absolute; inset-block: 6px; inset-inline-start: 0;
  background: var(--accent);
  transition: inline-size .5s var(--ease);
}

/* Taller than the bar and shorter than the band, so a few pixels of
   every marker stand on the pale green above and below it. That
   overhang is what makes the Now marker legible where it sits on the
   green — a dark tick on the bar alone is barely more than a shade of
   it. */
.wt-tick {
  position: absolute; inset-block: 2px; width: 2.5px; border-radius: 2px;
  transform: translateX(-50%);
  transition: inset-inline-start .5s var(--ease);
}
[dir="rtl"] .wt-tick { transform: translateX(50%); }
.wt-tick.wt-mark-now    { background: var(--accent-ink); }
.wt-tick.wt-mark-target { background: var(--text); }

.wt-cap {
  position: absolute; transform: translateX(-50%);
  text-align: center; white-space: nowrap; line-height: 1.2;
  transition: inset-inline-start .5s var(--ease);
}
[dir="rtl"] .wt-cap { transform: translateX(50%); }
/* Anchored to the track's edges rather than to the widget's, so the
   caption stays with the bar it labels however much room is reserved
   above the widget. */
.wt-cap.wt-top { bottom: calc(100% - var(--wt-cap)); }
.wt-cap.wt-bot { top: calc(100% - var(--wt-cap)); }
.wt-cap span {
  display: block; font-size: .7rem; font-weight: 500; color: var(--text-dim);
  margin-bottom: var(--pair-y);
}
.wt-cap b {
  display: block; font-size: 1.05rem; font-weight: 700; letter-spacing: -.02em;
}
.wt-cap.wt-top b { color: var(--accent-ink); }

/* Above the starting weight. Only the marker and its own figure change
   colour — the bar stays the track it is, and recolouring the whole
   width would say the card was in trouble rather than the number. */
.wt.gained .wt-tick.wt-mark-now { background: var(--danger); }
.wt.gained .wt-cap.wt-top b { color: var(--danger-ink); }

