/* Hangover Free — calm, warm, adult, editorial.
   Reference points: a good journaling app, a well-set book. Explicitly NOT a
   clinic, a dashboard, or a game. No progress bars racing to full, no confetti,
   no badges, no leaderboard. The one big number is set in a serif because that
   makes it read as a fact about your life rather than a score.

   THEMING: a theme sets a hue + accent; the mode (light/dark) sets the lightness
   ramp. Everything else is derived, so a new theme is three custom properties. */

/* ---------- palette ------------------------------------------------------- */
:root {
  --h: 28;          /* paper hue */
  --sat: 26%;       /* paper saturation — warmth of the stock */
  --ah: 20;         /* accent hue */
  --as: 52%;        /* accent saturation */
}
[data-theme="dawn"] { --h: 32; --sat: 28%; --ah: 22; --as: 55%; }
/* "Dusk" used to be hue 26 — i.e. Dawn with less saturation — because it was
   designed as the dark theme back when dark was a theme rather than a mode.
   Since mode is now an independent axis, that made the two FREE themes almost
   identical in both modes. It's a cool near-neutral slate now: clearly not Dawn,
   and far enough from Ink's saturated indigo to not read as a duplicate either. */
[data-theme="dusk"] { --h: 214; --sat: 8%;  --ah: 209; --as: 22%; }
[data-theme="sea"]  { --h: 182; --sat: 16%; --ah: 180; --as: 38%; }
[data-theme="ink"]  { --h: 234; --sat: 14%; --ah: 232; --as: 46%; }
[data-theme="clay"] { --h: 14;  --sat: 24%; --ah: 8;   --as: 44%; }
[data-theme="moss"] { --h: 92;  --sat: 16%; --ah: 96;  --as: 32%; }
[data-theme="plum"] { --h: 310; --sat: 14%; --ah: 316; --as: 34%; }

/* Light: warm paper. Dark: the same paper at night, never pure black — black
   reads as "system UI", and this is meant to feel like an object, not a screen. */
:root, [data-mode="light"] {
  --bg:       hsl(var(--h) var(--sat) 96.5%);
  --bg-deep:  hsl(var(--h) var(--sat) 93%);
  --surface:  hsl(var(--h) calc(var(--sat) * 0.7) 99.5%);
  --ink:      hsl(var(--h) 22% 13%);
  --ink-soft: hsl(var(--h) 12% 38%);
  --ink-faint:hsl(var(--h) 10% 58%);
  --line:     hsl(var(--h) 14% 87%);
  --accent:   hsl(var(--ah) var(--as) 38%);
  --accent-sf:hsl(var(--ah) var(--as) 94%);
  --shadow:   0 1px 2px hsl(var(--h) 20% 20% / 0.05), 0 8px 24px hsl(var(--h) 20% 20% / 0.05);
  --drank:    hsl(var(--h) 8% 72%);
  color-scheme: light;
}
[data-mode="dark"] {
  /* Warm night, not black — black reads as system chrome, and this is meant to
     feel like an object you're holding. Keep enough saturation to see the hue. */
  --bg:       hsl(var(--h) calc(var(--sat) * 0.6) 10.5%);
  --bg-deep:  hsl(var(--h) calc(var(--sat) * 0.6) 7.5%);
  --surface:  hsl(var(--h) calc(var(--sat) * 0.5) 14.5%);
  --ink:      hsl(var(--h) 14% 92%);
  --ink-soft: hsl(var(--h) 10% 68%);
  --ink-faint:hsl(var(--h) 8% 48%);
  --line:     hsl(var(--h) 10% 20%);
  --accent:   hsl(var(--ah) calc(var(--as) * 0.9) 62%);
  --accent-sf:hsl(var(--ah) calc(var(--as) * 0.5) 18%);
  --shadow:   0 1px 2px hsl(0 0% 0% / 0.3), 0 8px 24px hsl(0 0% 0% / 0.25);
  --drank:    hsl(var(--h) 6% 34%);
  color-scheme: dark;
}

/* ---------- type ---------------------------------------------------------- */
:root {
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Noto Serif", serif;
  --sans: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", system-ui, sans-serif;
  --pad: 22px;
  --r: 16px;

  /* SAFE AREAS. targetSdk is 36, and Android 15+ enforces edge-to-edge with no
     opt-out, so the WebView really does draw under the status and navigation
     bars — every screen has to inset itself or content sits underneath them.

     Capacitor's SystemBars plugin sets --safe-area-inset-* on <html> (it needs
     viewport-fit=cover, which index.html has). We prefer those because env()
     is broken in Android WebView below v140, and fall back to env() for the web
     build and iOS. Never use env() directly anywhere else in this file. */
  --safe-t: var(--safe-area-inset-top, env(safe-area-inset-top, 0px));
  --safe-b: var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px));
  --safe-l: var(--safe-area-inset-left, env(safe-area-inset-left, 0px));
  --safe-r: var(--safe-area-inset-right, env(safe-area-inset-right, 0px));

  /* Height of the in-app tab bar, excluding the system inset below it. */
  --nav-h: 58px;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* Feel like an app, not a web page: no long-press text selection or callout on
   labels, numbers and buttons — that popup is a dead "this is a webview" tell,
   and it now also fires on the tappable counter/tiles. Re-enabled below only
   where it earns its place: form fields (or editing breaks) and the user's own
   words, which they may legitimately want to copy. */
* {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
input, textarea, select, [contenteditable],
.j-body, .j-title, .letter-body, .reason, .selectable {
  -webkit-user-select: text;
  user-select: text;
}

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}
body { transition: background-color .4s ease, color .4s ease; }

h1, h2, h3 { font-family: var(--serif); font-weight: 600; letter-spacing: -0.01em; margin: 0; }
h1 { font-size: 30px; line-height: 1.2; }
h2 { font-size: 21px; line-height: 1.3; }
h3 { font-size: 16px; font-family: var(--sans); font-weight: 600; }
p { margin: 0 0 1em; color: var(--ink-soft); }
p:last-child { margin-bottom: 0; }

.eyebrow {
  font-size: 11.5px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
}

/* ---------- app frame ----------------------------------------------------- */
#app { min-height: 100%; display: flex; flex-direction: column; }

.screen {
  flex: 1;
  /* Bottom clearance is the tab bar + the system nav inset behind it, so the
     last card is never hidden under either. */
  padding:
    calc(var(--safe-t) + 18px)
    calc(var(--pad) + var(--safe-r))
    calc(var(--nav-h) + var(--safe-b) + 34px)
    calc(var(--pad) + var(--safe-l));
  max-width: 560px;
  width: 100%;
  margin: 0 auto;
  animation: rise .35s cubic-bezier(.2, .7, .3, 1) both;
}
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }
/* Set while re-rendering the screen you're already on — see render() in app.js. */
#app.no-anim .screen { animation: none; }
@media (prefers-reduced-motion: reduce) {
  .screen { animation: none; }
  * { transition-duration: .01ms !important; }
}

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

/* ---------- the counter --------------------------------------------------- */
.counter { text-align: center; padding: 34px 0 26px; }
.counter .big {
  font-family: var(--serif);
  font-size: clamp(76px, 26vw, 128px);
  line-height: 0.92;
  font-weight: 500;
  letter-spacing: -0.045em;
  color: var(--ink);
  font-variant-numeric: tabular-nums lining-nums;
  display: block;
}
.counter .unit {
  font-family: var(--serif);
  font-size: 19px;
  font-style: italic;
  color: var(--ink-soft);
  margin-top: 10px;
  display: block;
}
/* Week one is measured in hours — a real, upvoted request against the comp, and
   on day two "37 hours" is a much better number to look at than "1". */
.counter .fine {
  font-size: 13px;
  color: var(--ink-faint);
  margin-top: 8px;
  font-variant-numeric: tabular-nums;
}

.stat-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 14px; }
.stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 13px 10px;
  text-align: center;
  box-shadow: var(--shadow);
}
.stat .v {
  font-family: var(--serif);
  font-size: 25px;
  font-weight: 500;
  display: block;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.stat .k { font-size: 11px; color: var(--ink-faint); margin-top: 5px; display: block; line-height: 1.3; }

/* ---------- reasons ------------------------------------------------------- */
/* Set in the serif, italic, generously spaced: these are the user's own words and
   they should look like something worth having written, not like list items. */
.reason {
  font-family: var(--serif);
  font-size: 18px;
  line-height: 1.45;
  font-style: italic;
  color: var(--ink);
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
  display: flex;
  gap: 12px;
  align-items: flex-start;
}
.reason:last-of-type { border-bottom: 0; padding-bottom: 2px; }
.reason::before { content: "—"; color: var(--accent); font-style: normal; flex: none; opacity: .7; }
.reason .del {
  margin-left: auto; flex: none; background: none; border: 0; color: var(--ink-faint);
  font-size: 20px; line-height: 1; padding: 2px 4px; cursor: pointer; font-style: normal;
}

.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  font: inherit; font-size: 14px;
  background: var(--bg-deep); color: var(--ink-soft);
  border: 1px solid var(--line); border-radius: 999px;
  padding: 8px 14px; cursor: pointer; text-align: left;
  transition: background .18s, color .18s, border-color .18s;
}
.chip:active, .chip.on { background: var(--accent-sf); color: var(--accent); border-color: var(--accent); }

/* ---------- buttons ------------------------------------------------------- */
.btn {
  font: inherit; font-weight: 600; font-size: 16px;
  width: 100%; padding: 15px 18px;
  border-radius: 13px; border: 1px solid transparent;
  background: var(--accent); color: var(--surface);
  cursor: pointer; display: block; text-align: center;
  transition: transform .12s ease, opacity .18s;
}
.btn:active { transform: scale(.985); }
.btn.ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn.quiet { background: transparent; color: var(--ink-soft); border: 0; font-weight: 500; font-size: 14.5px; padding: 12px; }
.btn.sm { width: auto; padding: 10px 16px; font-size: 14.5px; display: inline-block; }
.btn[disabled] { opacity: .45; pointer-events: none; }

/* The wobble button. Big, calm, always reachable, and never red — the moment it
   looks like an emergency button is the moment this becomes a recovery app. */
.wobble-btn {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--accent);
  color: var(--accent);
  border-radius: var(--r);
  padding: 17px;
  font: inherit;
  cursor: pointer;
  box-shadow: var(--shadow);
  transition: transform .12s ease;
}
.wobble-btn:active { transform: scale(.985); }
.wobble-btn .t { font-family: var(--serif); font-size: 19px; display: block; }
.wobble-btn .s { font-size: 13px; color: var(--ink-faint); display: block; margin-top: 3px; }

/* ---------- inputs -------------------------------------------------------- */
input[type="text"], input[type="number"], input[type="date"], textarea, select {
  font: inherit; width: 100%;
  background: var(--bg-deep); color: var(--ink);
  border: 1px solid var(--line); border-radius: 11px;
  padding: 13px 14px;
  appearance: none;
}
textarea { resize: vertical; min-height: 76px; font-family: var(--serif); font-size: 17px; }
input:focus, textarea:focus, select:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent; }
label { display: block; font-size: 13.5px; color: var(--ink-soft); margin: 0 0 6px; font-weight: 500; }
.field { margin-bottom: 15px; }
.row { display: flex; gap: 10px; align-items: center; }
.row > input { flex: 1; }

/* ---------- nav ----------------------------------------------------------- */
.nav {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 30;
  display: flex; justify-content: center; gap: 2px;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border-top: 1px solid var(--line);
  padding: 7px 8px calc(var(--safe-b) + 7px);
}
/* Invisible behind an open sheet's scrim, so stop paying for it. */
.sheet-open .nav { backdrop-filter: none; -webkit-backdrop-filter: none; }

.nav button {
  flex: 1; max-width: 110px;
  background: none; border: 0; cursor: pointer;
  color: var(--ink-faint); font: inherit; font-size: 10.5px; font-weight: 600;
  padding: 5px 2px; display: flex; flex-direction: column; align-items: center; gap: 3px;
  letter-spacing: .02em;
  transition: color .2s;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nav button svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.nav button.on { color: var(--accent); }

/* ---------- calendar ------------------------------------------------------ */
.cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.cal-head h2 { font-size: 18px; }
.cal-nav { background: none; border: 1px solid var(--line); border-radius: 9px; color: var(--ink-soft); width: 34px; height: 34px; cursor: pointer; font-size: 16px; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; }
/* Three letters, not one: "M T W T F S S" has two Ts and two Ss and makes you
   count across the row to work out which column you're in. */
.cal-dow { font-size: 10.5px; color: var(--ink-faint); text-align: center; font-weight: 600; padding-bottom: 6px; letter-spacing: .02em; }
.cal-cell {
  aspect-ratio: 1; border-radius: 9px; border: 1px solid transparent;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; color: var(--ink-faint);
  background: transparent; cursor: default; font-variant-numeric: tabular-nums;
  font-family: inherit;
}
.cal-cell.in { cursor: pointer; color: var(--accent); background: var(--accent-sf); }
.cal-cell.drank { background: var(--drank); color: var(--surface); }
.cal-cell.future { opacity: .3; }
.cal-cell.today { border-color: var(--ink); font-weight: 700; }
/* Selected day gets the strong fill; today keeps its outline underneath, so a
   day can read as both at once. */
.cal-cell.sel { background: var(--accent); color: var(--surface); border-color: var(--accent); font-weight: 700; }
.cal-cell.sel.drank { background: var(--ink-soft); border-color: var(--ink-soft); }
.cal-hint { margin-top: 12px; font-size: 12.5px; }

/* ---------- selected-day panel --------------------------------------------- */
/* Stack the date and the status pill so a long date (e.g. "Wednesday, 30
   September 2026") can't push the pill onto its own line on some days and keep
   it inline on others — that inconsistency read as a bug. Always: date, pill
   below it, left-aligned. */
.day-head { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.day-date { font-family: var(--serif); font-size: 17.5px; }
.day-status {
  font-size: 12px; font-weight: 600; letter-spacing: .03em;
  padding: 5px 11px; border-radius: 999px; white-space: nowrap;
}
.day-status.af-state { background: var(--accent-sf); color: var(--accent); }
.day-status.drank-state { background: var(--drank); color: var(--surface); }
.day-status.muted-state { background: var(--bg-deep); color: var(--ink-faint); }
.legend { display: flex; gap: 16px; font-size: 12px; color: var(--ink-faint); margin-top: 14px; flex-wrap: wrap; }
.legend i { width: 11px; height: 11px; border-radius: 4px; display: inline-block; margin-right: 6px; vertical-align: -1px; }

/* ---------- health ---------------------------------------------------------
   Card-per-benefit, like the category leader's — the structure genuinely reads
   better than a bare timeline. What isn't copied: their emoji icons (they'd look
   pasted in from another app), the "UNLOCKED" achievement badges, and the
   "Your Recovery" header. This is information, not a trophy cabinet. */
.hl-summary {
  background: var(--accent-sf);
  border-radius: var(--r);
  padding: 16px 18px;
  margin-bottom: 16px;
}
.hl-count { font-family: var(--serif); font-size: 19px; color: var(--accent); }
.hl-track {
  height: 5px; border-radius: 999px; background: var(--surface);
  overflow: hidden; margin: 10px 0 0;
}
.hl-track span { display: block; height: 100%; background: var(--accent); border-radius: 999px; transition: width .5s ease; }
.hl-track.thin { height: 3px; margin: 10px 0 2px; background: var(--line); }
.hl-next { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.hl-next .eyebrow { color: var(--accent); opacity: .75; }
.hl-next-title { font-family: var(--serif); font-size: 16px; }
.hl-next-when { font-size: 13px; color: var(--ink-soft); margin-left: auto; white-space: nowrap; }

.hl-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 16px;
  margin-bottom: 12px;
  box-shadow: var(--shadow);
}
.hl-card.next { border-color: var(--accent); }
/* Reached items step back visually — they're reference now, not the thing
   you're looking forward to. */
.hl-card.done { background: transparent; box-shadow: none; }
.hl-card.done .hl-body { color: var(--ink-faint); }

.hl-head { display: flex; align-items: center; gap: 12px; }
.hl-icon {
  flex: none; width: 42px; height: 42px; border-radius: 12px;
  background: var(--accent-sf); display: flex; align-items: center; justify-content: center;
}
.hl-icon svg {
  width: 21px; height: 21px; stroke: var(--accent); fill: none;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
}
.hl-head-text { flex: 1; min-width: 0; }
.hl-head-text h3 { font-family: var(--serif); font-size: 17.5px; font-weight: 600; line-height: 1.25; }
.hl-when { font-size: 12.5px; color: var(--ink-faint); margin-top: 2px; }
.hl-tick {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  background: var(--accent); display: flex; align-items: center; justify-content: center;
}
.hl-tick svg { width: 13px; height: 13px; stroke: var(--surface); fill: none; stroke-width: 2.8; stroke-linecap: round; stroke-linejoin: round; }

.hl-label { margin: 14px 0 5px; }
.hl-body { font-size: 14.5px; margin: 0; }

/* Per-card attribution: a caption, not a control. Enough to see where a claim
   came from without turning every card into a bibliography entry. */
.hl-attrib {
  font-size: 12px; font-style: italic; color: var(--ink-faint);
  margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--line);
}
.hl-card.done .hl-attrib { border-top-color: transparent; }

.hl-sources { margin-top: 18px; }
.hl-sources .hl-src + .hl-src { margin-top: 7px; }
.hl-src {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  background: var(--bg-deep); border: 1px solid var(--line); border-radius: 10px;
  padding: 9px 12px; font: inherit; font-size: 13px; color: var(--ink-soft); cursor: pointer;
}
.hl-src svg { flex: none; width: 15px; height: 15px; stroke: var(--accent); fill: none; stroke-width: 1.6; stroke-linecap: round; }
.hl-src span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hl-src.plain { cursor: default; }

/* ---------- health timeline (legacy, unused) ------------------------------- */
.tl { position: relative; padding-left: 26px; }
.tl::before { content: ""; position: absolute; left: 5px; top: 6px; bottom: 6px; width: 1.5px; background: var(--line); }
.tl-item { position: relative; padding: 0 0 22px; }
.tl-item::before {
  content: ""; position: absolute; left: -26px; top: 6px;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--bg); border: 1.5px solid var(--line);
}
.tl-item.done::before { background: var(--accent); border-color: var(--accent); }
.tl-item.next::before { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-sf); }
.tl-item .when { font-size: 11.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-faint); font-weight: 600; }
.tl-item.done .when, .tl-item.next .when { color: var(--accent); }
.tl-item h3 { margin: 3px 0 4px; font-family: var(--serif); font-size: 17.5px; font-weight: 600; }
.tl-item p { font-size: 14.5px; margin: 0; }
.tl-item .src { font-size: 11.5px; color: var(--ink-faint); margin-top: 5px; font-style: italic; }
.tl-item:not(.done):not(.next) { opacity: .62; }

.disclaimer { font-size: 12.5px; color: var(--ink-faint); line-height: 1.5; border-top: 1px solid var(--line); padding-top: 14px; margin-top: 6px; }

/* ---------- mood ---------------------------------------------------------- */
.mood-pick { display: grid; grid-template-columns: repeat(5, 1fr); gap: 7px; }
.mood-pick button {
  font: inherit; font-size: 12px; padding: 12px 2px; cursor: pointer;
  background: var(--bg-deep); border: 1px solid var(--line); border-radius: 11px; color: var(--ink-soft);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  transition: background .18s, color .18s, border-color .18s;
}
.mood-pick button .dot { width: 12px; height: 12px; border-radius: 50%; background: currentColor; opacity: .55; }
.mood-pick button.on { background: var(--accent-sf); border-color: var(--accent); color: var(--accent); }
.mood-pick button.on .dot { opacity: 1; }

.spark { display: flex; align-items: flex-end; gap: 3px; height: 74px; margin: 6px 0 4px; }
.spark .b { flex: 1; background: var(--accent); border-radius: 3px 3px 1px 1px; min-height: 3px; opacity: .8; }
.spark .b.none { background: var(--line); height: 3px !important; opacity: 1; }
.spark .b.drank { background: var(--drank); }
.spark-x { display: flex; justify-content: space-between; font-size: 11px; color: var(--ink-faint); }

/* ---------- sheets & overlays --------------------------------------------- */
.sheet-bg {
  position: fixed; inset: 0; z-index: 50;
  /* NO backdrop-filter here. A blur over the full viewport forces the WebView
     to re-composite the whole backdrop on every frame the sheet scrolls over
     it, which is what made scrolling inside a sheet feel sticky on device. A
     slightly heavier flat scrim reads the same at 3px of blur and costs
     nothing. */
  background: hsl(var(--h) 20% 8% / .55);
  display: flex; align-items: flex-end; justify-content: center;
  animation: fade .25s ease both;
  touch-action: none;
}
@keyframes fade { from { opacity: 0; } }
.sheet {
  touch-action: pan-y;
  /* Promote to its own compositor layer so scrolling it doesn't repaint the
     scrim and the shadow underneath. */
  will-change: transform;
  background: var(--bg); width: 100%; max-width: 560px;
  border-radius: 22px 22px 0 0;
  padding: 24px var(--pad) calc(var(--safe-b) + 22px);
  max-height: 92vh;
  /* dvh tracks the visual viewport, so the sheet shrinks with the keyboard
     instead of extending underneath it. */
  max-height: 92dvh;
  overflow-y: auto;
  /* Stops the scroll continuing into the page behind once this hits its end.
     The body lock in app.js is the belt; this is the braces. */
  overscroll-behavior: contain;
  animation: slide .34s cubic-bezier(.2, .75, .3, 1) both;
  box-shadow: 0 -8px 40px hsl(0 0% 0% / .18);
}

/* ---- top-bar variant -----------------------------------------------------
   The conventional mobile modal for anything with a text field: cancel and the
   confirm action pinned to the top, content scrolling beneath. A sticky bottom
   footer competes with the keyboard for the same space and ends up covering the
   very field it belongs to. */
.sheet.bar { padding: 0; }
.sheet-topbar {
  position: sticky; top: 0; z-index: 2;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px;
  padding: 11px 14px;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.sheet-title {
  font-family: var(--serif); font-size: 17.5px; font-weight: 600;
  text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sheet-cancel, .sheet-action {
  font: inherit; font-size: 15.5px; background: none; border: 0;
  cursor: pointer; padding: 7px 6px; border-radius: 8px;
}
.sheet-cancel { color: var(--ink-soft); justify-self: start; }
.sheet-action { color: var(--accent); font-weight: 700; justify-self: end; }
.sheet-action[disabled] { color: var(--ink-faint); opacity: .5; pointer-events: none; }
.sheet-body { padding: 18px var(--pad) calc(var(--safe-b) + 26px); }
.sheet.bar h2 { margin-bottom: 10px; }
@keyframes slide { from { transform: translateY(100%); } }
.sheet .grab { width: 38px; height: 4px; border-radius: 2px; background: var(--line); margin: -8px auto 18px; }
.sheet h2 { margin-bottom: 10px; }

/* Confirm actions stay pinned to the bottom of the sheet, so they're reachable
   without scrolling past a long list of options. The negative margins let the
   backdrop run to the sheet's edges while the buttons keep their inset. */
.sheet-actions {
  position: sticky;
  bottom: 0;
  margin: 18px calc(var(--pad) * -1) calc(var(--safe-b) * -1 - 22px);
  padding: 12px var(--pad) calc(var(--safe-b) + 16px);
  background: var(--bg);
  border-top: 1px solid var(--line);
}
.sheet-actions .btn + .btn { margin-top: 4px; }

/* ---------- the wobble screen --------------------------------------------- */
/* Full-bleed, no navigation, nothing to click for the first while. The point is
   that there is nowhere else to be for ninety seconds. */
.wobble-full {
  position: fixed; inset: 0; z-index: 60;
  background: var(--bg-deep); color: var(--ink);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 34px 26px calc(var(--safe-b) + 26px);
  text-align: center; animation: fade .5s ease both;
}
.breath {
  width: 190px; height: 190px; border-radius: 50%;
  background: radial-gradient(circle at 50% 45%, var(--accent-sf), transparent 72%);
  border: 1.5px solid var(--accent);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 30px;
  transition: transform 4s cubic-bezier(.4, 0, .3, 1), opacity 4s ease;
  transform: scale(.62); opacity: .6;
}
.breath.in { transform: scale(1); opacity: 1; }
.breath.hold { transform: scale(1); opacity: 1; }
.breath .ph { font-family: var(--serif); font-size: 24px; font-style: italic; color: var(--accent); }
.wobble-full .count { font-variant-numeric: tabular-nums; font-size: 14px; color: var(--ink-faint); letter-spacing: .04em; }
.wobble-full .yours { max-width: 400px; margin: 26px 0 0; }
.wobble-full .yours .eyebrow { margin-bottom: 10px; display: block; }
.wobble-full .yours blockquote {
  font-family: var(--serif); font-style: italic; font-size: 21px; line-height: 1.4;
  margin: 0; color: var(--ink); transition: opacity .6s ease;
}
/* Deliberately NOT margin-top:auto — an auto margin absorbs all the free space in
   the centred column and shoves the breathing circle to the top of the screen. */
.wobble-actions { margin-top: 34px; width: 100%; max-width: 400px; }

/* ---------- milestone ----------------------------------------------------- */
.milestone { text-align: center; }
.milestone .num {
  font-family: var(--serif); font-size: 74px; line-height: 1; color: var(--accent);
  font-variant-numeric: tabular-nums; letter-spacing: -.04em;
}

/* ---------- settings rows ------------------------------------------------- */
.srow {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 14px 0; border-bottom: 1px solid var(--line);
}
.srow:last-child { border-bottom: 0; }
.srow .lbl { font-size: 15px; }
.srow .sub { font-size: 12.5px; color: var(--ink-faint); margin-top: 2px; }
.srow select, .srow input { width: auto; min-width: 116px; text-align: right; padding: 9px 11px; }

.theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 10px; }
.theme-opt {
  border: 1.5px solid var(--line); border-radius: 13px; padding: 10px 8px;
  background: none; cursor: pointer; font: inherit; font-size: 12px; color: var(--ink-soft);
  display: flex; flex-direction: column; align-items: center; gap: 8px; position: relative;
}
.theme-opt.on { border-color: var(--accent); color: var(--accent); font-weight: 600; }
.theme-opt .sw { width: 100%; height: 30px; border-radius: 8px; border: 1px solid var(--line); }
.theme-opt .lock { position: absolute; top: 5px; right: 6px; font-size: 10px; opacity: .6; }

.preview-bar {
  margin-top: 14px; padding: 13px 14px; border-radius: 13px;
  background: var(--accent-sf); border: 1px solid var(--accent);
}
.preview-text { font-family: var(--serif); font-size: 15.5px; color: var(--accent); }
.preview-text span {
  display: block; font-family: var(--sans); font-size: 12.5px;
  color: var(--ink-soft); margin-top: 3px;
}
.preview-actions { display: flex; gap: 8px; margin-top: 11px; }
.preview-actions .btn { flex: 1; }

/* Sits just above the tab bar so a running preview is never invisible. */
.preview-pill {
  position: fixed; z-index: 40;
  left: 50%; transform: translateX(-50%);
  bottom: calc(var(--safe-bottom, env(safe-area-inset-bottom, 0px)) + 74px);
  display: flex; align-items: center; gap: 12px;
  background: var(--ink); color: var(--bg);
  border-radius: 999px; padding: 8px 8px 8px 16px;
  font-size: 13.5px; box-shadow: var(--shadow); max-width: 92vw;
  animation: fade .25s ease both;
}
.preview-pill button {
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  background: var(--bg); color: var(--ink);
  border: 0; border-radius: 999px; padding: 6px 13px; white-space: nowrap;
}

/* ---------- paywall ------------------------------------------------------- */
.pay-list { list-style: none; padding: 0; margin: 0 0 20px; }
.pay-list li {
  padding: 11px 0 11px 28px; position: relative;
  font-size: 15px; color: var(--ink); border-bottom: 1px solid var(--line);
}
.pay-list li:last-child { border-bottom: 0; }
.pay-list li::before {
  content: ""; position: absolute; left: 4px; top: 19px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--accent);
}
.price-tag {
  font-family: var(--serif); font-size: 42px; color: var(--ink);
  text-align: center; display: block; letter-spacing: -.03em;
}
.price-sub { text-align: center; font-size: 14px; color: var(--ink-soft); margin-bottom: 20px; }
.pay-fine { font-size: 12.5px; color: var(--ink-faint); text-align: center; margin-top: 14px; line-height: 1.5; }
.locked-note { font-size: 12px; color: var(--ink-faint); }

.toast {
  position: fixed; left: 50%; bottom: calc(var(--safe-b) + 84px);
  transform: translateX(-50%);
  background: var(--ink); color: var(--bg);
  padding: 11px 18px; border-radius: 999px; font-size: 14px; z-index: 90;
  box-shadow: var(--shadow); animation: fade .2s ease both; max-width: 88vw; text-align: center;
}

/* ---------- onboarding ----------------------------------------------------- */
/* Structure mirrors the category leader's validated flow (dots + step counter +
   Skip on top, Back/Next pinned to the bottom, one icon per screen). The look is
   ours: warm paper, serif headings, thin line icons instead of filled glyphs. */
.ob {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  max-width: 560px;
  margin: 0 auto;
  padding: calc(var(--safe-t) + 14px) var(--pad) calc(var(--safe-b) + 16px);
}
.ob-head { display: flex; flex-direction: column; align-items: center; position: relative; gap: 6px; }
.ob-dots { display: flex; gap: 6px; align-items: center; }
.ob-dots i {
  width: 6px; height: 6px; border-radius: 999px;
  background: var(--line); display: block;
  transition: width .3s cubic-bezier(.2,.7,.3,1), background-color .3s;
}
.ob-dots i.done { background: var(--accent); opacity: .45; }
.ob-dots i.on { width: 22px; background: var(--accent); }
.ob-step { font-size: 12px; color: var(--ink-faint); letter-spacing: .03em; }
.ob-skip {
  position: absolute; right: 0; top: -2px;
  background: none; border: 0; font: inherit; font-size: 14.5px; font-weight: 600;
  color: var(--ink-faint); padding: 8px 4px; cursor: pointer;
}

.ob-body { flex: 1; padding: 22px 0 24px; animation: rise .3s cubic-bezier(.2,.7,.3,1) both; }

.ob-icon {
  width: 66px; height: 66px; border-radius: 50%;
  background: var(--accent-sf);
  display: flex; align-items: center; justify-content: center;
  margin: 4px auto 20px;
}
.ob-icon svg {
  width: 30px; height: 30px;
  stroke: var(--accent); fill: none; stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}
.ob-icon.big { width: 88px; height: 88px; margin-bottom: 26px; }
.ob-icon.big svg { width: 42px; height: 42px; stroke-width: 1.8; }

.ob-title { font-size: 28px; text-align: center; line-height: 1.18; }
.ob-sub {
  text-align: center; font-family: var(--serif); font-style: italic;
  font-size: 17px; color: var(--ink-soft); margin-top: 7px;
}
.ob-lead { text-align: center; margin: 14px auto 0; max-width: 400px; font-size: 15px; }

/* welcome + done screens: no chrome, hero type, single action */
.ob-welcome, .ob-done { min-height: 100dvh; display: flex; flex-direction: column; }
.ob-welcome-body, .ob-done-body {
  flex: 1; display: flex; flex-direction: column; justify-content: center; text-align: left;
}
.ob-done, .ob-done-body { text-align: center; }
.ob-hero { font-family: var(--serif); font-size: 33px; line-height: 1.15; margin-bottom: 18px; font-weight: 600; }
.ob-hero-lead { font-size: 16.5px; line-height: 1.6; white-space: pre-line; }

.ob-foot { display: grid; grid-template-columns: 1fr 2fr; gap: 10px; padding-top: 6px; }
.ob-foot.single { grid-template-columns: 1fr; }

/* option rows — the validated interaction. Toggled by class, never re-rendered. */
.ob-rows { display: flex; flex-direction: column; gap: 9px; margin-top: 20px; }
.ob-row {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  background: var(--surface); border: 1.5px solid var(--line); border-radius: 14px;
  padding: 15px 16px; font: inherit; color: var(--ink); cursor: pointer;
  transition: border-color .18s, background-color .18s;
}
.ob-row.on { border-color: var(--accent); background: var(--accent-sf); }
.ob-row-text { flex: 1; display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ob-row-title { font-size: 15.5px; font-weight: 600; line-height: 1.35; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ob-row-sub { font-size: 13.5px; color: var(--ink-soft); line-height: 1.45; font-weight: 400; }
.ob-row.on .ob-row-title { color: var(--accent); }
.ob-check {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  border: 1.5px solid var(--line); display: flex; align-items: center; justify-content: center;
  transition: background-color .18s, border-color .18s;
}
.ob-check svg { width: 14px; height: 14px; stroke: var(--surface); fill: none; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; opacity: 0; transition: opacity .18s; }
.ob-row.on .ob-check { background: var(--accent); border-color: var(--accent); }
.ob-row.on .ob-check svg { opacity: 1; }
/* Rows used purely as a list (the "what you get back" summary) have no control. */
.ob-row:not([data-a]) { cursor: default; }
.ob-row:not([data-a]) .ob-check { display: none; }

.ob-tag {
  font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  background: var(--accent); color: var(--surface);
  padding: 3px 8px; border-radius: 999px; white-space: nowrap;
}

.ob-own { margin-top: 18px; }
.ob-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 16px; margin-top: 18px;
}
.ob-date { font-family: var(--serif); font-size: 21px; margin: 2px 0 12px; }

.ob-spend { display: flex; align-items: center; gap: 8px; }
.ob-spend #ob-cur {
  width: auto; min-width: 76px; flex: none; text-align: center;
  font-size: 14px; font-weight: 600; padding: 11px 6px;
  background: var(--bg-deep); color: var(--ink-soft);
}
.ob-spend #ob-spend {
  flex: 1; min-width: 0; font-family: var(--serif); font-size: 32px; font-weight: 500;
  text-align: center; padding: 6px 8px; letter-spacing: -.02em;
}
.ob-per { font-size: 13px; color: var(--ink-faint); white-space: nowrap; }
.ob-derived {
  text-align: center; margin-top: 10px; font-size: 13px; color: var(--accent);
  background: var(--accent-sf); border-radius: 999px; padding: 7px 12px;
}
.ob-presets { margin-top: 12px; justify-content: center; }

.ob-nights { margin-top: 22px; text-align: center; }
.ob-nights .eyebrow { display: block; margin-bottom: 10px; }
/* All seven on one row — a lone wrapped "7" reads like a mistake. */
.ob-nights .chips { justify-content: center; gap: 6px; flex-wrap: nowrap; }
/* Tapping a number beats typing one: no keyboard, no dead-end empty field. */
.chip.num {
  flex: 1 1 0; min-width: 0; max-width: 46px; padding: 11px 0;
  text-align: center; font-variant-numeric: tabular-nums;
}

.ob-date-quick { margin: 4px 0 12px; }
.ob-date-input { font-size: 16px; padding: 14px; text-align: center; }

/* Compact variant for the densest step. Same design language, tighter rhythm —
   scrolling an input-heavy screen on a phone is where people give up. */
.ob.compact .ob-icon { width: 52px; height: 52px; margin-bottom: 14px; }
.ob.compact .ob-icon svg { width: 24px; height: 24px; }
.ob.compact .ob-title { font-size: 25px; }
.ob.compact .ob-sub { font-size: 15.5px; margin-top: 5px; }
.ob.compact .ob-body { padding: 16px 0 18px; }
.ob.compact .ob-card { margin-top: 14px; }
.ob-card.tight { padding: 13px; }

/* Shared by the onboarding reminder switch and the developer card. */
.ob-toggle, .toggle {
  width: 50px; height: 30px; border-radius: 999px; border: 0; cursor: pointer;
  background: var(--line); padding: 3px; flex: none; transition: background-color .22s;
}
.ob-toggle span, .toggle span {
  display: block; width: 24px; height: 24px; border-radius: 50%;
  background: var(--surface); box-shadow: 0 1px 3px rgb(0 0 0 / .2);
  transition: transform .22s cubic-bezier(.2,.7,.3,1);
}
.ob-toggle.on, .toggle.on { background: var(--accent); }
.ob-toggle.on span, .toggle.on span { transform: translateX(20px); }
.srow.off { opacity: .42; }

.ob-list { list-style: none; padding: 0; margin: 22px 0 0; }
.ob-list li {
  padding: 12px 0 12px 28px; position: relative;
  border-bottom: 1px solid var(--line); font-size: 15px;
}
.ob-list li:last-child { border-bottom: 0; }
.ob-list li::before {
  content: ""; position: absolute; left: 5px; top: 20px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--accent);
}
.ob-note { font-size: 13px; color: var(--ink-faint); margin-top: 16px; text-align: center; line-height: 1.5; }

.ob-gain-head { margin-top: 24px; }

/* Step 9 "what you get back" — informational benefits, NOT selectable rows.
   A quiet feature list: a soft accent icon tile and a label, no card, no tap. */
.ob-benefits { display: flex; flex-direction: column; gap: 12px; margin-top: 14px; }
.ob-benefit { display: flex; align-items: center; gap: 14px; font-size: 16px; font-weight: 500; color: var(--ink); }
.ob-benefit-ic {
  width: 42px; height: 42px; flex: none; border-radius: 13px;
  background: var(--accent-sf); display: flex; align-items: center; justify-content: center;
}
.ob-benefit-ic svg {
  width: 22px; height: 22px; fill: none; stroke: var(--accent);
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.ob-gains { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px; }
.ob-gain {
  background: var(--accent-sf); border-radius: 14px; padding: 16px 12px; text-align: center;
}
.ob-gain .v {
  display: block; font-family: var(--serif); font-size: 27px; color: var(--accent);
  letter-spacing: -.02em; line-height: 1.1;
}
.ob-gain .k { display: block; font-size: 11.5px; color: var(--ink-soft); margin-top: 5px; }

/* These two use .screen but run before the tab bar exists, so they don't need
   its clearance — only the system inset. */
.screen.ob-pay, .screen.ob-done { padding-bottom: calc(var(--safe-b) + 26px); }
.ob-done { justify-content: space-between; }

/* ---------- pricing tiers -------------------------------------------------- */
/* Note what is NOT here: no struck-through "original" price, no countdown, no
   "most popular" badge on the recurring option. The only badge marks the tier
   that never charges you again. See src/monetize.src.js. */
.tiers { display: flex; flex-direction: column; gap: 9px; margin: 4px 0 18px; }
.tier {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  background: var(--surface); border: 1.5px solid var(--line); border-radius: 14px;
  padding: 15px 16px; font: inherit; color: var(--ink); cursor: pointer;
  transition: border-color .18s, background-color .18s;
}
.tier.on { border-color: var(--accent); background: var(--accent-sf); }
.tier-radio {
  flex: none; width: 21px; height: 21px; border-radius: 50%;
  border: 2px solid var(--line); transition: border-color .18s, box-shadow .18s;
}
.tier.on .tier-radio { border-color: var(--accent); box-shadow: inset 0 0 0 4.5px var(--accent-sf), inset 0 0 0 11px var(--accent); }
.tier-text { flex: 1; display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.tier-name { font-size: 16px; font-weight: 600; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tier-sub { font-size: 13px; color: var(--ink-soft); }
.tier-price { font-family: var(--serif); font-size: 21px; white-space: nowrap; letter-spacing: -.01em; }
.tier-unit { font-family: var(--sans); font-size: 11.5px; color: var(--ink-faint); }

/* Conventional dismiss, top-left. A paywall you can't obviously leave is the
   other half of the dark-pattern playbook — this is deliberately full-size and
   in the place people already reach for. */
.pay-x {
  /* Optical alignment, not box alignment. The glyph sits 11.5px inside its own
     40px circle, so aligning the *button* to the content column pushed the ×
     itself well past it. Pulling the button back by that inset lands the glyph
     on the column instead, and lets the circle overhang slightly — which is how
     icon buttons sit in a nav bar on both platforms. */
  position: absolute; top: 10px; left: calc(var(--pad) - 12px);
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--bg-deep); border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; padding: 0; z-index: 2;
}
.pay-x svg {
  width: 17px; height: 17px; stroke: var(--ink-soft); fill: none;
  stroke-width: 2; stroke-linecap: round;
}
.ob-pay, .sheet { position: relative; }
/* The onboarding paywall is a full screen, so its content is inset by the
   status-bar safe area. The close button is absolutely positioned, so it has to
   add that inset itself — otherwise it floats up under the status bar, adrift
   from the content. (The sheet paywall needs no inset: it opens below the bar.) */
.ob-pay .pay-x {
  top: calc(var(--safe-t) + 12px);
  left: calc(var(--safe-l) + var(--pad) - 12px);
}
/* Keep the title clear of the button on both surfaces, and size it so it still
   fits on one line inside the narrowed space. */
/* Scoped to the paywall title. As `.sheet > h2:first-of-type` this also
   indented the slip sheet, the lock warning and the drink-logged sheet by
   46px — none of which have a close button to clear — so their headings sat
   out of line with their own body text. */
.ob-pay > h2, .sheet > h2.pay-title { padding: 4px 46px 0; font-size: 19px; }

.pay-free {
  margin-top: 20px; padding: 16px; border-radius: 14px;
  background: var(--bg-deep); font-size: 13.5px; color: var(--ink-soft); line-height: 1.75;
}

.muted { color: var(--ink-faint); font-size: 13.5px; }
.center { text-align: center; }
.sp { height: 14px; }
.hr { height: 1px; background: var(--line); border: 0; margin: 20px 0; }

/* Developer card — visually set apart so it's never mistaken for a real
   setting. Only rendered while RevenueCat is unconfigured. */
.dev-card { border-style: dashed; border-color: var(--ink-faint); }

/* ---------- screen + sub-screen headers ------------------------------------ */
.screen-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.sub-head { display: flex; align-items: center; gap: 6px; margin: -4px 0 18px -8px; }
.sub-head h1 { font-size: 26px; }
.icon-btn {
  background: none; border: 0; cursor: pointer; color: var(--ink);
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
}
.icon-btn svg { width: 23px; height: 23px; stroke: currentColor; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.icon-btn:active { background: var(--bg-deep); }

/* ---------- journal -------------------------------------------------------- */
.j-card {
  display: block; width: 100%; text-align: left; font: inherit; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 15px 16px; margin-bottom: 10px; box-shadow: var(--shadow);
  transition: transform .12s ease;
}
.j-card:active { transform: scale(.99); }
.j-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.j-date { font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-faint); font-weight: 600; }
.j-mood { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-soft); }
.j-mood i { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); display: inline-block; }
/* Rough reads dimmest, Great strongest — a quiet intensity ramp, no red/green. */
.j-mood.m1 i { opacity: .28; } .j-mood.m2 i { opacity: .45; }
.j-mood.m3 i { opacity: .62; } .j-mood.m4 i { opacity: .8; } .j-mood.m5 i { opacity: 1; }
.j-title { font-family: var(--serif); font-size: 18.5px; font-weight: 600; margin-top: 7px; color: var(--ink); }
.j-body {
  font-family: var(--serif); font-size: 15.5px; line-height: 1.5;
  color: var(--ink-soft); margin: 6px 0 0;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
/* The empty state has no entry-count line under the title to space it, so it
   carries its own top margin. */
.j-empty { text-align: center; padding: 34px 22px; margin-top: 20px; }
.j-empty-icon {
  width: 62px; height: 62px; border-radius: 50%; margin: 0 auto 16px;
  background: var(--accent-sf); display: flex; align-items: center; justify-content: center;
}
.j-empty-icon svg { width: 28px; height: 28px; stroke: var(--accent); fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.j-empty h2 { margin-bottom: 8px; }
.j-empty p { margin-bottom: 20px; }
.j-textarea { min-height: 168px; }
.btn.danger { color: hsl(4 55% 48%); margin-top: 4px; }
[data-mode="dark"] .btn.danger { color: hsl(4 70% 68%); }

/* ---------- more ----------------------------------------------------------- */
.more-feature {
  display: block; width: 100%; text-align: left; font: inherit; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 20px; margin-bottom: 16px; box-shadow: var(--shadow);
  transition: transform .12s ease;
}
.more-feature:active { transform: scale(.99); }
.more-feature-icon {
  width: 44px; height: 44px; border-radius: 12px; background: var(--accent-sf);
  display: flex; align-items: center; justify-content: center; margin-bottom: 13px;
}
.more-feature-icon svg { width: 23px; height: 23px; stroke: var(--accent); fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.more-feature-title { font-family: var(--serif); font-size: 20px; font-weight: 600; display: block; color: var(--ink); }
.more-feature-sub { font-size: 14px; color: var(--ink-soft); display: block; margin-top: 4px; }

.more-group {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); overflow: hidden; box-shadow: var(--shadow); margin-bottom: 14px;
}
.more-version { text-align: center; font-size: 12px; color: var(--ink-faint); margin: 20px 0 8px; }
.more-row, .more-upgrade {
  display: flex; align-items: center; gap: 14px; width: 100%;
  font: inherit; text-align: left; cursor: pointer;
  background: none; border: 0; padding: 15px 16px;
}
.more-row + .more-row { border-top: 1px solid var(--line); }
.more-row:active, .more-upgrade:active { background: var(--bg-deep); }
.more-icon, .more-upgrade-icon {
  width: 36px; height: 36px; border-radius: 10px; flex: none;
  background: var(--accent-sf); display: flex; align-items: center; justify-content: center;
}
.more-icon svg, .more-upgrade-icon svg { width: 19px; height: 19px; stroke: var(--accent); fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.more-text { flex: 1; min-width: 0; }
.more-label { display: block; font-size: 15.5px; color: var(--ink); }
.more-sub { display: block; font-size: 12.5px; color: var(--ink-faint); margin-top: 2px; }
.more-chev { width: 18px; height: 18px; flex: none; stroke: var(--ink-faint); fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.more-upgrade {
  background: var(--accent-sf); border: 1px solid var(--accent);
  border-radius: var(--r); margin-bottom: 16px;
}
.more-upgrade .more-label { font-family: var(--serif); font-size: 17px; color: var(--accent); font-weight: 600; }
.more-upgrade-icon { background: var(--surface); }

/* ---------- letters -------------------------------------------------------- */
.letter-due-banner {
  background: var(--accent-sf); border: 1px solid var(--accent); color: var(--accent);
  border-radius: 12px; padding: 12px 14px; margin-bottom: 14px;
  font-family: var(--serif); font-size: 15.5px;
}
.letter {
  position: relative;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 16px; margin-bottom: 11px; box-shadow: var(--shadow);
}
/* Sealed letters read as paper you can't open yet: the fold line at the top and
   a muted body. Ready ones pick up the accent so they pull the eye. */
.letter.sealed { background: var(--bg-deep); }
.letter.ready { border-color: var(--accent); background: var(--accent-sf); }
.letter-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.letter-state { font-size: 12px; letter-spacing: .05em; text-transform: uppercase; font-weight: 600; color: var(--ink-faint); }
.letter.ready .letter-state { color: var(--accent); }
.seal-svg { width: 22px; height: 22px; stroke: var(--ink-faint); fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.seal-svg.ready { stroke: var(--accent); }
.letter-body { font-family: var(--serif); font-size: 17px; line-height: 1.55; color: var(--ink); margin: 0 0 10px; white-space: pre-wrap; }
.letter-foot { font-size: 12px; color: var(--ink-faint); font-style: italic; }

/* Optional writing prompt above the letter textarea — a quiet suggestion. */
.letter-prompt {
  background: var(--accent-sf); border-radius: 14px;
  padding: 16px 16px 12px; margin-bottom: 16px;
}
.letter-prompt[hidden] { display: none; }
.lp-quote {
  font-family: var(--serif); font-style: italic; font-size: 16.5px;
  line-height: 1.5; color: var(--ink); margin-bottom: 12px;
}
.lp-actions { display: flex; gap: 18px; align-items: center; }
.lp-link {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-size: 14px; font-weight: 600; color: var(--accent);
}
.lp-link.muted { color: var(--ink-faint); font-weight: 500; }
.letter-del {
  position: absolute; top: 10px; right: 10px;
  background: none; border: 0; color: var(--ink-faint);
  font-size: 20px; line-height: 1; padding: 4px 7px; cursor: pointer; opacity: .5;
}
.letter.ready .letter-del { display: none; }
.w-letter-foot { font-size: 12.5px; color: var(--ink-faint); font-style: italic; margin-top: 12px; }

/* ---------- weekly review -------------------------------------------------- */
.wk-big { display: flex; align-items: baseline; justify-content: center; gap: 8px; }
.wk-num { font-family: var(--serif); font-size: 62px; line-height: 1; color: var(--ink); letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.wk-of { font-family: var(--serif); font-style: italic; font-size: 19px; color: var(--ink-soft); }
.wk-label { text-align: center; font-size: 12px; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-faint); font-weight: 600; margin-top: 7px; }
.wk-strip { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; margin: 20px 0 4px; }
.wk-day { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.wk-dot { width: 100%; height: 30px; border-radius: 9px; background: var(--line); }
.wk-day.af .wk-dot { background: var(--accent); }
.wk-day.drank .wk-dot { background: var(--drank); }
.wk-day.muted-day .wk-dot { background: var(--line); opacity: .4; }
.wk-dow { font-size: 11px; color: var(--ink-faint); font-weight: 600; }
.wk-summary { font-family: var(--serif); font-size: 17px; font-style: italic; text-align: center; color: var(--ink); margin: 16px 0 0; }

/* ---------- lock screen ---------------------------------------------------- */
/* Nothing here hints at what the app is for. Someone glancing at a locked phone
   shouldn't learn that its owner is tracking their drinking. */
body.locked { overflow: hidden; }
.lock-screen {
  position: fixed; inset: 0; z-index: 100;
  background: var(--bg);
  display: flex; align-items: center; justify-content: center;
  padding: calc(var(--safe-t) + 24px) 24px calc(var(--safe-b) + 24px);
}
.lock-inner { width: 100%; max-width: 320px; text-align: center; position: relative; }
.lock-inner.shake { animation: shake .4s cubic-bezier(.36,.07,.19,.97) both; }
@keyframes shake {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(4px); }
  30%, 50%, 70% { transform: translateX(-7px); }
  40%, 60% { transform: translateX(7px); }
}
.lock-icon {
  width: 56px; height: 56px; border-radius: 50%; margin: 0 auto 18px;
  background: var(--accent-sf); display: flex; align-items: center; justify-content: center;
}
.lock-icon svg { width: 26px; height: 26px; stroke: var(--accent); fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.lock-label { font-family: var(--serif); font-size: 19px; color: var(--ink); margin-bottom: 22px; }
.lock-dots { display: flex; justify-content: center; gap: 15px; margin-bottom: 38px; }
.lock-dots span {
  width: 13px; height: 13px; border-radius: 50%;
  border: 1.5px solid var(--ink-faint); background: transparent;
  transition: background-color .15s, border-color .15s, transform .15s;
}
.lock-dots span.on { background: var(--accent); border-color: var(--accent); transform: scale(1.12); }
.keypad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.keypad button {
  font: inherit; font-family: var(--serif); font-size: 27px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 50%; aspect-ratio: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background-color .12s, transform .1s;
}
.keypad button:active { background: var(--accent-sf); transform: scale(.94); }
.keypad button svg { width: 25px; height: 25px; stroke: currentColor; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.keypad-help { font-family: var(--sans) !important; font-size: 19px !important; color: var(--ink-faint) !important; background: transparent !important; border-color: transparent !important; }
.lock-cancel {
  position: absolute; top: -6px; left: -6px;
  font: inherit; font-size: 15.5px; color: var(--ink-soft);
  background: none; border: 0; cursor: pointer; padding: 8px;
}

/* Share card preview — 4:5, matching the exported image. */
.share-preview {
  display: block; width: 100%; border-radius: 14px;
  border: 1px solid var(--line); box-shadow: var(--shadow);
}

/* ---------- home tiles ------------------------------------------------------
   The four features that otherwise only live under More. A 2x2 grid rather than
   four full-width cards: Home's job is the counter, the timer and your reasons,
   and stacking four more banners under them would bury exactly that. */
.home-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 14px; }
.home-tile {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 14px;
  box-shadow: var(--shadow); cursor: pointer;
  font: inherit; text-align: left; color: var(--ink);
  transition: transform .12s ease;
}
.home-tile:active { transform: scale(.985); }
.home-tile-icon {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 9px;
  background: var(--accent-sf); margin-bottom: 7px;
}
.home-tile-icon svg {
  width: 18px; height: 18px; stroke: var(--accent);
  fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.home-tile-title { font-family: var(--serif); font-size: 15.5px; font-weight: 600; line-height: 1.25; }
.home-tile-sub { font-size: 12px; color: var(--ink-faint); line-height: 1.35; }
/* A letter waiting to be opened is the one thing here worth interrupting for. */
.home-tile.flagged { border-color: var(--accent); }
.home-tile.flagged .home-tile-sub { color: var(--accent); font-weight: 600; }

/* ---------- year grid -------------------------------------------------------
   366 days, one dot each, columns as calendar weeks and rows as weekdays — the
   contribution-graph shape people already know how to read.

   Deliberately not a growing plant. A slip is one neutral dot among many, not
   something visibly killed, and a year of dots keeps getting more impressive
   where a plant hits its ceiling in a fortnight. */
.year-scroll {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  /* 53 columns can't fit a phone, so it scrolls — parked at today on render. */
  margin: 0 -4px;
  padding: 0 4px 4px;
  scrollbar-width: none;
}
.year-scroll::-webkit-scrollbar { display: none; }
.year-inner { display: inline-block; min-width: 100%; }

.year-months {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 13px;      /* must equal dot + gap below */
  margin-bottom: 5px;
}
.year-months span {
  font-size: 9.5px;
  color: var(--ink-faint);
  white-space: nowrap;
  letter-spacing: .02em;
}

.year-grid {
  display: grid;
  grid-template-rows: repeat(7, 10px);
  grid-auto-flow: column;
  grid-auto-columns: 10px;
  gap: 3px;
}
.yd {
  width: 10px; height: 10px; border-radius: 3px;
  border: 0; padding: 0; background: var(--line);
}
button.yd { cursor: pointer; }
.yd.pad { background: transparent; }
.yd.before { background: var(--line); opacity: .45; }
.yd.af { background: var(--accent); }
.yd.drank { background: var(--drank); }
.yd.today { box-shadow: 0 0 0 1.5px var(--ink); }
.yd.sel { box-shadow: 0 0 0 1.5px var(--accent); }

.legend i.sw-af { background: var(--accent); }
.legend i.sw-drank { background: var(--drank); }

/* ---------- segmented control ---------------------------------------------- */
.seg {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  background: var(--bg-deep); border: 1px solid var(--line);
  border-radius: 12px; padding: 3px; margin-bottom: 16px;
}
.seg-btn {
  font: inherit; font-size: 14px; font-weight: 600;
  background: none; border: 0; border-radius: 9px; padding: 9px 4px;
  color: var(--ink-soft); cursor: pointer;
  transition: background-color .18s, color .18s;
}
.seg-btn.on { background: var(--surface); color: var(--ink); box-shadow: var(--shadow); }

/* Milestone chips are read-only markers, not buttons — reached ones fill in. */
.ms-chip { cursor: default; font-size: 13px; }
.ms-chip.on { background: var(--accent-sf); color: var(--accent); border-color: var(--accent); font-weight: 600; }

/* ---------- next milestone ring -------------------------------------------- */
.next-card { display: flex; align-items: center; gap: 18px; }
.ring { width: 104px; height: 104px; flex: none; transform: rotate(-90deg); }
.ring-bg { fill: none; stroke: var(--line); stroke-width: 9; }
.ring-fg {
  fill: none; stroke: var(--accent); stroke-width: 9; stroke-linecap: round;
  transition: stroke-dashoffset .5s cubic-bezier(.2,.7,.3,1);
}
/* Counter-rotated so the text sits upright inside the rotated ring. */
.ring-num, .ring-lbl {
  transform: rotate(90deg); transform-origin: 60px 60px;
  text-anchor: middle; fill: var(--ink); font-family: var(--serif);
}
.ring-num { font-size: 34px; }
.ring-lbl { font-size: 12px; fill: var(--ink-faint); font-family: var(--sans); }
.next-meta { min-width: 0; }
.next-title { font-family: var(--serif); font-size: 24px; line-height: 1.15; margin: 3px 0 4px; }
.next-date { font-size: 13.5px; color: var(--ink-faint); }

/* Custom-milestones card (Insights → Next up). The free-user variant is a
   tappable locked teaser that opens the paywall. */
.ms-card { display: block; }
button.ms-card { font: inherit; color: var(--ink); border: 1px solid var(--line); background: var(--surface); }
.ms-card.locked { border-style: dashed; }
.ms-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ms-card-head .lock { font-size: 13px; opacity: .6; }

/* "Premium" tag on paid rows in the More list (shown only to non-subscribers). */
.more-badge {
  display: inline-block; margin-left: 8px; vertical-align: 1px;
  font-size: 9.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent); background: var(--accent-sf);
  padding: 2px 7px; border-radius: 999px;
}

/* Premium corner badge on Home tiles (shown to non-subscribers), matching the
   More-list badge. Absolute so it never disturbs the title wrap. */
.home-tile { position: relative; }
.home-tile-badge {
  position: absolute; top: 12px; right: 12px;
  font-size: 8.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent); background: var(--accent-sf);
  padding: 2px 6px; border-radius: 999px;
}

/* Custom-milestone rows — each a full-width tappable row that opens the edit
   sheet, so the label never has to share a line with a Remove button. */
.ms-list { display: flex; flex-direction: column; }
.ms-item {
  display: flex; align-items: center; gap: 12px; width: 100%;
  font: inherit; text-align: left; cursor: pointer;
  background: none; border: 0; border-top: 1px solid var(--line);
  padding: 13px 2px;
}
.ms-item:first-child { border-top: 0; }
.ms-item:active { background: var(--bg-deep); }
.ms-item-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.ms-item-label { font-size: 15.5px; color: var(--ink); line-height: 1.3; }
.ms-item-sub { font-size: 12.5px; color: var(--ink-faint); }
.ms-item-chev { width: 18px; height: 18px; flex: none; stroke: var(--ink-faint); fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ---------- drink count (v8) ---------------------------------------------- */
/* The optional "how many?" on drank days, and the drinks-per-week trend. */
.drinks-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin: 14px 0 6px; padding: 10px 12px;
  background: var(--bg-deep); border-radius: 12px;
}
.drinks-row .lbl { font-size: 14px; color: var(--ink-soft); }
.drinks-step { display: flex; align-items: center; gap: 4px; }
.drinks-step button {
  width: 40px; height: 40px; border-radius: 11px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink); font-size: 21px; line-height: 1;
}
.drinks-step button:disabled { opacity: .35; }
.drinks-step .n {
  min-width: 40px; text-align: center; font-family: var(--serif);
  font-size: 21px; font-variant-numeric: tabular-nums;
}
.dw-chart {
  position: relative; display: flex; align-items: flex-end; gap: 8px;
  height: 130px; padding-top: 18px;
}
.dw-col {
  flex: 1; height: 100%; display: flex; flex-direction: column;
  justify-content: flex-end; align-items: center; gap: 4px; position: relative; z-index: 1;
}
.dw-bar {
  width: 100%; max-width: 34px; min-height: 0;
  background: var(--accent); opacity: .85; border-radius: 7px 7px 3px 3px;
}
.dw-n { font-size: 11px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.dw-base { position: absolute; left: 0; right: 0; border-top: 2px dashed var(--line); }
.dw-base span {
  position: absolute; right: 0; top: -17px; font-size: 10.5px; color: var(--ink-faint);
}

/* A whole card that behaves as one tap target. It's a <button>, so the UA
   defaults (centred text, button font, border) have to be undone explicitly. */
.card-tap {
  display: block; width: 100%; text-align: left; font: inherit; color: inherit;
  border: 0; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.card-tap:active { transform: scale(.995); }

/* Inline reason editing. Looks exactly like the read-only .reason text until
   focused, so the list doesn't read as a form. */
.reason-edit {
  flex: 1; min-width: 0; font: inherit; font-family: var(--serif); font-size: 18px;
  font-style: italic; line-height: 1.45; color: var(--ink);
  background: none; border: 0; border-radius: 6px; padding: 2px 4px; margin: -2px -4px;
}
.reason-edit:focus {
  outline: 0; background: var(--bg-deep); font-style: normal;
}
.reason-edit:empty::before {
  content: attr(data-placeholder); color: var(--ink-faint);
}

/* "See all 6" under the previewed reasons. Reads as the affordance for the
   whole card being tappable, not as a separate control — the card is already
   one button, so a nested button would be invalid. */
.reason-more {
  padding-top: 12px; font-size: 15px; font-weight: 600; color: var(--accent);
}

/* Drag handle. Replaces the em-dash on the manage sheet's rows — the dash is a
   ::before on .reason, so it's suppressed when a handle is present. */
.reason:has(.reason-drag)::before { content: none; }
.reason-drag {
  flex: none; display: flex; align-items: center; padding: 4px 2px; margin-left: -2px;
  color: var(--ink-faint); cursor: grab;
  touch-action: none;              /* or the sheet scrolls instead of dragging */
  -webkit-tap-highlight-color: transparent;
}
.reason-drag svg {
  width: 17px; height: 17px; stroke: currentColor; fill: none;
  stroke-width: 2; stroke-linecap: round;
}
.reason.dragging {
  opacity: .9; position: relative; z-index: 2;
  background: var(--bg-deep); border-radius: 8px;
}
.reason.dragging .reason-drag { cursor: grabbing; }
