/* The SHELL: everything every page stands on - the fonts, the colour tokens,
 * the type, the bars and tabs, buttons, forms, cards, pills, tables, the dialog,
 * the (i) sheets and the folds. Pruned and merged from Sokkara's two
 * stylesheets (sokkara.css + app-shell.css, 2026-10-18): the glucose, clinic,
 * chart and meal-log sections stayed behind. This app's own pages are in
 * tholoth.css.
 *
 * THE RULES FOR EDITING THIS FILE (and tholoth.css):
 *
 *  * TOKENS ONLY. var(--surface), var(--text), var(--muted), var(--border),
 *    var(--accent), var(--bg) and the rest are declared ONCE, below, in all
 *    three places a palette must be: bare :root (light), the
 *    prefers-color-scheme block guarded with :not([data-theme="light"]), and
 *    the explicit [data-theme="dark"]. A value declared only inside a media
 *    query does not exist for a reader whose browser is set the other way. A
 *    literal colour is a control that stays light-grey in dark mode and prints
 *    as a grey smear.
 *  * LOGICAL PROPERTIES ONLY (house rule 14): 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.
 *  * MOBILE FIRST: the base rules are the 360-375px phone; min-width queries
 *    widen. Nothing may scroll the page sideways at 375px - a wide table
 *    scrolls inside its own .scroll box.
 *  * svh/dvh, not vh: on a phone `vh` is measured without the address bar, so
 *    100vh puts the bottom of a screen under the browser chrome.
 *
 * WHAT THE ACCENT MAY COLOUR, AND WHAT IT NEVER DOES (ported from Sokkara's
 * theme block, 2026-10-13; the palette is new):
 *
 *   THEMED - the app's own chrome, through the one token --accent: the lit tab
 *   and the raised tab (.tab), the gear and the language pill (.appbar-icon,
 *   .appbar-lang, .langswitch), menu-row icons (.more-row), links and back
 *   links, the primary and quiet buttons (.btn-primary, .btn-quiet), chosen
 *   choice tiles (.choice-option), the hover of the (i), close and fold
 *   controls, the sign-in page's wash and its button, the Appearance tiles
 *   (.look-*), index.html's loading bar and Update button, every focus ring,
 *   and (through <meta name="theme-color">) the phone's status bar.
 *
 *   FIXED - everything that MEANS something, the same on every phone:
 *     --third-food, --third-drink, --third-breath   the three thirds
 *     --protein, --carbs, --fat                      the macros (from M2)
 *     --over, --under, --on-target                   the day's verdict (from M3)
 *     --ok, --danger, --warn-*                       states and warnings
 *     --bg, --surface, --border, --text, --muted     the page itself
 *   An accent that could be taken for one of these would make a chrome colour
 *   say "over target". So nothing but --accent is set under [data-accent], and
 *   no rule that colours a FIXED meaning reads --accent.
 */

/* --- Cairo, self-hosted ----------------------------------------------------
   Served from this origin, so opening a personal health app never issues a
   third-party request. `local()` first: a reader who already has Cairo installed
   downloads nothing. The unicode-range split means an English page with no
   Arabic on it never needs the Arabic file. Cairo ships as a variable font, so
   one file covers the whole weight axis. Licence: SIL OFL 1.1 - fonts/README.md. */
@font-face {
  font-family: 'Cairo';
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: local('Cairo'), url('../fonts/cairo-arabic.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1,
                 U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41,
                 U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC;
}
@font-face {
  font-family: 'Cairo';
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: local('Cairo'), url('../fonts/cairo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ===========================================================================
 * TOKENS - light, then dark (as the phone is set), then dark (chosen)
 * =========================================================================== */
:root {
  color-scheme: light dark;

  /* the page */
  --bg: #f5f8f7;
  --surface: #ffffff;
  --border: #dce4e1;
  --text: #10201b;
  --muted: #5a6b65;

  /* the chrome accent (THEMED). A calm deep teal by default, for an app named
     after balance; each theme's colour in this mode, so the Appearance tiles
     can show every choice as this phone would. */
  --accent-teal: #0f766e;
  --accent-indigo: #4338ca;
  --accent-plum: #86198f;
  --accent-slate: #334155;
  --accent: var(--accent-teal);
  /* Text ON a filled accent: white on the deep light-mode accents. */
  --on-accent: #ffffff;

  /* states and warnings (FIXED). --ok is green-800, not 700 (2026-10-18,
     review): the "Active" pill draws it at 13.6px bold on its own 15% tint,
     and #15803d there was 3.85:1 - under WCAG's 4.5 for text that small. */
  --ok: #166534;
  --danger: #b91c1c;
  --warn-bg: #fffbeb;
  --warn-border: #d97706;
  --warn-text: #92400e;

  /* meanings (FIXED) - see the header. Chosen apart from each other and from
     every accent: orange, sky, violet for the thirds; rose, gold, olive for the
     macros; red, amber, green for the verdict. Every one is also SAID in words
     wherever it is used; the colour only echoes the word. */
  --third-food: #c2410c;
  --third-drink: #0369a1;
  --third-breath: #7e22ce;
  --protein: #be123c;
  --carbs: #a16207;
  --fat: #4d7c0f;
  --over: #b91c1c;
  --under: #b45309;
  --on-target: #15803d;

  /* shapes. A shadow is not a colour that flips roles - dark in light mode and
     darker still in dark - so it is declared per mode too. */
  --lift-shadow: 0 1px 2px rgba(15, 23, 42, 0.05), 0 24px 48px -28px rgba(15, 23, 42, 0.40);
  --sheet-shadow: 0 -12px 40px rgba(15, 23, 42, 0.22);
  --tabbar-h: 62px;
  --card-radius: 16px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0b1210;
    --surface: #121b18;
    --border: #23312c;
    --text: #e4eeea;
    --muted: #97a8a2;
    --accent-teal: #5eead4;
    --accent-indigo: #a5b4fc;
    --accent-plum: #f0abfc;
    --accent-slate: #cbd5e1;
    /* Dark text on the pale dark-mode accents: white on them is unreadable. */
    --on-accent: #06201b;
    --ok: #4ade80;
    --danger: #f87171;
    --warn-bg: #261d08;
    --warn-border: #b45309;
    --warn-text: #fcd34d;
    --third-food: #fb923c;
    --third-drink: #7dd3fc;
    --third-breath: #d8b4fe;
    --protein: #fda4af;
    --carbs: #fde047;
    --fat: #bef264;
    --over: #f87171;
    --under: #fcd34d;
    --on-target: #4ade80;
    --lift-shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 24px 48px -30px rgba(0, 0, 0, 0.85);
    --sheet-shadow: 0 -12px 40px rgba(0, 0, 0, 0.6);
  }
}
:root[data-theme="dark"] {
  --bg: #0b1210;
  --surface: #121b18;
  --border: #23312c;
  --text: #e4eeea;
  --muted: #97a8a2;
  --accent-teal: #5eead4;
  --accent-indigo: #a5b4fc;
  --accent-plum: #f0abfc;
  --accent-slate: #cbd5e1;
  --on-accent: #06201b;
  --ok: #4ade80;
  --danger: #f87171;
  --warn-bg: #261d08;
  --warn-border: #b45309;
  --warn-text: #fcd34d;
  --third-food: #fb923c;
  --third-drink: #7dd3fc;
  --third-breath: #d8b4fe;
  --protein: #fda4af;
  --carbs: #fde047;
  --fat: #bef264;
  --over: #f87171;
  --under: #fcd34d;
  --on-target: #4ade80;
  --lift-shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 24px 48px -30px rgba(0, 0, 0, 0.85);
  --sheet-shadow: 0 -12px 40px rgba(0, 0, 0, 0.6);
}
/* A chosen mode tells the browser too, so its own parts - radios, scrollbars,
   date pickers - match the page rather than the phone. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

/* THE THEME MAP: [data-accent] points --accent at one theme's colour, once, for
   every mode, and sets NOTHING else. Screen only: paper is never themed. */
@media screen {
  :root[data-accent="indigo"] { --accent: var(--accent-indigo); }
  :root[data-accent="plum"] { --accent: var(--accent-plum); }
  :root[data-accent="slate"] { --accent: var(--accent-slate); }
}

/* ===========================================================================
 * BASE - reset, type, the page column
 * =========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

/* A percentage rather than px: everything is sized in rem, so this one number
   scales the page, multiplied by whatever base size the reader set in their
   own browser. 106.25% is 17px against the stock 16px. */
html { font-size: 106.25%; }

body {
  margin: 0;
  min-block-size: 100svh;
  background: var(--bg);
  color: var(--text);
  font: 1rem/1.65 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  overflow-wrap: break-word;
}

/* Cairo first, then system fonts that ship Arabic glyphs. If the woff2 files are
   ever missing the page degrades to the system stack rather than breaking.

   On `.wrap[lang="ar"]`, NOT on <html>: a Blazor WASM app serves ONE static
   index.html for every route, so the root cannot carry the language without
   script. `.wrap` carries lang and dir (PageShell sets both). Scoped to
   `.wrap[...]` rather than a bare `[lang="ar"]` on purpose: the language switch
   carries lang="ar" on the ENGLISH page, and a bare selector would restyle that
   one link. Bars outside .wrap name the stack again. */
.wrap[lang="ar"],
.appbar[lang="ar"],
.err-notice[lang="ar"],
.dlg-frame[lang="ar"] {
  font-family: 'Cairo', "Segoe UI", Tahoma, "Noto Sans Arabic", "Noto Naskh Arabic",
               "Geeza Pro", "Arabic Typesetting", system-ui, sans-serif;
}
.wrap[lang="ar"] {
  line-height: 1.95;
  /* Cairo's Arabic glyphs run slightly small against Latin digits at the same
     size, and these pages mix the two constantly. */
  font-size: 1.05rem;
}

.wrap {
  max-inline-size: 1100px;
  margin-inline: auto;
  padding-block: 12px 28px;
  padding-inline: 16px;
}
@media screen and (min-width: 641px) {
  .wrap { padding-block: 28px 64px; padding-inline: 20px; }
}

h1 { font-size: 1.55rem; line-height: 1.35; margin: 0 0 6px; }
h2 { font-size: 1.08rem; line-height: 1.45; margin: 0 0 8px; }
h3 { font-size: 0.98rem; margin: 0 0 8px; }
h4 { font-size: 0.95rem; margin: 0 0 6px; }
/* NEVER track Arabic. It is a joined script and letter-spacing prises the joins
   apart; negative tracking on a heading is a Latin habit. */
.wrap[lang="en"] h1,
.wrap[lang="en"] h2 { letter-spacing: -0.01em; }
p { margin: 0 0 10px; }
.sub { color: var(--muted); font-size: 0.9rem; }
a { color: var(--accent); text-underline-offset: 2px; }
img, svg { max-inline-size: 100%; }
svg { display: block; }

/* A numeric or technical token: a date, a clock time, a signed value, a
   schedule. `isolate` alone is NOT enough in RTL - an isolate keeps the token
   from disturbing its neighbours, but its own base direction is still the
   inherited `rtl`. A leading "+" or "-" is bidi class ES; with no European
   number to its left, rule W6 turns it into ON and N1 then resolves it to R,
   so it takes the RTL level and is reordered to the far side of the digits:
   "+76" painted as "76+", "2026-08-14 03:12" as "03:12 2026-08-14". Pinning the
   island to ltr keeps the token exactly as written in both languages - this is
   what the Arabic locale itself does, emitting U+200E before the sign.
   Alignment is unaffected: text-align:start/end resolves against the
   surrounding cell's direction, not this span's. (Bidi.Iso emits it.)
   NOWRAP everywhere, not only in tables as in Sokkara (2026-10-18): the
   disclaimer's "20–30%" broke after its dash on a 375px phone, leaving "20–"
   at the end of one Arabic line and "30%" at the start of the next. A token is
   one value; a long one goes in its own .scroll box, never split. */
.num-inline { direction: ltr; unicode-bidi: isolate; white-space: nowrap; }

/* Focus is always visible, in the accent, for every control that has none of
   its own below. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* ...except the page title FocusOnNavigate moves focus to (it adds
   tabindex="-1"): a heading is not a control, and after a sign-in with Enter
   the ring drew a box round the shown title of the forced password page. The
   screen reader still lands on it and reads it. */
h1[tabindex="-1"]:focus { outline: none; }

.printonly { display: none; }

/* Read by a screen reader and a password manager, not painted. Never
   display:none, which both skip. */
.visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The one loading sentence. A sentence, not a spinner and not a skeleton: a
   placeholder in the shape of data is a page pretending to have some. */
.loading { color: var(--muted); text-align: center; margin-block: 24px; }

/* ===========================================================================
 * THE TOP BAR AND THE TABS (Components/Nav/AppNav.razor)
 * =========================================================================== */
.appbar {
  margin: 0;
  background: var(--surface);
  border-block-end: 1px solid var(--border);
}
.appbar-inner {
  max-inline-size: 1100px;
  margin-inline: auto;
  min-block-size: 54px;
  padding-block: 6px;
  /* The notch, in landscape: viewport-fit=cover (for the tab bar's bottom
     inset) also stops Safari insetting the sides, so the bar pads itself. */
  padding-inline: max(12px, env(safe-area-inset-left), env(safe-area-inset-right));
  display: flex;
  align-items: center;
  gap: 8px;
}
.appbar-title {
  flex: 1 1 auto;
  min-inline-size: 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,
.langswitch {
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;
  padding-block: 4px;
  padding-inline: 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--accent);
  font-size: 0.88rem;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.appbar-lang:hover,
.langswitch:hover { border-color: var(--accent); }
/* The other language's word is named in its own script's font, wherever it
   sits: an Arabic word inside the English bar is still Cairo. */
.appbar-lang[lang="ar"],
.langswitch[lang="ar"] { font-family: 'Cairo', "Segoe UI", Tahoma, system-ui, sans-serif; }
.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);
}

/* ON A PHONE the tabs are fixed to the bottom edge, where a thumb is, above the
   phone's own home indicator (viewport-fit=cover is what makes
   env(safe-area-inset-bottom) report it). As many equal columns as there are
   links - two in M0, five from M2 - so adding a tab is one line in
   NavItems.cs and nothing here. */
.tabbar {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: 30;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  background: var(--surface);
  border-block-start: 1px solid var(--border);
  padding-block-end: env(safe-area-inset-bottom);
  /* The notch, in landscape. The insets are physical (left, right) and these
     sides logical, so the larger is taken on both: never a tab under a notch. */
  padding-inline: max(env(safe-area-inset-left), env(safe-area-inset-right));
  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-block: 6px 5px;
  padding-inline: 2px;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.76rem;
  font-weight: 700;
  line-height: 1.2;
  -webkit-tap-highlight-color: transparent;
}
.tab .ico {
  box-sizing: content-box;
  padding-block: 3px;
  padding-inline: 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; }

/* THE RAISED TAB (from M2: the camera). The app's main action, lifted out of
   the bar's centre as a filled round button so it is the one thing under the
   thumb. Chrome, so it takes the accent. Unused in M0; see NavItems.cs. */
.tab-raised .ico {
  padding: 12px;
  margin-block-start: -24px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: 0 6px 16px rgba(15, 23, 42, 0.22);
}
.tab-raised[aria-current="page"] .ico { background: var(--accent); }

/* Room under the page for the fixed bar - only on a page that has one (the
   sign-in page and the forced password change do not). */
body:has(.tabbar) { padding-block-end: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); }

/* ON A WIDE SCREEN the same links move into the top bar. */
@media screen and (min-width: 900px) {
  body:has(.tabbar) { padding-block-end: 0; }
  .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-block: 6px;
    padding-inline: 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; }
  .tab-raised .ico { padding: 0; margin: 0; background: none; color: inherit; box-shadow: none; }
}

/* ===========================================================================
 * THE PAGE
 * =========================================================================== */

/* THE TITLE BLOCK IS FOR A SCREEN READER AND FOR PAPER. 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. A page drawn without the bar shows it (.page-head-shown). */
.page-head { margin-block-end: 16px; }
@media screen {
  .page-head:not(.page-head-shown) {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    margin: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* "<- Settings" above a page one level down: a whole 44px target. */
.back-link {
  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;
}

/* ===========================================================================
 * CARDS (Components/Shell/Section.razor)
 * =========================================================================== */
section {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--card-radius);
  padding-block: 14px;
  padding-inline: 14px;
  margin-block-end: 12px;
}
@media screen and (min-width: 641px) {
  section { padding: 20px; margin-block-end: 18px; }
}
section > h2 { margin-block-end: 10px; }
.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; }
h2 .info-btn,
h3 .info-btn,
h4 .info-btn { margin-block: -6px; }

/* ===========================================================================
 * BUTTONS
 * =========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-block-size: 48px;
  padding-block: 10px;
  padding-inline: 20px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.btn-block { display: flex; inline-size: 100%; }
/* 44px is the floor even for the small ones: they are pressed with a thumb. */
.btn-small { min-block-size: 44px; padding-block: 6px; padding-inline: 14px; font-size: 0.92rem; }
/* Filled with the accent: chrome, not a meaning. var(--on-accent), not #fff:
   in dark mode the accent is pale and white on it is unreadable. */
.btn-primary {
  background: var(--accent);
  border-color: transparent;
  color: var(--on-accent);
}
.btn-primary:hover { filter: brightness(1.08); }
.btn-primary-danger { background: var(--danger); color: var(--surface); }
.btn-quiet { color: var(--accent); }
.btn-quiet:hover { border-color: var(--accent); }
/* Outlined, never filled: "disable" is the exception and must not look like the
   button the thumb is already travelling towards. */
.btn-danger { color: var(--danger); border-color: var(--danger); }
/* aria-disabled, not disabled: a pressed-and-busy button keeps its focus (see
   Login.razor), so the busy look hangs off the ARIA state. */
.btn[aria-disabled="true"],
.btn[disabled] { opacity: 0.65; cursor: progress; filter: none; }

/* ===========================================================================
 * FORMS - fields, credentials, choices
 * =========================================================================== */
.field { margin-block-end: 18px; }
.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;
}
.field-name { font-size: 1rem; font-weight: 600; }
/* "required" / "optional" in words beside the label rather than a bare asterisk,
   whose legend would be somewhere else on the page, unread. */
.field-flag { font-size: 0.78rem; font-weight: 500; color: var(--muted); }
.field-hint { margin-block: 6px 0; color: var(--muted); font-size: 0.87rem; line-height: 1.6; }
/* 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 at minimum
   brightness. */
.field-err { margin-block: 6px 0; color: var(--danger); font-size: 0.88rem; font-weight: 600; }

.input,
.login-input {
  display: block;
  inline-size: 100%;
  /* A finger, not a mouse pointer. */
  min-block-size: 48px;
  padding-block: 11px;
  padding-inline: 12px;
  /* Controls do not inherit type: without `font: inherit` the Arabic page draws
     its inputs in the browser's UI font under a Cairo label. The size stays at
     1rem or more: iOS zooms the whole page when a focused field is under 16px. */
  font: inherit;
  font-size: 1rem;
  line-height: 1.4;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.input:focus-visible,
.login-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.field-bad .input,
.field-bad .login-input { border-color: var(--danger); }
/* Number boxes (from M1): type=text inputmode=decimal, never type=number
   (house rule 4); the digits line up and stay LTR in both languages. */
.input-num { direction: ltr; text-align: end; font-variant-numeric: tabular-nums; }
/* Free text in any language - a person's name (TextBox Prose, 2026-10-18):
   what is typed takes its direction from its own first strong letter, as <bdi>
   shows it; the empty box keeps the page's. Not for a username, which is
   pinned LTR. Pinned by PageMarkupTests.
   The_display_name_box_takes_the_direction_of_what_is_typed. */
.input-prose { unicode-bidi: plaintext; }

/* Chrome paints its own pale fill over an autofilled field and ignores
   background-color, which in dark mode leaves a bright block in a dark card. An
   inset shadow is the only thing it honours. */
.input:-webkit-autofill,
.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);
}

/* The credential fields (Components/Auth/CredentialField.razor), on the sign-in
   page and the password page alike. */
.login-field { margin-block-end: 15px; }
.login-label {
  display: block;
  margin-block-end: 6px;
  font-size: 0.88rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--muted);
}

/* The live region (Components/Auth/LoginError.razor) 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. */
.login-live { margin: 0; padding: 0; border: 0; }
/* The same for a success line's always-present status region (SettingsPassword). */
.ok-live { margin: 0; padding: 0; border: 0; }
/* Amber, not red, with the side rule of a notice: a refused form is a
   correction, not an alarm. */
.login-error {
  margin-block: 0 16px;
  padding-block: 11px;
  padding-inline: 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;
}

/* One choice of a few, as tiles (Components/Entry/ChoiceGroup.razor). Each a
   whole <label>; the native radio stays visible, so the chosen tile is marked by
   its dot AND its border, never by colour alone. */
fieldset.field { margin-inline: 0; padding: 0; border: 0; min-inline-size: 0; }
fieldset.field > legend { padding: 0; margin-block-end: 6px; }
.choice-options {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* Every tile the height of the tallest: an Arabic label that wraps on a 360px
     phone must not leave one tile taller than its neighbour. */
  grid-auto-rows: 1fr;
  gap: 8px;
}
.choice-option {
  display: flex;
  align-items: center;
  gap: 8px;
  min-block-size: 48px;
  padding-block: 8px;
  padding-inline: 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;
}
.choice-option input {
  flex: none;
  inline-size: 18px;
  block-size: 18px;
  margin: 0;
  accent-color: var(--accent);
}
.choice-option:has(input:checked) {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.choice-option:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 1px; }
.field-bad .choice-option { border-color: var(--danger); }

/* ===========================================================================
 * NOTICES
 * =========================================================================== */

/* A sentence that must stay on screen, with the rest of it behind its (i). The
   warn palette, because it means "read this first". */
.notice-line {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px 6px;
  margin-block-end: 12px;
  padding-block: 8px;
  padding-inline: 12px 8px;
  border: 1px solid var(--warn-border);
  border-inline-start-width: 4px;
  border-radius: 12px;
  background: var(--warn-bg);
  font-size: 0.93rem;
  line-height: 1.6;
}
.notice-line > strong,
.notice-line > span:first-child { flex: 1 1 0; min-inline-size: 0; }

/* Something that worked, said in words with a check beside them. */
.ok-line {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-block-end: 12px;
  padding-block: 10px;
  padding-inline: 12px;
  border: 1px solid var(--ok);
  border-inline-start-width: 4px;
  border-radius: 12px;
  background: var(--surface);
}
.ok-line .ico { color: var(--ok); margin-block-start: 3px; }

/* A read that failed: words and a Retry, never an empty list (house rule 12). A
   dashed warn box: an absence of knowledge, not a refusal. */
.read-failed {
  padding-block: 12px;
  padding-inline: 14px;
  border: 1px dashed var(--warn-border);
  border-radius: 12px;
  background: var(--warn-bg);
}
.read-failed p { margin-block-end: 8px; }

/* The wellness disclaimer at the foot of Today: 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;
  margin-block-start: 20px;
  padding-block: 8px;
  padding-inline: 14px 6px;
  background: var(--warn-bg);
  border: 1px solid var(--warn-border);
  border-inline-start-width: 4px;
  border-radius: 10px;
  font-size: 0.9rem;
}
.disclaimer > strong { flex: 1 1 0; min-inline-size: 0; }

/* ===========================================================================
 * PILLS - a status in a word, its FIXED colour only echoing it
 * =========================================================================== */
.pill {
  display: inline-block;
  padding-block: 2px;
  padding-inline: 10px;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 700;
  line-height: 1.6;
  white-space: nowrap;
}
/* EVERY PILL'S WORD IS 4.5:1 ON ITS OWN TINT, over the page and over a card, in
   light, dark and print (2026-10-18, review; ThemeCssTests.
   Every_pill_word_is_legible_on_its_own_tint). A pill is 13.6px bold - small
   text - and "Active" and the neutral pills (You, the role, the language) were
   3.85 and 4.28 in light mode. The neutral word is drawn in --text: --muted on
   a --muted tint cannot reach 4.5 at any tint worth seeing. */
.pill-ok { background: color-mix(in srgb, var(--ok) 15%, transparent); color: var(--ok); }
.pill-warn { background: color-mix(in srgb, var(--warn-border) 16%, transparent); color: var(--warn-text); }
.pill-bad { background: color-mix(in srgb, var(--danger) 14%, transparent); color: var(--danger); }
.pill-neutral { background: color-mix(in srgb, var(--muted) 16%, transparent); color: var(--text); }

/* ===========================================================================
 * TABLES (from M2 on: meals, foods, body history)
 * =========================================================================== */
/* A wide table scrolls INSIDE this box, never the page. */
.scroll { overflow-x: auto; }
table { border-collapse: collapse; inline-size: 100%; font-size: 0.93rem; }
th, td {
  text-align: start;
  padding-block: 8px;
  padding-inline: 10px;
  border-block-end: 1px solid var(--border);
  white-space: nowrap;
}
th { color: var(--muted); font-weight: 600; font-size: 0.84rem; }
/* Prose cells wrap; value cells do not, so a number never splits. */
th.txt, td.txt { white-space: normal; min-inline-size: 9rem; }
/* Do NOT add `direction: ltr` here to fix a stray minus sign - measured in
   Sokkara, it breaks two things at once: `text-align: end` then resolves
   against ltr and the numbers jump to the start edge in Arabic, and cells that
   mix a number with an Arabic unit ("46 دقيقة") flip to the wrong order. Wrap
   the number itself in Bidi.Iso / .num-inline instead. */
td.num, th.num { text-align: end; font-variant-numeric: tabular-nums; unicode-bidi: isolate; }

/* ===========================================================================
 * THE DIALOG (Components/Shell/Dialog.razor, on the native <dialog>)
 * =========================================================================== */

/* FULL SCREEN ON A PHONE: a centred card on a 375px screen is a small window
   scrolling inside a page that also scrolls. One scroll, one surface. It paints
   in the top layer, so there is no z-index to lose an argument with. */
.dlg {
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text);
  max-inline-size: 100%;
  max-block-size: 100%;
  inline-size: 100%;
  block-size: 100%;
}
/* THE FALLBACK, pinned. A dialog opened by [open] rather than showModal() is
   not in the top layer and has no backdrop; fixing it to the viewport above the
   tabs (z-index 30) makes the degraded mode look and behave like the real one.
   In Sokkara the tab bar painted over a fallback dialog's Save and a tap meant
   for it navigated away. */
.dlg[open] { position: fixed; inset: 0; z-index: 40; }
.dlg::backdrop { 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 {
  position: sticky;
  inset-block-start: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  padding-block: 12px;
  padding-inline: 16px;
  border-block-end: 1px solid var(--border);
  background: var(--bg);
}
.dlg-title { flex: 1 1 auto; min-inline-size: 0; margin: 0; font-size: 1.05rem; line-height: 1.4; }
.dlg-close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: 44px;
  min-block-size: 44px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
}
.dlg-close:hover { color: var(--text); }
.dlg-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding-block: 16px max(24px, env(safe-area-inset-bottom));
  padding-inline: 16px;
}
/* On a larger screen it becomes a card: 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(560px, 92vw);
    /* fit-content, not auto: a fixed box with both block insets at 0 and an
       auto height stretches to the whole viewport. */
    block-size: fit-content;
    max-block-size: 88dvh;
    border-radius: 14px;
    overflow: hidden;
    box-shadow: var(--lift-shadow);
  }
  .dlg[open] { inset: 0; margin: auto; }
}
.confirm-text { margin-block-end: 18px; line-height: 1.7; }
.confirm-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.confirm-actions .btn { flex: 1 1 10rem; }

/* ===========================================================================
 * THE (i) AND ITS SHEET, AND THE "?" BADGE (InfoTip.razor, Tip.razor)
 * ===========================================================================
   The native Popover API. Where it is missing the (i) is not left as a dead
   button: the sheet is printed in place instead (the @supports not block). */
.info-btn,
.tip-btn,
.info-pop,
.tip-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 otherwise. */
    font: inherit;
    letter-spacing: normal;
    text-transform: none;
    position: relative;
  }
  /* THE CIRCLE IS 32px, THE TARGET 44px (2026-10-18, review): the shell's floor
     for anything pressed with a thumb (see .btn-small). The extra 6px a side is
     an invisible part of the button itself, so lines, headings and .field-head
     keep their height; beside a field it overlaps the label's edge by a sliver,
     and the (i) - later in the DOM - wins it, which is the right way round:
     missing the (i) used to land on the label, focus the input and open the
     keyboard. Pinned by ThemeCssTests.
     Targets_are_44px_long_usernames_break_and_names_take_their_own_direction. */
  .info-btn::before { content: ""; position: absolute; inset: -6px; }
  .info-btn:hover { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
  .info-ico { inline-size: 19px; block-size: 19px; }

  /* 22px is modest against a 44px ideal, but this sits inside running text and
     a larger circle disrupts the line. */
  .tip-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 22px;
    block-size: 22px;
    padding: 0;
    margin-inline-start: 6px;
    border-radius: 50%;
    border: 1px solid var(--border);
    background: var(--bg);
    color: var(--muted);
    /* The page's own family, so the Arabic badge (؟, an Arabic letter) comes
       from Cairo. */
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    vertical-align: middle;
  }
  .tip-btn:hover { border-color: var(--accent); color: var(--accent); }

  .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 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;
  }
  /* Deliberately no open/close animation. Sokkara faded these in with
     @starting-style, and in a tab where the transition did not advance the
     sheet stayed at opacity 0 while computing to display:block - an invisible
     full-screen overlay swallowing taps. A hard show/hide is worth more. */
  .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. */
  .info-pop,
  .tip-pop {
    position: fixed;
    inset: auto 0 0 0;
    margin: 0;
    inline-size: 100%;
    max-inline-size: none;
    max-block-size: 78dvh;
    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 {
      inset: 0;
      margin: auto;
      inline-size: min(92vw, 460px);
      block-size: fit-content;
      max-block-size: 78dvh;
      border-radius: 16px;
      border-block-end: 1px solid var(--border);
      padding-block-end: 0;
    }
  }
}
.info-head {
  position: sticky;
  inset-block-start: 0;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding-block: 14px 10px;
  padding-inline: 16px;
  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; }
/* 44px like .dlg-close: it sits in a corner of a bottom sheet, under a thumb
   (2026-10-18, review; it was 40). */
.info-close {
  flex: none;
  inline-size: 44px;
  block-size: 44px;
  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-body { padding-block: 12px 20px; padding-inline: 16px; }
.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; }

/* WITHOUT THE POPOVER API THE SHEETS ARE PRINTED IN PLACE (Safari before 17).
   Hiding the (i) there left every explanation with no way to reach it. */
@supports not selector(:popover-open) {
  .info-pop {
    display: block;
    position: static;
    margin-block: 4px 8px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--muted);
    font-size: 0.9rem;
    line-height: 1.7;
  }
  .info-pop .info-head { display: none; }
  .info-pop .info-body { padding: 0; }
}

/* ===========================================================================
 * FOLDS - a card shut until tapped (Fold.razor)
 * =========================================================================== */
.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-block: 8px;
  padding-inline: 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: ""; }
/* The disclosure triangle. VERTICAL arrows are NOT mirrored in RTL and must not
   be: down means "open me" in both languages. */
.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-offset: -2px; border-radius: var(--card-radius); }
.fold[open] > summary { border-block-end: 1px solid var(--border); }
.fold-body { padding: 12px; }

/* ===========================================================================
 * MENUS - the More list and the Settings menu (MenuRow.razor)
 * =========================================================================== */
.more-group {
  margin-block: 18px 8px;
  margin-inline: 8px;
  color: var(--muted);
  font-size: 0.84rem;
  font-weight: 800;
}
.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-block: 10px;
  padding-inline: 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-offset: -2px; }
.more-row > .ico:first-child { color: var(--accent); }
.more-name { flex: 1 1 auto; min-inline-size: 0; }
.more-row .ico-flip { color: var(--muted); }
.morelist .more-account { margin-block-start: 20px; }
.more-signout,
.more-signout > .ico:first-child { color: var(--danger); }
.more-signout[aria-disabled="true"] { opacity: 0.6; cursor: progress; }

/* A chevron points the way the reader reads - the direction the element itself
   computes, not an 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; }

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

/* PAPER IS INK ON WHITE, WHATEVER THE PHONE SHOWS. The print palette is stated
   under a selector that outranks both dark blocks (:root alone loses to
   :root[data-theme="dark"] and to the guarded block - two classes against one;
   Sokkara printed pale text for white paper from a dark phone before this).
   Every token a dark block declares is restated here. */
@media print {
  :root,
  :root:not([data-theme="light"]) {
    color-scheme: light;
    --bg: #ffffff;
    --surface: #ffffff;
    --border: #b9c2cc;
    --text: #000000;
    --muted: #414b56;
    --accent: #0b4f4a;
    --accent-teal: #0b4f4a;
    --accent-indigo: #0b4f4a;
    --accent-plum: #0b4f4a;
    --accent-slate: #0b4f4a;
    --on-accent: #ffffff;
    --ok: #166534;
    --danger: #b91c1c;
    --warn-bg: #ffffff;
    --warn-border: #777777;
    --warn-text: #000000;
    --third-food: #c2410c;
    --third-drink: #0369a1;
    --third-breath: #7e22ce;
    --protein: #be123c;
    --carbs: #a16207;
    --fat: #4d7c0f;
    --over: #b91c1c;
    --under: #b45309;
    --on-target: #15803d;
    --lift-shadow: none;
    --sheet-shadow: none;
  }
  @page { size: A4 portrait; margin: 12mm 10mm; }
  /* Print keeps its own scale: the screen bump must not inflate a printout. */
  html { font-size: 100%; }
  body { background: #fff; color: #000; font-size: 11pt; padding: 0 !important; }
  .wrap { max-inline-size: none; padding: 0; }
  .appbar, .tabbar, .info-btn, .info-pop, .tip-btn, .tip-pop, .langswitch,
  .noprint, .dlg, .dlg::backdrop { display: none !important; }
  .printonly { display: block; }
  section, .fold { break-inside: avoid; page-break-inside: avoid; box-shadow: none; }
  /* A shut fold is content that did not print. */
  .fold::details-content { content-visibility: visible !important; block-size: auto !important; }
  .fold > *:not(summary) { display: block !important; }
  .fold > summary::after { content: ""; }
  tr { break-inside: avoid; page-break-inside: avoid; }
  thead { display: table-header-group; }
  .scroll { overflow: visible !important; }
  h1 { font-size: 15pt; }
  h2 { font-size: 12pt; }
  a { color: #000; text-decoration: none; }
  /* Colour carries meaning in a pill and a third; keep it on paper. */
  .pill, [data-third] { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
