/* ============================================================
   css/ — iOS-style glass UI, RTL-aware, light + dark
   ============================================================ */

:root {
  /* Set by trackKeyboard() when a software keyboard is up, and zero
     the rest of the time. Declared here so the stylesheet owns the
     name and the JS only ever changes its value. */
  --kb: 0px;

  /* How far each end of a weight track dissolves — and why it is here
     rather than on the elements that fade.

     A custom property holding a var() is substituted where it is
     DEFINED, not where it is finally used. --wt-mask lives on :root, so
     its var(--wt-fade) resolves against :root; with the width declared
     further down on .wt the whole gradient computed to invalid and both
     fades silently vanished. Anything the ramp reads has to be next to
     the ramp, which also means one fade width for everything wearing
     it — no bad thing, since two different ones on the same card would
     be a difference nobody could name a reason for. */
  /* The logo itself, in its own colours, wherever the app draws it.
     brand.js points this at the logo set in the admin panel. */
  --brand-logo: url(../icons/logo.png);

  --wt-fade: 30px;
  --wt-mask: linear-gradient(to right,
    rgb(0 0 0 / 0)     0,
    rgb(0 0 0 / .043)  calc(var(--wt-fade) * .125),
    rgb(0 0 0 / .156)  calc(var(--wt-fade) * .25),
    rgb(0 0 0 / .316)  calc(var(--wt-fade) * .375),
    rgb(0 0 0 / .5)    calc(var(--wt-fade) * .5),
    rgb(0 0 0 / .684)  calc(var(--wt-fade) * .625),
    rgb(0 0 0 / .844)  calc(var(--wt-fade) * .75),
    rgb(0 0 0 / .957)  calc(var(--wt-fade) * .875),
    rgb(0 0 0 / 1)     var(--wt-fade),
    rgb(0 0 0 / 1)     calc(100% - var(--wt-fade)),
    rgb(0 0 0 / .957)  calc(100% - var(--wt-fade) * .875),
    rgb(0 0 0 / .844)  calc(100% - var(--wt-fade) * .75),
    rgb(0 0 0 / .684)  calc(100% - var(--wt-fade) * .625),
    rgb(0 0 0 / .5)    calc(100% - var(--wt-fade) * .5),
    rgb(0 0 0 / .316)  calc(100% - var(--wt-fade) * .375),
    rgb(0 0 0 / .156)  calc(100% - var(--wt-fade) * .25),
    rgb(0 0 0 / .043)  calc(100% - var(--wt-fade) * .125),
    rgb(0 0 0 / 0)     100%);
  color-scheme: light dark;

  /* mesh backdrop — glass needs something behind it to refract */
  --mesh-1: #6ee7b7;
  --mesh-2: #38bdf8;
  --mesh-3: #a78bfa;
  --mesh-4: #fcd34d;
  --page:   #eef2f0;

  --text:      #0f1a17;
  --text-dim:  #5b6b66;
  --text-mute: #586561;
  /* Twenty-five rules ask for --muted and nothing ever declared it, so
     every caption and secondary line on the eating-out sheet, the
     appointment page, the FIB-4 result, the snapshot and the metric
     detail has been rendering at full body colour. An unresolvable
     var() on `color` computes to inherit, which is why it looked like a
     design choice rather than a fault. */
  --muted: var(--text-mute);

  /* Letters on a button fill. This used to be a near-black, because the
     button was mint and white measured 1.88:1 on it. The button is now
     a deep teal of the same hue, five steps down, which is what lets
     the letters be white: 4.64:1 at the lightest point of the light
     gradient and 4.64:1 on the dark one. Both stated below. */
  --on-accent: #ffffff;

  /* The button fill, and only the button fill. --accent stays where it
     is — it draws rings, bars and washes, which are graphics and answer
     to 3:1, not to 4.5:1, and which would go muddy if they were dragged
     down here with the buttons. Anything with LETTERS on an accent fill
     uses these two: .btn, .seg button.on, .take-btn, .chat-send, the
     selected calendar day and the user's own chat bubble. The last two
     were already asking for white on the mint and getting 1.88:1. */
  --btn:   #0b8377;
  --btn-2: #06564f;

  /* And the ink for the fills that did NOT go deep — a goal colour under
     a tick, the mint under a checkmark. This is the near-black that used
     to be --on-accent, kept because those fills still need it: white on
     a goal colour measures 1.74:1 on --veg, and white on flat --accent
     measures 3.40 in light and 1.88 in dark. */
  --on-bright: #04231f;

  /* the glass itself */
  --glass:        rgba(255, 255, 255, .62);
  --glass-strong: rgba(255, 255, 255, .78);
  --glass-sunk:   rgba(255, 255, 255, .38);
  --hairline:     rgba(255, 255, 255, .65);
  --hairline-dim: rgba(15, 26, 23, .07);
  /* No shadow on a card or a button (the owner, 2026-10-12): each says
     what it is with its surface and its edge. What floats keeps
     --shadow-lift, or the glass's --lg-shadow-float. */
  --shadow:       none;
  --shadow-lift:  0 2px 6px rgba(15,26,23,.06), 0 18px 48px rgba(15,26,23,.14);

  --accent:      #0e9c8f;
  --accent-2:    #3dd598;
  --accent-ink:  #0b6b62;
  --accent-wash: rgba(14, 156, 143, .12);
  /* A medicine's own tint (home-meds.js): teal, sage, slate, mauve —
     none red, blue or warm, which already mean something here. */
  --mt1: #0b6b62; --mt1-wash: rgba(14, 156, 143, .13);
  --mt2: #4a6419; --mt2-wash: rgba(110, 150, 40, .16);
  --mt3: #3d5566; --mt3-wash: rgba(70, 100, 125, .14);
  --mt4: #8a3560; --mt4-wash: rgba(160, 60, 110, .12);

  /* Badge letters, for a badge sitting on a wash of its own colour.
     --warn on an 18% wash of --warn measured 2.52:1 in the light theme,
     which is the steatosis and fibrosis grade on the liver card — the
     one piece of measured medical information on that screen. The wash
     is unchanged; these are the letters on it. Dark needs none of this:
     the same colours reach 6:1 on a dark surface, so the dark blocks
     point these back at the originals. */
  --warn-ink:   #985404;   /* 4.61:1 on the liver card, 5.5 on a plain one */
  --danger-ink: #c2193e;   /* 4.52:1 */
  --gold-ink:   #925e06;   /* 4.54:1 */
  --sleep-ink:  #4959f5;   /* 4.60:1 on the page, 5.2 on a card */

  /* end-of-day verdict tones */
  --ds-good:  #0b6b62;
  --ds-mixed: #b45309;
  --ds-bad:   #be123c;

  --water:   #38bdf8;
  --move:    #fb923c;
  --veg:     #4ade80;
  --coffee:  #a97142;
  --sleep:   #818cf8;
  --resist:  #f472b6;
  --med:     #22d3ee;
  --gold:    #eab308;
  --danger:  #e11d48;
  --warn:    #d97706;

  /* The same eight, dark enough to read. The set above are FILLS — a
     lit pip, a ring stroke, a wash under a glyph — and they were also
     being used as the letters and line art on top of a pale card, where
     every one of them failed: --veg measured 1.66:1, --med 1.73, --gold
     1.83, --water 2.05. Nine rules wanted an ink and had only a fill to
     reach for.

     Dark needs no such set — the bright values clear 5.8:1 to 9.9:1 on
     a dark card — so the dark blocks point these back at the originals,
     the way --warn-ink and --danger-ink already do. --coffee is the one
     exception: it fails in BOTH themes (3.91 light, 4.22 dark) and so
     moves in both.

     Reached through --ci, which the views set beside --c. */
  /* The ink a goal's own colour resolves to, set beside --c by whatever
     renders a goal, an avoid tile, a metric row or the live sheet. It is
     declared here as well so that a rule reaching for it outside any of
     those still lands on a readable colour rather than on nothing —
     which is how --muted went twenty-five rules deep before anyone
     noticed. */
  --ci: var(--accent-ink);

  --water-ink:  #0679ad;
  --move-ink:   #b95504;
  --veg-ink:    #18833f;
  --coffee-ink: #9a673c;
  --resist-ink: #db117a;
  --med-ink:    #0b7d8f;

  /* The ink a run of days is written in. Not --warn itself: that is a
     mid amber, and at eleven pixels bold it lands near 3.2:1 on a pale
     card. Darkened here and lightened in the dark blocks — the filled
     tiers state their own colours, so only this one has to move. */
  --streak-ink: color-mix(in srgb, var(--warn) 68%, #000);

  /* Surfaces and edges that sit ON a card rather than on the page.
     --glass-sunk and --hairline are white at low alpha, which reads as
     a recess over the mesh background and as nothing at all inside a
     card — in light mode the meal card's Cook/Swap/Edit/Skip were
     white buttons with whiter borders on a white panel. These two go
     the other way in light and the same way in dark. */
  --sunk-ink: rgba(6, 28, 24, .055);
  --edge:     rgba(6, 28, 24, .14);

  /* A run of days, once it is worth looking at: amber into orange, with
     ink dark enough to read on the lighter end. The same three values
     serve both themes — a lit pill is lit against anything. */
  --streak-lit:    #fbbf24;
  --streak-hot:    var(--warm);
  --streak-on-lit: var(--on-warm);

  /* The warm accent. Teal is the brand and does the work — buttons,
     links, focus, progress. Apricot is the other half of the palette
     and is kept for the moments that are about a person rather than a
     task: a run of days, something new, a welcome. It is a FILL: on
     white it measures 2.49:1, so letters on it are --on-warm (6.27:1)
     and letters in it are --warm-ink (5.66:1, 5.01 on the page). Next
     to teal it stays apart for a deuteranope (ΔE 18.5).
     See BRAND.md before reaching for it anywhere new. */
  --warm:      #f08a4b;
  --warm-ink:  #a84c14;
  --on-warm:   #3d1a00;
  --warm-wash: color-mix(in srgb, var(--warm) 16%, transparent);

/* One relationship, one number.

   A value with its caption under it is the single most repeated shape
   in this app — a stat tile, a chip in the day summary, a metric row, a
   medication dose, a lab value, a saved meal. It was built twenty-three
   times with six different gaps (0, 1, 2, 3, 6) and three different
   mechanisms (flex gap, margin-bottom, margin-top), and nothing chose
   any of them: they are what each component happened to be given on the
   day it was written. Two of them were 0, which is why the hero number
   and the word under it looked stuck together.

   --pair-x is the same pair laid out side by side, which four hero
   readouts do. */
/* And the two insets. A surface sitting on the page gets --pad-card; a
   panel sitting inside one gets --pad-panel, because 16 inside 16 reads
   as a box in a box.

   Eleven surfaces were on nine different values — 14, 16 16 14, 14 16,
   12 13, 12 14, 13 14, 14 15, 14 14 — and the differences are not
   design, they are the day each one was written. Two of them sit
   directly beside a .card at 16 on the same screen, which is where the
   eye catches it. */
  --pad-card: 16px;
  --pad-panel: 12px;

  --pair-y: 2px;
  --pair-x: 10px;
  /* And the other vertical pair: a sentence, and the sentence that
     explains it. --pair-y is a value with its caption — a number over
     a word — where two pixels is right because the two read as one
     object. Two full sentences at two sizes need air between them or
     they run together. */
  --say-y: 6px;

  --r-xl: 28px;
  --r-lg: 22px;
  --r-md: 16px;
  --r-sm: 12px;

  --font: 'SF Pro Text', 'SF Pro Display', -apple-system, BlinkMacSystemFont,
          system-ui, 'Segoe UI', Roboto, sans-serif;
  --font-ar: 'SF Arabic', 'SF Arabic Text', -apple-system, BlinkMacSystemFont,
             'Geeza Pro', 'Noto Naskh Arabic', 'Noto Sans Arabic', Tahoma, sans-serif;
  --ease: cubic-bezier(.32, .72, 0, 1);
}

/* Dark tokens live in three places on purpose:
   · the media query, for anyone who has not chosen (guarded so an
     explicit "light" choice still wins)
   · the [data-theme="dark"] block, for an explicit choice
   Keeping them in sync matters — change one, change the other. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --mesh-1: #065f52;
    --mesh-2: #0c4a6e;
    --mesh-3: #4c1d95;
    --mesh-4: #78350f;
    --page:   #070b0a;

    --text:      #f0f5f3;
    --text-dim:  #a3b3ae;
    --text-mute: #8ea39c;

    --glass:        rgba(30, 41, 38, .58);
    --glass-strong: rgba(38, 50, 46, .74);
    --glass-sunk:   rgba(12, 20, 18, .42);
    --hairline:     rgba(255, 255, 255, .10);
    --hairline-dim: rgba(255, 255, 255, .07);
    --shadow:       none;
    --shadow-lift:  0 2px 8px rgba(0,0,0,.4), 0 22px 54px rgba(0,0,0,.55);

    --accent:      #3dd598;
    --accent-2:    #6ee7b7;
    --accent-ink:  #9df3cf;
    --accent-wash: rgba(61, 213, 152, .14);
    --mt1: #9df3cf; --mt1-wash: rgba(61, 213, 152, .16);
    --mt2: #c5e38a; --mt2-wash: rgba(160, 200, 90, .16);
    --mt3: #b4cadb; --mt3-wash: rgba(150, 180, 205, .16);
    --mt4: #f2b3d3; --mt4-wash: rgba(230, 140, 190, .16);

    /* Dark is squeezed from both sides and the window is one step wide.
       White at 4.5:1 caps the fill at Y ≤ .1833; standing clear of a
       sheet at 3:1 floors it at Y ≥ .1574. Only a near-flat pair fits,
       which is why this one barely moves: 4.55:1 under white at the top
       and 3.04:1 against a sheet at the bottom. The gradient is the
       price of white letters in dark, and .btn carries a hairline so
       the edge does not depend on the fill at all. */
    --btn:   #178479;
    --btn-2: #167d72;

    --water-ink:  var(--water);
    --move-ink:   var(--move);
    --veg-ink:    var(--veg);
    --resist-ink: var(--resist);
    --med-ink:    var(--med);
    /* the one that fails on a dark card too */
    --coffee-ink: #d9a06e;

    /* Gold and the indigo do reach 4.7:1 unchanged on a dark surface.
       Warn and danger do not — danger measured 3.68:1, which is why
       these two get their own lighter values rather than an alias. */
    --warn-ink:   #fbbf24;   /* 10.35:1 */
    --danger-ink: #fb7185;   /* 6.42:1  */
    --gold-ink:   var(--gold);
    --sleep-ink:  var(--sleep);

    --ds-good:  #6ee7b7;
    --ds-mixed: #fbbf24;
    --ds-bad:   #fb7185;

    --streak-ink: color-mix(in srgb, var(--warn) 45%, #fff);
    --warm-ink:   #fdba8c;   /* 10.4:1 on a dark card */

    --sunk-ink: rgba(255, 255, 255, .06);
    --edge:     rgba(255, 255, 255, .15);
  }
}

:root[data-theme="dark"] {
    --mesh-1: #065f52;
    --mesh-2: #0c4a6e;
    --mesh-3: #4c1d95;
    --mesh-4: #78350f;
    --page:   #070b0a;

    --text:      #f0f5f3;
    --text-dim:  #a3b3ae;
    --text-mute: #8ea39c;

    --glass:        rgba(30, 41, 38, .58);
    --glass-strong: rgba(38, 50, 46, .74);
    --glass-sunk:   rgba(12, 20, 18, .42);
    --hairline:     rgba(255, 255, 255, .10);
    --hairline-dim: rgba(255, 255, 255, .07);
    --shadow:       none;
    --shadow-lift:  0 2px 8px rgba(0,0,0,.4), 0 22px 54px rgba(0,0,0,.55);

    --accent:      #3dd598;
    --accent-2:    #6ee7b7;
    --accent-ink:  #9df3cf;
    --accent-wash: rgba(61, 213, 152, .14);
    --mt1: #9df3cf; --mt1-wash: rgba(61, 213, 152, .16);
    --mt2: #c5e38a; --mt2-wash: rgba(160, 200, 90, .16);
    --mt3: #b4cadb; --mt3-wash: rgba(150, 180, 205, .16);
    --mt4: #f2b3d3; --mt4-wash: rgba(230, 140, 190, .16);

    /* Dark is squeezed from both sides and the window is one step wide.
       White at 4.5:1 caps the fill at Y ≤ .1833; standing clear of a
       sheet at 3:1 floors it at Y ≥ .1574. Only a near-flat pair fits,
       which is why this one barely moves: 4.55:1 under white at the top
       and 3.04:1 against a sheet at the bottom. The gradient is the
       price of white letters in dark, and .btn carries a hairline so
       the edge does not depend on the fill at all. */
    --btn:   #178479;
    --btn-2: #167d72;

    --water-ink:  var(--water);
    --move-ink:   var(--move);
    --veg-ink:    var(--veg);
    --resist-ink: var(--resist);
    --med-ink:    var(--med);
    /* the one that fails on a dark card too */
    --coffee-ink: #d9a06e;

    /* Gold and the indigo do reach 4.7:1 unchanged on a dark surface.
       Warn and danger do not — danger measured 3.68:1, which is why
       these two get their own lighter values rather than an alias. */
    --warn-ink:   #fbbf24;   /* 10.35:1 */
    --danger-ink: #fb7185;   /* 6.42:1  */
    --gold-ink:   var(--gold);
    --sleep-ink:  var(--sleep);

    /* The verdict colours. These were only ever set in the media query,
       so choosing Dark by hand while the phone was on Light left the day
       summary drawn in colours meant for a white background. */
    --ds-good:  #6ee7b7;
    --ds-mixed: #fbbf24;
    --ds-bad:   #fb7185;

    --streak-ink: color-mix(in srgb, var(--warn) 45%, #fff);
    --warm-ink:   #fdba8c;   /* 10.4:1 on a dark card */

    --sunk-ink: rgba(255, 255, 255, .06);
    --edge:     rgba(255, 255, 255, .15);
}

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

/* hidden means hidden. The attribute is only a user-agent display:none,
   so any class that sets display wins over it — the sign-in screen's
   message box shares .msg with the coach's chat bubbles (display: flex)
   and sat on the screen as an empty pill under the email field. */
[hidden] { display: none !important; }

html, body {
  margin: 0; padding: 0;
  min-height: 100%;
  color: var(--text);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  /* Both axes: sideways is what the phone's edge-swipe rides on, and
     letting it through means a stray horizontal drag walks out of the
     app entirely. */
  overscroll-behavior: contain;
}
html[dir="rtl"] body { font-family: var(--font-ar); }

/* The page colour lives on <html>; <body> stays transparent so the
   negative z-index mesh shows through it rather than being painted over. */
html { background: var(--page); }
body { background: transparent; }

/* ---------- mesh backdrop ---------- */
/* The colour is masked away from the top third, so the greeting and the
   first card sit on a plain background instead of a wash. */
#mesh {
  position: fixed; inset: -20vh -20vw; z-index: -1;
  pointer-events: none;
  filter: blur(64px) saturate(150%);
  opacity: .5;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, transparent 30%, #000 62%, #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0, transparent 30%, #000 62%, #000 100%);
}
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) #mesh { opacity: .5; } }
:root[data-theme="dark"] #mesh { opacity: .5; }
:root[data-theme="dark"] { color-scheme: dark; }
:root[data-theme="light"] { color-scheme: light; }
#mesh i {
  position: absolute; display: block;
  border-radius: 50%;
  animation: drift 26s var(--ease) infinite alternate;
}
#mesh i:nth-child(1) { width: 58vw; height: 58vw; left: -6vw;  top: -4vh;  background: var(--mesh-1); }
#mesh i:nth-child(2) { width: 52vw; height: 52vw; right: -8vw; top: 12vh;  background: var(--mesh-2); animation-delay: -6s; }
#mesh i:nth-child(3) { width: 60vw; height: 60vw; left: 4vw;   bottom: 2vh; background: var(--mesh-3); animation-delay: -13s; opacity: .8; }
#mesh i:nth-child(4) { width: 40vw; height: 40vw; right: 2vw;  bottom: 14vh; background: var(--mesh-4); animation-delay: -19s; opacity: .6; }

@keyframes drift {
  from { transform: translate3d(0,0,0) scale(1); }
  to   { transform: translate3d(4vw, 6vh, 0) scale(1.16); }
}
@media (prefers-reduced-motion: reduce) {
  #mesh i { animation: none; }
}
