/* ============================================================================
   Clinic Card — Family Medical Tracker design system
   Token-driven, LIGHT-FIRST (dark override via [data-theme="dark"]).

   Light-first is deliberate and inverts apps/swimming-meet-tracker, whose
   structure this file copies. Mom is a USER of this app and its least technical
   one; light is the designed-for case and dark is a supported override
   (CARL family_medical_tracker-003).

   Single source of visual truth. EVERY colour in this app is a custom property
   declared in the two token blocks below and NOWHERE else — no hex literal may
   appear in any template, and none may appear outside :root / [data-theme] here.
   Hand-authored CSS + custom properties only: no framework, no build step.

   Elderly-first floors, enforced by scripts/check_contrast.py:
     - body text >= 18px (--text-body); information never rendered smaller
     - tap targets >= 48px (--tap-min)
     - WCAG AA (4.5:1) floor for text, AAA (7:1) for body text,
       3:1 for graphical marks
   Every ratio in the comments below is MEASURED, not estimated.
   ============================================================================ */

/* === Reset === */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

/* === Tokens — LIGHT (default): Clinic Card ================================== */
:root {
  /* surfaces — cool blue-grey ground, white cards */
  --bg-ground:      #eef2f6;
  --bg-card:        #ffffff;
  --bg-soft:        #f4f9fc;   /* secondary button face */
  --bg-alarm:       #fdf3e8;   /* needs-company tint */

  /* text */
  --text-ink:       #0f2231;   /* 16.24:1 on card — AAA body */
  --text-secondary: #5a6b7a;   /*  5.50:1 on card — AA */
  --text-past:      #657787;   /*  4.62:1 on card — AA. Mockup's #93a5b2 measured
                                  2.54:1 and FAILED AA; corrected, never reproduced. */
  --text-on-accent: #ffffff;   /*  6.02:1 on --accent — AA */
  --text-on-alarm:  #ffffff;   /*  6.26:1 on --alarm-strong — AA */

  /* accent — deep teal-blue */
  --accent:         #0f6b8a;   /*  6.02:1 on card — AA */
  --accent-ink:     #0f5c78;   /*  7.02:1 on --bg-soft — AAA. Accent AS TEXT. */

  /* alarm — needs-company. TWO tokens, never one (CARL rule 5 / decision -010).
     They answer different WCAG rules and must never be collapsed:
       --alarm-strong carries TEXT  -> held to AA 4.5:1 for --text-on-alarm
       --alarm-mark   carries NO text -> held to the 3:1 non-text floor
     The mockup used #b8600c for both; white on it measures 4.45:1 and FAILS AA,
     which is the entire reason the split exists. */
  --alarm-strong:   #9a4a06;   /* white on it 6.26:1 — AA. Button face. */
  --alarm-ink:      #9a4a06;   /*  5.71:1 on --bg-alarm — AA. Alarm text. */
  --alarm-mark:     #b8600c;   /*  4.45:1 on card, 4.06:1 on alarm bg — over the
                                  3:1 graphical floor. Dot ring + cell border ONLY. */

  /* lines & structure */
  --border-card:    #d6e0e8;
  --border-cell:    #dce5ec;
  --border-divider: #e4ebf0;
  --border-hair:    #eef2f5;
  --border-soft:    #b9cbd8;   /* secondary button edge */
  --border-alarm:   #e8c9a3;
  --border-alarm-2: #e0b183;

  /* marks — dots. Non-text, 3:1 floor. */
  --dot-done:       #78899a;   /* 3.59:1 on card, 3.19:1 on ground — over the 3:1
                                  graphical floor. The mockup's #b7c6d1 measured
                                  1.75:1 and FAILED; it is a UI component conveying
                                  state, so the non-text floor applies to it. Still
                                  clearly recessive against --accent at 6.02:1, so the
                                  done/upcoming hierarchy the mockup drew is intact. */

  /* per-person stripes — GRAPHICAL marks, never text colours. A person's name
     always renders in --text-ink, so colour is never the sole carrier of
     identity (an elderly primary user should not have to memorise a palette;
     CARL family_medical_tracker-006). All measured on --bg-card: */
  --person-1:       #0f6b8a;   /* 6.02:1 */
  --person-2:       #7a5ea8;   /* 5.26:1 */
  --person-3:       #a8386b;   /* 6.10:1 */
  --person-4:       #2f6f4f;   /* 5.99:1 */
  --person-5:       #3d5a9e;   /* 6.66:1 */

  /* status */
  --success:        #1e6b4f;
  --danger:         #a32020;
  --bg-success:     #e4f1eb;
  --bg-danger:      #fbeaea;

  /* elevation */
  --shadow-sm:      0 1px 3px rgba(15, 34, 49, 0.07);
  --shadow:         0 4px 14px rgba(15, 34, 49, 0.10);

  /* ---- scale: type ----
     --text-body (18px) is the FLOOR for anything carrying information.
     The three tokens below it are CHROME ONLY and the list is CLOSED
     (decision -010). Each is annotated where it is permitted to be used. */
  --font-sans:      system-ui, -apple-system, "Segoe UI", sans-serif;
  --text-chrome:    0.75rem;    /* 12px — weekday initials + stat-strip labels ONLY */
  --text-label:     0.8125rem;  /* 13px — uppercase section labels ONLY */
  --text-daynum:    0.9375rem;  /* 15px — month-grid day numerals ONLY */
  --text-body:      1.125rem;   /* 18px — FLOOR for all information-bearing text */
  --text-lg:        1.1875rem;  /* 19px */
  --text-xl:        1.25rem;    /* 20px */
  --text-2xl:       1.625rem;   /* 26px */
  --text-3xl:       2rem;       /* 32px */

  /* ---- scale: touch ---- */
  --tap-min:        48px;       /* every interactive control's min-height */
  --tap-cta:        58px;       /* primary page action */
  --tap-avatar:     48px;

  /* ---- scale: radius + space ---- */
  --radius-card:    12px;
  --radius-cta:     11px;
  --radius-stat:    10px;
  --radius-btn:     9px;
  --radius-cell:    7px;
  --radius-pill:    999px;
  --space-1: 0.25rem; --space-2: 0.5rem;  --space-3: 0.75rem;
  --space-4: 1rem;    --space-5: 1.5rem;  --space-6: 2rem;
}

/* === Tokens — DARK override ================================================= */
[data-theme="dark"] {
  --bg-ground:      #121a21;
  --bg-card:        #1b262f;
  --bg-soft:        #223039;
  --bg-alarm:       #33220f;

  --text-ink:       #e4ecf2;   /* 12.88:1 on card — AAA body */
  --text-secondary: #a3b3c0;   /*  7.16:1 on card — AA */
  --text-past:      #7d8f9d;   /*  4.61:1 on card — AA */
  --text-on-accent: #08222b;   /*  8.14:1 on --accent — AA */
  --text-on-alarm:  #241505;   /*  6.62:1 on --alarm-strong — AA */

  --accent:         #5fc3e0;   /*  7.60:1 on card — AA */
  --accent-ink:     #7fd0e8;   /*  7.80:1 on --bg-soft — AAA */

  --alarm-strong:   #e08a2e;   /* --text-on-alarm on it 6.62:1 — AA */
  --alarm-ink:      #f0b070;   /*  8.10:1 on --bg-alarm — AA */
  --alarm-mark:     #e08a2e;   /*  5.75:1 on card — over the 3:1 graphical floor */

  --border-card:    #2d3b47;
  --border-cell:    #2a3742;
  --border-divider: #26323c;
  --border-hair:    #212c35;
  --border-soft:    #3a4c5a;
  --border-alarm:   #5c4020;
  --border-alarm-2: #6d4d26;

  --dot-done:       #748695;   /* 4.09:1 on card — over the 3:1 graphical floor.
                                  First choice #4a5a67 measured 2.16:1 and FAILED;
                                  caught by check_contrast.py, not by eye. */

  --person-1:       #4fb4d4;   /* 6.46:1 */
  --person-2:       #a98fd4;   /* 5.54:1 */
  --person-3:       #e07aa8;   /* 5.51:1 */
  --person-4:       #5faa80;   /* 5.53:1 */
  --person-5:       #7d99d8;   /* 5.44:1 */

  --success:        #57b98f;
  --danger:         #e8807a;
  --bg-success:     #16281f;
  --bg-danger:      #2b1717;

  --shadow-sm:      0 1px 3px rgba(0, 0, 0, 0.40);
  --shadow:         0 4px 14px rgba(0, 0, 0, 0.50);
}

/* === Base elements ========================================================== */
body {
  font-family: var(--font-sans);
  font-size: var(--text-body);
  background: var(--bg-ground);
  color: var(--text-ink);
  line-height: 1.55;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

h1 { font-size: var(--text-2xl); font-weight: 700; line-height: 1.2; letter-spacing: -0.015em; }
h2 { font-size: var(--text-xl); font-weight: 700; line-height: 1.25; }
h3 { font-size: var(--text-lg); font-weight: 700; line-height: 1.3; }

a { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 2px; }
a:hover { text-decoration-thickness: 2px; }

strong, b { font-weight: 700; }

:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* === Layout ================================================================= */
.main { max-width: 720px; margin: 0 auto; padding: var(--space-4); }

.topbar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-ground);
}
.brand {
  font-size: var(--text-xl); font-weight: 700; letter-spacing: -0.01em;
  color: var(--text-ink); text-decoration: none;
}
.brand:hover { text-decoration: none; }

/* Sign out sits in the topbar beside the theme toggle. A <form> because logout is a
   POST (CSRF token, unfireable by a link); this rule just stops the form element from
   adding a block-level line break inside the flex row. The button inside is a plain
   .btn-soft — no bespoke styling, and a text label rather than a glyph. */
.signout-form { display: inline-flex; }

.nav-links { list-style: none; display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.nav-links a {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: var(--tap-min);
  padding: 0 var(--space-3); border-radius: var(--radius-btn);
  font-size: var(--text-body); font-weight: 600;
  color: var(--accent-ink); text-decoration: none;
  border: 1px solid transparent;   /* reserved so the active state adds no layout shift */
}
/* WHERE AM I — deliberately loud. The first version tinted the background one shade
   and was invisible on a phone in daylight, which for an elderly-first app is the same
   as having no active state at all. Now the current page is a filled accent chip:
   --text-on-accent measures 6.02:1 on --accent (AA), the same pair the primary button
   uses, so the strongest signal in the nav costs no contrast. */
.nav-links a.active {
  background: var(--accent);
  color: var(--text-on-accent);
  border-color: var(--accent);
  font-weight: 700;
}

/* Theme toggle — the app's ONE icon-only control.
   CARL rule 4 requires text labels rather than icons alone; this is a deliberate,
   operator-approved exception (2026-08-17) on the grounds that a theme switcher is
   chrome, not an action anyone needs to find. It carries an aria-label so it is never
   actually unlabelled, and it keeps the full --tap-min target in both dimensions. */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--tap-min); min-height: var(--tap-min);
  border-radius: var(--radius-pill); border: 1px solid var(--border-soft);
  background: var(--bg-soft); color: var(--accent-ink);
  cursor: pointer;
}
.theme-toggle .icon { width: 24px; height: 24px; }
/* The moon shows in light mode (press it to go dark) and the sun in dark mode — the
   icon names the DESTINATION, matching what the old text label said. */
.theme-toggle .icon-sun { display: none; }
[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
[data-theme="dark"] .theme-toggle .icon-sun { display: inline-flex; }

/* === Icons ==================================================================
   Inline SVG, always currentColor — an icon inherits the token of whatever it sits
   in, so it cannot introduce a colour or fail contrast on its own. Sized in `em` so
   it scales with its label rather than needing a size per call site. */
.icon {
  width: 1.15em; height: 1.15em;
  flex-shrink: 0;               /* never squashed by a long label */
  vertical-align: -0.15em;
}

/* === Section label — CHROME (13px permitted here) =========================== */
.sec-label {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-label);
  letter-spacing: 0.1em; text-transform: uppercase; font-weight: 700;
  color: var(--text-secondary);
  margin-bottom: var(--space-2);
}
/* Icons in a section label read at the label's weight, not the body's — they are
   wayfinding marks, so they scale up slightly against the small uppercase text. */
.sec-label .icon { width: 1.35em; height: 1.35em; }

/* === Card — white, 6px leading per-person stripe ============================ */
.card {
  position: relative; overflow: hidden;
  background: var(--bg-card);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-card);
  padding: var(--space-3) var(--space-4) var(--space-3) var(--space-5);
  box-shadow: var(--shadow-sm);
}
.card::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 6px;
  background: var(--stripe, var(--accent));
}
.card + .card { margin-top: var(--space-3); }
.card-stack { display: flex; flex-direction: column; gap: var(--space-3); }

/* Per-person stripe binding. Templates select a person by CLASS, never by passing a
   colour — that is what keeps every colour in this file and templates hex-free. */
.stripe-1 { --stripe: var(--person-1); }
.stripe-2 { --stripe: var(--person-2); }
.stripe-3 { --stripe: var(--person-3); }
.stripe-4 { --stripe: var(--person-4); }
.stripe-5 { --stripe: var(--person-5); }

/* === Change-feed row ======================================================== */
.chg      { font-size: var(--text-body); line-height: 1.45; color: var(--text-ink); }
.chg-meta { font-size: var(--text-body); color: var(--text-secondary); margin-top: var(--space-1); }

/* === Appointment row ======================================================== */
.appt-top  { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2); }
.appt-when { font-size: var(--text-lg);  font-weight: 700; color: var(--text-ink); }
.appt-who  { font-size: var(--text-body); font-weight: 700; color: var(--text-ink); }
.appt-doc  { font-size: var(--text-body); margin-top: var(--space-1); color: var(--text-ink); }
.appt-spec { font-size: var(--text-body); color: var(--text-secondary); }

/* === Companion line — "Going with her: ..." ================================= */
.companion        { font-size: var(--text-body); color: var(--text-secondary); margin-top: var(--space-2); }
.companion b      { color: var(--text-ink); font-weight: 700; }
.companion.none b { color: var(--alarm-ink); }

/* === Buttons — all >= --tap-min, all text-labelled ========================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--tap-min); padding: 0 var(--space-4);
  border-radius: var(--radius-btn); border: 1px solid transparent;
  font-family: var(--font-sans); font-size: var(--text-body); font-weight: 600;
  cursor: pointer; text-decoration: none;
}
.btn:hover { text-decoration: none; filter: brightness(1.05); }
.btn-primary { background: var(--accent); color: var(--text-on-accent); font-weight: 700; }
.btn-soft    { background: var(--bg-soft); color: var(--accent-ink); border-color: var(--border-soft); }
.btn-alarm   { background: var(--alarm-strong); color: var(--text-on-alarm); font-weight: 700; }
.btn-alarm-alt { background: var(--bg-card); color: var(--alarm-ink); border-color: var(--border-alarm-2); }
.btn-row     { display: flex; gap: var(--space-2); margin-top: var(--space-3); }
.btn-row .btn { flex: 1; }

.cta {
  display: flex; align-items: center; justify-content: center;
  min-height: var(--tap-cta); margin: var(--space-4);
  /* Horizontal padding is NOT optional here. .btn carries `padding: 0 var(--space-4)`
     and .cta was written without it, so a long label ("Add appointment") ran into the
     button's own edges at 375px. Operator caught it at the Phase 2 visual checkpoint. */
  padding: 0 var(--space-5);
  border-radius: var(--radius-cta); border: 0;
  background: var(--accent); color: var(--text-on-accent);
  font-family: var(--font-sans); font-size: var(--text-lg); font-weight: 700;
  cursor: pointer; text-decoration: none;
}
.cta:hover { text-decoration: none; }

/* === Address — one tap to navigate ==========================================
   The whole address is the tap target, not a small icon beside it, and it is a real
   <a> so a long-press offers copy/share. Deep-linked to Google Maps with no API key
   (.seed/PLANNING.md -> Integration Points, Phase 2). */
.address-link {
  display: inline-flex; align-items: center;
  min-height: var(--tap-min); padding: var(--space-2) 0;
  font-size: var(--text-body); color: var(--accent-ink);
  text-decoration: underline; text-underline-offset: 3px;
}

/* === Phone — one tap to call ================================================
   Same treatment as .address-link and for the same reason: the number and the address
   are the two things somebody digs for in a waiting room, so both are whole-line tap
   targets rather than icons. Reuses existing tokens — adds no colour. */
.phone-link {
  display: inline-flex; align-items: center;
  min-height: var(--tap-min); padding: var(--space-2) 0;
  font-size: var(--text-body); color: var(--accent-ink);
  text-decoration: underline; text-underline-offset: 3px;
}

/* === Detail field — a labelled value pair =================================== */
.detail-field { padding: var(--space-3) 0; border-bottom: 1px solid var(--border-hair); }
.detail-field:last-child { border-bottom: 0; }
.detail-field .lbl {
  display: block; font-size: var(--text-label);
  letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;
  color: var(--text-secondary); margin-bottom: var(--space-1);
}
.detail-field .val { font-size: var(--text-body); color: var(--text-ink); }

/* === Change history — a dated, attributed timeline ==========================
   Compact by design: this is a record you scan, not a feed you read. The card-style
   .chg row on Home is the reading surface; this is its archive. */
.history { list-style: none; }
.history-entry {
  position: relative; padding: var(--space-3) 0 var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--border-hair);
}
.history-entry:last-child { border-bottom: 0; }
.history-entry::before {
  content: ""; position: absolute; left: 0; top: 1.35rem;
  width: 9px; height: 9px; border-radius: 50%; background: var(--dot-done);
}
.history-entry .what { font-size: var(--text-body); color: var(--text-ink); }
.history-entry .who  { font-size: var(--text-body); color: var(--text-secondary); margin-top: var(--space-1); }

/* === Provider row — directory + search results ============================== */
.provider-row { padding: var(--space-3) 0; border-bottom: 1px solid var(--border-divider); }
.provider-row:last-child { border-bottom: 0; }
.provider-row .name { font-size: var(--text-lg); font-weight: 700; color: var(--text-ink); }
.provider-row .spec { font-size: var(--text-body); color: var(--text-secondary); }

/* === Pills + chips ========================================================== */
.pill {
  display: inline-flex; align-items: center;
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill);
  font-size: var(--text-body); font-weight: 600;
  background: var(--bg-soft); color: var(--accent-ink);
}
.pill-alarm { background: var(--bg-alarm); color: var(--alarm-ink); }
.pill-done  { background: var(--bg-success); color: var(--success); }

.companion-chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: var(--tap-min); padding: 0 var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--bg-soft); color: var(--text-ink);
  font-size: var(--text-body);
}
.avatar {
  display: grid; place-items: center; flex-shrink: 0;
  width: var(--tap-avatar); height: var(--tap-avatar); border-radius: 50%;
  background: var(--stripe, var(--accent)); color: var(--text-on-accent);
  font-size: var(--text-body); font-weight: 700;
}

/* === Stat strip — labels are CHROME (12px permitted) ======================== */
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-2); }
.stat {
  background: var(--bg-card); border: 1px solid var(--border-card);
  border-radius: var(--radius-stat); padding: var(--space-2) var(--space-1); text-align: center;
}
.stat .lab {
  font-size: var(--text-chrome);
  letter-spacing: 0.04em; text-transform: uppercase; font-weight: 700;
  color: var(--text-secondary);
}
.stat .val {
  font-size: var(--text-2xl); font-weight: 700; line-height: 1.15;
  font-variant-numeric: tabular-nums; color: var(--text-ink);
}
.stat.alert { background: var(--bg-alarm); border-color: var(--border-alarm); }
.stat.alert .lab, .stat.alert .val { color: var(--alarm-ink); }

/* === Month grid — a NAVIGATOR, not a reading surface ========================
   7 columns at 375px yields ~46px cells, under the --tap-min floor. Rather than
   break the elderly-first standard, cells carry ONLY a day number and dots; every
   word actually read lives in the day-detail panel below at full --text-body size
   (CARL family_medical_tracker-006). This is why --text-daynum and --text-chrome
   are permitted here and nowhere else. */
.monthbar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.monthtitle { font-size: var(--text-xl); font-weight: 700; }
.navarrow {
  display: grid; place-items: center;
  min-width: var(--tap-min); min-height: var(--tap-min);
  border-radius: var(--radius-stat); border: 1px solid var(--border-card);
  background: var(--bg-card); color: var(--accent-ink);
  font-family: var(--font-sans); font-size: var(--text-xl); font-weight: 700; cursor: pointer;
}
.today-jump {
  display: flex; align-items: center; justify-content: center;
  min-height: var(--tap-min); margin-top: var(--space-2);
  font-size: var(--text-body); font-weight: 600; color: var(--accent-ink);
}

.wk { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; margin-bottom: var(--space-1); }
.wk div {
  text-align: center; font-size: var(--text-chrome);
  font-weight: 700; letter-spacing: 0.04em; color: var(--text-secondary);
}
.grid7 { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.cell {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  min-height: 46px; padding: 3px 2px 4px;
  background: var(--bg-card); border: 1px solid var(--border-cell);
  border-radius: var(--radius-cell);
  /* Cells became <a> when the grid was made tappable. Without this every day number
     renders underlined in link blue, which turns the navigator into a wall of links
     and hides the today/needs states under the underline. Colour still comes from
     .dnum, so the past-day and alarm treatments are unaffected. */
  color: inherit; text-decoration: none;
}
.cell:hover { text-decoration: none; }
.cell.empty { background: transparent; border-color: transparent; }
.cell.today { border: 2px solid var(--accent); }
.cell.needs { background: var(--bg-alarm); border-color: var(--border-alarm); }
.dnum {
  font-size: var(--text-daynum); font-weight: 700;
  font-variant-numeric: tabular-nums; line-height: 1.1; color: var(--text-ink);
}
.cell.past .dnum { color: var(--text-past); }
.dots { display: flex; gap: 3px; align-items: center; min-height: 10px; }
.dot { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); }
.dot.done  { background: var(--dot-done); }
/* Hollow ring — the SECOND of the alarm's three redundant signals (CARL rule 5):
   amber tint on the cell, this ring on the dot, and its own labelled section. */
.dot.needs { background: var(--bg-card); border: 2.5px solid var(--alarm-mark); }

/* === Day-detail panel — full-size reading surface =========================== */
.detail {
  background: var(--bg-card); border: 1px solid var(--border-card);
  border-radius: var(--radius-card); overflow: hidden;
}
.dhead {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2);
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border-divider);
}
.dhead h3 {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-lg); font-weight: 700;
}
.dhead span { font-size: var(--text-body); color: var(--text-secondary); }
/* Third redundant alarm signal: the section is labelled, not merely tinted. */
.dhead.alert { background: var(--bg-alarm); border-bottom-color: var(--border-alarm); }
.dhead.alert h3 { color: var(--alarm-ink); }
.ditem { padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border-hair); }
.ditem:last-child { border-bottom: 0; }
.ditem .t { font-size: var(--text-body); font-weight: 700; }
.ditem .s { font-size: var(--text-body); color: var(--text-secondary); margin-top: var(--space-1); }
/* The whole row opens the appointment. Block + full tap height rather than a small
   link on the title, because the elderly-first floor applies to a list row the same
   way it applies to a button. Inherits ink so the row does not read as blue-underlined
   web text. */
.ditem-link {
  display: block; min-height: var(--tap-min);
  color: inherit; text-decoration: none;
}
.ditem-link:hover { text-decoration: none; }
.ditem-link .s { display: block; }

/* === Forms ================================================================== */
.field { margin-bottom: var(--space-4); }
.field label {
  display: block; margin-bottom: var(--space-1);
  font-size: var(--text-body); font-weight: 600; color: var(--text-ink);
}
input, select, textarea {
  width: 100%; min-height: var(--tap-min); padding: var(--space-2) var(--space-3);
  font-family: var(--font-sans); font-size: var(--text-body);
  background: var(--bg-card); color: var(--text-ink);
  border: 1px solid var(--border-soft); border-radius: var(--radius-btn);
}
input:focus, select:focus, textarea:focus { outline: 3px solid var(--accent); outline-offset: 1px; }
.field-hint { font-size: var(--text-body); color: var(--text-secondary); margin-top: var(--space-1); }

/* === Person picker — the login name list ====================================
   Five named buttons, not a dropdown. Five names is a list, and a name Mom can hit
   with a thumb beats one she has to open a menu to reach. Each is a full .btn, so it
   inherits --tap-min. */
.person-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-2); }
.person-grid .btn { width: 100%; }
@media (min-width: 420px) { .person-grid { grid-template-columns: repeat(2, 1fr); } }

/* PIN entry. inputmode="numeric" on the element makes the phone show a number pad;
   the wide tracking makes four digits readable at a glance for an older eye. */
.pin-input {
  text-align: center; letter-spacing: 0.5em; font-size: var(--text-2xl);
  font-variant-numeric: tabular-nums;
}

/* === Search =================================================================
   The field and its button sit on one row at full tap height. */
.search-row { display: flex; gap: var(--space-2); align-items: flex-end; }
.search-row .field { flex: 1; margin-bottom: 0; }

/* === Flash banners ========================================================== */
.flash {
  display: block; padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-btn); margin-bottom: var(--space-3);
  font-size: var(--text-body);
}
.flash-success { background: var(--bg-success); color: var(--success); border: 1px solid var(--success); }
.flash-error   { background: var(--bg-danger);  color: var(--danger);  border: 1px solid var(--danger); }

/* === Empty state ============================================================ */
.empty-state {
  text-align: center; padding: var(--space-6) var(--space-4);
  background: var(--bg-card); border: 1px dashed var(--border-soft);
  border-radius: var(--radius-card); color: var(--text-secondary);
  font-size: var(--text-body);
}
.empty-state-title {
  font-size: var(--text-lg); font-weight: 700;
  color: var(--text-ink); margin-bottom: var(--space-2);
}

/* === Utilities ============================================================== */
.row     { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.spread  { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.section { margin-top: var(--space-5); }

/* === Responsive — mobile-first; 375px is the designed-for width ============= */
@media (min-width: 760px) {
  .main { padding: var(--space-5); }
  .stats { gap: var(--space-3); }
}
