/* The APP shell: everything the ported report never had — sign-in, the menu,
 * and the data-entry forms.
 *
 * WHY THIS FILE EXISTS AT ALL. wwwroot/css/sokkara.css is a verbatim port of
 * the Python report's stylesheet; every rule in it was arrived at against a
 * real phone and a real A4 printer and each carries the comment of the bug it
 * fixes. It is not edited. Anything genuinely new lives here instead, so the
 * report's stylesheet can still be diffed against render.py line for line.
 *
 * THE RULES FOR EDITING THIS FILE:
 *
 *  * Same token vocabulary as sokkara.css — var(--surface), var(--text),
 *    var(--muted), var(--border), var(--accent), var(--bg). They are defined
 *    there, once, together with the dark-mode and print overrides; a literal
 *    colour here is a control that stays light-grey in dark mode and prints as
 *    a grey smear.
 *  * Logical properties only. margin-inline-start, border-inline-end,
 *    inset-inline, padding-block. NEVER left/right — the Arabic page is a
 *    mirror of the English one and every physical side is a bug there.
 *  * No class here duplicates one that already exists in sokkara.css. Reuse
 *    .langswitch, .pill, .noprint, .num-inline and the rest by name.
 *
 * SECTIONS, in order. Each is one agent's; do not reformat another's.
 *   1. LOGIN   — the sign-in page.
 *   2. (NAV — retired; the app bar and tabs are section 13.)
 *   3. ENTRY   — the three forms that write.
 *   4. (EXPLAIN — retired; every explanation is behind an (i), section 13.)
 *   5. PVA     — prescribed vs in use, on the clinic sheet.
 *   6. VISITS  — the doctor-visit list and the form that records one.
 *   7. PRINT   — repairs sokkara.css's folded-section print rule, which current
 *                Chrome no longer honours. Not a new look; a rescued one.
 *   8. MOBILE  — the phone pass, last so it outranks the seven above.
 *   9. MOBILE, THE TABLES — the phone pass for the report's own tables, and the
 *                meal log stacked into one card per meal below 640px.
 *  10. MEALS   — the per-meal detail table on /meals. Two rules; everything
 *                else that table needs it gets by wearing .meallog.
 *  11. RANGE   — the date-range picker above every data page. .noprint.
 *  12. CHARTS  — the hover/tap/focus readout layer the six charts share.
 *  13. REDESIGN — mobile first: the app bar, the bottom tabs, the (i) sheets,
 *                the cards, the chips, the export buttons, and the new pages.
 *                LAST, so on equal specificity it wins over everything above.
 */

/* ===========================================================================
 * 1. LOGIN
 * ===========================================================================
 * Owned by the sign-in work: Pages/Login.razor and Components/Auth/**. Every
 * rule it needs is in this one block, above the NAV banner below it.
 */

/* Drawn by Pages/Login.razor at /login and /login/ar. It is the first screen of
 * the morning for a parent who does not yet know how the night went, so it is
 * built to be quiet: one card, the name, one line, two fields. No image, no
 * icon font, no third-party stylesheet — the CSP and the self-hosted-font rule
 * forbid them, and this page above all must never tell an outside host when
 * this family checks a child's glucose.
 *
 * The wash behind the card is built from var(--accent) through color-mix, not
 * from a colour of its own, so it follows light, dark and print for free and
 * there is nothing to keep in step. A browser without color-mix drops the
 * background-image declaration as invalid and lands on the flat var(--bg)
 * underneath it — the page loses its glow and nothing else.
 */

/* The card's lift. This one CANNOT be derived from a token: a shadow is dark in
 * light mode and darker still in dark mode, so it is not a colour that flips
 * roles. Declared in all three of the places sokkara.css declares its palette —
 * bare :root, the prefers-color-scheme block guarded with
 * :not([data-theme="light"]), and the explicit [data-theme="dark"] — because a
 * value whose only definition sits inside a media query does not exist at all
 * for a reader whose browser is set the other way. */
:root {
  --login-shadow: 0 1px 2px rgba(15, 23, 42, 0.05), 0 24px 48px -28px rgba(15, 23, 42, 0.40);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --login-shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 24px 48px -30px rgba(0, 0, 0, 0.85);
  }
}
:root[data-theme="dark"] {
  --login-shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 24px 48px -30px rgba(0, 0, 0, 0.85);
}

/* `.wrap.login-page`, two classes, so it outranks sokkara.css's `.wrap` on
   specificity whatever order the two files are linked in. The page keeps the
   .wrap class ONLY because `.wrap[lang="ar"]` is where the report stylesheet
   hangs the entire Arabic type treatment; the box metrics it comes with are
   all replaced here. */
.wrap.login-page {
  max-width: none;
  margin: 0;
  /* svh, not vh: on a phone `vh` is measured against the viewport WITHOUT the
     address bar, so 100vh puts the bottom of the card under the browser
     chrome on first paint and moves it as the reader scrolls. */
  min-block-size: 100svh;
  padding-block: clamp(24px, 8vh, 64px);
  padding-inline: 16px;
  display: grid;
  justify-items: center;
  /* `safe` centre: on a short landscape phone the card is taller than the
     viewport, and plain centring overflows EQUALLY in both directions — which
     puts the top of the card, and the name, above the top of the page where it
     cannot be scrolled back to. */
  align-content: safe center;
  background-color: var(--bg);
  background-image:
    radial-gradient(125% 80% at 50% -12%,
      color-mix(in srgb, var(--accent) 20%, transparent) 0%,
      transparent 62%),
    radial-gradient(95% 55% at 50% 112%,
      color-mix(in srgb, var(--accent) 10%, transparent) 0%,
      transparent 58%);
  background-repeat: no-repeat;
}

.login-stack {
  inline-size: 100%;
  max-inline-size: 25rem;
}

.login-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: clamp(22px, 6vw, 34px);
  box-shadow: var(--login-shadow);
}

/* -- the wordmark ---------------------------------------------------------
   Both spellings, stacked, because a name is the same object in either
   language. This is the page's <h1>; the children carry the type. */
.login-name {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  margin: 0 0 12px;
  /* sokkara.css sets h1 to 1.65rem with -0.01em tracking. Both are reset here:
     the size is the children's business, and negative tracking on the Arabic
     line would pull a joined script's letters into each other. */
  font-size: 1rem;
  letter-spacing: normal;
}

.login-name-ar {
  /* Named explicitly, because the Arabic name is painted on the ENGLISH page
     too, where `.wrap[lang="ar"]` does not match and the Cairo stack would
     never reach it. Same stack as sokkara.css's, so there is one look. */
  font-family: 'Cairo', "Segoe UI", Tahoma, "Noto Sans Arabic", "Noto Naskh Arabic",
               "Geeza Pro", "Arabic Typesetting", system-ui, sans-serif;
  font-size: clamp(2.1rem, 11vw, 2.7rem);
  font-weight: 700;
  /* Headroom for the shadda and the damma — at a tighter line-height the marks
     above the word are clipped by the flex line's box. */
  line-height: 1.5;
  /* NEVER track Arabic. It is a joined script and letter-spacing prises the
     joins apart. sokkara.css resets it in .metric and .verdict for the same
     reason. */
  letter-spacing: 0;
  color: var(--text);
}

.login-name-en {
  font-size: clamp(0.82rem, 3.4vw, 0.95rem);
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.34em;
  /* Tracking adds its space AFTER the last letter too, which shifts a centred
     word half a step towards the start. This puts it back. It resolves against
     the span's own dir="ltr", so it is correct on both pages. */
  margin-inline-start: 0.34em;
  text-transform: uppercase;
  color: var(--muted);
}

.login-tagline {
  margin: 0 0 22px;
  text-align: center;
  color: var(--muted);
  font-size: 0.93rem;
  line-height: 1.7;
}

/* -- messages -------------------------------------------------------------
   The live region itself draws NOTHING and takes no space; only its child is
   visible. It must never become display:none — a live region has to be in the
   accessibility tree before its content changes or the change is announced
   late, or not at all. See the note at the top of LoginError.razor. */
.login-live {
  margin: 0;
  padding: 0;
  border: 0;
}

/* Amber and not red, and the same border-inline-start rule as .disclaimer: a
   wrong password is a correction, not an alarm, and the page already has a
   visual language for "read this carefully". */
.login-error {
  margin: 0 0 16px;
  padding: 11px 14px;
  background: var(--warn-bg);
  border: 1px solid var(--warn-border);
  border-inline-start-width: 4px;
  border-radius: 10px;
  color: var(--text);
  font-size: 0.93rem;
  line-height: 1.65;
}

/* "You were signed out" — a statement of fact, so it is neutral. Nothing was
   done wrong and it must not look as though something was. */
.login-notice {
  margin: 0 0 16px;
  padding: 10px 13px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.65;
}

/* -- the fields ----------------------------------------------------------- */
.login-field { margin-block-end: 15px; }

.login-label {
  display: block;
  margin-block-end: 6px;
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--muted);
}

.login-input {
  display: block;
  inline-size: 100%;
  /* A finger, not a mouse pointer. */
  min-block-size: 46px;
  padding: 11px 13px;
  /* Form controls do not inherit type. Without this the Arabic page renders its
     inputs in the browser's default UI font while the label directly above them
     is Cairo. `font` also drags the inherited line-height in — 1.95 on the
     Arabic page — so that is set back immediately afterwards.
     The SIZE is deliberately left inherited: 1rem here, 1.05rem on the Arabic
     page, both comfortably over the 16px below which iOS zooms the whole page
     when a field takes focus. */
  font: inherit;
  line-height: 1.4;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  transition: border-color 0.15s ease;
}

.login-input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-color: var(--accent);
}

/* Chrome paints its own pale-yellow fill over an autofilled field and ignores
   background-color, which in dark mode leaves a bright block in the middle of a
   dark card. An inset shadow is the only thing it honours. A password manager
   filling this form is the intended path, so it has to look right. */
.login-input:-webkit-autofill,
.login-input:-webkit-autofill:hover,
.login-input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 1000px var(--bg) inset;
  -webkit-text-fill-color: var(--text);
  caret-color: var(--text);
}

.login-submit {
  display: block;
  inline-size: 100%;
  min-block-size: 48px;
  margin-block-start: 20px;
  padding: 12px 18px;
  font: inherit;
  line-height: 1.4;
  font-weight: 700;
  /* var(--surface), not #fff: in dark mode the accent is a pale blue and white
     on it is unreadable, while --surface is the near-black the card is already
     made of. One token, right in both themes and on paper. */
  color: var(--surface);
  background: var(--accent);
  border: 1px solid transparent;
  border-radius: 10px;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  transition: filter 0.15s ease;
}

.login-submit:hover { filter: brightness(1.08); }
.login-submit:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* aria-disabled, not disabled — Login.razor explains why the button stays
   focusable while a sign-in is in flight. The styling has to come from the ARIA
   state for the same reason. */
.login-submit[aria-disabled="true"] {
  cursor: progress;
  opacity: 0.72;
  filter: none;
}

.login-foot {
  margin: 20px 0 0;
  text-align: center;
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.6;
}

.login-alt {
  margin: 18px 0 0;
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .login-input,
  .login-submit { transition: none; }
}

@media print {
  /* Nobody prints a sign-in page on purpose, and one that is printed by
     accident must not spend a sheet of ink on a gradient. */
  .wrap.login-page {
    min-block-size: 0;
    padding: 0;
    background-image: none;
  }
  .login-card { box-shadow: none; }
}


/* ===========================================================================
 * 3. ENTRY
 * ===========================================================================
 * Owned by the three data-entry forms: Pages/Add*.razor and
 * Components/Entry/**. Nothing above this banner is touched.
 *
 * WHO IS USING THIS. A parent, on a phone, one-handed, often at night, often
 * worried, about to write a number that syncs into AAPS and changes the doses
 * it recommends for a five-year-old. Every measurement below was taken against
 * a 375px viewport, which is the actual device.
 *
 *   * 48px minimum on every control. A 44px target is the usual floor; these
 *     are hit with a thumb, at arm's length, by someone who has been awake
 *     since 03:00.
 *   * 1rem on every input — 17px against this page's 106.25% root — because
 *     iOS Safari zooms the whole viewport when a focused field is under 16px,
 *     and a page that jumps as the keyboard opens is a page that gets the wrong
 *     field typed into.
 *   * The action bar is sticky to the bottom. Save and Review are then always
 *     inside the thumb arc rather than at the end of a scroll.
 *   * Logical properties throughout. The Arabic page is a mirror; every
 *     physical side would be a bug there, and this is a form, which is the one
 *     place a mirrored layout is most obvious.
 *
 * THE ACCENT IS SAFETY EQUIPMENT, NOT DECORATION. A meal bolus and a correction
 * bolus are the same four fields, and this family has already had corrections
 * recorded as meal doses — which corrupted the ratio analysis, because every
 * meal-response calculation downstream divides carbohydrate by the insulin it
 * believes was given for it. So the two forms are different colours, and the
 * colour is on the panel edge, the primary button and the record cards at once:
 * one cue can be missed, three at a glance cannot.
 */

/* The three kind colours, plus the two the panels need. Declared in all three
 * of the places sokkara.css declares its palette — bare :root, the
 * prefers-color-scheme block guarded with :not([data-theme="light"]), and the
 * explicit [data-theme="dark"] — because a value whose only definition sits
 * inside a media query does not exist for a reader whose browser is set the
 * other way.
 *
 * The light values are the ones already in sokkara.css for the same meanings:
 * --accent for the ordinary case, the .pill-warn amber for the exception, the
 * .pill-ok green for a success, the .bg-low red for a refusal. The dark values
 * are their dark-mode counterparts from the same file. Nothing new is invented;
 * a fourth red on a page about a child would be a fourth thing to keep in step.
 */
:root {
  --entry-bolus: #0369a1;
  --entry-correction: #b45309;
  --entry-meal: #15803d;
  /* A glucose reading typed in by hand: neither food nor insulin (2026-10-10). */
  --entry-glucose: #7c3aed;
  /* A feeling marked by hand: none of the above, nor a reading (2026-10-11).
     GlucoseGraph.FeelingColor. */
  --entry-feeling: #db2777;
  /* A sensor change: none of the above, nor a feeling (2026-10-13).
     GlucoseGraph.SensorColor. */
  --entry-sensor: #0f766e;
  --entry-danger: #dc2626;
  --entry-ok: #15803d;
  /* Text ON a filled accent. A foreground, not a surface: it is the one value
     here that cannot be var(--surface), because in dark mode the accents are
     light and the text on them has to be dark. */
  --entry-on-accent: #ffffff;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --entry-bolus: #38bdf8;
    --entry-correction: #f59e0b;
    --entry-meal: #4ade80;
    --entry-glucose: #a78bfa;
    --entry-feeling: #f472b6;
    --entry-sensor: #2dd4bf;
    --entry-danger: #f87171;
    --entry-ok: #4ade80;
    --entry-on-accent: #08111f;
  }
}
:root[data-theme="dark"] {
  --entry-bolus: #38bdf8;
  --entry-correction: #f59e0b;
  --entry-meal: #4ade80;
  --entry-glucose: #a78bfa;
  --entry-feeling: #f472b6;
  --entry-sensor: #2dd4bf;
  --entry-danger: #f87171;
  --entry-ok: #4ade80;
  --entry-on-accent: #08111f;
}

/* data-kind picks the accent, and everything downstream reads --entry-accent
 * rather than naming a colour. `.record` restates it because a record card is
 * nested inside .entry and would otherwise inherit the FORM's colour — a meal
 * already in the recent list, drawn in correction amber because it happens to
 * be sitting on the correction page, would say the opposite of the truth on the
 * one list a parent reads to decide whether a dose has already been given. */
.entry,
.record { --entry-accent: var(--entry-bolus); }
.entry[data-kind="correction"],
.record[data-kind="correction"] { --entry-accent: var(--entry-correction); }
.entry[data-kind="meal"],
.record[data-kind="meal"] { --entry-accent: var(--entry-meal); }
.entry[data-kind="glucose"],
.record[data-kind="glucose"] { --entry-accent: var(--entry-glucose); }
.entry[data-kind="feeling"],
.record[data-kind="feeling"] { --entry-accent: var(--entry-feeling); }
.entry[data-kind="sensor"],
.record[data-kind="sensor"] { --entry-accent: var(--entry-sensor); }
/* A kind the server sent that is none of the three. Grey, deliberately: an
   unrecognised record must not borrow one of the three meanings. */
.record[data-kind="unknown"] { --entry-accent: var(--muted); }

/* --- the hazard notice ---------------------------------------------------
   The warn palette, not the kind accent: this says the same thing on all three
   forms and must not read as part of the colour code that distinguishes them. */
.entry-hazard {
  background: var(--warn-bg);
  border: 1px solid var(--warn-border);
  border-radius: 12px;
  padding: 14px 16px;
  margin-block-end: 18px;
}
.entry-hazard strong { display: block; margin-block-end: 4px; }
.entry-hazard p { margin: 0; color: var(--muted); font-size: 0.93rem; line-height: 1.7; }

/* The stage panels — the form, the read-back, the duplicate, the confirmation.
   The accent edge is on these and NOT on the recent list below them, which
   carries its own per-record colours. */
.entry-stage > section { border-inline-start: 4px solid var(--entry-accent); }

/* --- fields --------------------------------------------------------------- */
.field { margin-block-end: 18px; }
.field > label {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  margin-block-end: 6px;
  font-weight: 600;
}
.field-name { font-size: 1rem; }
/* "required" / "optional" beside the label rather than a bare asterisk. An
   asterisk needs a legend somewhere else on the page, and a legend somewhere
   else is a legend nobody reads at 03:00. */
.field-flag { font-size: 0.78rem; font-weight: 500; color: var(--muted); }
.field-hint { margin: 0 0 8px; color: var(--muted); font-size: 0.87rem; line-height: 1.65; }

.entry-input {
  width: 100%;
  min-height: 48px;
  padding: 11px 12px;
  font: inherit;
  font-size: 1rem;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.entry-input:focus-visible {
  outline: 2px solid var(--entry-accent);
  outline-offset: 1px;
}
.entry-input[disabled] { opacity: 0.55; cursor: not-allowed; }
.entry-textarea { min-height: 96px; line-height: 1.6; resize: vertical; }

/* The error state is a colour AND a message, never a colour alone: red on a
   border is invisible to a reader who cannot see red and to one reading in
   daylight on a phone at minimum brightness. */
.field-bad .entry-input { border-color: var(--entry-danger); }
.field-err { margin: 6px 0 0; color: var(--entry-danger); font-size: 0.87rem; font-weight: 600; }

.time-row { display: flex; align-items: stretch; gap: 8px; }
.time-row .entry-time { flex: 1 1 auto; min-width: 0; }
.btn-now { flex: 0 0 auto; padding-inline: 16px; }

.field-group {
  margin: 0 0 18px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
}
.field-group > legend {
  padding-inline: 6px;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.field-group .field:last-child { margin-block-end: 0; }

/* --- which meal this is ----------------------------------------------------
   Four tappable boxes, two by two on a phone and four across where they fit.
   Each is a whole <label>, so a thumb can land anywhere on the box, and the
   native radio stays visible inside it: the chosen box is marked by the radio's
   own dot AND by the accent border, never by colour alone. */
/* EVERY BOX THE HEIGHT OF THE TALLEST (grid-auto-rows: 1fr), and a compact line
   height inside them. "وجبة خفيفة" needs 89px and a box on a 375px phone has about
   80, so the Arabic snack label wraps there - measured, not guessed. Squeezing
   the padding to make it fit would fail again on a 360px phone; instead a
   wrapped label stays tight and the grid stays even rather than leaving one row
   half again as tall as the other. */
.meal-type-options {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: 1fr;
  gap: 8px;
}
@media (min-width: 560px) {
  .meal-type-options { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.meal-type-option {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 48px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
  font-weight: 600;
  line-height: 1.3;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.meal-type-option input {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--entry-accent);
}
.meal-type-option:has(input:checked) {
  border-color: var(--entry-accent);
  box-shadow: inset 0 0 0 1px var(--entry-accent);
}
.meal-type-option:has(input:focus-visible) {
  outline: 2px solid var(--entry-accent);
  outline-offset: 1px;
}
.field-bad .meal-type-option { border-color: var(--entry-danger); }
/* Blood test or sensor: two tiles side by side at every width. */
@media (min-width: 560px) {
  .meal-type-options.glucose-type-options { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --- buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 12px 20px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn-primary {
  background: var(--entry-accent);
  border-color: transparent;
  color: var(--entry-on-accent);
}
.btn-primary:hover { filter: brightness(1.08); }
.btn-quiet { color: var(--accent); }
.btn-quiet:hover { border-color: var(--accent); }
/* "Save it anyway" after a 409. Outlined rather than filled: it is the
   exception, and it must not look like the button the thumb is already
   travelling towards. */
.btn-danger { color: var(--entry-danger); border-color: var(--entry-danger); }
.btn-danger:hover { filter: brightness(1.08); }
.btn[disabled] { opacity: 0.6; cursor: default; filter: none; }

/* Sticky, so Review and Save stay inside the thumb arc instead of at the end of
   a scroll. It sits on var(--surface) because it is inside a section and has to
   hide the content sliding under it. */
.entry-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-block-start: 20px;
  padding-block: 12px;
  position: sticky;
  bottom: 0;
  background: var(--surface);
  border-block-start: 1px solid var(--border);
}
.entry-actions .btn { flex: 1 1 12rem; }

/* --- the read-back -------------------------------------------------------- */
.entry-pending { margin: 0 0 10px; }
/* The sentence, read rather than scanned: "1.25 U correction bolus at 21:48".
   Bigger than the table under it on purpose — it is the last thing between a
   typo and a child's clinical record. */
.entry-says { margin: 0 0 16px; font-size: 1.2rem; font-weight: 600; line-height: 1.6; }
.entry-ok { color: var(--entry-ok); }

.entry-kv { margin: 0 0 16px; }
.entry-kv > div {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding-block: 8px;
  border-block-end: 1px solid var(--border);
}
.entry-kv dt { color: var(--muted); font-size: 0.87rem; }
.entry-kv dd {
  margin: 0;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: end;
}
/* Free text — what she ate, the notes — wraps under its own label instead of
   being squeezed into a value column. */
.entry-kv > .record-text { display: block; }
.entry-kv > .record-text dd { margin-block-start: 2px; font-weight: 500; text-align: start; }

/* --- records: the recent list, and the 409 pair --------------------------- */
.record-list { display: grid; gap: 10px; }
.record {
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-inline-start: 4px solid var(--entry-accent);
  border-radius: 10px;
}
.record-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.record-kind { color: var(--entry-accent); font-size: 0.9rem; font-weight: 600; }
.record-when {
  color: var(--muted);
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
}
.record-body { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 6px 0 0; }
.record-body > div { display: flex; align-items: baseline; gap: 6px; }
.record-body dt { color: var(--muted); font-size: 0.8rem; }
.record-body dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }
.record-body > .record-text { display: block; flex-basis: 100%; }
.record-body > .record-text dd { font-weight: 500; }
.record-source { margin: 6px 0 0; color: var(--muted); font-size: 0.78rem; }

.dup-pair { display: grid; gap: 14px; }
.dup-side h3 { margin-block-end: 6px; }

/* --- what went wrong ------------------------------------------------------ */
.entry-trouble {
  margin-block-end: 16px;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--entry-danger);
  border-radius: 12px;
}
.entry-trouble-line { margin: 0; font-weight: 600; }
.entry-said-head { margin: 10px 0 4px; color: var(--muted); font-size: 0.85rem; }
.entry-said { margin: 0; padding-inline-start: 18px; color: var(--muted); font-size: 0.87rem; }
/* No punctuation between the label and the server's sentence, and a gap
   instead: a colon sitting between an Arabic label and an LTR English span is a
   neutral character with an isolate on each side, and where it lands is a
   coin toss. A margin cannot be reordered. */
.entry-said-field { font-weight: 600; margin-inline-end: 6px; }

/* "We could not read the recent list", and "the server would not say what this
   collided with". A dashed warn box, distinct from the solid red of a refusal:
   this is an absence of knowledge, not a rejection, and the two lead to
   different next actions. */
.entry-unknown {
  margin: 0;
  padding: 10px 12px;
  background: var(--warn-bg);
  border: 1px dashed var(--warn-border);
  border-radius: 10px;
  font-size: 0.9rem;
}

.entry-crosslink { margin-block-start: 18px; }

/* --- wider screens -------------------------------------------------------- */
@media (min-width: 720px) {
  /* The colliding record and the pending one, side by side. Below this width
     they stack, and the order in the DOM — existing first, yours second — is
     the order they should be read in. */
  .dup-pair { grid-template-columns: 1fr 1fr; }
  .entry-actions .btn { flex: 0 1 auto; min-width: 11rem; }
}

/* --- print ---------------------------------------------------------------- */
@media print {
  /* A sticky bar prints as a band across the middle of the sheet. Nothing on
     these pages is meant for paper, but a parent who prints one should get the
     record, not the controls. */
  .entry-actions { position: static; display: none; }
  .entry-input { border-color: #b9c2cc; }
  .entry-stage > section { border-inline-start-width: 1px; }
}





/* --- add, edit and remove on the meal log ------------------------------------
 *
 * Components/Clinic/RowActions.razor and the Add row above the table. NONE of it
 * prints: the clinic sheet is a record of what happened, and a page of pencils
 * and crosses is not that. */
.log-add {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-block-end: 12px;
}

.log-confirm {
  border: 1px solid var(--entry-danger);
  background: color-mix(in srgb, var(--entry-danger) 10%, transparent);
  border-radius: 10px;
  padding: 12px 14px;
  margin-block: 12px;
}

.log-confirm p { margin: 0 0 10px; line-height: 1.6; }

/* The pair beside one thing on a row. INLINE, so it reads as belonging to the
 * figure next to it rather than to the row as a whole - a meal row carries
 * several of these and each one edits a different record. */
.row-actions {
  display: inline-flex;
  gap: 4px;
  margin-inline-start: 6px;
  vertical-align: middle;
}

.row-act {
  min-inline-size: 32px;
  min-block-size: 32px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}

.row-act:hover { color: var(--text); }

.row-act-remove:hover { color: var(--entry-danger); border-color: var(--entry-danger); }

/* A record AndroidAPS wrote gets the sentence rather than a dead button. */
.row-not-ours { display: block; font-size: 0.78rem; margin-block-start: 2px; }

/* ON A PHONE THE TABLE STACKS INTO CARDS, and 32px controls inside a stacked
 * cell are too small for a thumb. They grow to the full target there, where
 * there is room for them to. */
@media (max-width: 640px) {
  .row-act { min-inline-size: 44px; min-block-size: 44px; font-size: 1rem; }
}

@media print {
  .log-add, .log-confirm, .row-actions, .row-not-ours { display: none !important; }
}
/* --- the modal --------------------------------------------------------------
 *
 * Components/Shell/Dialog.razor, on the browser's native <dialog>. It paints in
 * the top layer, so there is no z-index here to lose an argument with something
 * else on the page.
 *
 * FULL SCREEN ON A PHONE, and that is not a stylistic choice: the content is an
 * entry form with ten fields and a component list, and a centred card on a 375px
 * screen would be a small window scrolling inside a page that is also scrolling.
 * One scroll, one surface. */
.dlg {
  padding: 0;
  border: 0;
  background: transparent;
  max-inline-size: 100%;
  max-block-size: 100%;
  inline-size: 100%;
  block-size: 100%;
}

/* THE FALLBACK, pinned. A dialog opened by the [open] attribute rather than by
 * showModal() is position:absolute with no backdrop, so it would land wherever
 * the report happens to have scrolled to and let the page behind scroll under
 * it. Fixing it to the viewport makes the degraded mode look and behave like
 * the real one; .dlg-frame is opaque, so it covers the page on its own.
 * Harmless for the modal case, which is already fixed. */
.dlg[open] {
  position: fixed;
  inset: 0;
}

.dlg::backdrop {
  /* Dark enough that the page behind reads as unavailable rather than merely
   * dimmed - the dialog is genuinely inert, and it should look it. */
  background: rgba(2, 6, 23, 0.62);
}

.dlg-frame {
  display: flex;
  flex-direction: column;
  block-size: 100%;
  background: var(--bg);
  color: var(--text);
}

.dlg-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  /* STICKY, so the row a parent is editing stays named while they scroll down
   * ten fields to the time box. */
  position: sticky;
  inset-block-start: 0;
  background: var(--bg);
  z-index: 1;
}

.dlg-title {
  margin: 0;
  font-size: 1.05rem;
  line-height: 1.4;
  flex: 1 1 auto;
  min-width: 0;
}

.dlg-close {
  flex: 0 0 auto;
  min-inline-size: 44px;
  min-block-size: 44px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  color: var(--muted);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
}

.dlg-close:hover { color: var(--text); }

.dlg-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 0 16px 24px;
  /* Room under the last control for a thumb, and for the phone's own home bar. */
  padding-block-end: max(24px, env(safe-area-inset-bottom));
}

/* On a larger screen it becomes a card, because full-bleed on a desktop reads as
 * a page navigation rather than as something layered over what you were doing. */
@media (min-width: 700px) {
  .dlg {
    inline-size: min(680px, 92vw);
    block-size: auto;
    max-block-size: 88vh;
    border-radius: 14px;
    overflow: hidden;
    box-shadow: var(--login-shadow);
  }
}

@media print {
  /* A working surface, never part of the printed record. */
  .dlg, .dlg::backdrop { display: none !important; }
}
/* --- correcting a record ----------------------------------------------------
 *
 * Pages/Amend.razor and Components/Amend/**. Built from the SAME parts as the
 * entry page - .record, .record-body, .entry-form, .entry-input, .btn - because
 * it is the same act seen twice: what a parent typed, and what they meant to
 * type. Two different-looking screens for one record is how a household comes to
 * believe the two hold different kinds of thing.
 *
 * THE TWO BANNERS ARE NOT DECORATION and they are deliberately different
 * weights. .amend-alarm is the AndroidAPS warning - a correction here does NOT
 * reach the loop's insulin-on-board - and it is the loudest thing this app draws
 * anywhere. .amend-warn is quieter: a correction that has stopped applying
 * because the record moved underneath it. One is about the next dose; the other
 * is about the record. */

.amend-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-block-end: 12px;
}

/* The window currently being shown. A pressed state, not a hover: on a phone
 * there is no hover, and "which of these three am I looking at" is the whole
 * question the row answers. */
.btn.btn-on {
  background: var(--accent);
  color: var(--entry-on-accent, #fff);
  border-color: var(--accent);
}

.amend-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-inline-start: auto;
  font-size: 0.9rem;
}

.amend-toggle input { inline-size: 20px; block-size: 20px; }

/* -- one record in the list ------------------------------------------------ */

.amend-record .record-actions {
  display: flex;
  gap: 8px;
  margin-block-start: 10px;
}

/* FULL TAP TARGETS, overriding .btn-small's 36px. "Put it back" is a four-letter
 * word in Arabic and came out 49px wide by 38 tall - a thumb-sized miss away
 * from a record staying deleted, on the one control whose whole job is undoing a
 * mistake. */
.amend-record .record-actions .btn {
  min-height: 44px;
  padding-inline: 14px;
}

/* A CORRECTED RECORD IS MARKED, always. A changed figure that looks exactly like
 * a recorded one leaves the household - and the doctor - comparing this against
 * Nightscout and finding two numbers with nothing to explain the difference.
 *
 * A TINT, NOT THE EDGE. .record already carries a 4px inline-start border whose
 * COLOUR is the kind code - meal green, bolus blue, correction amber - so a
 * second rule on that edge cannot say "corrected" without overwriting what the
 * edge already means. The first attempt did exactly that and, being 3px against
 * the base 4px, actually de-emphasised a corrected record. */
.amend-record.amend-corrected {
  background: color-mix(in srgb, var(--accent) 7%, transparent);
}

/* A REMOVED RECORD IS STILL DRAWN. Struck through, dimmed, and with its undo on
 * the card: a removal a parent cannot find again is a removal they cannot take
 * back, and this list is the only place they would look.
 *
 * This one DOES take the edge, and deliberately: a record the household has
 * struck out is no longer a meal or a correction, it is a record that does not
 * count, and the kind code has nothing left to say about it. The width matches
 * .record's so the row does not shift as records change state. */
.amend-record.amend-removed {
  border-inline-start-color: var(--muted);
  opacity: 0.72;
}

.amend-record.amend-removed .record-body dd,
.amend-record.amend-removed .record-kind {
  text-decoration: line-through;
}

.amend-badge {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 10px;
  align-items: baseline;
  margin: 0 0 8px;
}

.pill-gone {
  background: color-mix(in srgb, var(--muted) 18%, transparent);
  color: var(--muted);
}

/* " (was 12)" beside a corrected figure. Quiet, because the number that counts
 * is the corrected one - but present, because the disagreement with Nightscout
 * is otherwise something a doctor has to work out unaided. */
.amend-was { font-size: 0.82rem; }

.amend-stale { color: var(--entry-correction, var(--muted)); font-weight: 600; }

/* -- the fix screen -------------------------------------------------------- */

.amend-form { margin-block-start: 8px; }

.amend-alarm,
.amend-warn {
  border-radius: 10px;
  padding: 12px 14px;
  margin-block: 12px;
  border: 1px solid var(--border);
}

/* THE LOUDEST THING IN THE APP, and it earns it: correcting a dose here fixes
 * her history and does NOT change what AndroidAPS is dosing from. */
.amend-alarm {
  border-color: var(--entry-danger);
  background: color-mix(in srgb, var(--entry-danger) 10%, transparent);
}

.amend-alarm strong { color: var(--entry-danger); }

.amend-warn {
  border-color: var(--warn-border, var(--border));
  background: var(--warn-bg, transparent);
}

.amend-alarm p,
.amend-warn p { margin: 6px 0 0; line-height: 1.6; }

/* Withdrawing a correction sits between the form and the removal link: it is an
 * ordinary action on a corrected record, not a dangerous one, so it keeps the
 * quiet button treatment rather than the red link below it. */
.amend-undo { margin-block-start: 16px; }

/* Removal sits alone, below the form, separated by a rule. Never a checkbox in
 * the form and never a pop-up: it is the one action that takes a record out of
 * her history, and it goes to a screen that says so first. */
.amend-danger {
  margin-block-start: 20px;
  padding-block-start: 12px;
  border-top: 1px solid var(--border);
}

.btn-link-danger {
  background: none;
  border: 0;
  padding: 8px 0;
  min-height: 44px;
  color: var(--entry-danger);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* -- what will change ------------------------------------------------------ */

.amend-diff {
  inline-size: 100%;
  border-collapse: collapse;
  margin-block: 12px;
}

.amend-diff th,
.amend-diff td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  text-align: start;
}

.amend-diff thead th { font-size: 0.85rem; color: var(--muted); }

/* THE CHANGED ROWS ARE MARKED AND THE UNCHANGED ONES STAY. A diff that hides
 * what did not move answers "what changed" and leaves "what did I leave alone",
 * which on a record carrying a dose and a time is the question a parent is
 * actually checking before they press save. */
.amend-diff .amend-changed td:last-child {
  font-weight: 700;
  color: var(--accent);
}

.amend-diff .amend-same { color: var(--muted); }

.amend-reason-echo { margin-block: 10px; }

.amend-done-t { font-size: 1.05rem; margin-block-end: 10px; }

@media print {
  /* A working screen, not a record. What was actually saved prints from the
   * meal log and the clinic sheet, where the correction is already applied. */
  .amend-filters, .record-actions, .amend-danger, .amend-form { display: none !important; }
}
/* --- the saved-food list ---------------------------------------------------
 *
 * Pages/Foods.razor. Deliberately built from the SAME parts as the component
 * editor - .component-fields for the four number boxes, .entry-input, .btn-small -
 * because the two are the same act: correcting an amount for a food. Two
 * different-looking editors for one job is how a household comes to think the
 * numbers in them are different kinds of thing. */
.foods-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.food-row {
  padding-block: 10px;
  border-top: 1px solid var(--border);
}

.food-row:first-child { border-top: 0; }

.food-name { margin-block-end: 6px; }

.food-name .entry-input { width: 100%; }

.food-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-block-start: 8px;
}

/* A row just saved holds its mark until it is touched again, so a household
 * working down a list can see how far they have got. */
.food-row.saved { border-inline-start: 3px solid var(--ok, var(--border)); padding-inline-start: 8px; }

@media print {
  /* A working list, not a record. */
  .foods-list, .food-actions { display: none !important; }
}
/* --- the component list ----------------------------------------------------
 *
 * Components/Entry/ComponentEditor.razor, between the description box and the
 * carbohydrate box on the meal form. It is where components are entered whether
 * or not the assistant was involved, so it is styled as PART OF THE FORM rather
 * than as an assistant artefact - solid border, ordinary inputs, no dashed
 * "suggestion" treatment. A household typing every row by hand should not feel
 * like it is correcting a machine.
 *
 * FOUR NUMBER BOXES PER ROW ON A 375px SCREEN is the constraint that shapes
 * everything below. They wrap to two-by-two rather than scrolling sideways,
 * because a number box a thumb has to scroll to reach is one that gets left
 * empty - and the whole point of the row is that all four are easy to fix. */
.components {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  margin-block: 12px;
}

.components-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-block-end: 2px;
}

/* The list's title and the (i) that says a weight restates the other three. */
.components-title {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
}

/* Where the unit went when the four labels lost their "(g)". */
.components-unit { margin: 0 0 8px; font-size: 0.8rem; }

/* THE PHOTO'S WEIGHT BESIDE HER USUAL, under a row the photo proposed. Wraps
 * rather than widening the row: at 320px the line, the "Use 130 g" button and
 * the (i) go onto two lines, never under a horizontal scroll. */
.portion-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 8px;
  margin-block-start: 6px;
  font-size: 0.8rem;
  line-height: 1.4;
}

.portion-line > span { min-width: 0; flex: 1 1 12em; overflow-wrap: anywhere; }

/* THE WARNINGS about the plate, above the rows (MealWarnings): her record's
 * foods to watch for this type of meal (2026-10-13) and the assistant's
 * (2026-10-07). The app's caution tint, the sentences at reading size but no
 * larger, and each part's one-line caveat small under it. Tinted, not red: it
 * reports what followed past meals and what to check, it is not an alarm. */
.ai-warnings {
  margin-block: 8px 12px;
  padding: 8px 12px;
  border-radius: 10px;
  border: 1px solid var(--warn-border);
  background: var(--warn-bg);
  font-size: 0.9rem;
  line-height: 1.5;
}

.ai-warnings-head { display: flex; align-items: center; gap: 4px; }

.ai-warnings ul { margin: 6px 0 4px; padding-inline-start: 1.2em; }

.ai-warnings li { margin-block: 3px; overflow-wrap: anywhere; }

.ai-warnings p.sub { margin: 4px 0 0; font-size: 0.78rem; }

/* Two sources in one box: each part under its own small heading with its (i),
 * and a hairline between them so a caveat is read with the part it belongs to. */
.warn-group + .warn-group {
  margin-block-start: 8px;
  padding-block-start: 8px;
  border-block-start: 1px solid var(--warn-border);
}

.warn-from {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-block-start: 6px;
  font-weight: 600;
  font-size: 0.85rem;
}

.warn-from > span { min-width: 0; overflow-wrap: anywhere; }

.settings-age { margin: 4px 0 0; }

.components-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* EACH ROW IS ONE ITEM ON THE PLATE, and it is drawn as one block rather than
 * as five loose controls between two hairlines. Four number boxes stacked under
 * a name read as a wall of boxes when nothing groups them; the tint and the
 * inset say where one food ends and the next begins, which is what a parent is
 * scanning for when they are looking for the one figure that is wrong. */
.component-row {
  padding: 8px 10px 10px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 4%, transparent);
  margin-block-start: 8px;
}

.component-row:first-child { margin-block-start: 0; }

/* The name takes the width; the remove control sits at the end of the row and
 * stays a 44px target - it deletes a row someone typed, so a mis-tap costs
 * real work. */
.component-name {
  display: flex;
  gap: 8px;
  align-items: center;
}

.component-name .entry-input {
  flex: 1 1 auto;
  /* The name is the row's heading, so it carries the weight the four number
     boxes under it deliberately do not. */
  font-weight: 600;
  background: var(--surface);
}

.btn-remove {
  flex: 0 0 auto;
  min-width: 44px;
  min-height: 44px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

.btn-remove:hover { color: var(--text); }

/* FOUR ACROSS, EVEN ON A 375px PHONE. Two-by-two was the first attempt and it
 * was wrong: it doubled the height of every row, and a plate of three items came
 * to more than a whole phone screen - so the carbohydrate box the row exists to
 * fill sat below the fold, and the total under it was never seen at all.
 *
 * Four boxes across leaves about 78px each, which holds three digits at a
 * comfortable size. It only works with the short labels (comp_f_carbs and its
 * two neighbours); putting the form's own "Carbohydrate (g)" back here wraps
 * every label to three lines and undoes it. */
.component-fields {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
  margin-block-start: 6px;
}

/* CAPPED WHERE THERE IS ROOM. Stretched to a quarter of a desktop column, a box
 * holding "45" is a 300px field with two digits in the corner of it - which
 * reads as an empty field, not a filled one. */
@media (min-width: 560px) {
  .component-fields { grid-template-columns: repeat(4, minmax(0, 150px)); }
}

.component-fields label {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

/* SMALLER THAN A FIELD LABEL, because there are four of them per row and a
 * plate of three items was running to more than a phone screen - the carb box
 * the row exists to fill ended up below the fold. */
.component-fields label > .sub {
  font-size: 0.76rem;
  line-height: 1.35;
}

/* SHORTER THAN A FORM INPUT, and no shorter. 40px is still a comfortable thumb
 * target, and the font stays at 1rem: anything under 16px makes iOS Safari zoom
 * the page on focus, which throws a parent out of the layout mid-entry. */
.component-fields .entry-input {
  width: 100%;
  min-height: 40px;
  padding: 8px 6px;
  /* Tighter than the form's 1.65: at 78px wide the line box, not the text, is
     what sets the height of the row. */
  line-height: 1.3;
  background: var(--surface);
}

.component-fields .num { text-align: end; }

.components-total {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  align-items: baseline;
  margin-block: 10px 0;
  padding-block-start: 10px;
  border-top: 1px solid var(--border);
}

/* The sum is the number a dose gets worked out from, so it is the one thing in
 * this box that is bigger than body text. */
.components-total > strong { font-size: 1.05rem; }

/* The undercount warning. It sits ON the total line, never below it: a partial
 * number read without its qualifier is the failure this whole warning exists to
 * prevent. */
.components-total .partial { font-size: 0.82rem; }

.btn-small {
  padding: 6px 10px;
  font-size: 0.85rem;
  min-height: 36px;
}

@media print {
  /* Working state, not a record. What was actually saved is the carbohydrate,
   * fat and protein on the treatment, and those print from the meal log. */
  .components { display: none !important; }
}
/* --- the meal assistant ----------------------------------------------------
 *
 * Components/Entry/BreakdownPanel.razor, sitting between the description box
 * and the carbs box on the meal form.
 *
 * IT MUST NOT LOOK LIKE A RESULT. Everything here is deliberately quieter than
 * the form around it: a dashed border rather than solid, muted text, no accent
 * colour on the numbers. The fields below it are where the real values live,
 * and a panel styled with any more confidence than this would invite reading
 * the proposal as the answer and pressing straight past it.
 *
 * The one thing that IS loud is an unrecognised component, because that is the
 * row a reader most needs to notice. */
.breakdown {
  border: 1px dashed var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  margin-block: 10px;
}

.breakdown-status {
  margin-block: 10px;
}

/* A WAIT THAT MOVES. A sentence that only says "working" looks exactly like a
 * page that has stopped, so every wait on the assistant has a spinner beside its
 * words. Used by the photo status and the breakdown panel. */
.busy-line {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
  color: var(--text);
}
.spinner {
  flex: none;
  inline-size: 1.15em;
  block-size: 1.15em;
  border-radius: 50%;
  border: 2.5px solid color-mix(in srgb, var(--accent) 22%, transparent);
  border-block-start-color: var(--accent);
  animation: sk-spin 0.8s linear infinite;
}
@keyframes sk-spin { to { transform: rotate(360deg); } }
/* Slowed, not stopped: a still spinner is the very "nothing is happening" this
 * exists to answer. */
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2.4s; }
}

.breakdown-list {
  list-style: none;
  margin: 8px 0;
  padding: 0;
}

.breakdown-list li {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 10px;
  padding-block: 6px;
  border-top: 1px solid var(--border);
}

.breakdown-list li:first-child { border-top: 0; }

.part-name { font-weight: 600; }

.part-carbs { white-space: nowrap; }

/* The basis spans both columns, under the pair it explains. */
.part-basis {
  grid-column: 1 / -1;
  font-size: 0.82rem;
}

/* AN UNRECOGNISED COMPONENT IS MARKED, not muted into the background. Italic
 * and a dotted start rule, the same two cues the meal log uses for a row that
 * is not counted - geometry rather than colour, so it survives a monochrome
 * print and a colour-blind reader. */
.breakdown-list li.unknown {
  font-style: italic;
  border-inline-start: 3px dotted var(--border);
  padding-inline-start: 8px;
}

.breakdown-total {
  margin-block: 10px 6px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  align-items: baseline;
}

/* The "counts only what was recognised" warning. It has to sit ON the total
 * line, not below it: a partial number read without its qualifier is the
 * failure mode this whole panel is guarding against. */
.breakdown-total .partial { font-size: 0.82rem; }

.breakdown-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-block: 8px 4px;
}

.breakdown-caveat {
  font-size: 0.8rem;
  margin-block: 6px 0;
}

/* The trigger under the description box. */
.ai-trigger { margin-block: -4px 12px; }

/* The meal photo - Components/Entry/MealPhoto.razor. Each button is a <label>
 * wrapping a real file input, so the whole button is the tap target and the
 * input stays in the tab order (it is hidden from sight, not from a keyboard). */
.photo-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-block: 4px 12px;
}
/* Side by side at 375 px: the default 20 px padding put them on two lines. */
.photo-picker .btn { position: relative; cursor: pointer; flex: 1 1 auto; padding-inline: 12px; }
.photo-picker .btn:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.photo-picker .btn.is-disabled { opacity: 0.6; cursor: default; }
.photo-picker input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: inherit;
}
/* The line under the buttons: "preparing", then "analysing". Empty when idle, and
 * then it takes no room - the live region stays in the page so it is announced. */
.photo-status .busy-line {
  margin-block: -2px 12px;
  padding: 10px 12px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 9%, transparent);
}
.photo-preview {
  display: block;
  max-width: min(100%, 320px);
  max-height: 240px;
  border-radius: 10px;
  border: 1px solid var(--border);
  margin-block: 0 12px;
  object-fit: cover;
  transition: opacity 0.2s;
}
/* Faded while it is read, so the photo itself says "not done yet". */
.photo-preview.is-busy { opacity: 0.6; }

@media print {
  /* None of this is a record of anything. The panel is a transient proposal and
   * the trigger is a control; neither belongs on a printed sheet. */
  .breakdown, .ai-trigger, .breakdown-status { display: none !important; }
}

/* ===========================================================================
 * 5. PVA — prescribed vs in use
 * ===========================================================================
 * Owned by Components/Clinic/PrescribedVsActual.razor, which is rendered once,
 * on the clinic sheet. Four rules and nothing else: the table itself is the
 * report's own `table`, its cells are the report's own .txt/.num, and the
 * verdicts in the last column are the report's own .pill. Only the MARK on a
 * disagreeing row is new, because nothing in sokkara.css marks a table row.
 *
 * WHAT THE MARK HAS TO SURVIVE. This is the sheet a doctor reads on paper, and
 * a printer may drop backgrounds — most do by default. So the mark is three
 * independent things, and the two that matter are ink:
 *
 *   1. the word "Differs", inside a .pill, in the row itself;
 *   2. the two disagreeing numbers in <strong>;
 *   3. and only then this tint and rule, which are the screen's cue.
 *
 * No print-color-adjust is forced here for that reason — a row whose colour is
 * dropped loses nothing a reader needs. That is deliberately unlike .bg-high /
 * .bg-low in sokkara.css, where the colour IS the value and the print block
 * pins it.
 */

/* Every first cell carries the rule, transparent until the row is marked, so a
   flagged row does not shunt its own column 3px sideways relative to the rest
   of the table. border-inline-start, never border-left: on the Arabic page the
   first cell is the RIGHTMOST one and the rule belongs on its outer edge. */
.pva td:first-child,
.pva th:first-child { border-inline-start: 3px solid transparent; }

/* --warn-bg / --warn-border, not a colour of their own: sokkara.css declares
   both in all three palettes, and its print block resets them to white-on-grey
   — which is exactly right here. On paper the tint disappears and the rule
   stays as a grey bar down the start edge of the row. */
.pva tr.pva-differs > td { background: var(--warn-bg); }
.pva tr.pva-differs > td:first-child { border-inline-start-color: var(--warn-border); }

/* Cells hold a value, a .sub line under it and sometimes a pill under that.
   Baseline-aligned, the short cells in the same row would float to the middle
   of the tall one. */
.pva td { vertical-align: top; }

/* The date under "Prescribed". `th` is uppercased by the report stylesheet,
   which is right for a column heading and wrong for the sentence under it. */
.pva th .sub { text-transform: none; }

/* The two lines under the table — what the mark means, and what a dash means.
   They are the reader's key to the table and belong tight under it, not a full
   paragraph away. */
.pva-legend { margin-block: 8px 0; }

/* ===========================================================================
 * 6. VISITS — the doctor-visit list, and the form that records one
 * ===========================================================================
 * Owned by Pages/Visits.razor, Pages/AddVisit.razor and Components/Visit/**.
 * Nothing above this banner is touched, and the MOBILE section below it is left
 * last so it keeps outranking everything — this section declares none of the
 * properties it sets, and its own narrow-screen rules are in its own media
 * query at the foot of this block.
 *
 * TWO SCREENS, TWO PEOPLE, THE SAME COMPONENTS.
 *
 *   * The LIST is read in a waiting room, on a phone, and sometimes held out for
 *     a clinician to look at across a desk. So a card leads with the date and
 *     what changed, at a size that survives being read at arm's length, and the
 *     full set of values is folded below it.
 *   * The FORM is filled in afterwards, one-handed, from a piece of paper. It
 *     is seven <details> sections because twenty-two fields in one column is
 *     four screens of scrolling and an abandoned record.
 *
 * The form reuses .entry, .field, .entry-input, .btn and .entry-actions from
 * the ENTRY section wholesale — same controls, same 48px targets, same 1rem
 * inputs that keep iOS from zooming the viewport. What is new here is the
 * grouping, the change list and the file picker.
 *
 * .entry[data-kind="meal"] is what both pages carry, so --entry-accent is the
 * green already used for food and for anything that is not insulin about to be
 * given. It is deliberately NOT a fourth accent: the colour code in this app
 * exists to separate a correction from food at a glance, and a new colour would
 * dilute a distinction that has already gone wrong once in this family's
 * records.
 *
 * Logical properties only, as everywhere in this file. The Arabic page is a
 * mirror; a physical `left` here would put a change list's rule on the wrong
 * edge and the summary's disclosure triangle on the wrong side.
 */

/* --- the folded sections of the form --------------------------------------
   Native <details>. No script, and the browser gives the expanded state to a
   screen reader, opens a closed section when Ctrl+F finds a match inside it,
   and prints folded content through sokkara.css's own @media print rule. */
.visit-group {
  margin-block-end: 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
}

.visit-group > summary {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding: 14px 16px;
  /* A whole summary row is the tap target, not just the words in it: this is a
     thumb at arm's length, and 48px is the floor everything on these forms is
     built to. */
  min-block-size: 48px;
  cursor: pointer;
  user-select: none;
  list-style: none;
  border-radius: 12px;
}
.visit-group > summary::-webkit-details-marker { display: none; }
.visit-group > summary::marker { content: ""; }

/* The disclosure triangle. VERTICAL arrows are NOT mirrored in RTL and must not
   be swapped — sokkara.css says the same about .more > summary, and Strings.cs
   says the opposite about the horizontal ones, which are. margin-inline-start
   auto pushes it to the far end in both directions with no media query. */
.visit-group > summary::after {
  content: "\25be";
  margin-inline-start: auto;
  color: var(--muted);
  font-size: 0.8em;
}
.visit-group[open] > summary::after { content: "\25b4"; }

.visit-group > summary:hover { border-color: var(--accent); }
.visit-group > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.visit-group[open] > summary {
  border-block-end: 1px solid var(--border);
  border-end-start-radius: 0;
  border-end-end-radius: 0;
}

.visit-group-name { font-weight: 700; font-size: 1rem; }

/* The one line that says what is inside a section nobody has opened. It takes
   the whole second row on a phone rather than being squeezed beside the name. */
.visit-group-hint {
  flex: 1 1 100%;
  color: var(--muted);
  font-size: 0.87rem;
  line-height: 1.6;
}

/* .pill is sokkara.css's, reused by name. Only the placement is new. */
.visit-group-badge { flex: none; }

.visit-group-body { padding: 16px; }
.visit-group-body > .field:last-child { margin-block-end: 0; }

/* --- the change list ------------------------------------------------------
   "Carb ratio (g per U)   10 → 8". The label carries the unit so the value
   column is numbers alone, which is what lets every value sit inside a
   .num-inline LTR isolate without dragging an Arabic unit word into it. */
.visit-changes { margin: 0 0 16px; }

.visit-change {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 4px 12px;
  padding-block: 9px;
  border-block-end: 1px solid var(--border);
}
.visit-change:last-child { border-block-end: 0; }

.visit-change dt { color: var(--muted); font-size: 0.87rem; }
.visit-change dd {
  margin: 0;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  font-variant-numeric: tabular-nums;
  text-align: end;
}

/* A real difference is the only thing on this list that gets weight and colour.
   Everything else is present without competing — that is the whole design of
   the read-back. */
.visit-change[data-change="changed"] .visit-now {
  font-weight: 700;
  color: var(--entry-accent);
}
.visit-change[data-change="new"] .visit-now { font-weight: 700; }
.visit-change[data-change="same"] .visit-now { font-weight: 600; }
.visit-change[data-change="unknown"] .visit-now { font-weight: 600; }

/* The old value: muted but readable, and NOT struck through. A line through a
   clinical number is a second meaning nobody agreed on, and at 375px it is
   mostly just harder to read. */
.visit-was { color: var(--muted); font-weight: 500; }

.visit-arrow { color: var(--muted); }

/* "not written down before" / "not compared". A statement about these records,
   set quietly so it cannot be mistaken for part of the value. */
.visit-tag {
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 500;
  font-variant-numeric: normal;
}

/* The "written down again, unchanged" group, and the "everything at this visit"
   fold. A shade back, so the eye lands on the differences first. */
.visit-changes-quiet .visit-change dd { font-weight: 500; }
.visit-changes-quiet { opacity: 0.85; }

/* --- what the doctor said -------------------------------------------------- */
.visit-words { margin-block: 16px 0; }
.visit-words-t {
  margin: 0 0 4px;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

/* pre-wrap, so the line breaks a parent typed while listening to a doctor
   survive into the record instead of collapsing into one paragraph. overflow-wrap
   because a pasted URL or a long drug name must not push the card sideways. */
.visit-said {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.75;
}

.visit-next { margin: 12px 0 0; display: flex; flex-wrap: wrap; gap: 8px; }
.visit-next-t { color: var(--muted); font-size: 0.87rem; }

/* --- the file picker ------------------------------------------------------- */
.visit-picker { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }

/* A file input's button is drawn by the browser and cannot be restyled without
   script. What CAN be set is the box around it, so it lines up with every other
   control on the form and takes the same focus ring. */
.visit-file-input {
  flex: 1 1 14rem;
  min-inline-size: 0;
  padding-block: 10px;
  line-height: 1.4;
}
.visit-file-input:focus-visible { outline: 2px solid var(--entry-accent); outline-offset: 1px; }

.visit-file-clear { flex: 0 0 auto; min-block-size: 44px; padding-inline: 16px; }

.visit-file-list { margin: 12px 0 0; padding: 0; list-style: none; display: grid; gap: 8px; }

.visit-file {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  padding: 9px 11px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 0.9rem;
}

/* A device filename is arbitrary text of arbitrary length. It wraps and breaks
   rather than pushing the whole card into a sideways scroll. */
.visit-file-name { flex: 1 1 10rem; min-inline-size: 0; overflow-wrap: anywhere; font-weight: 600; }
.visit-file-size {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

/* Refused. The colour is never the only cue — the reason is written beside it,
   because red on a border is invisible to a reader who cannot see red and to
   one reading in daylight at minimum brightness. */
.visit-file-bad { border-color: var(--entry-danger); }
.visit-file-why {
  flex: 1 1 100%;
  color: var(--entry-danger);
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.55;
}

.visit-files-partial { color: var(--entry-danger); margin: 0 0 8px; }

/* --- the prescription itself ----------------------------------------------- */
.visit-shots {
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.visit-shot { inline-size: 8.5rem; }

.visit-shot-link {
  display: block;
  color: var(--accent);
  text-decoration: none;
  font-size: 0.85rem;
}
.visit-shot-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A fixed box with object-fit, so a portrait photograph of a prescription and a
   landscape one line up in the same row instead of stepping down it. */
.visit-shot-img {
  display: block;
  inline-size: 100%;
  block-size: 8.5rem;
  object-fit: cover;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
}

/* The stand-in for a file that is not an image. The mark is CSS content, not
   text in the markup and not an icon font: a literal "PDF" in a .razor would be
   a hard-coded English string, and routing an acronym through Strings.cs would
   put the same three letters in both language columns — which the translation
   test reads as a missed translation. U+25A4 is a ruled-page shape and is
   direction-neutral, so it needs no RTL variant; the file's real name is printed
   underneath it either way. */
.visit-shot-doc {
  display: flex;
  align-items: center;
  justify-content: center;
  block-size: 8.5rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
  color: var(--muted);
  font-size: 2.4rem;
  line-height: 1;
}
.visit-shot-doc::before { content: "\25a4"; }

.visit-shot-name { display: block; margin-block-start: 5px; overflow-wrap: anywhere; }

/* An attachment the server would not say the location of. Not a link, so it
   cannot be tapped into a 404. */
.visit-shot-dead { color: var(--muted); }

/* --- the list page --------------------------------------------------------- */
.visit-add { margin-block: 0 18px; }

.visit-list { display: grid; gap: 16px; }

.visit-card {
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-inline-start: 4px solid var(--entry-accent);
  border-radius: 12px;
  /* A card is the unit a clinician reads. It must never be split across two
     sheets of A4 — the same rule sokkara.css applies to section and to a table
     row. */
  break-inside: avoid;
  page-break-inside: avoid;
}

.visit-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 12px;
  margin-block-end: 8px;
}

/* The date is the first thing read and the thing a card is found by, so it is
   the biggest thing on it. */
.visit-card-when {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.visit-card-who { margin: 0; color: var(--muted); font-size: 0.95rem; }
.visit-card-none { font-style: italic; }

/* The sentence, read rather than scanned: "What changed since the visit on
   2026-05-04". Bigger than the list under it on purpose. */
.visit-card-says {
  margin: 0 0 10px;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.6;
}

.visit-more { margin-block-start: 14px; }

/* The notice is the same .entry-hazard box the entry forms use, and it keeps
   that section's spacing. Only the bottom margin differs on the list, where it
   sits directly above a button rather than above a form. */
.visit-notice { margin-block-end: 16px; }

/* --- the saved screen ------------------------------------------------------
   "This visit is written down. It has NOT been entered into AAPS." It is the
   first line under a heading that says "Saved", which is precisely the moment a
   parent concludes the new ratio is in force. Amber, not green: the sentence is
   a correction to the reading the word "Saved" invites, and green would join the
   thing it is arguing with. It reuses --entry-correction so it follows light,
   dark and print with no colour of its own. */
.visit-saved-line { color: var(--entry-correction); }

/* The next action, and it is not a button on this page — it is a thing to go and
   do in another app. Boxed so it does not read as one more line of summary. */
.visit-do-next {
  margin: 16px 0 0;
  padding: 12px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--entry-correction);
  border-radius: 10px;
  font-weight: 600;
  line-height: 1.7;
}

/* --- narrow screens -------------------------------------------------------- */
@media (max-width: 640px) {
  .visit-group-body { padding: 14px; }
  .visit-group > summary { padding: 13px 14px; }
  .visit-card { padding: 14px; }

  /* Three thumbnails across a 375px screen at 8.5rem each is a sideways
     scroll. Two, at whatever is left after the gap. */
  .visit-shot { inline-size: calc(50% - 6px); }

  /* The label and the value stack rather than being squeezed into two columns
     — "Correction factor (mg/dL per U)" is three words wider than half a phone
     screen, and at nowrap it pushed the number off the card. */
  .visit-change dd { text-align: start; }
}

/* --- print ----------------------------------------------------------------- */
@media print {
  /* A visit list printed for an appointment. The "Record a visit" button is
     chrome and carries .noprint in the markup, which sokkara.css already hides
     here — no rule for it is repeated, because two places deciding whether a
     control prints is two places for the answer to differ.
     The NOTICE is deliberately not hidden: a printed page of prescribed
     settings needs the line that says these are a record and not a live setting
     more than the screen does, not less. */
  .visit-card { border-color: #b9c2cc; border-inline-start-width: 2px; }
  .visit-group { border-color: #b9c2cc; }
  /* Folded detail must still reach the paper — sokkara.css already does this
     for .more; the form's own sections need the same, or a printed form is
     seven headings and no fields. */
  .visit-group > summary::after { content: ""; }
  .visit-group > *:not(summary) { display: block !important; }
}

/* ===========================================================================
 * 7. PRINT — the folded sections that stopped reaching the paper
 * ===========================================================================
 * A COLLAPSED <details> NO LONGER PRINTS ITS CONTENTS, and the rule that used
 * to make it do so has been quietly dead for a while.
 *
 * sokkara.css opens every `.more` on paper the only way that existed when it
 * was written:
 *
 *     @media print { .more > *:not(summary) { display: block !important; } }
 *
 * That worked while a closed <details> hid its children by giving them
 * `display: none`. Current Chrome does not: the UA stylesheet now hides the
 * whole subtree with `details::details-content { content-visibility: hidden }`,
 * which is a property on the PSEUDO-ELEMENT, not on the children. Setting
 * `display: block` on the children does not reach it, so the children stay
 * unrendered no matter how loudly they are told to be blocks.
 *
 * MEASURED, NOT INFERRED. In the browser this app runs in, a closed <details>
 * carrying only the legacy rule reports checkVisibility() === false for its
 * child; add the ::details-content rule and the same child reports true. The
 * same probe reports `content-visibility: hidden` on the UA pseudo-element.
 * AppNav.razor's header comment records the same UA change as the reason a
 * CSS-opened <details> was ruled out for the menu, so this is that bug's
 * other half.
 *
 * WHAT WAS LOST. Every `.more` on the dashboard — the day-by-day table, the
 * profile table, the extra findings, the overnight table, the loop-deviation
 * detail. On screen they open on a tap and nothing looks wrong, so this is
 * invisible until someone compares a printout against the page and finds the
 * tables simply absent. Section 4's `.explain` already carries the modern rule
 * and is unaffected; the clinic sheet uses no `.more` at all.
 *
 * WHY IT IS HERE AND NOT IN sokkara.css. That file is a verbatim port and is
 * not edited (see the top of this one). The selectors below are its own class
 * names, reused by name, adding only the declaration the UA change now needs.
 * The legacy rule underneath is left exactly where it is: a browser that does
 * not know ::details-content drops this block's first rule as an invalid
 * selector and still lands on it.
 */
@media print {
  .more::details-content {
    content-visibility: visible !important;
    block-size: auto !important;
  }
}

/* ===========================================================================
 * 8. MOBILE — the phone pass
 * ===========================================================================
 * THE DEVICE IS A ~375px PHONE, HELD IN ONE HAND, OFTEN AT 03:00. Everything
 * below was measured in a browser at 360x640 and 414x896, in BOTH languages,
 * and every rule carries the number it was taken from. Nothing here is a
 * preference; each one closes a gap that was reproduced at one of those widths.
 *
 * WHY THIS SECTION IS LAST AND WHY IT IS SEPARATE. Three of the rules below
 * have to outrank a phone rule that already exists — sokkara.css's
 * `@media (max-width: 640px)` block, and the NAV and ENTRY sections above.
 * Equal specificity, later in the cascade, so they win without an !important
 * and without anyone reformatting another agent's block. Read together with
 * those blocks: this section only ever raises a floor, it never restyles.
 *
 * 640px, NOT A NEW NUMBER. sokkara.css already means "phone" by 640px — the
 * .opt columns drop there, .wrap loses padding there, the tall charts appear
 * there. A second breakpoint would be a second definition of "phone".
 *
 * `screen and`, ALWAYS. `max-width` in print resolves against the PAGE box,
 * and A4 at 96dpi is 794px, so an unscoped max-width:640 block is merely
 * inert on paper rather than harmless — but the clinic page is a print
 * artefact and "merely inert" is not a thing to leave to a page size. The
 * report stylesheet scopes its own narrow-screen chart rules the same way and
 * says so.
 */

@media screen and (max-width: 640px) {

  /* --- 1. NAV: the three controls that are always on screen ---------------
     MEASURED, at 360 and at 414, in English and in Arabic: 79.6x38.4 (Menu),
     66.8x38.4 (the language pill) and 82.3x38.4 (Sign out). All three come
     from the NAV block's own narrow-screen rule, which drops them to 0.87rem
     and 6px of block padding — 24.4px of line box plus 12px plus 2px of
     border is 38.4, six pixels under the 44px floor, on the three controls a
     thumb reaches for first. The ENTRY section holds itself to 48px on the
     same device and says why; this is the same hand.

     min-block-size and not padding: padding would move the text down as well
     as grow the box, and the burger's caret and the pill's word are already
     optically centred. inline-flex + centring is what makes a min height mean
     anything on .langswitch, which sokkara.css draws as an inline-block —
     without it the extra 5.6px would hang below the word.

     .langswitch UNQUALIFIED, so BOTH copies grow: the one in the bar and the
     one ReportShell puts in the page header. They are the same control and
     the NAV block is explicit that they must not drift into two different
     pills — and they had drifted, 38.4px in the bar against 42.1px in the
     header, because only the bar's copy is inside that narrow-screen rule.
     One floor puts both at 44 and closes the gap rather than widening it.

     Cost, measured at 360x640: the bar grows 55.4px -> 61px, the status card
     starts at 276.9 instead of 271.3, and the three headline numbers still
     finish at 576.5 — above the fold on the shortest phone in the range. */
  /* The menu bar's burger, sign-out and explain switch left with the redesign
     (section 13); the language pill on the sign-in page keeps the floor. */
  .langswitch {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-block-size: 44px;
  }

  /* --- 2. The meal summary's verdict column -------------------------------
     THE VERDICT WAS ENTIRELY OFF-SCREEN AT 360px. Measured: the .mealtbl is
     449.7px wide inside a 290px .scroll box, and the Verdict column starts at
     x=297 — seven pixels past the right edge of the visible box, so the one
     column that section exists to deliver could not be seen at all without a
     sideways swipe nobody is told about. At 414 it was 47px of a 153px column,
     which cuts the pill mid-word.

     THE CAUSE is not the column count — the two .opt columns are already
     dropped by this width. It is that `th, td { white-space: nowrap }` reaches
     the .sub line inside the numeric cells, and that line is a SENTENCE:
     "typical 45 g · 22 – 71". Unbreakable, it set the Average-carbs column to
     154.5px on its own. sokkara.css already lets `.mealtbl td:not(.num)` wrap
     for exactly this reason and records that it took the table from 356px to
     ~266px; the .num cells were left out of that fix because a VALUE must not
     split, and the sub-line under the value was collateral.

     So: the sub-line wraps, the value line above it does not, and every
     .num-inline token inside the sub-line is pinned back to nowrap. That last
     line is the important one — without it the range broke as "22" / "– 71",
     splitting a number across two lines, which is precisely what the nowrap
     was there to prevent, and in Arabic leaves a dangling signed token.
     Measured after: 449.7 -> 372.7px, Verdict starts at x=220 (inside the
     290px box at 360, and fully visible at 414), row heights unchanged at
     146px because the meal-window column already sets them, and the pill is
     still one line. Arabic re-measured: 355.3px, same result mirrored.

     THE ANALYSIS TAB (2026-10-05) keeps the "Ratio now" column on a phone,
     which brought this defect back: 358.5px (Arabic) and 380.2px (English) in
     a 314px box, half the verdict pill cut off. There the table drops AVERAGE
     CARBS instead (MealClockTable's RatioOnPhone marks it .opt), so the phone
     card is Meal, When, Ratio now, Verdict. Measured with the AndroidAPS line
     present: min-content 274px Arabic, 307px English (300px in Cairo), inside
     314px at 360 with every pill in the box; both fit 368px at 414. */
  .mealtbl td.num .sub { white-space: normal; }
  .mealtbl td.num .sub .num-inline { white-space: nowrap; }

  /* --- 3. The 14px floor --------------------------------------------------
     The operator's word for this report was "tiny", and these are the labels
     that were, measured against the 17px root: the three headline metric
     labels at 12.92px (this width SHRINKS them, from 0.8rem to 0.76rem), the
     goal line under each at 13.6px, the chart axis captions at 13.6px, the
     clinic header's field names at 13.26px, the therapy line's at 13.26px,
     and on the entry forms the required/optional marker at 13.26px, the
     record-card field names at 13.6px and the record's source at 13.26px —
     the field a parent reads to decide whether a dose is already in AAPS.

     0.85rem is 14.45px here. In rem and not px, like everything else in both
     files, so a reader who has raised their own browser's base size still
     gets the increase rather than being capped at our idea of fourteen.

     WHAT IS DELIBERATELY NOT IN THIS LIST:
       * `th`, `.pill` and the fieldset legends sit at 0.82rem = 13.94px. That
         rounds to the floor; raising it would widen every table by a hair to
         win a twentieth of a pixel, and the table above is the one thing on
         this page fighting for width.
       * `.tip-btn` is 12.5px, and that is a single "?" glyph inside a 20px
         circle, not prose. sokkara.css states the trade — a bigger circle
         breaks the line it sits in — and it is right. The 20px TAP target is
         the real cost there, and it is left alone for the same reason: the
         badges appear in adjacent pairs six pixels apart, so an invisible
         44px hit area would have each one swallowing its neighbour's.

     Cost, measured: metric cards 135.5 -> 142.4px tall with no new wrapping
     ("Variability" is still one line), axis captions 20.4 -> 21px, the clinic
     header grid unchanged at two columns, and 9px on the whole clinic page. */
  .metric .label,
  .metric .target,
  .axis,
  .clinic-head div span,
  .therapy-line b,
  .field-flag,
  .record-body dt,
  .record-source { font-size: 0.85rem; }
}

/* ===========================================================================
 * 9. MOBILE, THE TABLES — reclaiming the width the phone never had
 * ===========================================================================
 * MEASURED AT 375x812 WITH REAL DATA, AND EVERY TABLE ON THE REPORT SCROLLED
 * SIDEWAYS. The numbers, before any rule below:
 *
 *   .scroll wrapper                          305px of a 375px screen
 *   day-by-day table                         347px   (42px hidden)
 *   "when she eats" summary                  374px   (69px hidden)
 *   loop deviations                          456px  (151px hidden)
 *   MEAL LOG, on the clinic page             829px  (524px hidden)
 *
 * The meal log is the one that matters. It is the table this whole report was
 * built to produce, the one a parent shows a doctor, and on a phone it was
 * displaying about a third of itself behind a sideways scroll that nothing on
 * the page advertises.
 *
 * TWO CAUSES, and they are worth separating because the fixes are different.
 *
 * (a) 68 OF THE 375 PIXELS — 18% — WERE HORIZONTAL PADDING before any content:
 *     .wrap 14+14, and the section card another 20+20. Those are the desktop
 *     values, carried onto a phone unchanged.
 *
 * (b) TEXT CELLS COULD NOT WRAP. The base rule is `th, td { white-space:
 *     nowrap }`, which is right for a number and wrong for a sentence: the
 *     day-by-day Date column measured 181px — over half that table — because
 *     "2026-08-14 (partial day)" is one unbreakable run, and the profile table
 *     held 208px cells for "00:00 -> 10, 20:00 -> 11 g/U".
 *
 * WHY NOT JUST HIDE MORE COLUMNS. sokkara.css already drops `.opt` here, and
 * what remains is what a reader actually came for. Hiding the +1h and +2h
 * glucose from the meal log would leave a meal log that cannot show a meal's
 * effect. The width has to be found, not taken out of the content.
 */

@media screen and (max-width: 640px) {

  /* --- (a) the 68 pixels ---------------------------------------------------
     14 -> 8 and 20 -> 10 gives the wrapper 337px instead of 305: a 10.5% gain
     across every table at once, and enough on its own to bring the day-by-day
     (347) and profile (328) tables inside their wrapper.

     Padding-inline only. The block padding is what separates one card from the
     next on a long scrolling page and is not what is short here. */
  .wrap { padding-inline: 8px; }
  .status,
  section { padding-inline: 10px; }

  /* Cells: 7px 8px -> 6px 4px. Eight pixels a column, which on the six-column
     summary is 48 - most of its 69px overrun. The block padding stays near 6px
     because these rows are also tap targets on a touch screen. */
  .meallog td, .meallog th,
  .mealtbl td, .mealtbl th,
  table td, table th { padding: 6px 4px; }

  /* --- (b) let a sentence wrap ---------------------------------------------
     `.num` is excluded, always: a number split across two lines is unreadable,
     and a glucose value that wraps between its digits is worse than a scroll.
     This is the same reasoning sokkara.css gives for .mealtbl td:not(.num). */
  .scroll table td:not(.num) { white-space: normal; }

  /* HEADERS wrap unconditionally, .num included. A heading is a label, not a
     value: "Median deviation" at nowrap measured 142px and set the width of a
     column whose widest datum is "1.0". Only td.num keeps nowrap, because a
     number broken across two lines is unreadable. */
  .scroll table th { white-space: normal; }

  /* .txt carries min-width: 9rem from sokkara.css - 153px at this root size.
     A sensible floor for a prose column on a desktop, and arithmetically
     impossible here: the deviations table has TWO of them, 306px of a 337px
     wrapper before its numbers get a pixel. 5rem lets them wrap to two or
     three short lines, which is what a phone has room for. */
  .scroll td.txt,
  .scroll th.txt { min-width: 5rem; }
}

/* ---------------------------------------------------------------------------
 * THE MEAL LOG BECOMES A LIST OF MEALS, not a table, below 640px.
 * ---------------------------------------------------------------------------
 * Eight columns do not fit in 375px and no amount of padding will make them.
 * At 829px it was 2.7 screens wide. So on a phone each row stops being a row:
 * the cells stack, and each one is labelled by the header it belongs to.
 *
 * `content: attr(data-label)` — the labels come from the MARKUP, which means
 * they are the SAME localised strings the <th> uses and they follow the
 * language automatically. A list of labels in CSS would be a second copy of
 * the header row, in one language, drifting.
 *
 * SCREEN ONLY. The clinic page is a print artefact and the printed table is
 * correct as a table: this whole block sits inside `screen and`, so paper is
 * untouched and a doctor still gets the grid.
 */
@media screen and (max-width: 640px) {

  .meallog thead { position: absolute; width: 1px; height: 1px;
                   overflow: hidden; clip-path: inset(50%); }

  .meallog, .meallog tbody, .meallog tr, .meallog td { display: block; width: auto; }

  /* One card per meal. The day header keeps its full-width bar. */
  .meallog tr.mealrow,
  .meallog tr.corrrow {
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 8px 10px;
    margin-block-end: 8px;
  }

  /* A correction still reads as hanging off the meal above it. Indent by the
     logical start so Arabic mirrors it. */
  .meallog tr.corrrow { margin-inline-start: 14px; border-style: dashed; }

  .meallog tr.daybreak td { border-radius: 6px; }

  /* Each cell on its own line, label first, value after — with the label in
     the muted colour so the eye lands on the number. */
  .meallog tr.mealrow td,
  .meallog tr.corrrow td {
    border: 0;
    padding: 2px 0;
    display: flex;
    gap: 8px;
    justify-content: space-between;
    align-items: baseline;
    text-align: start;
  }
  .meallog tr.mealrow td::before,
  .meallog tr.corrrow td::before {
    content: attr(data-label);
    color: var(--muted);
    font-size: 0.82rem;
    flex: 0 0 auto;
  }
  /* A cell with nothing in it is noise in a stacked list; the grid needed the
     placeholder to keep its columns aligned, a list does not. */
  .meallog tr.mealrow td:empty,
  .meallog tr.corrrow td:empty,
  .meallog tr.corrrow td.carbs-na { display: none; }

  /* The free-text description is the reason this table exists. Give it the
     full width with its label above rather than beside. */
  .meallog td.txt { display: block; }
  .meallog td.txt::before { display: block; margin-block-end: 2px; }

  /* THE DESCRIPTION ROW JOINS THE CARD ABOVE IT rather than becoming a second
     card. Stacked, a meal is already one card per row; a description floating
     under it in a box of its own would read as a separate event.

     :has() does the joining. Where it is not supported the two simply sit as
     adjacent boxes - untidy, never wrong. */
  .meallog tr.mealrow:has(+ tr.descrow) {
    margin-block-end: 0;
    border-block-end: 0;
    border-end-start-radius: 0;
    border-end-end-radius: 0;
  }

  .meallog tr.descrow {
    border: 1px solid var(--border);
    border-block-start: 0;
    border-start-start-radius: 0;
    border-start-end-radius: 0;
    border-end-start-radius: 8px;
    border-end-end-radius: 8px;
    padding: 0 10px 8px;
    margin-block-end: 8px;
  }

  .meallog tr.descrow > td { padding: 0; display: block; }
}

/* ===========================================================================
 * 10. MEALS — the response sparkline at the end of every meal-log row
 * ===========================================================================
 * Components/Charts/MealTrace.razor draws one row's glucose from ten minutes
 * before the entry to ten past the two-hour mark, against a shaded target band.
 * It is the last cell of every row in Components/Clinic/MealLogTable.razor, on
 * both /clinic and /meals.
 *
 * WHAT USED TO BE HERE. This section held the rules for a SECOND table,
 * `<table class="meallog mealdetail">`, which listed each meal's rise, ratio,
 * four-hour value and verdict beside a small delta sparkline. That table was
 * removed on request — every column of it summarised in words the response this
 * drawing now shows — and MealDetailTable.razor and MealSpark.razor were deleted
 * with it. Its two rules went too: a print rule releasing `break-after: avoid`
 * on a table with no correction rows, and a block margin on the old spark. Both
 * were about markup that no longer exists; neither applies to the log, which
 * DOES have correction rows and needs that break rule kept exactly as it is.
 *
 * WHY THERE IS SO LITTLE HERE. Almost everything the column needs already
 * exists and is reused by name: sokkara.css supplies the printed grid and caps
 * every svg at `max-width: 100%`; section 9 above turns the whole table into one
 * card per meal below 640px, labelling each value from `attr(data-label)` — the
 * same localised string the <th> carries, so the labels follow the language with
 * no second copy in CSS.
 *
 * THIS COLUMN PRINTS, and that is the one thing to preserve. The old spark was
 * a ninth column on a table whose proven A4 budget was eight, so it carried
 * .noprint and existed on screen only. This one REPLACES two columns rather
 * than adding a ninth, and the shape of a response is the reason the section is
 * on the sheet a doctor is handed. Do not add .noprint here.
 */

/* The cell. Fixed to the drawing's own width so sixty rows share one x-axis
 * down the page — a column that reflowed to its content would put minute 0 at a
 * different offset on every row and quietly destroy the comparison the column
 * exists to make. Never wraps, for the same reason. */
.meallog th.trace,
.meallog td.trace {
  width: 132px;
  min-width: 132px;
  white-space: nowrap;
}

/* `.meallog td` is vertical-align: top, which is right for text and leaves the
 * drawing sitting high against neighbours two and three lines tall. A little
 * block margin rather than a vertical-align override: the report stylesheet's
 * `svg { display: block }` would make one meaningless anyway. */
.meallog .trace svg.trace { margin-block: 3px; }

/* The sentence that stands in for the drawing when the sensor recorded nothing.
 * Deliberately not styled to look like a value. */
.meallog .trace-none { font-size: 0.82rem; }

/* The merged meal cell: the bucket name and what was actually eaten, which were
 * two columns until the drawing needed the width. The name reads as a label for
 * the text under it rather than as a second field. */
.meallog td.txt .mealname {
  display: block;
  font-weight: 600;
}

.meallog td.txt .mealwhat { display: block; }

/* The description's own row, spanning every column.
 *
 * NO TOP BORDER AND NO TOP PADDING, so it reads as the continuation of the row
 * above rather than as a record of its own. The meal row draws the rule that
 * separates one meal from the next; this sits inside that boundary. */
/* THE MEAL ROW HANDS ITS BOTTOM RULE DOWN. Every cell carries
 * `border-bottom: 1px` from the base table rule, so a meal and the description
 * belonging to it were separated by a line - which said they were two records
 * when they are one. The description row is the real end of the meal, so it
 * keeps the rule and the row above it gives its up. */
.meallog tr.mealrow > td { border-block-end: 0; }

.meallog tr.descrow > td {
  border-block-start: 0;
  padding-block-start: 0;
  padding-block-end: 10px;
  text-align: start;

  /* IT MUST WRAP, and this is not a nicety.
   *
   * The base table rule in sokkara.css sets white-space to nowrap on every th
   * and td, which td.txt opted out of - and the description lived in td.txt.
   * On a cell of its own with no class it inherited nowrap again, and a single
   * long meal on one unbreakable line stretched the WHOLE table from 903px to
   * 1563px: every other column pushed off the screen behind a horizontal
   * scrollbar, to widen the one thing that was already readable.
   *
   * Measured on the live report, not reasoned about. */
  white-space: normal;
}

/* An "other" row is muted and italic across the whole row (see .notmeal); the
 * bucket cell there says "not counted as a meal", which is a statement about
 * the row and not a name, so it drops the bold that would make it look like
 * one. */
.meallog tr.notmeal td.txt .mealname { font-weight: 400; }

@media screen and (max-width: 640px) {
  /* Stacked, the drawing gets the full width of the card with its label above
   * rather than beside — same treatment as the free-text cell, and for the same
   * reason: at 132px it is the widest thing in the card and a flex row would
   * squeeze the label to nothing. The fixed width above is released here; on a
   * phone there is no column of rows to compare across, because each card is
   * read on its own. */
  .meallog th.trace,
  .meallog td.trace {
    width: auto;
    min-width: 0;
    display: block;
  }
  .meallog tr.mealrow td.trace::before { display: block; margin-block-end: 2px; }

  /* The correction row's trace cell is empty by design — a correction's
   * response is the tail of the meal's own drawing one row above. Section 9
   * already hides `td:empty` in a stacked card; this is only here to say that
   * the emptiness is intended, so nobody "fixes" it by filling it in. */
}
/* ===========================================================================
 * 11. RANGE — the date-range picker above every data page
 * ===========================================================================
 * Drawn by Layout/MainLayout.razor via Components/Shell/RangePicker.razor, on
 * the two routes that draw a report and nowhere else. Like the NAV bar above it
 * this sits OUTSIDE .wrap — full-bleed, its content aligned to the same 1100px
 * column the report uses — so nothing in the report stylesheet reaches it and
 * everything it needs is here.
 *
 * AFTER SECTIONS 8 AND 9 and that is not a mistake. Those two are the phone
 * pass, "last so it outranks the seven above"; nothing in either names a class
 * defined here, so this block raises no floor of theirs and overrides nothing.
 * It carries its own 44px targets instead, for the same hand and the same
 * measured reason — see below.
 *
 * The bar carries .noprint, which sokkara.css already hides inside its
 * @media print block. That matters more here than on the menu: this control is
 * on the CLINIC page too, and that page is a print artefact — a row of buttons
 * has no business on the sheet a doctor is handed, which already prints the
 * period it covers in its own header.
 */

.rangebar {
  background: var(--bg);
  border-block-end: 1px solid var(--border);
  font-size: 0.95rem;
}

/* The report stylesheet selects Arabic typography with `.wrap[lang="ar"]`, and
   this bar is outside .wrap by design — so the Cairo stack has to be named
   again here or the Arabic picker renders in the Latin UI font above an Arabic
   page set in Cairo. Same stack, same order, same fallbacks as .appnav[lang="ar"]
   in section 2: if it is ever changed there, change it here too. */
.rangebar[lang="ar"] {
  font-family: 'Cairo', "Segoe UI", Tahoma, "Noto Sans Arabic", "Noto Naskh Arabic",
               "Geeza Pro", "Arabic Typesetting", system-ui, sans-serif;
}

.range-inner {
  max-width: 1100px;
  margin-inline: auto;
  /* 20px matches .wrap's inline padding and .appnav-inner's, so the first
     button lines up with the report's <h1> underneath it. */
  padding: 8px 20px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
}

/* .wrap drops to 14px of inline padding at 640px; the bar follows it, exactly as
   .appnav-inner does, so the menu, the picker and the page keep one edge. */
@media screen and (max-width: 640px) {
  .range-inner { padding-inline: 14px; }
}

.range-presets,
.range-custom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
}

/* THE 44px FLOOR, unconditionally and not only on a phone. Section 8 holds the
   three always-on-screen nav controls to 44px at 640px and states the hand it
   was measured for — one hand, a ~375px phone, often at 03:00. These four
   buttons and two date fields are that same hand's, they are the first thing
   under those three, and a control that is 44px on a phone and 34px on a tablet
   is two controls. The row costs nothing to keep at 44 on a desktop.

   inline-flex + centring, not padding, for the same reason section 8 gives:
   padding would push the label off centre as it grows the box. */
.range-preset,
.range-apply {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: 44px;
  padding: 6px 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}
.range-preset:hover,
.range-apply:hover { border-color: var(--accent); color: var(--accent); }
.range-preset:focus-visible,
.range-apply:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.range-preset[disabled],
.range-apply[disabled] { opacity: 0.6; cursor: default; }
.range-preset[disabled]:hover,
.range-apply[disabled]:hover { border-color: var(--border); color: var(--text); }

/* The selected window. Drawn from aria-pressed rather than from a class of its
   own, so what a screen reader announces and what the eye sees cannot disagree
   — the same reasoning .explain-toggle and .appnav-item[aria-current] apply.

   var(--surface) for the text, not #fff: in dark mode the accent is a pale blue
   and white on it is unreadable, while --surface is the near-black the bar is
   already sitting on. .login-submit says the same thing. */
.range-preset[aria-pressed="true"] {
  color: var(--surface);
  background: var(--accent);
  border-color: var(--accent);
}
.range-preset[aria-pressed="true"]:hover { color: var(--surface); }

.range-field {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.range-label {
  color: var(--muted);
  /* 0.85rem is the floor section 8 raises the report's small labels to, and it
     is 14.45px against the 17px root. Set here rather than inherited so the
     two words beside the date fields are not the smallest text on the page. */
  font-size: 0.85rem;
  font-weight: 600;
}

.range-date {
  min-block-size: 44px;
  padding: 6px 10px;
  font: inherit;
  font-size: 0.9rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  /* A date control has an intrinsic min-content width of its own segments, and
     inside a flex row that floor is what pushes a 375px screen into a sideways
     scroll. Released here so the pair wraps instead. */
  min-inline-size: 0;
}
.range-date:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.range-date[disabled] { opacity: 0.6; }

/* The four sentences under the row: what is wrong, what is loading, what came
   back, and how long the choice lasts. flex-basis 100% puts each on its own
   line under the controls rather than beside them — at 375px there is no
   beside. */
.range-problem,
.range-busy,
.range-actual,
.range-note {
  flex-basis: 100%;
  margin: 0;
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--muted);
}

/* A COLOUR AND A MESSAGE, never a colour alone — the ENTRY section says the
   same thing about its field errors, and for the same reader: red on a border
   is invisible to somebody who cannot see red and to anybody reading on a phone
   at minimum brightness in daylight. The words are the signal; the edge is the
   emphasis. Built from --warn-border, which sokkara.css defines in all three
   palettes, so there is no new colour to keep in step. */
.range-problem {
  color: var(--text);
  font-weight: 600;
  border-inline-start: 3px solid var(--warn-border);
  padding-inline-start: 8px;
}

/* What came back, when it is not what was asked for. Not muted into the
   background: the clamp to the first CGM reading is the one thing on this bar a
   reader must not miss, because it means the chart below covers less than the
   label says. */
.range-actual { color: var(--text); }

/* ===========================================================================
 * 12. CHARTS — reading the numbers off a chart, by hover, by tap and by tab
 * ===========================================================================
 * Owned by Components/Charts/**. Six drawings — the glucose profile, the two
 * day-by-day layouts, the meal curves, where lows cluster, and the range bar —
 * each grew a layer that names the values under the pointer. A chart a parent
 * cannot read the numbers off is a picture, and the numbers are the point.
 *
 * ALMOST ALL OF IT IS IN THE SVG, NOT HERE. The panel is an SVG <g> drawn
 * inside the chart's own viewBox, in the chart's own user units, with its paint
 * taken from var(--surface) / var(--border) / var(--text) as presentation
 * attributes. That is deliberate: an HTML panel positioned over an SVG needs
 * the element's rendered size to convert coordinates into pixels, and getting
 * that is JavaScript. What is left for this file is the three things a
 * presentation attribute cannot say — a cursor, a focus ring, and how a touch
 * screen should treat a tap.
 *
 * NOTHING HERE HIDES THE READOUT FOR PRINT, and that is not an omission. Every
 * group it draws carries .noprint, which sokkara.css already hides inside its
 * @media print block; a second rule for the same thing here would be the
 * duplicate this file's own rules forbid. Nothing should be showing at print
 * time anyway — but state is state, and the clinic page is a print artefact.
 */

/* One invisible full-height rect per datum, laid over the drawing. It is what
   makes a 2.4-unit line catchable by a thumb.
 *
 * touch-action: manipulation, and NOT `none`. `none` would take the chart out
 * of the page's scrolling entirely — a finger that started its swipe on the
 * chart would find the page stuck, and on a phone the charts are most of the
 * page. `manipulation` keeps panning and pinch-zoom and gives up only the
 * double-tap zoom, whose 300ms wait is what otherwise sits between a tap and
 * its reading.
 *
 * The tap highlight is cleared because the browser's own is a grey rectangle
 * the size of the hit target — a full-height column flashing grey over a
 * medical chart, which reads as something having gone wrong. The readout
 * appearing is the feedback.
 *
 * pointer-events is also set as a presentation attribute on every one of these;
 * it is repeated here because fill="none" means these rects paint nothing, and
 * an element that paints nothing is not hit-testable under the default
 * `visiblePainted`. Both spellings, so neither being lost silently disarms the
 * whole layer. */
.chart-hit {
  pointer-events: all;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
}

/* THE CHART IS NOT MOUSE-ONLY. Every hit target takes focus, so the whole
   reading is available from the keyboard — tab through the day, Escape to
   close — and each one carries the same sentence as an aria-label that a
   sighted reader gets from the panel.
 *
 * :focus-visible only, so a mouse click does not leave a ring behind on a
 * chart the reader has already moved away from; a browser that does not know
 * :focus-visible keeps its own default ring rather than losing the indicator.
 * The offset is NEGATIVE, drawing the ring just inside the target's own edge:
 * an outset ring on a target that spans the full plot height would be clipped
 * by the viewBox at the top and bottom, which is a focus indicator with two of
 * its four sides missing. */
.chart-hit:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* The panel, and the marks it puts on the drawing beside it — the guide line,
   the median dot, the quartile ticks.
 *
 * pointer-events: none is load bearing, not tidiness. Without it the panel
 * appearing under the cursor takes the pointer off the hit target that opened
 * it, which fires pointerleave, which closes the panel, which puts the cursor
 * back over the target — a flicker loop at the frame rate. Set as an attribute
 * on both groups as well, for the same both-spellings reason as above. */
.chart-readout,
.chart-marks {
  pointer-events: none;
}

/* --- Food components and the two-hour change (ComponentEffectsTable.razor) ---
 * Styled AS the meal log (household request, 23/9): the table is a .meallog
 * and takes everything from it. Only what the log has no equivalent for is
 * here - the count box above the table, the wrapping MEALS cell, and the total
 * in bold because the rows are ordered by it.
 */
.compfilter { margin: 6px 0 10px; font-size: 0.92rem; color: var(--muted); }
.compfilter label { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.compfilter input {
  width: 4.2em; padding: 4px 8px; font: inherit; color: var(--text);
  background: var(--surface); border: 1px solid var(--border); border-radius: 6px;
  text-align: center; font-variant-numeric: tabular-nums;
}
.compfilter input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.compeff td.meals { white-space: normal; }
.compeff td.total { font-weight: 700; }
/* Seven columns on A4 portrait only fit if the long position headers ("1st
   meal 2 h") may break onto two lines; the meal log's print rule keeps its
   numeric headers on one, hence !important to win over it here. */
.meallog.compeff th, .meallog.compeff th.num { white-space: normal !important; }

/* ===========================================================================
 * 13. REDESIGN — mobile first
 * ===========================================================================
 * The household asked (2026-10-04) for an app simple enough to hand to Dodo's
 * mother, who reads it on her phone and nowhere else: four tabs that are the
 * whole of daily use, one place for everything else, Arabic first, and every
 * sentence of explanation behind a small (i) so a page shows its numbers.
 *
 * WRITTEN FOR A 360px PHONE FIRST, and widened where there is room. Same rules
 * as the rest of this file: tokens from sokkara.css, logical properties only,
 * no literal colour where a token means the same thing.
 *
 * LAST IN THE FILE, so on equal specificity it wins over the older sections it
 * restyles (the range bar, the section cards, the meal-log add row). The bars,
 * the sheets and the buttons are all .noprint in the markup.
 */

:root {
  --tabbar-h: 62px;
  --sheet-shadow: 0 -12px 40px rgba(15, 23, 42, 0.22);
  --card-radius: 16px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --sheet-shadow: 0 -12px 40px rgba(0, 0, 0, 0.6); }
}
:root[data-theme="dark"] { --sheet-shadow: 0 -12px 40px rgba(0, 0, 0, 0.6); }

/* --- the top bar ------------------------------------------------------------
   A <header>, and sokkara.css gives every header a 28px bottom margin for the
   report's own title block. Reset here, or the bar floats off the page. */
.appbar {
  margin: 0;
  background: var(--surface);
  border-block-end: 1px solid var(--border);
}
/* Outside .wrap, so the Arabic type has to be named again - same stack as
   sokkara.css's .wrap[lang="ar"]. */
.appbar[lang="ar"] {
  font-family: 'Cairo', "Segoe UI", Tahoma, "Noto Sans Arabic", "Noto Naskh Arabic",
               "Geeza Pro", "Arabic Typesetting", system-ui, sans-serif;
}
.appbar-inner {
  max-width: 1100px;
  margin-inline: auto;
  min-block-size: 54px;
  padding: 6px 12px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.appbar-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 1.1rem;
  font-weight: 800;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.appbar-actions { display: flex; align-items: center; gap: 6px; flex: none; }
.appbar-lang {
  display: inline-flex;
  align-items: center;
  min-block-size: 40px;
  padding: 4px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--accent);
  font-size: 0.85rem;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.appbar-lang:hover { border-color: var(--accent); }
.appbar-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 44px;
  block-size: 44px;
  border-radius: 12px;
  color: var(--muted);
}
.appbar-icon:hover,
.appbar-icon[aria-current="page"] {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 11%, transparent);
}
.appbar-lang:focus-visible,
.appbar-icon:focus-visible,
.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --- the tabs ----------------------------------------------------------------
   ON A PHONE they are fixed to the bottom edge, where a thumb is, above the
   phone's own home indicator (viewport-fit=cover in index.html is what makes
   env(safe-area-inset-bottom) report it). Five equal columns, icon over word. */
.tabbar {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: 30;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  background: var(--surface);
  border-block-start: 1px solid var(--border);
  padding-block-end: env(safe-area-inset-bottom);
  box-shadow: 0 -4px 16px rgba(15, 23, 42, 0.06);
}
.tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-block-size: var(--tabbar-h);
  padding: 6px 2px 5px;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.74rem;
  font-weight: 700;
  line-height: 1.2;
  -webkit-tap-highlight-color: transparent;
}
.tab .ico {
  box-sizing: content-box;
  padding: 3px 16px;
  border-radius: 999px;
}
.tab[aria-current="page"] { color: var(--accent); }
.tab[aria-current="page"] .ico { background: color-mix(in srgb, var(--accent) 15%, transparent); }
.tab-label { max-inline-size: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Room under the page for the fixed bar - only on a page that has one (the
   sign-in page does not). */
@media screen and (max-width: 899px) {
  body:has(.tabbar) { padding-block-end: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); }
  /* A sticky Save bar would otherwise sit underneath the tabs. Inside a dialog
     the tabs are behind the backdrop, so there it stays at the bottom edge. */
  body:has(.tabbar) .wrap .entry-actions {
    inset-block-end: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  }
  body:has(.tabbar) .dlg .entry-actions { inset-block-end: 0; }
}

/* ON A WIDE SCREEN the same five links move into the top bar. */
@media screen and (min-width: 900px) {
  .appbar-title { flex: 0 1 auto; }
  .tabbar {
    position: static;
    display: flex;
    gap: 4px;
    margin-inline: auto;
    padding: 0;
    background: none;
    border: 0;
    box-shadow: none;
  }
  .tab {
    flex-direction: row;
    gap: 8px;
    min-block-size: 44px;
    padding: 6px 14px;
    border-radius: 999px;
    font-size: 0.95rem;
  }
  .tab .ico { padding: 0; }
  .tab:hover { color: var(--accent); }
  .tab[aria-current="page"] { background: color-mix(in srgb, var(--accent) 12%, transparent); }
  .tab[aria-current="page"] .ico { background: none; }
}

/* --- the page --------------------------------------------------------------- */
@media screen and (max-width: 640px) {
  .wrap { padding: 12px 10px 28px; }
  footer { margin-top: 22px; font-size: 0.8rem; }
}

/* THE TITLE BLOCK IS FOR PAPER AND FOR A SCREEN READER. The bar above names the
   page on screen; this header is visually hidden there (not display:none - it
   is still announced, and still where FocusOnNavigate lands), and prints as the
   sheet's title block exactly as it always has. */
@media screen {
  .page-head {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    margin: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* --- cards ------------------------------------------------------------------ */
section { border-radius: var(--card-radius); }
section > h2 { margin-block-end: 10px; }
h2 .info-btn,
h3 .info-btn,
h4 .info-btn { margin-block: -6px; }
.sec-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin-block-end: 10px;
}
.sec-head h2 { margin: 0; }
.sec-actions .exportbar { margin: 0; }
@media screen and (max-width: 640px) {
  section { padding: 14px 12px; margin-bottom: 12px; }
  h2 { font-size: 1.06rem; }
}

/* --- the (i) and its sheet ----------------------------------------------------
   The native Popover API, as the glossary badges always were. Hidden where the
   API is missing rather than left as a dead button. */
.info-btn,
.info-pop { display: none; }

@supports selector(:popover-open) {
  .info-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    inline-size: 32px;
    block-size: 32px;
    padding: 0;
    margin-inline-start: 2px;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    vertical-align: middle;
    -webkit-tap-highlight-color: transparent;
    /* Inherited from a heading or a table header otherwise. */
    font: inherit;
    letter-spacing: normal;
    text-transform: none;
  }
  .info-btn:hover { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
  .info-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
  .info-ico { inline-size: 19px; block-size: 19px; }

  /* The sheet - and the glossary badge's own popover, so the two look and
     behave as one thing. */
  .info-pop,
  .tip-pop {
    padding: 0;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    box-shadow: var(--sheet-shadow);
    /* Reset whatever the heading or table header around the trigger imposed. */
    font-size: 0.97rem;
    font-weight: 400;
    line-height: 1.8;
    text-align: start;
    text-transform: none;
    letter-spacing: normal;
    white-space: normal;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .info-pop:popover-open,
  .tip-pop:popover-open { display: block; }
  .info-pop::backdrop,
  .tip-pop::backdrop { background: rgba(15, 23, 42, 0.45); }

  /* A PHONE GETS A BOTTOM SHEET: full width, from the bottom edge up, where the
     thumb that opened it already is. The UA centres a popover with inset:0 and
     margin:auto; both are taken back here. */
  @media screen and (max-width: 640px) {
    .info-pop,
    .tip-pop {
      position: fixed;
      inset: auto 0 0 0;
      margin: 0;
      inline-size: 100%;
      max-inline-size: none;
      max-block-size: 78vh;
      border-radius: 20px 20px 0 0;
      border-block-end: 0;
      padding-block-end: env(safe-area-inset-bottom);
    }
  }
  @media screen and (min-width: 641px) {
    .info-pop,
    .tip-pop {
      inline-size: min(92vw, 460px);
      max-block-size: 78vh;
      border-radius: 16px;
    }
  }
}
.info-head {
  position: sticky;
  inset-block-start: 0;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px 10px;
  background: var(--surface);
  border-block-end: 1px solid var(--border);
}
.info-head h4 { margin: 0; font-size: 1.02rem; font-weight: 800; line-height: 1.5; }
.info-close {
  flex: none;
  inline-size: 38px;
  block-size: 38px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
  color: var(--muted);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}
.info-close:hover { color: var(--accent); border-color: var(--accent); }
.info-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.info-body { padding: 12px 16px 20px; }
.info-body p { margin: 0 0 10px; color: var(--muted); }
.info-body p:last-child { margin-block-end: 0; }
.info-body strong { color: var(--text); }
.info-body .info-term { margin-block-start: 14px; color: var(--text); font-weight: 700; }
/* A section note drawn inside a sheet is a paragraph like the rest. */
.info-body .section-note { margin: 0 0 10px; font-size: inherit; }

/* --- one-line notices ----------------------------------------------------------
   A safety sentence that must stay on screen, in one line, with the rest of it
   behind its (i): "recorded, not applied", "enter each dose once", "for
   reference only". The warn palette, because they mean "read this first". */
.notice-line {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px 6px;
  margin-block-end: 12px;
  padding-block: 6px;
  padding-inline: 12px 6px;
  border: 1px solid var(--warn-border);
  border-inline-start-width: 4px;
  border-radius: 12px;
  background: var(--warn-bg);
  font-size: 0.92rem;
  line-height: 1.5;
}
.notice-line strong { font-weight: 700; }
.notice-line .printonly { display: none; }

.manual-note {
  display: flex;
  align-items: center;
  gap: 2px;
  margin: 0 0 10px;
  font-size: 0.88rem;
}

/* The disclaimer at the foot of the home page: its title on one line, the rest
   behind its (i) on screen and printed in full on paper. */
.disclaimer {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px 6px;
  padding-block: 8px;
  padding-inline: 14px 6px;
  font-size: 0.9rem;
}
.disclaimer strong { display: inline; margin: 0; }

/* --- the reading now ------------------------------------------------------------ */
.now-card {
  --now: var(--entry-ok);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  margin-block-end: 12px;
  padding: 16px 16px 12px;
  border: 1px solid var(--border);
  border-radius: 20px;
  background: var(--surface);
  text-align: center;
}
.now-card[data-band="high"],
.now-card[data-band="very_high"] { --now: var(--entry-correction); }
.now-card[data-band="low"],
.now-card[data-band="very_low"] {
  --now: var(--entry-danger);
  border-color: var(--entry-danger);
  background: color-mix(in srgb, var(--entry-danger) 8%, var(--surface));
}
/* The number and its arrow read left to right in both languages, as on every
   CGM app - "↗ 142" in Arabic order would be a different arrow. */
.now-main {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  direction: ltr;
}
.now-value {
  color: var(--now);
  font-size: 3.6rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.now-unit { align-self: flex-end; padding-block-end: 6px; color: var(--muted); font-size: 0.85rem; font-weight: 700; }
.now-arrow { color: var(--now); font-size: 2.4rem; font-weight: 700; line-height: 1; }
.now-sub {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px;
  color: var(--muted);
  font-size: 0.95rem;
}
.now-delta { color: var(--text); font-weight: 800; }
/* STALE: the number stays readable - a parent needs to see what it WAS - but
   fades, and the words "no new reading" lead the line under it. */
.now-stale .now-value,
.now-stale .now-unit { opacity: 0.5; }
.now-stale-word { color: var(--entry-correction); font-weight: 800; }
.now-none { padding: 18px 16px; }

/* --- chips: the hours above the graph, the range above a report ---------------- */
.chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-block-end: 10px;
}
.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: 40px;
  padding: 6px 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 0.88rem;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.chip:hover { border-color: var(--accent); }
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.chip[aria-pressed="true"] {
  color: var(--surface);
  background: var(--accent);
  border-color: var(--accent);
}
.chip[disabled] { opacity: 0.6; cursor: default; }

/* The range bar, compact: one row of chips that scrolls sideways on the
   narrowest phones rather than wrapping into a second and third row. */
.rangebar { background: transparent; border-block-end: 0; }
.range-inner { padding: 10px 10px 0; gap: 6px; }
.range-presets.chips {
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  margin: 0;
  padding-block-end: 2px;
  max-inline-size: 100%;
}
.range-presets.chips::-webkit-scrollbar { display: none; }
.range-preset.chip { min-block-size: 40px; padding: 6px 14px; flex: none; font-size: 0.88rem; }
.range-preset.chip[aria-pressed="true"] { color: var(--surface); background: var(--accent); border-color: var(--accent); }
.range-custom { padding-block-start: 6px; }
@media screen and (max-width: 640px) {
  .range-inner { padding-inline: 10px; }
}

/* --- the graph ------------------------------------------------------------------- */
.graph-card .chips { margin-block-end: 8px; }
.graph-note { margin: 10px 0; }
.glucose-graph { inline-size: 100%; }
.graph-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 16px;
  margin: 6px 0 8px;
  color: var(--muted);
  font-size: 0.82rem;
}
.graph-legend span { display: inline-flex; align-items: center; gap: 6px; }
/* Pinned colours, as in the drawing - see GlucoseGraph.razor. */
.lg { display: inline-block; inline-size: 11px; block-size: 11px; }
.lg-carbs { border-radius: 50%; background: #15803d; }
.lg-insulin {
  inline-size: 0;
  block-size: 0;
  border-inline: 6px solid transparent;
  border-block-start: 10px solid #0369a1;
}
.lg-rescue { inline-size: 9px; block-size: 9px; border-radius: 50%; border: 2px solid #dc2626; }
/* A reading typed in by hand - GlucoseGraph.CheckColor, a diamond like the marker. */
.lg-check { inline-size: 9px; block-size: 9px; background: #7c3aed; transform: rotate(45deg); }
.lg-check-sensor { background: transparent; border: 2px solid #7c3aed; box-sizing: border-box; }
/* A feeling marked by hand - GlucoseGraph.FeelingColor, the dot that tops its line. */
.lg-feeling { inline-size: 9px; block-size: 9px; border-radius: 50%; background: #db2777; }
/* A sensor change - GlucoseGraph.SensorColor, the square that tops its line. */
.lg-sensor { inline-size: 8px; block-size: 8px; background: #0d9488; }

/* --- downloads and print ---------------------------------------------------------- */
.exportbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-block: 2px 12px;
}
.xbtn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-block-size: 38px;
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--accent);
  font: inherit;
  font-size: 0.84rem;
  font-weight: 700;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.xbtn:hover { border-color: var(--accent); }
.xbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.page-tools {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-block-end: 12px;
}
.page-tools .exportbar { margin: 0; }
.page-tools .btn { gap: 6px; }

/* --- the meal log's add row --------------------------------------------------------
   Four buttons, two by two on a phone, each in the colour of the record it adds -
   the same meal green, bolus blue and correction amber as the forms they open. */
.log-add {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin-block-end: 10px;
}
/* SEVEN BUTTONS since 2026-10-13: four columns from 700px, so the four food and
   insulin buttons fill the first row and the reading, the feeling and the
   sensor change the second - six columns put the seventh alone on a row of
   its own. */
@media screen and (min-width: 700px) {
  .log-add { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* The fifth and sixth, a glucose reading and a feeling (2026-10-11): neither
   food nor insulin, so they share the third row on a phone and the four food
   and insulin buttons keep their two-by-two. */
.addbtn[data-kind="glucose"] { --k: var(--entry-glucose); }
.addbtn[data-kind="feeling"] { --k: var(--entry-feeling); }
/* The seventh, a sensor change (2026-10-13): the whole fourth row on a phone,
   one cell beside the other two from 700px. */
.addbtn[data-kind="sensor"] { --k: var(--entry-sensor); grid-column: 1 / -1; }
@media screen and (min-width: 700px) {
  .addbtn[data-kind="sensor"] { grid-column: auto; }
}

/* --- glucose readings typed in by hand, on the meals page ----------------------------
   Components/Clinic/ManualReadingsList.razor. Shut by default, one line. */
.readings-fold { margin-block-end: 12px; }
.readings-fold summary .fold-title { font-weight: 700; }
.readings {
  list-style: none;
  margin: 0;
  padding: 0;
}
.reading {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 8px;
  padding-block: 6px;
  line-height: 1.35;
  border-block-end: 1px solid var(--border);
}
.reading:last-child { border-block-end: 0; }
.reading-when { color: var(--muted); min-inline-size: 3.5em; }
.reading-value { font-weight: 800; color: var(--entry-glucose); }
.reading-type { font-weight: 600; }
.reading-notes { flex-basis: 100%; color: var(--muted); overflow-wrap: anywhere; }
.reading .row-actions { margin-inline-start: auto; }
/* Feelings marked by hand - Components/Clinic/FeelingList.razor, the same list. */
.feeling-label { font-weight: 800; color: var(--entry-feeling); }
.addbtn {
  --k: var(--entry-meal);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-block-size: 48px;
  padding: 8px 10px;
  border: 1px solid color-mix(in srgb, var(--k) 45%, transparent);
  border-radius: 12px;
  background: color-mix(in srgb, var(--k) 10%, var(--surface));
  color: var(--k);
  font: inherit;
  font-size: 0.92rem;
  font-weight: 800;
  line-height: 1.25;
  text-align: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.addbtn[data-kind="bolus"] { --k: var(--entry-bolus); }
.addbtn[data-kind="correction"] { --k: var(--entry-correction); }
.addbtn:hover { filter: brightness(1.05); }
.addbtn:focus-visible { outline: 2px solid var(--k); outline-offset: 2px; }

/* --- the More list ------------------------------------------------------------------ */
.more-group {
  margin: 18px 8px 8px;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: normal;
}
.morelist > .more-group:first-child { margin-block-start: 4px; }
.morelist ul {
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--card-radius);
  overflow: hidden;
}
.morelist li + li { border-block-start: 1px solid var(--border); }
.more-row {
  display: flex;
  align-items: center;
  gap: 14px;
  inline-size: 100%;
  min-block-size: 56px;
  padding: 10px 16px;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  font-weight: 700;
  text-align: start;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.more-row:hover { background: var(--bg); }
.more-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.more-row > .ico:first-child { color: var(--accent); }
.more-name { flex: 1 1 auto; }
.more-row .ico-flip { color: var(--muted); }
.morelist .more-account { margin-block-start: 20px; }
.more-signout { color: var(--entry-danger); }
.more-signout > .ico:first-child { color: var(--entry-danger); }
.more-signout[disabled] { opacity: 0.6; cursor: default; }

/* A chevron points the way the reader reads - the direction the element itself
   computes, not any ancestor's attribute: <html> is dir="rtl" on every route, the
   English pages included, and an ancestor match mirrored their chevrons. */
.ico-flip:dir(rtl) { transform: scaleX(-1); }
.ico { flex: none; }

/* --- folds: a card shut until tapped ----------------------------------------------- */
.fold {
  margin-block-end: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--card-radius);
}
.fold > summary {
  display: flex;
  align-items: center;
  gap: 4px;
  min-block-size: 56px;
  padding: 8px 14px;
  font-size: 1.02rem;
  font-weight: 800;
  cursor: pointer;
  list-style: none;
  -webkit-tap-highlight-color: transparent;
}
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::marker { content: ""; }
.fold > summary::after {
  content: "\25be";
  margin-inline-start: auto;
  padding-inline-start: 8px;
  color: var(--muted);
  font-size: 0.85em;
}
.fold[open] > summary::after { content: "\25b4"; }
.fold > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--card-radius); }
.fold[open] > summary { border-block-end: 1px solid var(--border); }
.fold-body { padding: 12px; }

/* --- forms: the label and its (i) ------------------------------------------------------ */
.field-head {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-block-end: 6px;
}
.field-head > label {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  font-weight: 600;
}
legend .info-btn { margin-inline-start: 2px; }
.photo-row { display: flex; align-items: flex-start; gap: 4px; }
.photo-row > :first-child { flex: 1 1 auto; min-width: 0; }
.hint-row { display: flex; margin-block: -4px 6px; }
.visit-group > summary .info-btn { align-self: center; }

/* The components table's count box shares its row with the CSV button. */
.compfilter {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 6px 10px;
}
.compfilter .exportbar { margin: 0; }

/* --- settings ------------------------------------------------------------------------- */
.settings form > section .field:last-child { margin-block-end: 0; }
.settings-stamp { margin: 8px 4px 16px; }
.settings-history { margin: 0; padding: 0; list-style: none; }
.settings-history li { padding-block: 10px; border-block-start: 1px solid var(--border); }
.settings-history li:first-child { border-block-start: 0; padding-block-start: 0; }
.settings-history strong { display: block; margin-block-end: 2px; }

/* --- print ----------------------------------------------------------------------------
   The bars, sheets and buttons are .noprint in the markup. What needs saying here:
   a shut fold is a table that did not print, so every fold opens on paper, and
   its summary prints as the card's heading. */
@media print {
  .appbar, .tabbar, .info-btn, .info-pop, .exportbar, .chips { display: none !important; }
  body { padding: 0 !important; }
  .fold::details-content { content-visibility: visible !important; block-size: auto !important; }
  .fold > *:not(summary) { display: block !important; }
  .fold { border-color: #b9c2cc; }
  .fold > summary::after { content: ""; }
  /* A card's title never prints alone at the foot of a sheet: it was a
     <section> with break-inside: avoid, and a fold has no such rule. */
  .fold > summary { break-after: avoid; page-break-after: avoid; }
  /* The legend swatches are backgrounds, which printers drop by default; the
     bars they name are SVG fills and print anyway. */
  .lg-high, .lg-veryhigh { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .notice-line { border-color: #777777; background: #ffffff; }
  .notice-line .printonly { display: inline; }
}

/* --- polish, measured at 320px -------------------------------------------------
   The four hour choices share one row as equal columns: as free chips the
   fourth wrapped onto a row of its own at 320px. */
.graph-card .chips {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.graph-card .chip { padding-inline: 4px; font-size: 0.85rem; }
/* The band legend: five swatches at full size and full gap took five lines of
   a 320px screen. Smaller, and closer together. */
.legend { gap: 4px 12px; font-size: 0.8rem; justify-content: center; }
/* The (i) of a notice stays on its line instead of dropping under the sentence. */
.notice-line > strong,
.notice-line > span:first-child,
.disclaimer > strong { flex: 1 1 0; min-width: 0; }

/* A chip that holds "14 يومًا" is a flex container, and a flex container trims
   the space between the number's isolate and the word - "14يومًا". The gap puts
   it back. */
.chip { gap: 0.3em; }

/* A date and a time inside a table cell never break in the middle: "2026-09-"
   on one line and "25 03:20" on the next is two half-values. */
table .num-inline { white-space: nowrap; }
/* Except a whole AAPS schedule, "00:00 → 0.05, 04:00 → 0.075, …", which is one
   isolate and wider than a phone: it wraps between its blocks. */
table td.schedule > .num-inline { white-space: normal; }

/* "Day by day, in more detail": on the Analysis tab every column is wanted, the
   phone included - mean, CV and coverage are the detail the page is for. The
   table scrolls sideways inside its own box if it must, never the page. */
.daily-detail th.opt,
.daily-detail td.opt { display: table-cell; }

/* --- review fixes, before the first deploy of the redesign ---------------------- */

/* THE FALLBACK DIALOG ABOVE THE TABS. A dialog opened by [open] rather than
   showModal() is not in the top layer, and the fixed tab bar (z-index 30)
   painted over its Save bar - a tap meant for Save navigated away and lost the
   record. The modal case is in the top layer and unaffected. */
.dlg[open] { z-index: 40; }

/* WITHOUT THE POPOVER API THE SHEETS ARE PRINTED IN PLACE. Hiding the (i) there
   left every explanation - including the second half of the safety notices -
   with no way to reach it. Inline text is the page of paragraphs the redesign
   undid, but only on a browser too old to do better (Safari before 17). */
@supports not selector(:popover-open) {
  .info-pop {
    display: block;
    position: static;
    margin: 4px 0 8px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--muted);
    font-size: 0.9rem;
    font-weight: 400;
    line-height: 1.7;
    text-align: start;
    text-transform: none;
    letter-spacing: normal;
    white-space: normal;
  }
  .info-pop .info-head { display: none; }
  .info-pop .info-body p { margin: 0 0 4px; }
}

/* THE RANGE CHIPS WRAP on a phone. One scrolling row with its scrollbar hidden
   put "Custom" - the only way to choose dates - off the edge of a 360px screen
   with nothing to say the row scrolled. */
@media screen and (max-width: 480px) {
  .range-presets.chips { flex-wrap: wrap; overflow-x: visible; }
  .range-preset.chip { padding-inline: 11px; }
}

/* THE NOTCH, IN LANDSCAPE. viewport-fit=cover (for the tab bar's bottom inset)
   also stops Safari insetting the sides, so the page pads itself. Each max()
   starts from the padding that width already has, so where there is no notch
   nothing moves; screen only, so the printed margins are untouched. */
@media screen and (orientation: landscape) and (min-width: 641px) {
  .appbar-inner {
    padding-left: max(12px, env(safe-area-inset-left));
    padding-right: max(12px, env(safe-area-inset-right));
  }
  .range-inner {
    padding-left: max(10px, env(safe-area-inset-left));
    padding-right: max(10px, env(safe-area-inset-right));
  }
  .wrap {
    padding-left: max(20px, env(safe-area-inset-left));
    padding-right: max(20px, env(safe-area-inset-right));
  }
}

/* The trend forecast's swatch: the wedge itself in miniature - translucent grey
   with a dashed edge, never a band colour (GlucoseGraph.razor). */
.lg-fc {
  inline-size: 14px;
  block-size: 10px;
  border-radius: 2px;
  border: 1.2px dashed var(--muted);
  background: color-mix(in srgb, var(--muted) 18%, transparent);
}

/* The forecast badges under the home graph (ForecastBadges.razor): the wedge's
   grey and dashed edge, big enough to read at arm's length on a phone. */
.fc-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-block: 10px 4px;
}
.fc-badge {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  flex: 1 1 0;
  min-inline-size: 120px;
  padding: 8px 12px;
  border: 1.5px dashed var(--muted);
  border-radius: 14px;
  background: color-mix(in srgb, var(--muted) 10%, transparent);
  line-height: 1.25;
}
.fc-badge-t { font-size: 0.82rem; color: var(--muted); }
.fc-badge-v {
  font-size: 1.3rem;
  font-weight: 800;
  color: var(--text);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.fc-badge-off {
  border: 1px solid var(--border);
  background: none;
  color: var(--muted);
  font-size: 0.88rem;
  text-align: center;
}
.fc-badge-u { font-size: 0.75rem; color: var(--muted); }
@media print { .fc-badges { display: none !important; } }

/* Active insulin and carbs left now (ActiveNow.razor): solid, tinted in the
   marks' own pinned colours, so they never read as part of the grey forecast. */
.act-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-block: 8px 4px;
}
.act-badge {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  flex: 1 1 0;
  min-inline-size: 120px;
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-inline-start-width: 4px;
  border-radius: 12px;
  line-height: 1.25;
}
.act-insulin { border-inline-start-color: #0369a1; background: color-mix(in srgb, #0369a1 7%, transparent); }
.act-carbs { border-inline-start-color: #15803d; background: color-mix(in srgb, #15803d 7%, transparent); }
.act-badge-t { font-size: 0.82rem; color: var(--muted); }
.act-badge-v {
  font-size: 1.15rem;
  font-weight: 800;
  color: var(--text);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.act-badge-u { font-size: 0.75rem; color: var(--muted); }
.act-note {
  margin: 6px 0;
  padding: 6px 10px;
  border-radius: 10px;
  border: 1px solid var(--warn-border);
  background: var(--warn-bg);
  color: var(--text);
}
.lg-iob, .lg-cob { inline-size: 14px; block-size: 9px; border-radius: 2px; }
.lg-iob { border: 1.2px solid #0369a1; background: color-mix(in srgb, #0369a1 22%, transparent); }
/* Her sport and school bands on the home graph (2026-10-10) - the drawing's own
   pinned colours (GlucoseGraph.BandColor), a little stronger so a swatch this
   small still reads. */
.lg-act { inline-size: 14px; block-size: 10px; border-radius: 2px; }
.lg-sport { background: color-mix(in srgb, #ea580c 35%, transparent); }
.lg-school { background: color-mix(in srgb, #64748b 28%, transparent); }
.lg-cob { border: 1.2px solid #15803d; background: color-mix(in srgb, #15803d 22%, transparent); }
@media print { .act-badges { display: none !important; } }

/* Settings: the mealtime-insulin kinds are three choices, one per row on a
   phone (their names are long), and the "recorded only" line sits tight
   under the heading. */
.settings-kinds { grid-template-columns: 1fr; }
@media screen and (min-width: 641px) { .settings-kinds { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.settings-rapid-note { margin-block: 0 10px; }
/* Settings in four pages (2026-10-13).
   .settings-menu     the menu's rows - the More list's own rows (.morelist,
                      .more-row), with the notice line above and the stamp below.
   .settings-back     "<- Settings" above each page: a whole 44px target, in the
                      page's direction (the arrow is the translated one).
   .settings-changed  "changed since this page was opened": a refusal (the solid
                      .entry-trouble box) with its Reload under the sentence.
   .settings-meals    the two school meal times side by side, one per row on the
                      narrowest phones (.act-pair's own rule). */
.settings-menu { margin-block: 12px 4px; }
.settings-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-block-size: 44px;
  margin-block-end: 4px;
  color: var(--accent);
  font-weight: 600;
  text-decoration: none;
}
.settings-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.settings-changed .entry-actions { margin-block: 10px 0; }
.settings-meals { margin-block-start: 4px; }
@media print { .settings-back, .settings-changed { display: none !important; } }

/* Her photo (2026-10-13) - HerHeader on Home, HerPhotoSection on Settings >
   General.
   .her-card        Home's one row above the reading now: the photo, her name,
                    her age. 64 px tall - never a banner the number is pushed
                    under.
   .her-photo       a circle. The phone sends her photo already cut to its
                    centre square; object-fit: cover still crops one that is
                    not (the cut failed, and the scaled copy was sent).
   .her-initial     the circle where no photo is: her initial on a soft tint of
                    the accent - never a broken image.
   .her-photo-big   Settings' larger circle, 120 px, beside the buttons (under
                    them on the narrowest phones).
   .her-photo-ask   "Remove her photo from every phone?": the refusal's red
                    border around its two answers.
   Tokens only: the tint and the initial follow the accent, so a colour theme
   recolours them with the rest of the brand. Nothing here is printed. */
.her-card {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-block-end: 10px;
  padding-inline: 4px;
}
.her-photo {
  flex: none;
  inline-size: 64px;
  block-size: 64px;
  border-radius: 50%;
  border: 2px solid var(--surface);
  box-shadow: 0 0 0 1px var(--border);
  background: var(--surface);
  object-fit: cover;
  object-position: center;
}
.her-initial {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--accent) 14%, var(--surface));
  color: var(--accent);
  font-size: 1.7rem;
  font-weight: 800;
  line-height: 1;
}
.her-words { min-inline-size: 0; }
.her-name {
  margin: 0;
  font-size: 1.2rem;
  font-weight: 800;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
.her-age { margin: 2px 0 0; color: var(--muted); font-size: 0.92rem; }
.her-photo-edit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 16px;
}
.her-photo-big { inline-size: 120px; block-size: 120px; font-size: 3rem; transition: opacity 0.2s; }
.her-photo-big.is-busy { opacity: 0.6; }
.her-photo-controls { flex: 1 1 12rem; min-inline-size: 0; }
.her-photo-controls .photo-picker { margin-block: 0 8px; }
.her-photo-remove { inline-size: 100%; }
.her-photo-ask {
  margin-block: 12px 0;
  padding: 12px 14px;
  border: 1px solid var(--entry-danger);
  border-radius: 12px;
  background: var(--surface);
}
.her-photo-ask-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-block-start: 10px; }
.her-photo-ask-actions .btn { flex: 1 1 8rem; }
.her-photo-status .her-photo-said { margin-block: 12px 0; }
.her-photo-status .busy-line { margin-block: 12px 0; }
@media print { .her-card, .her-photo-section { display: none !important; } }

/* The two high bands' swatches under "where highs cluster", in the chart's own
   colours (ChartColors.High / VeryHigh). */
.lg-high { background: #d97706; border-radius: 2px; }
.lg-veryhigh { background: #b45309; border-radius: 2px; }

/* The date choices (2026-10-04): today, yesterday, week, 2 weeks, month,
   3 months, day, range. One row beside its (i) on a wide screen; on a phone a
   4 x 2 block of equal buttons - eight chips in a scrolling row put half of
   them off the edge, and wrapped freely they made ragged rows of three. */
.range-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.range-row > .range-presets.chips { flex: 1 1 auto; min-inline-size: 0; }
@media screen and (max-width: 640px) {
  .range-row > .range-presets.chips {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 6px;
    overflow: visible;
  }
  .range-row .range-preset.chip {
    justify-content: center;
    padding-inline: 4px;
    min-inline-size: 0;
    white-space: nowrap;
  }
}

/* The choices wrap at every width - between 641 and ~720 px the eight chips no
   longer fit one row, and a row that cannot shrink pushed the page sideways -
   and on the narrowest phones the (i) takes its own line, so each of the four
   cells is wide enough for "Yesterday" and "3 months". */
.range-row { min-inline-size: 0; flex-wrap: wrap; }
.range-row > .range-presets.chips { flex-wrap: wrap; overflow-x: visible; }
@media screen and (max-width: 400px) {
  .range-row > .range-presets.chips { flex-basis: 100%; }
  .range-row .range-preset.chip { font-size: 0.78rem; }
}

/* The insulin line under the carbs on the meal form (2026-10-05): the sum, an
   offer on a button, and one caveat line. Set apart from the boxes around it so
   it reads as the app speaking, not as a field to fill. */
.dose-hint { margin: -2px 0 14px; padding: 10px 12px; border-inline-start: 3px solid var(--accent); background: var(--surface); border-radius: 8px; }
.dose-hint-sum { margin: 0 0 8px; font-weight: 600; line-height: 1.6; }
.dose-hint-use { margin: 0 0 6px; }
.dose-hint .field-hint:last-child { margin-bottom: 0; }
/* The unsaved meal (2026-10-05): the reminder under the menu, the
   continue-or-new question in the form, and the line on a restored form. */
.draft-banner { max-width: 1060px; margin: 10px auto 0; padding: 10px 14px; border: 1px solid var(--warn-border); border-inline-start-width: 4px; border-radius: 10px; background: var(--warn-bg); }
.draft-banner-text { margin: 0 0 8px; line-height: 1.6; }
.draft-banner-actions { display: flex; flex-wrap: wrap; gap: 8px; }
@media (max-width: 640px) { .draft-banner { margin: 8px 16px 0; } }
.draft-offer p { margin: 0 0 10px; line-height: 1.6; }
.draft-restored { margin: 0 0 12px; padding: 8px 10px; border-inline-start: 3px solid var(--warn-border); background: var(--warn-bg); border-radius: 6px; }
.draft-restored .field-hint:last-child { margin-bottom: 0; }
.draft-leave { margin-bottom: 14px; }
/* Outside any .entry, so the primary button has no accent of its own to read:
   the meal colour, as on the meal form these belong to. */
.draft-banner,
.draft-leave { --entry-accent: var(--entry-meal); }
/* Meals planned ahead (2026-10-13).
   .planned-line   under a meal's or a snack's time box while the time is ahead:
                   one visible line, in the meal colour's border so it reads as
                   about this record and not as an error.
   .planned        the meals page's "Planned" list above the log - the readings
                   list's row shape, open rather than folded (one or two rows,
                   and the whole point is that they are seen), on a quiet card.
   .planned-due    a row whose time has come, until the log on screen has it.
   .planned-failed the line drawn instead when the list could not be read. */
.planned-line { margin: 6px 0 0; padding-block: 4px; padding-inline: 10px 6px; border-inline-start: 3px solid var(--entry-meal); border-radius: 6px; background: var(--surface); font-size: 0.9rem; line-height: 1.5; }
.planned { margin-block: 4px 14px; padding: 10px 12px; border: 1px solid var(--border); border-inline-start: 4px solid var(--entry-meal); border-radius: 10px; background: var(--surface); }
.planned-title { margin: 0 0 8px; font-size: 1rem; font-weight: 700; }
.planned-note { margin-block-end: 6px; }
.planned-list { list-style: none; margin: 0; padding: 0; }
.planned-row { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; padding-block: 6px; line-height: 1.35; border-block-end: 1px solid var(--border); }
.planned-row:last-child { border-block-end: 0; }
.planned-when { font-weight: 800; min-inline-size: 3.5em; }
.planned-type { font-weight: 700; color: var(--entry-meal); }
.planned-carbs { font-weight: 600; }
.planned-dose { color: var(--muted); }
.planned-what { flex-basis: 100%; color: var(--muted); overflow-wrap: anywhere; }
.planned-due { flex-basis: 100%; font-size: 0.87rem; font-weight: 600; }
.planned-row .row-actions { margin-inline-start: auto; }
.planned-failed { margin-block: 4px 12px; color: var(--muted); font-size: 0.9rem; }
/* Written since the log was made (2026-10-13, from the review): meals already
   eaten that the log on screen predates - the same card, but not in the meal
   colour of a plan. */
.planned-since { border-inline-start-color: var(--muted); }
/* The school breakfast's time (2026-10-13): under the meal type on the meal
   form, the one line that says the time was set to a school meal, and its
   buttons - 1st, 2nd, now. The planned line's meal-colour border, so it reads
   as about this meal and not as an error. The slot in the box is marked by
   aria-pressed - a border a screen reader also says - never by colour alone.
   The buttons wrap rather than shrink on a 360px phone. */
.slot-line { margin-block: 10px 0; padding-block: 8px; padding-inline: 10px; border-inline-start: 3px solid var(--entry-meal); border-radius: 6px; background: var(--surface); }
.slot-line-text { margin: 0 0 8px; font-weight: 600; line-height: 1.5; }
.slot-line-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.btn-slot { padding-block: 8px; padding-inline: 12px; }
.btn-slot[aria-pressed="true"] { border-color: var(--entry-accent); box-shadow: inset 0 0 0 1px var(--entry-accent); }
/* "Ratio now" from Settings (2026-10-05): the ratio checks compare against the
   household's Settings page, not the AndroidAPS profile.

   .ratio-aaps     AndroidAPS's own ratio under the Settings one, only where the
                   two differ. Amber TEXT ink, bold - the words "AndroidAPS:" are
                   the cue that matters. It was --warn-border, a border colour:
                   2.15:1 on white and 3.53:1 in dark mode, under even the
                   large-text minimum. --entry-correction is the amber text pair
                   the app already reads in all three palettes (#b45309 light,
                   about 5:1 on white, the .pill-warn ink; #f59e0b dark, about
                   8:1 on #111827).
   .ratio-src      the one-line source note under the meal-times table (a
                   .notice-line), with room above it since it follows a table.
   .ratio-src-opt  that line on a phone where the ratio column is dropped (every
                   page but Analysis): no column there, nothing to explain. Screen
                   only - on paper the column and its note both print.
   .night-basal    the long-acting line above the after-the-last-meal verdict. */
.mealtbl .ratio-aaps { color: var(--entry-correction); font-weight: 600; }
.ratio-src { margin-block: 10px 0; }
@media screen and (max-width: 640px) {
  .ratio-src-opt { display: none; }
}
.night-basal { margin-block: 0 8px; }
/* Her sport and school (2026-10-10).
   Settings: one card per activity, the seven days as tap-sized chips that wrap on
   a phone, the two times and the two dates side by side where there is room.
   Analysis: the summary line, and a session that went below 70 marked on its row. */
.act-card {
  margin: 0 0 14px;
  padding: 12px 14px 4px;
  border: 1px solid var(--border);
  border-radius: 10px;
  min-inline-size: 0;
}
.act-card > legend { padding: 0 6px; font-weight: 600; }
.act-days { display: flex; flex-wrap: wrap; gap: 6px; }
.act-day {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-block-size: 40px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
}
.act-day input { margin: 0; }
.act-day:has(input:checked) { border-color: var(--entry-accent, currentColor); font-weight: 600; }
.act-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 12px; }
@media (max-width: 380px) { .act-pair { grid-template-columns: minmax(0, 1fr); } }
.act-remove { margin-top: 0; }
.act-summary { margin: 0 0 6px; padding: 0; list-style: none; font-weight: 600; line-height: 1.7; }
.act-caveat { margin: 0 0 10px; }
.act-table .act-at { margin-inline-start: 4px; }
.act-table .pill { margin-inline-start: 6px; }
/* The feelings card (2026-10-11) - Components/Dashboard/FeelingTable.razor. A
   counted mark that rose more than her usual is tinted so it can be found. */
.feel-few { margin: 0 0 6px; font-weight: 600; }
.feel-table tr.feel-above td { background: color-mix(in srgb, var(--entry-feeling) 9%, transparent); }
.feel-table .feel-note { max-inline-size: 18em; overflow-wrap: anywhere; }

/* --- her sensors (2026-10-13) ---------------------------------------------------------
   The sensor form's two type tiles (Components/Entry/SensorChangeForm.razor) and
   the sensors page (Pages/Sensors.razor, Components/Sensors). Written for a 360px
   phone first: the current sensor is a two-column list of label and value, a
   suggestion's buttons wrap under its sentence, and the tables scroll inside
   their card with the secondary columns (.opt) dropped. */
@media (min-width: 560px) {
  .meal-type-options.sensor-type-options { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.sensor-prefill { margin: 0 0 10px; font-weight: 600; color: var(--text); }
/* The page's "Record a sensor change" sits outside any .entry, where
   --entry-accent is set: without this the primary button had no fill at all. */
.sensor-actions { --entry-accent: var(--entry-sensor); }
.sensor-now {
  display: grid;
  grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
  gap: 6px 14px;
  margin: 0 0 10px;
}
.sensor-now > div { display: contents; }
.sensor-now dt { color: var(--muted); }
.sensor-now dd { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
.sensor-type { font-weight: 800; color: var(--entry-sensor); }
.sensor-later { margin: 0 0 10px; font-weight: 600; }
.sensor-failed { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.sensor-failed p { margin: 0; }
.sensor-suggestions { list-style: none; margin: 0; padding: 0; }
.sensor-suggestion {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  padding-block: 8px;
  border-block-end: 1px solid var(--border);
}
.sensor-suggestion:last-child { border-block-end: 0; }
.sensor-suggestion-when { flex: 1 1 14em; line-height: 1.5; }
/* A gap across midnight is one range, "27/8 14:49–28/8 14:16": broken inside
   its isolate it read as two times on two lines. */
.sensor-suggestion-when .num-inline { white-space: nowrap; }
.sensor-suggestion-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.sensor-suggestion-actions .btn[data-type] { color: var(--entry-sensor); border-color: var(--entry-sensor); }
.sensor-table td, .sensor-checks td, .sensor-types td { vertical-align: top; }
.sensor-table .pill { margin-inline-start: 6px; }
.sensor-table .sensor-when,
.sensor-table .sensor-other,
.sensor-table .sensor-note,
.sensor-table .sensor-also,
.sensor-table .sensor-early,
.sensor-table td.num .pill { display: block; margin-block-start: 2px; }
.sensor-table td.num .pill { margin-inline-start: 0; inline-size: fit-content; }
.sensor-table .sensor-note { max-inline-size: 16em; overflow-wrap: anywhere; }
.sensor-table .sensor-early { font-weight: 600; }
.sensor-table .row-actions { display: flex; margin-block-start: 4px; }
.sensor-table tr.sensor-worry td { background: color-mix(in srgb, var(--warn-border) 14%, transparent); }
.sensor-mard-inline { display: none; }
@media screen and (max-width: 640px) {
  .sensor-table .sensor-mard-inline { display: block; margin-block-start: 2px; }
}
@media screen and (min-width: 641px) {
  .sensor-verdict { min-inline-size: 9em; }
}
.sensor-checks tr.sensor-aside td { color: var(--muted); }
.sensor-checks .sensor-pct,
.sensor-checks .sensor-typed { display: block; }
.sensor-few { margin: 0 0 8px; font-weight: 600; }

/* The sensor suggestions' answers (2026-10-13 review) -
   Components/Sensors/SensorSuggestionList.razor. FULL TAP TARGETS, overriding
   .btn-small's 36px, and 8px between them: "ليس تغييرًا للسنسور" sat 6px from
   "لينكس" on a 360px phone, and a thumb that slipped lost the suggestion for
   good. It now only asks; the strip above the rows keeps the answer. */
.sensor-suggestion-actions { gap: 8px; }
.sensor-suggestion-actions .btn {
  min-height: 44px;
  padding-inline: 14px;
}
.sensor-dismiss-confirm { margin-block: 0 12px; }

/* ===========================================================================
 * HOW THE APP LOOKS, ON THIS PHONE (2026-10-13) - colour themes, and light or
 * dark as a choice. Pages/SettingsAppearance.razor, Services/PhoneLook.cs, and
 * the first script in index.html.
 * ===========================================================================
 * The owner asked for colours to choose from: blue (what the app has always
 * been, and still the default), purple, pink, red and green - chosen on each
 * phone by itself. And, since both palettes were already here, light or dark
 * as a choice rather than only as the phone is set.
 *
 * THE MECHANISM. Two attributes on <html>, set by index.html's first script
 * from the phone's storage BEFORE the stylesheets paint (no flash of blue on a
 * pink phone), and by the same function when the page is tapped:
 *   data-accent="purple|pink|red|green"   no attribute = blue
 *   data-theme="light|dark"               no attribute = as the phone is set
 * data-theme is the switch sokkara.css was always written for; it only had
 * nothing setting it.
 *
 * WHAT A THEME RECOLOURS - every colour in the app, classified.
 *
 *   THEMED: the app's own chrome, through the one token --accent - and only
 *   the classes named here may read it:
 *     the tab bar and its lit tab (.tab), the gear and the language pill
 *     (.appbar-icon, .appbar-lang), the More list's icons (.more-row); links
 *     (.settings-back, .glossary-link, .visit-shot-link, .langswitch); the
 *     quiet and pressed buttons (.btn-quiet, .btn-on, .chip, .range-preset,
 *     .range-apply, .xbtn, .login-submit), and the hover of the (i), close and
 *     fold buttons (.info-btn, .info-close, .tip-btn, .tip-close, .more,
 *     .visit-group); the side rule of .finding, .collecting, .therapy-line and
 *     .dose-hint; the sign-in page's wash and its box (.login-page,
 *     .login-input); Amend's corrected-record wash and changed value
 *     (.amend-corrected, .amend-changed) and the component rows' tint
 *     (.component-row); the spinner and the busy line (.spinner, .busy-line);
 *     index.html's loading bar, Retry and Update (.boot-fill, .boot-retry,
 *     .sw-update); the Appearance page's own tiles (.look, .look-swatch);
 *     her initial where her photo would be, on Home and in Settings
 *     (.her-initial); every focus ring (an outline, whatever it surrounds); and, through
 *     <meta name="theme-color">, the phone's status bar.
 *
 *   FIXED: everything that MEANS something, the same on every phone.
 *     - The glucose bands: low and very low, in range, high and very high
 *       (ChartPrimitives.ChartColors - the graph's dots, the TIR bar, DawnBlock;
 *       .bg-low, .bg-high, the pills, the verdict dots, the .lg-* swatches).
 *     - The target band and the AGP percentile bands (--target, --band-outer,
 *       --band-inner): blue on every phone.
 *     - The glucose line itself. The AGP median and the meal log's traces drew
 *       in --accent; they now draw in --glucose-line (below: the same blue in
 *       light, dark and on paper), so a red phone never draws a red line.
 *     - Every kind of record (--entry-*: dose, correction, meal, reading,
 *       feeling, sensor, danger, ok, and the text on them), and so .btn-primary,
 *       which is filled with the kind of record it saves; the graph's own marks
 *       (GlucoseGraph: carbs, insulin, readings, feelings, sensors, sport and
 *       school shading) and the forecast's grey wedge (--muted).
 *     - Warnings (--warn-bg, --warn-border, .notice-line), the page itself
 *       (--bg, --surface, --border, --text, --muted, --grid), and the meal
 *       buckets' colours.
 *     - The launcher icons and the loading screen's logo: the picture on her
 *       home screen, which a phone's choice cannot change.
 *   Tests parse every stylesheet - these two and index.html's own - and hold
 *   them to that. Nothing but --accent is set under [data-accent]. A rule
 *   reads the accent (or a :root token made from it) only if every selector in
 *   it names a THEMED class above, and never, but for a focus ring, if one
 *   names a FIXED family (.bg-*, .pill*, .verdict*, .trend*, .lg*, .legend,
 *   .chart*, .fc-*, .act-*, .record-kind, .btn-primary... - the full list is
 *   ThemeCssTests.ClinicalFamilies). And no component, page or script uses the
 *   accent at all: written inline, a use could not be classified here.
 *
 * THE COLOURS, chosen against the clinical ones they must never be taken for
 * (contrast on --surface and --bg is checked by a test, >= 4.5:1 as text, and
 * >= 4.5:1 for the text on a filled accent; "apart" is the OKLab distance,
 * also tested, >= 0.10 from low and from in range in the same mode):
 *   blue    #0369a1 / #38bdf8   today's, unchanged.
 *   purple  #6d28d9 / #a78bfa
 *   pink    #c0187a / #f472c8   bluer than the usual pinks: #be185d and
 *                               #f472b6 sit 0.10 from the low red.
 *   red     #9f1d47 / #fda4af   a raspberry, darker and bluer than low
 *                               (#dc2626, 0.14 apart) and lighter than very
 *                               low (#7f1d1d, 0.09); in dark a pale rose,
 *                               0.12 from dark mode's danger #f87171.
 *   green   #1a5d3a / #bbf7d0   a deep forest green. In range is #15803d (and
 *                               #4ade80 in dark), and the suggested #15803d WAS
 *                               in range; a teal would be the sensor's colour
 *                               (#0f766e). Darker than in range (0.11 apart) in
 *                               light, paler (0.16) in dark.
 *
 * THE --accent-<name> TOKENS hold each theme's colour in the mode in effect,
 * declared in the three places every palette here is (bare :root, the
 * prefers-color-scheme block guarded with :not([data-theme="light"]), and the
 * explicit [data-theme="dark"]); the swatches on the page read them, so each
 * shows its colour as this phone would. [data-accent] then points --accent at
 * one of them - once, for every mode.
 *
 * SCREEN ONLY. The theme map sits inside @media screen: paper is never themed,
 * and the clinic sheet prints the ink palette sokkara.css gives it. The print
 * block at the very end of this file makes that hold under the Dark choice too.
 */
:root {
  --accent-blue: #0369a1;
  --accent-purple: #6d28d9;
  --accent-pink: #c0187a;
  --accent-red: #9f1d47;
  --accent-green: #1a5d3a;
  /* Her glucose line in the report's charts - the old accent, kept blue on
     every phone. */
  --glucose-line: #0369a1;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --accent-blue: #38bdf8;
    --accent-purple: #a78bfa;
    --accent-pink: #f472c8;
    --accent-red: #fda4af;
    --accent-green: #bbf7d0;
    --glucose-line: #38bdf8;
  }
}
:root[data-theme="dark"] {
  --accent-blue: #38bdf8;
  --accent-purple: #a78bfa;
  --accent-pink: #f472c8;
  --accent-red: #fda4af;
  --accent-green: #bbf7d0;
  --glucose-line: #38bdf8;
}
/* A chosen mode tells the browser too, so its own parts - the date and time
   pickers, the radios, the scrollbars - match the page rather than the phone. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

@media screen {
  :root[data-accent="purple"] { --accent: var(--accent-purple); }
  :root[data-accent="pink"] { --accent: var(--accent-pink); }
  :root[data-accent="red"] { --accent: var(--accent-red); }
  :root[data-accent="green"] { --accent: var(--accent-green); }
}

/* The page. .look points the tiles' accent (--entry-accent, which .entry sets
   to a record kind's colour) at the theme's, so the page is its own preview.
   Five swatches, two to a row on a phone and five across where they fit; each
   a whole <label> with its radio visible - the chosen one is marked by the
   radio's dot AND the border, never by colour alone. The three ways one per
   row on a phone ("حسب الهاتف" is long), side by side from 560px. */
.look { --entry-accent: var(--accent); }
.look-note {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-block: 0 12px;
}
.look-swatches {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
@media (min-width: 700px) {
  .look-swatches { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
.look-swatch {
  display: flex;
  align-items: center;
  gap: 8px;
  min-block-size: 56px;
  padding-block: 8px;
  padding-inline: 10px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg);
  font-weight: 600;
  line-height: 1.3;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.look-swatch input {
  flex: none;
  inline-size: 18px;
  block-size: 18px;
  margin: 0;
  accent-color: var(--accent);
}
.look-swatch:has(input:checked) {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.look-swatch:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.look-dot {
  flex: none;
  inline-size: 28px;
  block-size: 28px;
  border-radius: 50%;
  background: var(--swatch);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--text) 18%, transparent);
}
.look-dot[data-swatch="blue"] { --swatch: var(--accent-blue); }
.look-dot[data-swatch="purple"] { --swatch: var(--accent-purple); }
.look-dot[data-swatch="pink"] { --swatch: var(--accent-pink); }
.look-dot[data-swatch="red"] { --swatch: var(--accent-red); }
.look-dot[data-swatch="green"] { --swatch: var(--accent-green); }
.look-name { min-inline-size: 0; overflow-wrap: anywhere; }
.meal-type-options.look-modes { grid-template-columns: 1fr; }
@media (min-width: 560px) {
  .meal-type-options.look-modes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* PAPER IS INK ON WHITE, WHATEVER THE PHONE SHOWS (2026-10-13). sokkara.css
   forces its print palette with `@media print { :root {...} }`, and a bare :root
   loses to `:root[data-theme="dark"]` and to the guarded dark-mode block - two
   classes against one. With Dark now a choice, the clinic sheet printed from a
   dark phone came out in pale text for white paper. So the ink palette is
   restated here, last in the file, under a selector that outranks both: every
   token any dark block declares, with sokkara.css's print value where it has
   one and the light value otherwise (--glucose-line takes the print accent, as
   the median always printed). The --accent-<name> tokens are not here: only
   the screen-only theme map reads them. A test holds this list to the dark
   blocks - a new dark token is a line here too. */
@media print {
  :root,
  :root:not([data-theme="light"]) {
    color-scheme: light;
    --bg: #ffffff;
    --surface: #ffffff;
    --border: #b9c2cc;
    --text: #000000;
    --muted: #414b56;
    --accent: #0b4f80;
    --grid: #ccd3da;
    --band-outer: rgba(11,79,128,0.13);
    --band-inner: rgba(11,79,128,0.26);
    --target: rgba(21,128,61,0.13);
    --warn-bg: #ffffff;
    --warn-border: #777777;
    --login-shadow: 0 1px 2px rgba(15, 23, 42, 0.05), 0 24px 48px -28px rgba(15, 23, 42, 0.40);
    --entry-bolus: #0369a1;
    --entry-correction: #b45309;
    --entry-meal: #15803d;
    --entry-glucose: #7c3aed;
    --entry-feeling: #db2777;
    --entry-sensor: #0f766e;
    --entry-danger: #dc2626;
    --entry-ok: #15803d;
    --entry-on-accent: #ffffff;
    --sheet-shadow: 0 -12px 40px rgba(15, 23, 42, 0.22);
    --glucose-line: #0b4f80;
  }
}
