/* Aura — headache tracker.
   Same warm-paper family as Castellano and the coach, so the three feel like
   one house. The accent moves to indigo: the coach is green, Castellano is
   clay, and at 3am with a headache nobody should open the wrong app.

   No web fonts. Castellano pulls Hanken Grotesk and Newsreader from Google,
   which is fine for an app that needs the network anyway. This one has to work
   in a basement with no signal, and a page that blocks on a font CDN is a page
   that shows nothing. The system stack below is already on the device, renders
   instantly, and on an iPhone is San Francisco — which is what the rest of her
   phone uses, so the app looks native rather than merely styled. */

:root {
  --paper:#F7F5F2; --panel:#FFFFFF; --ink:#1A1A1F; --ink-2:#42424E;
  --muted:#6E6E79; --line:#E4E2DE; --line-2:#D5D2CC; --track:#EBE8E3;

  --indigo:#4F46A8; --indigo-deep:#3A3382; --indigo-soft:#EEEDF8;

  /* Severity, 1–10, cool to hot. Not a red-to-green scale: red/green is the
     one pair a colourblind reader cannot separate, and this is the only
     colour in the app that carries meaning on its own. Lightness drops as
     severity rises, so it still reads correctly in greyscale or on a
     printout handed to a consultant. */
  --sev-low:#4F8A6B; --sev-mid:#C08A2E; --sev-high:#C2543A; --sev-worst:#9B2F2F;

  --ok:#3F7D5A; --warn:#B27A1E; --alert:#B23A34;

  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,"Helvetica Neue",Arial,sans-serif;
  --mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,monospace;
  /* The system serif — New York on an Apple device, Georgia elsewhere. Used
     for the wordmark, the login screen and card titles only. It is on the
     device already, so it costs nothing and cannot block the first paint,
     which is the whole reason there are no web fonts here. What it buys is a
     second voice: a diary that opens with a serif reads as something made for
     a person, and the rest of the app stays in the phone's own UI face. */
  --serif:ui-serif,"New York",Iowan Old Style,Georgia,serif;

  --ease:cubic-bezier(.22,.61,.36,1);
  --pad:1rem;
  --radius:.75rem;
  --tabbar-h:4.1rem;
  /* Both of these are reservations, not measurements — main's bottom padding
     has to leave room for the dock before the dock has been laid out, and a
     layout that reads its own height back is one that shifts on every
     appearance. They are deliberately a little generous: the bar measures
     60.2px against the 65.6px claimed here, which costs five pixels of empty
     space under the last card and never hides anything. Nothing is positioned
     from them; see .dock. */
  --mini-h:3.3rem;
  /* Two shadows, not one. The tight dark one is the contact edge; the wide
     soft one is the ambient occlusion. A single blurred shadow reads as a
     smudge under the card — this reads as a card resting on paper. */
  --lift:0 1px 2px rgb(26 26 31 / .04), 0 6px 18px -8px rgb(26 26 31 / .10);
  --lift-2:0 1px 2px rgb(26 26 31 / .05), 0 14px 34px -12px rgb(26 26 31 / .18);
  color-scheme:light;
}

[data-theme="dark"] {
  --paper:#151519; --panel:#1E1E24; --ink:#ECEAF2; --ink-2:#C0BECB;
  --muted:#8B8996; --line:#2A2A33; --line-2:#3A3A46; --track:#26262E;

  --indigo:#A8A2E8; --indigo-deep:#C3BDF5; --indigo-soft:#26243A;

  --sev-low:#6FAE8B; --sev-mid:#D8A94E; --sev-high:#E07A5F; --sev-worst:#D45B5B;

  --ok:#6FAE8B; --warn:#D8A94E; --alert:#E07A73;
  /* Shadows do almost nothing on a dark background — the card would float on
     nothing. Depth in dark mode comes from the panel being lighter than the
     paper, so these are nearly off and the border does the work. */
  --lift:0 1px 2px rgb(0 0 0 / .30);
  --lift-2:0 10px 30px -12px rgb(0 0 0 / .60);
  color-scheme:dark;
}

* { box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
html,body { height:100%; }
[hidden] { display:none !important; }

body {
  margin:0; background:var(--paper); color:var(--ink);
  font-family:var(--sans); font-size:calc(16px * var(--text-scale, 1)); line-height:1.5;
  -webkit-font-smoothing:antialiased;
  overscroll-behavior-y:none;
  /* Notes are free text and can contain a long unbroken run — a medicine name
     with no spaces, a pasted URL. Without this the note widens its box, which
     widens the page, and the whole app slides sideways under a thumb. */
  overflow-wrap:break-word;
  transition:background-color .28s var(--ease), color .28s var(--ease);
}

button,input,select,textarea { font-family:inherit; font-size:1rem; color:inherit; }
button { cursor:pointer; }
/* iOS zooms the page when a focused input's text is under 16px, and never
   zooms back out. Every input here stays at 16px for that reason alone. */
/* 16px is the threshold below which iOS Safari zooms the page in on focus and
   never zooms back out. max() keeps that as a floor while still letting the
   text-size setting grow it. */
input,select,textarea { font-size:max(16px, calc(16px * var(--text-scale, 1))); }

.mono { font-family:var(--mono); font-variant-numeric:tabular-nums; }
:focus-visible { outline:2px solid var(--indigo); outline-offset:2px; border-radius:.4rem; }

h1,h2,h3 { margin:0; font-weight:600; letter-spacing:-.01em; }
p { margin:0; }

/* ── The first second ──────────────────────────────────────────────────── */

/* The dedication that opens every launch. It is the same paper as everything
   else, so what she sees is one screen settling rather than two screens
   swapping.

   Above the login screen and the app, below the dimming veil: if light hurts,
   it hurts on the first frame too.

   The animation is the safety net, not the mechanism. Normally splash.mjs
   fades this out after a second; the delay below is long enough that it only
   ever fires when JavaScript never arrived at all — and a dedication that
   will not go away is a worse first impression than a login screen. */
.splash {
  position:fixed; inset:0; z-index:55; display:grid; place-items:center;
  padding:1.5rem; background:var(--paper);
  transition:opacity .45s var(--ease);
  animation:splash-out .45s var(--ease) 5s forwards;
}
.splash.is-gone { animation:none; opacity:0; }
/* Faded by 99% and only then taken out of the page: `visibility` steps rather
   than interpolates, so hiding it at the halfway point of the fade — which is
   what a two-stop keyframe would do — snaps it away mid-dissolve. */
@keyframes splash-out {
  from { opacity:1; visibility:visible; }
  99%  { opacity:0; visibility:visible; }
  to   { opacity:0; visibility:hidden; }
}

.splash-inner { text-align:center; animation:splash-in .5s var(--ease) both; }
@keyframes splash-in { from { opacity:0; transform:translateY(6px); } }
.splash-mark { width:3.4rem; height:3.4rem; margin:0 auto 1.1rem; display:block; }
/* The serif, and larger than the same line is on the login screen. There it is
   a footnote; here it is the only thing on the page. */
.splash-line {
  font-family:var(--serif); font-size:1.1rem; color:var(--ink-2);
  letter-spacing:.01em;
}
.splash-line .heart { color:var(--indigo); }

/* ── Login ─────────────────────────────────────────────────────────────── */

.login { position:fixed; inset:0; display:grid; place-items:center; padding:1.5rem; background:var(--paper); z-index:40; }
.login-inner { width:min(22rem,100%); }
.login-mark { width:3rem; height:3rem; margin:0 auto 1rem; display:block; }
.login-title {
  font-family:var(--serif); font-size:2.7rem; font-weight:400; text-align:center;
  letter-spacing:-.015em; margin:0 0 .3rem;
}
.login-sub { text-align:center; color:var(--muted); font-size:.92rem; margin:0 0 1.8rem; }
.login form { display:flex; flex-direction:column; gap:.85rem; }

/* The dedication. Small, permanent, and on the first thing she sees. */
.made-for {
  text-align:center; color:var(--muted); font-size:.78rem;
  letter-spacing:.02em; margin:1.6rem 0 0;
}
.made-for .heart { color:var(--indigo); }

.field { display:flex; flex-direction:column; gap:.35rem; }
.field-label { font-size:.78rem; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); }
.field input, .field select, .field textarea {
  width:100%; background:var(--panel); color:var(--ink);
  border:1.5px solid var(--line-2); border-radius:var(--radius);
  padding:.8rem .9rem; transition:border-color .18s var(--ease);
}
.field input:focus, .field select:focus, .field textarea:focus { outline:none; border-color:var(--indigo); }
.field textarea { resize:vertical; min-height:4.5rem; line-height:1.45; }

.btn {
  border:0; border-radius:var(--radius); padding:.85rem 1.1rem;
  font-weight:600; font-size:1rem; background:var(--track); color:var(--ink);
  transition:transform .12s var(--ease), opacity .18s var(--ease);
}
.btn:active { transform:scale(.98); }
.btn[disabled] { opacity:.45; cursor:not-allowed; }
.btn-primary { background:var(--indigo); color:#fff; }
[data-theme="dark"] .btn-primary { color:#17161F; }
.btn-quiet { background:transparent; border:1.5px solid var(--line-2); }
.btn-text { background:none; border:0; padding:.5rem; color:var(--indigo); font-weight:600; }
.btn-danger { background:transparent; border:1.5px solid var(--line-2); color:var(--alert); }
.btn-row { display:flex; gap:.6rem; }
.btn-row > * { flex:1; }

.error { color:var(--alert); font-size:.88rem; margin:.2rem 0 0; }
.hint { color:var(--muted); font-size:.85rem; }

/* ── Frame ─────────────────────────────────────────────────────────────── */

.app { display:flex; flex-direction:column; min-height:100%; }

/* The background is deliberately OPAQUE, and that is not a style choice.
   Safari 26 stopped honouring theme-color for the status bar. It now takes the
   colour from the background of whatever fixed or sticky element is anchored
   to the top edge — this bar — and falls back to the system appearance when it
   cannot get a definite colour. A translucent background is not a definite
   colour, which is how a dark app ended up under a cream status strip on a
   phone that was set to Light. Anything with `transparent` in it here puts the
   strip back under the system's control. The frosted blur went with it: over
   an opaque fill backdrop-filter renders nothing. */
.topbar {
  position:sticky; top:0; z-index:20;
  display:flex; align-items:center; justify-content:space-between; gap:.6rem;
  padding:calc(env(safe-area-inset-top) + .8rem) var(--pad) .8rem;
  background:var(--paper);
  border-bottom:1px solid var(--line);
}
/* The bottom tab bar already names the current screen and marks it with
   aria-current, so repeating it up here was saying the same word twice on a
   small screen. The brand goes in its place: it is what tells her which of the
   three home-screen apps she has opened, which the tab bar cannot. */
.brand { display:flex; align-items:center; gap:.5rem; color:var(--indigo); }
.brand-mark { width:1.5rem; height:1.5rem; flex:none; }
.brand-name {
  font-family:var(--serif); font-size:1.35rem; font-weight:500;
  letter-spacing:0; color:var(--ink);
}
.topbar-actions { display:flex; align-items:center; gap:.2rem; }
.icon-btn {
  background:none; border:0; padding:.5rem; border-radius:.6rem;
  color:var(--muted); display:grid; place-items:center; line-height:0;
}
.icon-btn:hover { color:var(--ink); background:var(--track); }
.icon-btn svg { width:1.25rem; height:1.25rem; }

main {
  flex:1; padding:var(--pad);
  padding-left:calc(var(--pad) + env(safe-area-inset-left));
  padding-right:calc(var(--pad) + env(safe-area-inset-right));
  padding-bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 1.5rem);
  max-width:44rem; width:100%; margin:0 auto;
  /* A column so the dedication can be pushed to the bottom of a short screen
     rather than hanging directly under a single card. */
  display:flex; flex-direction:column;
}

/* A screen fades and lifts a few pixels when it is swapped in. Six pixels and
   180ms — under the threshold where it feels like an animation, but enough
   that the eye is told "this is new content" instead of finding a different
   page already there. Off entirely under prefers-reduced-motion, which is not
   a nicety here: motion is a migraine trigger. */
.view-enter { animation:view-in .18s var(--ease); }
@keyframes view-in { from { opacity:0; transform:translateY(6px); } }

/* The dedication at the foot of every screen. It sits below the last card, is
   never in the way of a button, and is inside #app so the print stylesheet
   drops it from the consultant's chart. */
.signature {
  text-align:center; color:var(--muted); font-size:.76rem;
  letter-spacing:.02em; padding:1.6rem .5rem .4rem; margin-top:auto;
}
.signature .heart { color:var(--indigo); font-style:normal; }

/* ── Now playing ───────────────────────────────────────────────────────
   A strip above the tab bar, present only while a sound is on. It is fixed
   rather than in the flow because it has to survive a tab change without
   being re-rendered mid-sound, and it is deliberately not a sheet: she may be
   logging an attack while the chanting plays, and taking the screen away to
   offer a pause button would be worse than having no button at all. */
.miniplayer {
  display:flex; align-items:center; gap:.5rem;
  padding:.45rem calc(var(--pad) + env(safe-area-inset-right)) .45rem calc(var(--pad) + env(safe-area-inset-left));
  background:color-mix(in oklab,var(--panel) 94%,transparent);
  backdrop-filter:saturate(180%) blur(14px);
  -webkit-backdrop-filter:saturate(180%) blur(14px);
  border-top:1px solid var(--line);
  animation:mini-in .22s var(--ease);
}
@keyframes mini-in { from { opacity:0; transform:translateY(100%); } }
@media (prefers-reduced-motion:reduce) { .miniplayer { animation:none; } }

/* The name and countdown are one target the width of the bar. Two small
   targets that mean the same thing is a worse design than one big one. */
.mini-label {
  flex:1; min-width:0; display:flex; flex-direction:column; align-items:flex-start;
  gap:.05rem; background:none; border:0; padding:.2rem .1rem; text-align:left;
  color:inherit; font:inherit; cursor:pointer;
}
.mini-title {
  font-size:.9rem; font-weight:600; color:var(--ink);
  max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.mini-sub { font-size:.72rem; color:var(--muted); }

.mini-controls { display:flex; align-items:center; gap:.35rem; flex:none; }
.mini-btn {
  display:grid; place-items:center; width:2.4rem; height:2.4rem;
  border-radius:50%; border:1px solid var(--line);
  background:var(--paper); color:var(--ink); cursor:pointer;
  transition:transform .16s var(--ease), background .16s var(--ease);
}
.mini-btn:active { transform:scale(.92); }
.mini-stop { color:var(--muted); }

/* Room for it, so the strip never covers the last thing on the page. Set on
   body rather than on the player so the padding and the bar cannot disagree
   about whether it is showing. */
body.has-mini main { padding-bottom:calc(var(--tabbar-h) + var(--mini-h) + env(safe-area-inset-bottom) + 1.5rem); }
body.has-mini .toast { bottom:calc(var(--tabbar-h) + var(--mini-h) + env(safe-area-inset-bottom) + 1rem); }

/* Bottom tabs, not top. The whole app is used one-handed, often lying down,
   and the top of a modern phone is out of thumb reach.

   The dock is what is pinned to the bottom; the bar and the now-playing strip
   simply stack inside it. That is not a refactor for tidiness — see the note in
   index.html. */
.dock {
  position:fixed; left:0; right:0; bottom:0; z-index:25;
}
.tabbar {
  display:grid; grid-template-columns:repeat(5,1fr);
  padding:.35rem .25rem calc(env(safe-area-inset-bottom) + .25rem);
  background:color-mix(in oklab,var(--panel) 92%,transparent);
  backdrop-filter:saturate(180%) blur(14px);
  -webkit-backdrop-filter:saturate(180%) blur(14px);
  border-top:1px solid var(--line);
}
.tab {
  position:relative;
  background:none; border:0; padding:.4rem .2rem .3rem;
  display:flex; flex-direction:column; align-items:center; gap:.15rem;
  color:var(--muted); font-size:.68rem; font-weight:600; letter-spacing:.01em;
  border-radius:.6rem; min-height:3.1rem;
  transition:color .18s var(--ease);
}
.tab svg { width:1.4rem; height:1.4rem; transition:transform .22s var(--ease); }
.tab[aria-selected="true"] { color:var(--indigo); }
/* A lit dot above the selected tab. The colour change alone is a 10% shift in
   a small glyph, which is easy to miss on a dimmed screen — this is the mark
   that says where you are without needing to read the label. */
.tab::before {
  content:""; position:absolute; top:.05rem; left:50%;
  width:.3rem; height:.3rem; border-radius:999px; background:var(--indigo);
  transform:translate(-50%,-.35rem) scale(0); opacity:0;
  transition:transform .22s var(--ease), opacity .18s var(--ease);
}
.tab[aria-selected="true"]::before { transform:translate(-50%,0) scale(1); opacity:1; }
.tab[aria-selected="true"] svg { transform:translateY(.05rem); }
.tab .dot {
  position:absolute; transform:translate(.85rem,-.15rem);
  width:.45rem; height:.45rem; border-radius:999px; background:var(--alert);
}

/* ── Cards ─────────────────────────────────────────────────────────────── */

.card {
  background:var(--panel); border:1px solid var(--line); border-radius:1.15rem;
  padding:1.15rem; margin-bottom:.9rem; box-shadow:var(--lift);
}
/* A card whose only job is to hold a list. The rows draw their own dividers
   right to the edge, so the padding would put a gap in every one of them. */
.card-flush { padding:.25rem .9rem; }
.card-head { display:flex; align-items:baseline; justify-content:space-between; gap:.6rem; margin-bottom:.7rem; }
.card-title { font-family:var(--serif); font-size:1.12rem; font-weight:500; letter-spacing:0; }
.card-note { color:var(--muted); font-size:.82rem; font-variant-numeric:tabular-nums; }
.card-sub { margin-top:1.1rem; padding-top:.9rem; border-top:1px solid var(--line); }

.section-label {
  font-size:.75rem; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
  color:var(--muted); margin:1.4rem 0 .5rem;
}
.section-label:first-child { margin-top:0; }

.group { margin-bottom:1.1rem; }
.group-head { display:flex; align-items:baseline; justify-content:space-between; gap:.6rem; }
.group-head .section-label { margin-bottom:.5rem; }
.group-action { font-size:.82rem; padding:.15rem .25rem; flex:none; }
.group-action.is-on { color:var(--ink); }

.rule { border:0; border-top:1px solid var(--line); margin:1.4rem 0; }

.empty { text-align:center; color:var(--muted); padding:2.5rem 1rem; font-size:.92rem; }

/* ── Severity picker ───────────────────────────────────────────────────── */

.sev { display:grid; grid-template-columns:repeat(10,1fr); gap:.3rem; }
.sev button {
  /* Ten across means the width is whatever a tenth of the phone is — about
     31px, and no arrangement of ten boxes in a row makes that bigger. The
     height is not constrained by anything, so it is set to the 44px a thumb
     actually needs rather than left square. This is the control she uses
     while in pain, so the difference is the whole point of it. */
  min-height:2.75rem; border:1.5px solid var(--line-2); background:var(--panel);
  border-radius:.55rem; font-weight:700; font-size:1rem; color:var(--ink-2);
  display:grid; place-items:center; padding:0;
  transition:transform .12s var(--ease);
}
.sev button:active { transform:scale(.92); }
.sev button[aria-pressed="true"] { color:#fff; border-color:transparent; transform:scale(1.06); }
[data-theme="dark"] .sev button[aria-pressed="true"] { color:#15151A; }
.sev button[aria-pressed="true"][data-band="low"]   { background:var(--sev-low); }
.sev button[aria-pressed="true"][data-band="mid"]   { background:var(--sev-mid); }
.sev button[aria-pressed="true"][data-band="high"]  { background:var(--sev-high); }
.sev button[aria-pressed="true"][data-band="worst"] { background:var(--sev-worst); }

/* The words matter more than the number. "7" drifts in meaning over a year of
   logging; "very bad" does not, and a consultant reading the export can tell
   what she meant. */
.sev-word { text-align:center; margin-top:.55rem; font-size:.9rem; color:var(--ink-2); min-height:1.35rem; }
.sev-word b { font-weight:600; }

/* ── Chips ─────────────────────────────────────────────────────────────── */

.chips { display:flex; flex-wrap:wrap; gap:.4rem; }
.chip {
  border:1.5px solid var(--line-2); background:var(--panel); color:var(--ink-2);
  border-radius:999px; padding:.5rem .85rem; font-size:.9rem; font-weight:500;
  transition:transform .12s var(--ease), background-color .16s var(--ease), border-color .16s var(--ease);
  /* A long press on a phone otherwise raises the text-selection magnifier and
     the share sheet, both of which land on top of the editor that just
     opened. Holding a chip is a gesture here, not a text selection. */
  -webkit-touch-callout:none; -webkit-user-select:none; user-select:none;
}
.chip:active { transform:scale(.95); }
.chip[aria-pressed="true"] {
  background:var(--indigo); border-color:var(--indigo); color:#fff; font-weight:600;
}
[data-theme="dark"] .chip[aria-pressed="true"] { color:#17161F; }
.chip-add { border-style:dashed; color:var(--muted); }

/* Being edited: dashed and dimmed, so it is obvious that tapping one now
   renames it rather than ticking it. */
.chip-editing {
  border-style:dashed; border-color:var(--indigo);
  background:var(--indigo-soft); color:var(--ink-2); font-weight:500;
}
.chip-editing[aria-pressed="true"] { background:var(--indigo-soft); color:var(--ink-2); }
/* Put away. Shown only while editing, faded, with a + to bring it back. */
.chip-retired { border-style:dotted; color:var(--muted); opacity:.75; }

/* The slicer chips on the history sections. Smaller, because there are three
   rows of them and they are a filter rather than an answer. */
.chips-small { gap:.3rem; }
.chip-small {
  padding:.32rem .6rem; font-size:.8rem; border-width:1px;
  display:inline-flex; align-items:center; gap:.28rem;
}
.chip-hint {
  font-size:.68rem; font-variant-numeric:tabular-nums; opacity:.6; font-weight:600;
}

/* ── Folding sections ──────────────────────────────────────────────────── */

.fold { margin-top:1.6rem; }
.fold-head {
  display:flex; align-items:center; gap:.6rem; width:100%;
  background:none; border:0; padding:.55rem .1rem; text-align:left;
  border-bottom:1px solid var(--line);
}
.fold-title {
  font-family:var(--serif); font-size:1.12rem; font-weight:500; color:var(--ink); flex:1;
}
.fold-note { color:var(--muted); font-size:.82rem; font-variant-numeric:tabular-nums; }
.fold-chevron {
  color:var(--muted); font-size:.85rem; line-height:1; flex:none;
  transform:rotate(180deg); transition:transform .22s var(--ease);
}
.fold-head[aria-expanded="true"] .fold-chevron { transform:rotate(0deg); }
.fold-body { padding-top:.9rem; }
.fold-summary { margin:.2rem 0 .6rem; }
.fold-list { min-height:2rem; }

.slicers { display:flex; flex-direction:column; gap:.7rem; margin-bottom:.9rem; }
.slicer { display:flex; flex-direction:column; gap:.3rem; }
.slicer-label {
  font-size:.7rem; font-weight:700; letter-spacing:.07em; text-transform:uppercase;
  color:var(--muted);
}

.day-group { margin-bottom:.7rem; }
.day-head {
  display:flex; align-items:baseline; justify-content:space-between; gap:.6rem;
  padding:.3rem .1rem; margin-bottom:.1rem;
}
.day-name { font-weight:600; font-size:.92rem; }
.day-count { color:var(--muted); font-size:.78rem; font-variant-numeric:tabular-nums; }
.day-group .row { padding-left:.1rem; padding-right:.1rem; }

/* ── The day picker on Today ───────────────────────────────────────────── */

.day-picker { display:flex; align-items:center; justify-content:space-between; margin-bottom:.8rem; }
.day-picker-name { font-family:var(--serif); font-size:1.25rem; font-weight:500; }

/* A line that is also a link: how many attacks that day, and a way to them. */
.inline-note {
  display:flex; align-items:center; gap:.55rem; width:100%; text-align:left;
  background:var(--indigo-soft); border:0; border-radius:var(--radius);
  padding:.6rem .7rem; margin:0 0 1.1rem; font-size:.88rem; color:var(--ink-2);
}
.inline-note-go { margin-left:auto; color:var(--indigo); font-weight:600; flex:none; }
.sev-pip-sm { width:1.5rem; height:1.5rem; border-radius:.4rem; font-size:.75rem; }

/* ── The week timeline ─────────────────────────────────────────────────── */

.week-nav { display:flex; align-items:center; justify-content:space-between; margin-bottom:.4rem; }
.week-count { color:var(--ink-2); font-size:.85rem; font-variant-numeric:tabular-nums; }

/* ── Medicine grid ─────────────────────────────────────────────────────── */

.med-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(9rem,1fr)); gap:.6rem; }
.med-btn {
  background:var(--panel); border:1.5px solid var(--line-2); border-radius:var(--radius);
  padding:.9rem .8rem; text-align:left; min-height:5rem;
  display:flex; flex-direction:column; justify-content:center; gap:.2rem;
  transition:transform .12s var(--ease);
}
.med-btn:active { transform:scale(.97); }
.med-btn .name { font-weight:600; font-size:.98rem; }
.med-btn .dose { color:var(--muted); font-size:.8rem; }
.med-btn.taken { border-color:var(--indigo); background:var(--indigo-soft); }

.tag { font-size:.7rem; font-weight:700; letter-spacing:.04em; text-transform:uppercase; padding:.12rem .4rem; border-radius:.3rem; background:var(--track); color:var(--muted); }
.tag-acute { background:color-mix(in oklab,var(--sev-high) 18%,transparent); color:var(--sev-high); }
.tag-preventive { background:color-mix(in oklab,var(--ok) 18%,transparent); color:var(--ok); }

/* ── Lists ─────────────────────────────────────────────────────────────── */

.row {
  display:flex; align-items:center; gap:.75rem; width:100%;
  padding:.75rem .2rem; border:0; background:none; text-align:left;
  border-bottom:1px solid var(--line);
}
.row:last-child { border-bottom:0; }
.row-main { flex:1; min-width:0; }
.row-title { font-weight:500; }
.row-sub { color:var(--muted); font-size:.82rem; }
.row-time { color:var(--muted); font-size:.82rem; font-variant-numeric:tabular-nums; white-space:nowrap; }
/* The day she is currently looking at, marked inside a list of other days. */
.row-current .row-title { color:var(--indigo); font-weight:600; }

.sev-pip {
  width:2.1rem; height:2.1rem; flex:none; border-radius:.5rem;
  display:grid; place-items:center; font-weight:700; font-size:.88rem; color:#fff;
}
[data-theme="dark"] .sev-pip { color:#15151A; }
.sev-pip[data-band="low"]   { background:var(--sev-low); }
.sev-pip[data-band="mid"]   { background:var(--sev-mid); }
.sev-pip[data-band="high"]  { background:var(--sev-high); }
.sev-pip[data-band="worst"] { background:var(--sev-worst); }
/* A day with no score. An outline rather than a grey block, because a filled
   grey square in a column of coloured ones reads as "a bad day I can't tell
   the colour of" — an empty one reads as nothing recorded, which is true. */
.sev-pip-blank {
  background:none; border:1.5px dashed var(--line-2); color:var(--muted);
  font-weight:600;
}
[data-theme="dark"] .sev-pip-blank { color:var(--muted); }

/* ── Counter ───────────────────────────────────────────────────────────── */

.counter { display:flex; align-items:baseline; gap:.5rem; }
.counter b { font-size:2rem; font-weight:600; font-variant-numeric:tabular-nums; line-height:1; }
.counter[data-level="warn"] b { color:var(--warn); }
.counter[data-level="alert"] b { color:var(--alert); }

/* The classes underneath the headline. Deliberately quiet: the number that
   matters is the big one above, and these are the breakdown a consultant asks
   for rather than something she needs at 3am. */
.limit-rows {
  margin-top:.7rem; padding-top:.7rem; border-top:1px solid var(--line);
  display:flex; flex-direction:column; gap:.35rem;
}
.limit-row { display:flex; justify-content:space-between; gap:1rem; font-size:.9rem; }
.limit-label { color:var(--ink-2); }
.limit-count { font-variant-numeric:tabular-nums; color:var(--muted); }
.limit-row[data-level="warn"] .limit-count { color:var(--warn); font-weight:600; }
.limit-row[data-level="alert"] .limit-count { color:var(--alert); font-weight:600; }

/* The rolling thirty days under the counter. One column per day, always
   thirty of them, so the width of a cluster is a fortnight no matter which
   month it falls in. The empty days keep a hairline of track: a day she took
   nothing still has to occupy its place, or the gaps close up and the shape
   stops meaning anything. */
.med-strip-wrap { margin-top:.8rem; padding-top:.8rem; border-top:1px solid var(--line); }
.med-strip {
  display:grid; grid-template-columns:repeat(30, 1fr); gap:2px;
  align-items:end; height:34px;
}
.med-day {
  display:flex; align-items:flex-end; height:100%;
  background:var(--track); border-radius:2px; overflow:hidden;
}
.med-day-fill { display:block; width:100%; border-radius:2px; background:var(--muted); }
.med-day[data-state="painkiller"] .med-day-fill { background:var(--indigo); }
.med-day[data-state="none"] { background:none; border-bottom:1.5px solid var(--line); border-radius:0; }
.med-strip-axis {
  display:flex; justify-content:space-between; margin-top:.3rem;
  font-size:.72rem; color:var(--muted);
}
.med-strip-note { margin-top:.5rem; }
.med-strip-note b { color:var(--ink-2); font-weight:600; }

/* ── Something to fall asleep to ───────────────────────────────────────── */

/* The moon carries a dot while something is playing, in the same indigo the
   selected tab uses. It is small on purpose: this is a confirmation for
   somebody who already knows she pressed play, not a notification. */
.icon-btn.is-playing { color:var(--indigo); position:relative; }
.icon-btn.is-playing::after {
  content:''; position:absolute; top:.35rem; right:.35rem;
  width:.4rem; height:.4rem; border-radius:50%; background:var(--indigo);
}

.sound-list { display:flex; flex-direction:column; gap:.5rem; }
/* A heading, not a control. First one loses its top margin so the sheet does
   not open on a gap. */
.sound-group {
  margin:.75rem 0 -.1rem; font-size:.72rem; font-weight:600;
  letter-spacing:.07em; text-transform:uppercase; color:var(--muted);
}
.sound-group:first-child { margin-top:0; }
.sound {
  display:flex; flex-direction:column; gap:.15rem; text-align:left;
  padding:.85rem .95rem; border:1.5px solid var(--line-2); border-radius:var(--radius);
  background:var(--panel); color:inherit; font:inherit; cursor:pointer;
}
.sound[aria-pressed="true"] { border-color:var(--indigo); background:var(--indigo-soft); }
.sound:disabled { opacity:.5; cursor:default; }
/* The Chinese title is the name of the thing; the English line underneath is
   there for a glance, not for reading aloud. Hence the size difference. */
.sound-name { font-size:1.05rem; font-weight:600; }
.sound-gloss { font-size:.82rem; color:var(--muted); line-height:1.35; }
.sound-status { margin-top:.6rem; text-align:center; min-height:1.2rem; }
/* Stacked, not side by side. Stop next to Resume at thumb width invites the
   wrong one being hit in the dark, and the cost of that mistake is losing an
   hour-long reading rather than pausing it. */
.sound-actions { display:flex; flex-direction:column; gap:.5rem; }

/* ── Toast ─────────────────────────────────────────────────────────────── */

.toast {
  position:fixed; left:50%; bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 1rem);
  transform:translateX(-50%); z-index:50;
  background:var(--ink); color:var(--paper);
  padding:.7rem 1.1rem; border-radius:999px; font-size:.9rem; font-weight:500;
  box-shadow:0 6px 24px rgb(0 0 0 / .18);
  animation:toast-in .22s var(--ease);
  max-width:calc(100vw - 2rem);
}
@keyframes toast-in { from { opacity:0; transform:translate(-50%,.5rem); } }

/* ── Sheet ─────────────────────────────────────────────────────────────── */

.sheet-backdrop { position:fixed; inset:0; z-index:45; background:rgb(0 0 0 / .35); display:flex; align-items:flex-end; justify-content:center; }
.sheet {
  background:var(--panel); width:min(34rem,100%);
  border-radius:1.2rem 1.2rem 0 0; padding:1.1rem var(--pad);
  padding-bottom:calc(env(safe-area-inset-bottom) + 1.1rem);
  max-height:88vh; overflow-y:auto;
  animation:sheet-up .24s var(--ease);
  box-shadow:var(--lift-2);
}
@keyframes sheet-up { from { transform:translateY(100%); } }
.sheet-title { font-family:var(--serif); font-size:1.2rem; font-weight:500; margin-bottom:.9rem; }
.sheet-fields { display:flex; flex-direction:column; gap:.85rem; margin-bottom:1rem; }

@media (min-width:34rem) {
  .sheet-backdrop { align-items:center; }
  .sheet { border-radius:1.2rem; padding-bottom:1.1rem; }
}

/* ── Banner ────────────────────────────────────────────────────────────── */

.banner {
  display:flex; align-items:center; gap:.7rem;
  background:var(--indigo-soft); border:1px solid color-mix(in oklab,var(--indigo) 25%,transparent);
  border-radius:var(--radius); padding:.8rem .9rem; margin-bottom:.9rem;
  font-size:.92rem;
}
.banner-offline { background:color-mix(in oklab,var(--warn) 14%,transparent); border-color:color-mix(in oklab,var(--warn) 35%,transparent); }

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


/* ── Dimming ──────────────────────────────────────────────────────────────
   Light sensitivity is one of the symptoms on her consultant's chart, and a
   phone at minimum brightness is still bright in a dark room.

   An overlay rather than a paler palette, on purpose: this darkens text and
   background by the same amount, so contrast is preserved and nothing becomes
   unreadable at the far end of the slider. Recolouring would have dimmed the
   text into the background first.

   z-index above everything, including dialogs and toasts: if light hurts, a
   bright white sheet sliding up over a dimmed app is the exact thing that
   hurts. Everything dims together or the setting is pointless.

   pointer-events:none so it can never eat a tap. */
#dim-veil {
  position:fixed; inset:0; z-index:60; pointer-events:none;
  background:#000; opacity:var(--dim, 0);
  transition:opacity .18s ease;
}
html[data-dimmed="off"] #dim-veil { display:none; }

/* ── Settings ─────────────────────────────────────────────────────────── */

.settings { display:flex; flex-direction:column; gap:1.35rem; }
.setting { display:flex; flex-direction:column; gap:.5rem; }
.setting-label { font-size:.95rem; font-weight:600; }
/* The hint sits directly under its label, so the default paragraph gap would
   read as a break between two settings rather than part of one. */
.setting > .hint { margin:-.3rem 0 .1rem; }

.slider-row { display:flex; align-items:center; gap:.9rem; }
.slider-value {
  min-width:3.2rem; text-align:right; font-variant-numeric:tabular-nums;
  color:var(--muted); font-size:.9rem;
}

/* Hand-styled because the stock control is a different size and colour on every
   platform, and this one is dragged by someone who cannot look at the screen
   for long. The thumb is 28px — comfortably over the 24px touch minimum. */
.slider {
  flex:1; -webkit-appearance:none; appearance:none;
  height:28px; background:none; padding:0; border:0;
}
.slider::-webkit-slider-runnable-track {
  height:6px; border-radius:3px; background:var(--track);
}
.slider::-moz-range-track { height:6px; border-radius:3px; background:var(--track); }
.slider::-webkit-slider-thumb {
  -webkit-appearance:none; appearance:none;
  width:28px; height:28px; border-radius:50%; margin-top:-11px;
  background:var(--indigo); border:3px solid var(--paper);
  box-shadow:0 1px 4px rgb(0 0 0 / .28);
}
.slider::-moz-range-thumb {
  width:28px; height:28px; border-radius:50%;
  background:var(--indigo); border:3px solid var(--paper);
  box-shadow:0 1px 4px rgb(0 0 0 / .28);
}
.slider:focus-visible::-webkit-slider-thumb { outline:2px solid var(--indigo); outline-offset:2px; }

/* ── The consultant's chart ───────────────────────────────────────────── */

/* Hidden on screen entirely. This exists to be printed. */
#print-host { display:none; }

/* ── Waiting to upload ────────────────────────────────────────────────── */

/* The count sits on the icon rather than beside it, because the top bar is
   already three buttons wide on a small phone and a fourth would push the
   wordmark off. */
.badge { position: relative; }
.badge-count {
  position: absolute;
  top: 0; right: 0;
  min-width: 1.05rem;
  padding: 0 0.2rem;
  border-radius: 999px;
  background: var(--indigo);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 600;
  line-height: 1.05rem;
  text-align: center;
  pointer-events: none;
}
/* Amber, not red. Nothing has been lost — an entry the server refused is
   still on the phone and still retryable, and a red alarm on a headache app
   is the wrong thing to hand somebody who already feels unwell. */
.badge.warn .badge-count { background: var(--warn); }

/* A row that has not reached Supabase yet. Deliberately quiet: it is a note,
   not a warning, and it must not make her doubt that the entry is safe. */
.pending-dot {
  display: inline-block;
  width: 0.4rem; height: 0.4rem;
  margin-left: 0.35rem;
  border-radius: 50%;
  background: var(--indigo);
  opacity: 0.55;
  vertical-align: middle;
}

@media print {
  /* Everything except the chart goes. Printing what is on screen would give
     her a tab bar and a Download button on a sheet of A4. */
  body > *:not(#print-host) { display:none !important; }
  #print-host { display:block; }

  /* Landscape, because 31 columns will not fit across a portrait page — and a
     chart that spills onto a second sheet is not the one-page-per-year form
     the consultant handed her. */
  @page { size:A4 landscape; margin:8mm; }

  /* Everything below is sized so that twelve months land on one sheet. That
     is not a cosmetic target: the form the consultant handed her is one page
     per year, and a diary that arrives as two loose sheets gets read as
     half a diary. tools/check-print.mjs asserts the page count, because this
     is exactly the kind of thing a later style tweak breaks silently. */
  body { background:#fff; color:#000; font-size:8pt; line-height:1.15; }

  .print-head h1 { font-size:11pt; font-weight:600; margin:0 0 1mm; }
  .print-head p { font-size:7pt; color:#444; margin:0 0 1.5mm; }

  .print-chart {
    width:100%; border-collapse:collapse; table-layout:fixed;
    font-variant-numeric:tabular-nums;
  }
  .print-chart th, .print-chart td {
    /* 4.6mm, when a worst-case year (every cell filled) stops fitting at
       5.0mm. The margin is deliberate: this is verified in Chrome on a Mac and
       printed from Safari on an iPhone, which do not lay text out identically.
       A height that only just fits here is one that spills there.
       tools/check-print.mjs is what keeps this honest. */
    border:0.4pt solid #BBB; text-align:center; padding:0 0; height:4.6mm;
    /* The 31 day columns share whatever is left after the two label columns.
       Fixed layout plus no width means the browser divides it evenly, which is
       what makes the grid read as a grid. */
    overflow:hidden;
  }
  .print-chart thead th { font-size:7pt; font-weight:600; background:#f0f0f0; height:4.5mm; }
  .pc-month { width:19mm; font-size:8pt; white-space:nowrap; font-weight:600; text-align:left !important; padding-left:1mm !important; }
  .pc-row { width:27mm; font-size:7pt; white-space:nowrap; font-weight:400; text-align:left !important; padding-left:1mm !important; }

  /* Days that do not exist in that month — the 30th of February. Shaded rather
     than blank, so an empty cell always means "nothing logged". */
  .pc-nil { background:#e8e8e8; }

  /* A heavier line every seventh column. Counting across 31 identical cells to
     find the 23rd is exactly the sort of thing that goes wrong in a
     consulting room. */
  .pc-rule { border-right:1.2pt solid #333 !important; }

  /* And a line under each month, for the same reason: three rows that belong
     together, thirty-six times, needs a visible grouping or the eye slides. */
  .print-chart tbody tr:nth-child(3n) td,
  .print-chart tbody tr:nth-child(3n) th { border-bottom:1.2pt solid #333; }

  /* A month must not be split across two pages: the three rows only mean
     anything together. */
  .pc-first, .print-chart tr { break-inside:avoid; page-break-inside:avoid; }
}
