/* ============================================================
   iTumbuh — design tokens (single source of truth)
   ============================================================

   WHY THIS FILE EXISTS
   --------------------
   Before this file, the same token set was declared in SIX places:
   assets/auth.css, assets/dashboard.css (twice), assets/onboarding.css,
   assets/clinic-report.css, and inline :root blocks in growth_report.html,
   nutrition_report.html, index.html and staff_dashboard_desa.html.

   They had already drifted apart. Measured before the change:

       --primary   #3B82F6  in the app
                   #2563EB  in growth_report.html and nutrition_report.html
       --fg        #111827  in the app
                   #1F2937  in those same two report pages

   Nobody decided that; it happened because there was no one place to
   change. This file is that place. Everything else now inherits.

   (staff_dashboard_desa.html also redefined --primary to #10B981. That
   one was DELIBERATE — Klinik Desa is themed green to distinguish it
   from Klinik Kesihatan. It is preserved below as an explicit theme,
   [data-clinic="desa"], so it reads as a decision rather than as more
   drift.)


   THE CONTRAST PROBLEM, AND WHAT WAS DONE ABOUT IT
   ------------------------------------------------
   The brand palette is vivid, which is the point of the design. But
   vivid mid-tones cannot carry small white text. Measured with the
   WCAG 2.1 relative-luminance formula, against the real rendered
   colours:

       white on --primary   #3B82F6   3.68:1    AA needs 4.5:1  FAIL
       white on --secondary #10B981   2.54:1                    FAIL
       white on --accent    #F59E0B   2.15:1                    FAIL

   775 text nodes across 28 pages failed AA on this account.

   The fix deliberately does NOT desaturate the palette — the bold
   colour blocking is the design's whole personality. Instead each hue
   gets two companions:

     --on-<hue>    the text colour to use ON that colour as a surface
     --<hue>-ink   a darker step of the hue, for the hue used AS text

   For blue, white still works once the surface steps down one notch:

       white on #2563EB  (Blue 600)     5.17:1  PASS

   For amber, dark ink on the vivid colour is both more accessible AND
   more true to the flat/poster aesthetic — an amber block with
   near-black text is a road sign, which is exactly the register the
   design system asks for:

       #111827 on #F59E0B (Amber 500)   8.26:1  PASS

   Emerald was given the same treatment at first (#111827 on Emerald 500
   is 6.99:1, so it passed) and it was the wrong call at SECTION scale:
   dark ink across a full-bleed saturated green reads as muddy, and the
   feedback said so. The two big emerald sections on the home page now
   use --secondary-deep with white text instead — 5.48:1, still a vivid
   emerald. --on-secondary remains #111827 for SMALL emerald surfaces,
   where dark ink on the bright green is right.

   Every ratio quoted in this file was computed, not estimated.
   ============================================================ */

:root {
  /* ---------- brand hues: surfaces, large display type, decoration ---- */
  --primary:          #3B82F6;   /* Blue 500    */
  --secondary:        #10B981;   /* Emerald 500 */
  --accent:           #F59E0B;   /* Amber 500   */
  --danger:           #DC2626;   /* Red 600     */

  /* ---------- action steps: the same hues at a weight that can carry
       white text. Use these for buttons, pills and anything where a
       white label sits on the colour. -------------------------------- */
  --primary-dark:     #2563EB;   /* white on this = 5.17:1  PASS  */
  --secondary-dark:   #047857;   /* white on this = 5.48:1  PASS  */
  /* A full-bleed emerald SECTION that carries white text. Emerald 500
     cannot: white on it is 2.54:1. Dark ink on Emerald 500 does work
     (6.99:1) and was the first answer here, but read as muddy at
     section scale, so the surface steps down instead and the text goes
     back to white — 5.48:1, and still a vivid emerald rather than a
     forest green. */
  --secondary-deep:   #047857;
  --accent-dark:      #B45309;   /* white on this = 5.02:1  PASS  */
  --danger-dark:      #B91C1C;   /* white on this = 6.47:1  PASS  */

  /* ---------- what to write ON each brand surface --------------------
       Paired with the hue above them, not chosen independently. */
  --on-primary:       #FFFFFF;   /* on --primary-dark  5.17:1 */
  --on-secondary:     #111827;   /* on --secondary     6.99:1 — dark ink,
                                    because white on emerald is 2.54:1 */
  --on-accent:        #111827;   /* on --accent        8.26:1 — same reason,
                                    white on amber is 2.15:1  */
  --on-danger:        #FFFFFF;   /* on --danger-dark   6.47:1 */
  --on-dark:          #FFFFFF;   /* on --dark / --dark-2      */
  /* The muted/secondary text colour for DARK sections. On a dark ground
     the muted colour has to move lighter, not darker — the footer used
     #6B7280 there, which is 3.67:1 on --dark-2 and fails. A single
     "muted grey" token cannot serve both grounds, so there are two. */
  --fg-muted-on-dark: #9CA3AF;   /* 6.99:1 on --dark-2, 5.78:1 on --dark */

  /* ---------- ink: the hues used AS TEXT, on white or on --muted ------ */
  --primary-ink:      #1D4ED8;   /* 6.70:1 on white, 6.09:1 on --muted */
  --secondary-ink:    #047857;   /* 5.48:1 on white, 4.98:1 on --muted */
  --accent-ink:       #B45309;   /* 5.02:1 on white, 4.56:1 on --muted */
  --danger-ink:       #B91C1C;   /* 6.47:1 on white, 5.88:1 on --muted */

  /* ---------- neutrals ------------------------------------------------ */
  --bg:               #FFFFFF;
  --fg:               #111827;   /* Gray 900 — 17.74:1 on white */
  --fg-soft:          #374151;   /* Gray 700 — 10.31:1 on white */
  --fg-muted:         #4B5563;   /* Gray 600 —  7.56:1 on white,
                                    6.87:1 on --muted. This replaces the
                                    old #9CA3AF (2.54:1) and #6B7280
                                    (4.39:1 on --muted, i.e. a near miss
                                    that failed on every grey card). */
  --muted:            #F3F4F6;   /* Gray 100 — secondary page ground */
  --muted-2:          #E5E7EB;   /* Gray 200 */
  /* Two border tokens, because they answer to different rules.
     WCAG 1.4.11 requires 3:1 for the boundary of a CONTROL — the edge
     that tells you where a field is. It does not apply to a purely
     decorative rule between two blocks of text. Naming them apart is
     what stops the decorative value being reached for on an input,
     which is how --border-strong (#D1D5DB, 1.47:1 on white) came to be
     doing a job it cannot do. */
  --border:            #E5E7EB;   /* decorative dividers only */
  --border-strong:     #D1D5DB;   /* heavier divider, still decorative */
  --border-interactive:#6B7280;   /* input & control edges — 4.83:1 on
                                     white, 4.39:1 on --muted */
  --dark:             #1F2937;
  --dark-2:           #111827;

  /* ---------- tinted surfaces (flat colour blocks, no gradients) ------ */
  --tint-primary:     #EFF6FF;   /* Blue 50    */
  --tint-secondary:   #ECFDF5;   /* Emerald 50 */
  --tint-accent:      #FFFBEB;   /* Amber 50   */
  --tint-danger:      #FEF2F2;   /* Red 50     */

  /* ---------- radii --------------------------------------------------- */
  --radius-sm:        6px;
  --radius-md:        8px;
  --radius-lg:        12px;
  --radius-pill:      999px;

  /* ---------- spacing: multiples of 4, as the design system asks ------ */
  --space-1:          4px;
  --space-2:          8px;
  --space-3:          12px;
  --space-4:          16px;
  --space-5:          20px;
  --space-6:          24px;
  --space-8:          32px;
  --space-10:         40px;
  --space-12:         48px;

  /* Container width. The design system asks for max-w-7xl; that is
     1280px. index.html defined this locally as --max-w. */
  --max-w:            1280px;

  /* ---------- motion --------------------------------------------------
       "Digital, snappy, direct" — two durations, not a dozen. */
  --motion-fast:      .15s;
  --motion:           .2s;
  --motion-slow:      .3s;
  --ease:             cubic-bezier(.2, 0, .2, 1);

  /* ---------- elevation ------------------------------------------------
       The design system is explicit: no box shadows on elements. Depth
       comes from flat colour blocks instead — a white card on the grey
       page ground reads as a card without pretending to float.

       This is a token rather than a deletion so the decision is in one
       place and is reversible: 271 shadowed elements were rendering
       across 28 pages before this. */
  --shadow-card:      none;
  --shadow-raised:    none;
  /* dashboard.css had its own pair of card-shadow variables. Aliased
     rather than find-and-replaced so that flipping the design decision
     back is one edit here, not a sweep through the stylesheet. */
  --pd-shadow:        var(--shadow-card);
  --pd-shadow-hover:  var(--shadow-raised);
  /* The one exception. An overlay must separate itself from the page it
     covers, and it is not "an element on the surface" — it is a
     different plane by definition. Kept deliberately, not by oversight. */
  --shadow-overlay:   0 8px 24px rgba(0, 0, 0, .14);

  /* ---------- focus ----------------------------------------------------
       With no shadows to lean on, the focus state has to be a hard,
       high-contrast outline. */
  --focus-ring:       3px solid var(--primary-ink);
  --focus-offset:     2px;
}

/* ============================================================
   Klinik Desa theme — an explicit decision, not drift.
   The Desa dashboard is green so that staff can tell at a glance
   which tier of clinic they are looking at. Applied by putting
   data-clinic="desa" on <html> or <body>.
   ============================================================ */
:root[data-clinic="desa"],
[data-clinic="desa"] {
  --primary:       #10B981;
  --primary-dark:  #047857;   /* white on this = 5.48:1 PASS */
  --primary-ink:   #047857;
  --tint-primary:  #ECFDF5;
}

/* ============================================================
   Base resets that belong with the tokens rather than with any
   one page's stylesheet.
   ============================================================ */
:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--radius-sm);
}

/* A word longer than its container pushes the whole page sideways. The
   XSS test seeds an 80-character clinic name and the two report pages
   overflowed by 42px at 390px because of it. The payload is not real
   content — but a long name, a pasted URL or an unspaced address is, and
   the app takes clinic and child names from a database it does not
   control the width of. This only affects words that would overflow
   anyway, so nothing that fits changes. */
body {
  overflow-wrap: break-word;
}

/* Someone who has asked their system for less motion gets the state
   change without the movement — never the state change withheld. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------------------------------------
   Touch targets — WCAG 2.5.8 (AA), 24x24 CSS px.

   Measured in a real browser at 1280px and 390px, these controls
   were 4px to 20px tall. None of them were small by intent; they
   were as tall as their text and no taller, because nothing had
   ever said otherwise.

   min-height (not height) so nothing that is already big enough
   is shrunk, and so a longer translation can still grow the box.

   A note on what does NOT work here, since it was tried: giving a
   small control a transparent ::after of 44x44 does not enlarge
   its hit area. Measured with elementFromPoint, the pseudo-element
   loses the paint order to later positioned siblings and the
   points 12px out still resolve to something else. The z-score
   help button was made genuinely 24x24 instead.

   Links inside a sentence are deliberately NOT listed: WCAG 2.5.8
   exempts them, and padding them out would break the line.
   ------------------------------------------------------------ */
.nav-links a,
.footer-col a,
.aud-cta,
.growth-panel a[href],
#explain-toggle-btn,
.acct-role-badge,
.acct-see-all,
.afc-back,
.flw-skip,
.ob-skip {
  min-height: 24px;
  display: inline-flex;
  align-items: center;
}

/* The carousel dots are 34x4 of paint. The bar stays 4px; the
   button around it grows to a reachable height. */
.sc-dot {
  min-height: 24px;
  display: inline-flex;
  align-items: center;
  padding: 10px 0;
  box-sizing: content-box;
}

/* A range input's own box is the track height unless told
   otherwise; the thumb is what you grab, and it is smaller still. */
input[type="range"] {
  min-height: 24px;
}
