/* ==========================================================================
   NoriClinic design system
   --------------------------------------------------------------------------
   Built on the supplied brand identity, not on a direction of our own. The
   mark is a figure held in an open cradle: an upright stem, a curve that
   carries, a head. Blue holds, cyan is the person. Everything below serves
   that and nothing invents alongside it.

   ONE DELIBERATE DEPARTURE FROM THE IDENTITY DECK

   The identity is presented on near-black (--ink). The product is not: the
   marketing site and the app both run on white. That is a decision, not a
   drift — the screen is read in a bright consulting room between patients,
   often on a cheap panel, and a dark interface in that room is harder to scan
   and prints badly. The brand's colours are carried at full strength; only
   the ground changes. The ink is kept as a real token so the identity can be
   rendered on its own terms wherever a dark surface is genuinely wanted.

   COLOUR CARRIES MEANING

   Blue is action. Cyan is the person or the live moment. Green is confirmed.
   Amber is waiting on someone. Red is a problem. No colour is decorative, and
   no state is ever knowable by colour alone — every one of them also carries
   a word.
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&family=IBM+Plex+Sans+Arabic:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap');

:root {
  /* --- brand ---------------------------------------------------------- */
  --nori-blue:      #2e63d8;   /* the mark's cradle — primary action     */
  --nori-blue-deep: #1f47a8;
  --nori-blue-lift: #5286f5;   /* on dark grounds only                   */
  --nori-cyan:      #4fdcec;   /* the head — the person, the live moment */
  --nori-cyan-ink:  #06525e;   /* cyan darkened until it clears 4.5:1 on
                                  its own tint — the tint carries the brand,
                                  the ink only has to be readable            */
  --nori-green:     #3aae85;
  --nori-ink:       #0a1424;   /* identity ground, kept for dark surfaces */

  /* --- neutrals -------------------------------------------------------
     Cool and tilted toward the brand blue so the greys belong to the mark
     rather than being generic UI grey. */
  --n-0:   #ffffff;
  --n-25:  #fbfcfe;
  --n-50:  #f4f7fb;   /* page ground */
  --n-100: #eaeff7;
  --n-150: #dde4ef;   /* hairlines */
  --n-200: #c8d2e2;
  --n-300: #a3b0c6;
  --n-400: #7b8aa3;
  --n-500: #5b6b85;   /* muted text — 5.2:1 on the page ground */
  --n-600: #45536b;
  --n-700: #303c50;
  --n-800: #1d2736;   /* body text */
  --n-900: #0a1424;   /* headings — the identity ink */

  /* --- action ---------------------------------------------------------- */
  --accent:       var(--nori-blue);
  --accent-hover: var(--nori-blue-deep);
  --accent-soft:  #e6ecfb;
  --accent-ring:  rgba(46, 99, 216, .22);

  /* --- status ----------------------------------------------------------
     Green confirmed, amber waiting, red wrong. Cyan is NOT a status: it
     belongs to the person and the live moment, and letting it also mean
     "info" would make the one brand colour ambiguous. */
  --ok:          #17654a;
  --ok-soft:     #e2f3ec;
  --warn:        #8a5a12;
  --warn-soft:   #fbf0da;
  --danger:      #b3202f;
  --danger-soft: #fbe9eb;

  /* --- space and shape -------------------------------------------------
     The mark is built from fully-rounded caps, so the system is soft rather
     than sharp — the radius follows the logo, not a fashion. */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
  --s-5: 24px; --s-6: 32px; --s-7: 48px; --s-8: 64px;

  --r-sm: 6px; --r-md: 10px; --r-lg: 16px; --r-pill: 999px;

  --topbar-h: 60px;
  --ctl-h:    40px;
  --ctl-h-sm: 32px;
  --nav-y:    9px;
  --fs-base:  15px;

  /* --- elevation -------------------------------------------------------
     Quiet, and tinted with the brand blue rather than neutral black, so a
     raised surface still belongs to the identity. */
  --shadow-card:    0 1px 2px rgba(10, 20, 36, .04), 0 2px 8px rgba(10, 20, 36, .04);
  --shadow-overlay: 0 24px 60px rgba(10, 20, 36, .20), 0 2px 8px rgba(10, 20, 36, .08);

  --surface-blur: rgba(255, 255, 255, .86);
  --backdrop:     rgba(10, 20, 36, .46);
  --warn-line:    #e6d29a;
  --warn-tint:    #fdf8ec;

  --sidebar-w: 258px;
  --content-max: 1200px;

  /* --- type ------------------------------------------------------------
     Manrope for Latin, IBM Plex Sans Arabic for Arabic, IBM Plex Mono for
     figures. Both pinned by the identity. Arabic falls through the stack
     because Manrope has no Arabic coverage, which is what makes one
     declaration serve both directions.

     IBM Plex is on our own house list of over-used faces. The identity picks
     it, and a supplied brand outranks that list — this is a note so the next
     person knows it was a decision rather than an oversight. */
  --font:         'Manrope', 'IBM Plex Sans Arabic', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-display: 'Manrope', 'IBM Plex Sans Arabic', system-ui, sans-serif;
  --font-mono:    'IBM Plex Mono', ui-monospace, SFMono-Regular, monospace;

  --track-display: -.02em;
  --track-label:   .08em;
}

/* ==========================================================================
   Themes
   --------------------------------------------------------------------------
   Light is the product's real ground and the default. Dark is the identity's
   own surface, and here it is a relief mode for a doctor reviewing the day at
   night — not an inversion. The blue lifts because #2e63d8 cannot be read as
   text on near-black, and the cyan stays: it is the one colour that was
   designed for this ground.
   ========================================================================== */

[data-theme="dark"] {
  --n-0:   #131c2e;
  --n-25:  #111927;
  --n-50:  #0a1424;
  --n-100: #1a2438;
  --n-150: #2a3548;
  --n-200: #384459;
  --n-300: #4e5b73;
  --n-400: #74829b;
  --n-500: #93a1b8;
  --n-600: #b0bccf;
  --n-700: #ccd5e3;
  --n-800: #e6ecf5;
  --n-900: #f7f9fc;

  --nori-blue: #5286f5;
  --accent:       #5286f5;
  --accent-hover: #7aa3f8;
  --accent-soft:  #1c2a47;
  --accent-ring:  rgba(82, 134, 245, .3);
  --nori-cyan-ink: #4fdcec;

  --ok:          #45c69b;
  --ok-soft:     #102a22;
  --warn:        #dcae57;
  --warn-soft:   #2b2413;
  --danger:      #ef7b8a;
  --danger-soft: #31151c;

  --surface-blur: rgba(10, 20, 36, .86);
  --backdrop:     rgba(0, 0, 0, .62);
  --warn-line:    #3d3319;
  --warn-tint:    #1c1810;

  --shadow-card:    0 1px 2px rgba(0, 0, 0, .3);
  --shadow-overlay: 0 24px 60px rgba(0, 0, 0, .55), 0 2px 8px rgba(0, 0, 0, .35);

  color-scheme: dark;
}

/* --- reset --------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--n-800);
  background: var(--n-50);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -.015em;
  color: var(--n-900);
  text-wrap: balance;
}
h1 { font-size: 26px; }
h2 { font-size: 19px; }
h3 { font-size: 15px; }
p  { margin: 0; }

a { color: inherit; text-decoration: none; text-underline-offset: .18em; text-decoration-thickness: 1px; }
a:hover { color: var(--accent); }

/* --- the display register ------------------------------------------------
   Wide tracking and caps are how the Latin face gets its air. Arabic letters
   JOIN: tracking them pulls the word apart and caps do not exist. So every
   tracked rule is switched off under RTL, and the Arabic display face carries
   the same airiness through Reem Kufi's own geometric openness and a taller
   line instead. Equal weight, two different means — never one language
   wearing the other's typography. */
.display {
  font-family: var(--font-display);
  font-stretch: 112%;
  font-weight: 400;
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  line-height: 1.08;
}
.label-track {
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--n-500);
}
[dir="rtl"] .display,
[dir="rtl"] .label-track {
  letter-spacing: normal;
  text-transform: none;
  font-stretch: normal;
  line-height: 1.5;
}
[dir="rtl"] .display { line-height: 1.35; }

/* --- browser surfaces ----------------------------------------------------
   The parts nobody draws still belong to the design. Left at their defaults
   these are the loudest signal that a page was assembled rather than built. */
::selection { background: var(--sheer-saffron); color: var(--n-900); }
html { scrollbar-color: var(--n-300) transparent; scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--n-300); border: 3px solid var(--n-50); }
::-webkit-scrollbar-thumb:hover { background: var(--n-400); }
input, textarea, select { caret-color: var(--accent); }
/* Every figure in this product is a time, a dose or a count. All of them
   have to line up in a column. */
table, .tabular, time, .num { font-variant-numeric: tabular-nums; }

/* Keyboard focus must be visible everywhere, not only on buttons and inputs.
   :focus-visible rather than :focus so a mouse click never leaves a ring. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 1px;
}

button, input, select, textarea { font: inherit; color: inherit; }

table { border-collapse: collapse; width: 100%; }

/* --- utilities ----------------------------------------------------------- */

.muted      { color: var(--n-500); }
.small      { font-size: 13px; }
.tiny       { font-size: 12px; }
.strong     { font-weight: 600; }
.tabular    { font-variant-numeric: tabular-nums; }

/* Line icons. One stroke weight across the whole set — that consistency is
   the entire reason these replaced the Unicode glyphs they used to be. */
.ic {
  width: 16px;
  height: 16px;
  flex: none;
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.nav a .ic { opacity: .9; }
.nav a.active .ic { opacity: 1; color: var(--accent); }
.search .ico .ic { width: 15px; height: 15px; }
.stack      { display: flex; flex-direction: column; gap: var(--s-4); }
.row        { display: flex; align-items: center; gap: var(--s-3); }
.row-between{ display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.wrap       { flex-wrap: wrap; }
.grow       { flex: 1; }
.center     { text-align: center; }
.hidden     { display: none !important; }
/* Off-screen, not display:none — a screen reader still announces it, which is
   the point of a label whose control is obvious to everyone else. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.plain-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}

/* The clinic you are in, under the wordmark. */
.clinic-switch, .clinic-here {
  padding: 0 var(--s-5) var(--s-3);
}
.clinic-switch select {
  width: 100%;
  height: 32px;
  font-size: 13px;
}
.clinic-here {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  font-weight: 600;
  color: var(--n-700);
  min-inline-size: 0;
}
.clinic-here .ic { color: var(--n-400); flex: none; }
.clinic-here span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mt-2 { margin-top: var(--s-2); }
.mt-3 { margin-top: var(--s-3); }
.mt-4 { margin-top: var(--s-4); }
.mt-5 { margin-top: var(--s-5); }
.mt-6 { margin-top: var(--s-6); }

/* --- app shell ----------------------------------------------------------- */

.app { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--sidebar-w);
  flex-shrink: 0;
  background: transparent;
  border-inline-start: 1px solid var(--n-150);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
}

.brand { padding: var(--s-5) var(--s-5) var(--s-4); }
.brand-mark { display: block; text-decoration: none; }
.brand-only { display: none; }        /* shown only when the rail is collapsed */

.nav { padding: var(--s-2) var(--s-3); display: flex; flex-direction: column; gap: 2px; flex: 1; }

.nav a {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--nav-y) var(--s-3);
  border-radius: var(--r-sm);
  color: var(--n-600);
  font-size: 14px;
  font-weight: 500;
  transition: background .12s ease, color .12s ease;
}
.nav a:hover { background: var(--n-100); color: var(--n-900); }
/* The active item is lifted off the ground rather than filled with colour:
   the accent is spent on the icon and the count, where it is a signal. */
.nav a.active {
  background: var(--n-0);
  color: var(--n-900);
  font-weight: 600;
  box-shadow: var(--shadow-card);
}
.nav a.active .ico { color: var(--accent); opacity: 1; }
.nav a .ico { width: 18px; text-align: center; opacity: .85; font-size: 15px; }
.nav a .count {
  margin-inline-start: auto;
  font-size: 11px;
  font-weight: 600;
  background: transparent;
  color: var(--n-400);
  padding: 1px 2px;
  font-variant-numeric: tabular-nums;
}
.nav a.active .count { background: transparent; color: var(--accent); padding-inline: 0; }

.sidebar-foot {
  padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--n-150);
  font-size: 13px;
}
.sidebar-foot .who { font-weight: 600; display: block; }
.sidebar-foot .role { color: var(--n-400); font-size: 12px; display: block; }

/* --- avatars ------------------------------------------------------------- */
/* One circle, three sizes. The initials fallback is not a placeholder to be
   replaced later — most staff will never upload a photo, so it has to look
   deliberate on its own. */
.avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  user-select: none;
}
.avatar.sm { width: 24px; height: 24px; font-size: 10px; }
.avatar.lg { width: 72px; height: 72px; font-size: 24px; }

.sidebar-foot .me {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  color: inherit;
  text-decoration: none;
  margin-bottom: var(--s-2);
}
.sidebar-foot .me:hover .who { text-decoration: underline; }
.acct {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-block-start: var(--s-3);
  padding-block-start: var(--s-3);
  border-block-start: 1px solid var(--n-150);
}
.acct a {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 8px;
  margin-inline: -8px;
  border-radius: var(--r-sm);
  font-size: 12.5px;
  color: var(--n-600);
  text-decoration: none;
}
.acct a .ic { color: var(--n-400); flex: none; }
.acct a:hover { background: var(--n-100); color: var(--n-900); }
.acct a:hover .ic { color: var(--accent); }
.acct a.out:hover { background: var(--danger-soft); color: var(--danger); }
.acct a.out:hover .ic { color: var(--danger); }
.acct a:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.sidebar-foot .me-text { min-width: 0; }
.sidebar-foot .me-text .who {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  height: var(--topbar-h);
  /* The bar stays full-bleed so its blurred background spans the window, but
     its CONTENTS line up with the centred column underneath — otherwise the
     search field and the buttons float away from the page they belong to. */
  padding-inline: max(var(--s-6), (100% - var(--content-max)) / 2 + var(--s-6));
  background: var(--surface-blur);
  backdrop-filter: blur(8px);
  border-bottom: 0;
  display: flex;
  align-items: center;
  gap: var(--s-4);
  position: sticky;
  top: 0;
  z-index: 20;
}

.search { position: relative; flex: 1; max-width: 460px; }
.search input {
  width: 100%;
  height: var(--ctl-h);
  padding: 0 36px 0 var(--s-3);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: var(--n-0);
  box-shadow: var(--shadow-card);
  font-size: 14px;
  outline: none;
  transition: border-color .12s, box-shadow .12s, background .12s;
}
.search input:focus {
  background: var(--n-0);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}
.search .ico {
  position: absolute;
  inset-inline-start: var(--s-3);
  top: 50%;
  transform: translateY(-50%);
  color: var(--n-400);
  pointer-events: none;
  font-size: 14px;
}
.search kbd {
  position: absolute;
  inset-inline-end: var(--s-2);
  top: 50%;
  transform: translateY(-50%);
  font-size: 11px;
  color: var(--n-400);
  background: var(--n-0);
  border: 1px solid var(--n-200);
  border-radius: 4px;
  padding: 1px 5px;
  font-family: var(--font);
}
.search input:focus ~ kbd { display: none; }

/* Centred rather than pinned to the inline start.
   On a wide monitor the column used to sit hard against the sidebar with all
   the leftover space dumped in one margin, which reads as a layout bug rather
   than as breathing room — and collapsing the sidebar made it worse, because
   the space it freed went entirely into that same empty margin. */
.content {
  padding: var(--s-6);
  max-width: var(--content-max);
  width: 100%;
  margin-inline: auto;
}

.page-head { margin-bottom: var(--s-5); }
.page-head .sub { color: var(--n-500); font-size: 14px; margin-top: 2px; }

/* --- cards --------------------------------------------------------------- */

/* A panel in this world is a tensioned field held by a ruled edge, not an
   object floating over the page. The old system's rule was the opposite —
   shadow and no border — and swapping it is the single change that stops
   every screen reading as a stack of cards. */
.card {
  background: var(--n-0);
  border: 1px solid var(--n-150);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
}
.card-head {
  padding: var(--s-4) var(--s-5) 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}
.card-head h2 { font-size: 15px; }
.card-body { padding: var(--s-5); }
/* A head already announces the card; the body under it does not need to
   restate the separation with a full stack of padding as well. */
.card-head + .card-body { padding-top: var(--s-3); }
.card-body.flush { padding: 0; }

/* --- stats --------------------------------------------------------------- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(132px, 100%), 1fr));
  gap: var(--s-3);
}
.stat {
  background: var(--n-0);
  border: 1px solid var(--n-150);
  border-radius: var(--r-md);
  padding: var(--s-4);
}
.stat b {
  display: block;
  font-size: 26px;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.stat span { font-size: 12.5px; color: var(--n-500); }
.stat.ok b     { color: var(--ok); }
.stat.warn b   { color: var(--warn); }
.stat.danger b { color: var(--danger); }

/* Movement against the previous period. Colour follows good/bad, never
   up/down: more visits rising is green, more no-shows rising is not. */
.stat .trend {
  display: flex;
  align-items: baseline;
  gap: 5px;
  margin-top: 6px;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.stat .trend.good { color: var(--ok); }
.stat .trend.bad  { color: var(--danger); }
.stat .trend.flat { color: var(--n-500); }
.stat .trend .was { color: var(--n-500); }

/* --- tables -------------------------------------------------------------- */

.table-wrap { overflow-x: auto; }

table thead th {
  text-align: start;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--n-500);
  padding: var(--s-3) var(--s-5);
  border-bottom: 1px solid var(--n-150);
  background: var(--n-25);
  white-space: nowrap;
}
table tbody td {
  padding: var(--s-3) var(--s-5);
  border-bottom: 1px solid var(--n-100);
  font-size: 14px;
  vertical-align: middle;
}
table tbody tr:last-child td { border-bottom: 0; }
table tbody tr { transition: background .1s ease; }
table tbody tr:hover { background: var(--n-25); }
table tbody tr.is-cancelled td { opacity: .5; }
table tbody tr.is-now { background: var(--accent-soft); }
table tbody tr.is-now:hover { background: var(--accent-soft); }

.cell-time { font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }
.cell-name { font-weight: 500; }
.cell-actions { text-align: end; white-space: nowrap; }

/* --- badges -------------------------------------------------------------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  font-weight: 500;
  padding: 3px 9px;
  border-radius: 20px;
  background: var(--n-100);
  color: var(--n-600);
  white-space: nowrap;
}
.badge.ok     { background: var(--ok-soft);     color: var(--ok); }
.badge.warn   { background: var(--warn-soft);   color: var(--warn); }
.badge.danger { background: var(--danger-soft); color: var(--danger); }
.badge.accent { background: var(--accent-soft); color: var(--accent); }
.badge .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* --- buttons ------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: var(--ctl-h);
  padding: 0 var(--s-4);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: #fff;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: background .12s ease, border-color .12s ease, box-shadow .12s ease;
}
.btn:hover  { background: var(--accent-hover); color: #fff; }
.btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-ring); }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.btn.secondary {
  background: var(--n-0);
  color: var(--n-700);
  border-color: var(--n-200);
}
.btn.secondary:hover { background: var(--n-50); border-color: var(--n-300); color: var(--n-900); }

.btn.ghost { background: transparent; color: var(--n-600); }
.btn.ghost:hover { background: var(--n-100); color: var(--n-900); }

.btn.danger { background: var(--n-0); color: var(--danger); border-color: var(--n-200); }
.btn.danger:hover { background: var(--danger-soft); border-color: var(--danger); }

.btn.sm { height: var(--ctl-h-sm); padding: 0 10px; font-size: 13px; }
.btn.block { width: 100%; }

/* --- forms --------------------------------------------------------------- */

.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: 13px; font-weight: 500; color: var(--n-700); }
.field .hint { font-size: 12px; color: var(--n-500); }

.input, input[type=text], input[type=email], input[type=password],
input[type=tel], input[type=date], input[type=time], input[type=number],
select, textarea {
  width: 100%;
  height: var(--ctl-h);
  padding: 0 var(--s-3);
  border: 1px solid var(--n-200);
  border-radius: var(--r-sm);
  background: var(--n-0);
  font-size: 14px;
  outline: none;
  transition: border-color .12s, box-shadow .12s;
}
textarea { height: auto; min-height: 90px; padding: var(--s-3); line-height: 1.6; resize: vertical; }
select { appearance: none; background-image: none; cursor: pointer; }

.input:focus, input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
  gap: var(--s-4);
}

fieldset { border: 0; padding: 0; margin: 0; }

/* --- typeahead ----------------------------------------------------------- */

.combo { position: relative; }

.combo-list {
  position: absolute;
  z-index: 40;
  top: calc(100% + 4px);
  inset-inline: 0;
  margin: 0;
  padding: var(--s-1);
  list-style: none;
  background: var(--n-0);
  border: 1px solid var(--n-200);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-overlay);
  max-height: 300px;
  overflow-y: auto;
}

.combo-list li {
  padding: 8px var(--s-3);
  border-radius: var(--r-sm);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.combo-list li:hover,
.combo-list li.active { background: var(--accent-soft); }

/* Field-level audit detail: old → new, in a grid so the values line up down
   the column and a changed number is findable at a glance. */
.audit-changes { margin-top: var(--s-1); }
.audit-changes summary {
  cursor: pointer;
  font-size: 12px;
  color: var(--accent);
}
.audit-change {
  display: grid;
  grid-template-columns: minmax(80px, 1fr) minmax(0, 1fr) 14px minmax(0, 1fr);
  gap: var(--s-2);
  align-items: baseline;
  padding: 2px 0;
  font-size: 12px;
}
.audit-field { color: var(--n-600); }
.audit-old   { color: var(--n-500); text-decoration: line-through; }
.audit-arrow { color: var(--n-400); text-align: center; }
.audit-new   { color: var(--n-800); font-weight: 500; }

.history-entry {
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--n-150);
}
.history-entry:last-child { border-bottom: 0; }

/* The research question box. Sized like the primary action on the screen,
   because it is — the filter builder underneath is for correcting it. */
.ask-label {
  display: block;
  font-size: 13px;
  color: var(--n-600);
  margin-bottom: var(--s-2);
}
.ask-row {
  display: flex;
  gap: var(--s-2);
  align-items: stretch;
}
/* Type selector to outrank the shared `input[type=text]` sizing rule. */
input[type=text].ask-input {
  flex: 1;
  min-width: 0;
  height: 48px;
  font-size: 16px;         /* 16px keeps iOS from zooming the page on focus */
}
.ask-row .btn { height: 48px; padding-inline: var(--s-5); }

@media (max-width: 560px) {
  .ask-row { flex-direction: column; }
}

/* Intake rows: one clinical record per line, so a second lab is another line
   rather than another trip through a dialog. */
.intake-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr)) auto;
  gap: var(--s-3);
  align-items: end;
  padding: var(--s-3) 0;
  border-bottom: 1px dashed var(--n-150);
}
.intake-row:last-child { border-bottom: 0; }
.intake-row .field { margin: 0; min-width: 0; }
.intake-row .combo { position: relative; }
.intake-remove {
  align-self: end;
  margin-bottom: 2px;
  color: var(--n-500);
}
.intake-remove:hover { color: var(--danger); }

@media (max-width: 700px) {
  .intake-row {
    grid-template-columns: 1fr;
    padding: var(--s-4) 0;
  }
  .intake-remove { justify-self: start; }
}

/* Care suggestions: visible but subordinate to the record itself. They are
   prompts about documentation, so they must never look like an alert. */
.suggest-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}
.suggest-chip {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: var(--s-2) var(--s-3);
  background: var(--n-0);
  border: 1px solid var(--n-200);
  border-radius: var(--r-sm);
  cursor: pointer;
  text-align: start;
  font: inherit;
  color: inherit;
}
.suggest-chip:hover  { border-color: var(--accent); background: var(--accent-soft); }
.suggest-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.suggest-name { font-size: 14px; font-weight: 500; }
.suggest-why  { font-size: 12px; color: var(--n-500); }

/* Confirmation that the typed term was linked to a standard concept. Quiet on
   purpose — it reports what happened, it is not something to read every time. */
.combo-linked {
  margin-top: var(--s-1);
  font-size: 12px;
  color: var(--ok);
  font-variant-numeric: tabular-nums;
}
.combo-linked[hidden] { display: none; }

/* A soft, non-blocking allergy note under the medication field — a heads-up,
   not a cleared/blocked verdict, so it stays quiet rather than alarming. */
.combo-allergy-warn {
  margin-top: var(--s-1);
  font-size: 12px;
  color: var(--warn);
}
.combo-allergy-warn[hidden] { display: none; }

.combo-name { font-size: 14px; font-weight: 500; }
.combo-meta { font-size: 12px; color: var(--n-500); font-variant-numeric: tabular-nums; }

.combo-empty {
  padding: var(--s-3);
  font-size: 13px;
  color: var(--n-500);
  cursor: default;
}
.combo-empty:hover { background: transparent; }

/* --- slot grid ----------------------------------------------------------- */

.slots { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.slot {
  min-width: 76px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--n-200);
  border-radius: var(--r-sm);
  background: var(--n-0);
  font-size: 13px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: all .12s ease;
}
.slot:hover { border-color: var(--accent); color: var(--accent); }
.slot.taken {
  background: var(--n-100);
  color: var(--n-400);
  border-color: var(--n-150);
  cursor: not-allowed;
  text-decoration: line-through;
}
.slot.past { opacity: .4; cursor: not-allowed; }
.slot.selected { background: var(--accent); border-color: var(--accent); color: #fff; }

/* --- day picker ---------------------------------------------------------- */

/* Scrolls sideways rather than wrapping: a fortnight stays one readable band,
   and the logical properties keep the scroll direction correct in both
   writing directions. */
.day-strip {
  display: flex;
  gap: var(--s-2);
  overflow-x: auto;
  padding-block-end: var(--s-2);
  scroll-snap-type: x proximity;
}
.day-chip {
  flex: 0 0 auto;
  min-width: 78px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--n-200);
  border-radius: var(--r-sm);
  background: var(--n-0);
  color: inherit;
  cursor: pointer;
  scroll-snap-align: start;
  font: inherit;
  line-height: 1.3;
}
.day-chip:hover:not(:disabled) { border-color: var(--accent); }
.day-chip .dow  { font-size: 13px; font-weight: 600; }
.day-chip .dom  { font-size: 12px; color: var(--n-500); }
.day-chip .free { font-size: 11px; color: var(--ok); }
.day-chip.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.day-chip.active .dom,
.day-chip.active .free { color: #fff; }
.day-chip.closed,
.day-chip:disabled { opacity: .45; cursor: not-allowed; }
.day-chip.closed .free { color: var(--n-500); }

/* --- timeline ------------------------------------------------------------ */

.timeline { position: relative; padding-inline-start: var(--s-5); }
.timeline::before {
  content: '';
  position: absolute;
  inset-inline-start: 5px;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: var(--n-200);
}
.tl-item { position: relative; padding-bottom: var(--s-5); }
.tl-item:last-child { padding-bottom: 0; }
.tl-item::before {
  content: '';
  position: absolute;
  inset-inline-start: calc(-1 * var(--s-5) + 1px);
  top: 7px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--n-0);
  border: 2px solid var(--n-300);
}
.tl-item.accent::before { border-color: var(--accent); }
.tl-item.warn::before   { border-color: var(--warn); }
.tl-date { font-size: 12px; color: var(--n-500); font-variant-numeric: tabular-nums; }
.tl-title { font-weight: 500; margin-top: 1px; }
.tl-detail { font-size: 13px; color: var(--n-600); margin-top: 2px; white-space: pre-wrap; }


/* --- empty / error states ------------------------------------------------ */

.empty {
  padding: var(--s-7) var(--s-5);
  text-align: center;
  color: var(--n-500);
}
.empty .ico { font-size: 30px; opacity: .35; }
.empty h3 { color: var(--n-700); margin: var(--s-3) 0 var(--s-1); }
.empty p { font-size: 14px; max-width: 380px; margin: 0 auto; }
.empty .btn { margin-top: var(--s-4); }

/* --- flash --------------------------------------------------------------- */

.flash-wrap {
  position: fixed;
  top: var(--s-4);
  inset-inline-end: var(--s-4);
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  max-width: 380px;
}
.flash {
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-overlay);
  background: var(--n-0);
  border: 1px solid var(--n-200);
  font-size: 14px;
  display: flex;
  gap: var(--s-2);
  animation: slide-in .22s cubic-bezier(.2,.8,.3,1);
}
/* A flash is coloured by its whole edge and its ink, not by a stripe down one
   side. The stripe is the single most recognisable tell of a generated
   interface, and it was never carrying information the border could not. */
.flash.success { border-color: var(--ok);     color: var(--ok); }
.flash.error   { border-color: var(--danger); color: var(--danger); }
.flash.info    { border-color: var(--accent); color: var(--accent); }

@keyframes slide-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* --- modal --------------------------------------------------------------- */

dialog.modal {
  border: 0;
  padding: 0;
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-overlay);
  max-width: 560px;
  width: calc(100vw - var(--s-6));
  background: var(--n-0);
  color: inherit;
}
dialog.modal::backdrop { background: var(--backdrop); backdrop-filter: blur(2px); }
dialog.modal .card-head { border-bottom: 1px solid var(--n-150); }
dialog.modal .modal-body { padding: var(--s-5); }
dialog.modal .modal-foot {
  padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--n-150);
  display: flex;
  justify-content: flex-end;
  gap: var(--s-2);
  background: var(--n-25);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}

/* --- patient record ------------------------------------------------------ */

.patient-header {
  position: sticky;
  top: 60px;                      /* clears the topbar */
  z-index: 15;
  background: var(--surface-blur);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--n-150);
  padding: var(--s-4) 0;
  margin-bottom: var(--s-4);
}
.patient-header h1 { font-size: 21px; }

.patient-alerts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-3);
}

/* Section jump-list. Horizontal so it never competes with the record itself. */
.record-nav {
  display: flex;
  gap: var(--s-1);
  overflow-x: auto;
  padding-bottom: var(--s-2);
  margin-bottom: var(--s-4);
  border-bottom: 1px solid var(--n-150);
}
.record-nav a {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 6px var(--s-3);
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 500;
  color: var(--n-500);
  white-space: nowrap;
}
.record-nav a:hover { background: var(--n-100); color: var(--n-900); }
.record-nav .nav-count {
  font-size: 11px;
  background: var(--n-150);
  color: var(--n-600);
  padding: 0 6px;
  border-radius: 20px;
}

/* Anchored jumps must not land under the sticky header. */
.record-section, #overview, #consultations, #timeline, #appointments,
#communications, #lab-trends { scroll-margin-top: 150px; }

.section-empty {
  padding: var(--s-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
}

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

.record {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--n-100);
}
.record:last-child { border-bottom: 0; }
.record:hover { background: var(--n-25); }

.record-main { min-width: 0; }
.record-title { font-weight: 500; }
.record-detail {
  font-size: 13px;
  color: var(--n-600);
  margin-top: 3px;
  font-variant-numeric: tabular-nums;
}
.record-note {
  font-size: 13px;
  color: var(--n-500);
  margin-top: 4px;
  white-space: pre-wrap;
}
.record-actions {
  display: flex;
  gap: var(--s-1);
  flex-shrink: 0;
  opacity: 0;
  transition: opacity .12s ease;
}
.record:hover .record-actions,
.record:focus-within .record-actions { opacity: 1; }
/* Touch has no hover — actions must always be reachable there. */
@media (pointer: coarse) { .record-actions { opacity: 1; } }

/* --- lab trend ----------------------------------------------------------- */

/* The chart is three fixed bands: value label, plot, date label. Fixing the
   label rows is what lets the reference band line up with the bars — both are
   measured against the same plot box. */
.trend {
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: var(--s-2);
  height: 130px;
  overflow-x: auto;
}
.trend-col {
  flex: 1;
  min-width: 44px;
  height: 100%;
  display: grid;
  grid-template-rows: 16px 1fr 16px;
  justify-items: center;
  align-items: end;
}
.trend-bar {
  width: 100%;
  align-self: end;
  background: var(--accent);
  border-radius: 4px 4px 0 0;
  min-height: 4px;
}
.trend-bar.warn { background: var(--warn); }

/* The plot box the band is measured in — same top and bottom insets as the
   label rows above, so 40% here is 40% of the bars' own scale. */
.trend-plotarea {
  position: absolute;
  inset-inline: 0;
  top: 16px;
  bottom: 16px;
  pointer-events: none;
}
.trend-band {
  position: absolute;
  inset-inline: 0;
  background: var(--ok-soft);
  border-top: 1px dashed var(--ok);
  border-bottom: 1px dashed var(--ok);
  opacity: .7;
}

/* --- drawer -------------------------------------------------------------- */

dialog.drawer {
  border: 0;
  padding: 0;
  margin: 0 0 0 auto;             /* RTL: slides in from the left edge */
  height: 100vh;
  max-height: 100vh;
  width: 460px;
  max-width: 100vw;
  background: var(--n-0);
  color: inherit;
  box-shadow: var(--shadow-overlay);
}
dialog.drawer::backdrop { background: var(--backdrop); backdrop-filter: blur(2px); }
dialog.drawer[open] { animation: drawer-in .18s cubic-bezier(.2,.8,.3,1); }

@keyframes drawer-in {
  from { transform: translateX(-24px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

dialog.drawer form {
  display: flex;
  flex-direction: column;
  height: 100vh;
}
.drawer-head {
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--n-150);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}
.drawer-head h2 { font-size: 16px; }
.drawer-body {
  padding: var(--s-5);
  overflow-y: auto;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}
.drawer-foot {
  padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--n-150);
  background: var(--n-25);
  display: flex;
  justify-content: flex-end;
  gap: var(--s-2);
  flex-wrap: wrap;
}

@media (max-width: 560px) {
  dialog.drawer { width: 100vw; }
  .patient-header { top: 0; }
}

/* --- tabs ---------------------------------------------------------------- */

.tabs { display: flex; gap: var(--s-1); border-bottom: 1px solid var(--n-150); }
.tabs a {
  padding: var(--s-3) var(--s-4);
  font-size: 14px;
  font-weight: 500;
  color: var(--n-500);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color .12s, border-color .12s;
}
.tabs a:hover { color: var(--n-800); }
.tabs a.active { color: var(--accent); border-bottom-color: var(--accent); }

/* --- auth / onboarding --------------------------------------------------- */

.auth-page {
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: var(--n-100);
  padding: var(--s-5);
}
.auth {
  width: 100%;
  max-width: 400px;
  background: var(--n-0);
  border: 1px solid var(--n-150);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
  padding: var(--s-6);
}
.auth .brand { padding: 0 0 var(--s-2); text-align: center; }
.auth .lead { text-align: center; color: var(--n-500); font-size: 14px; margin-bottom: var(--s-5); }


/* --- responsive ---------------------------------------------------------- */

@media (max-width: 900px) {
  .nav a { white-space: nowrap; }
  .brand { padding: var(--s-4) var(--s-4); }
  .content { padding: var(--s-4); }
  .topbar { padding: 0 var(--s-4); }
  .search kbd { display: none; }
}

/* Touch sizing. Desktop density is deliberate at a desk with a mouse; on a
   phone those same controls are 30px targets, and reception taps them
   one-handed while holding a receiver. */
/* Touch is touch at any width. This was gated behind max-width:900px, which
   left a 1024px tablet with a touchscreen on 38px controls — below the floor
   PRODUCT.md says is never overridden. The width condition was the override. */
@media (pointer: coarse) {
  .btn    { height: 44px; padding: 0 var(--s-5); }
  .hero-alt { min-block-size: 44px; align-items: center; }
  /* The brand is a link too, and was 32px on touch. */
  .site-brand, .site-head .brandmark { min-block-size: 44px; display: inline-flex; align-items: center; }
  .btn.sm { height: 38px; padding: 0 var(--s-3); }
  .nav a  { padding: 12px var(--s-3); }

  .input, input[type=text], input[type=email], input[type=password],
  input[type=tel], input[type=date], input[type=time], input[type=number],
  select { height: 44px; }

  /* 16px is the threshold below which iOS Safari zooms the page on focus. */
  input, select, textarea { font-size: 16px; }

  .table-cards td a { padding: 4px 0; }
}

/* Tables that matter on a phone opt into a card layout via .table-cards.
   Every element in the chain must become a block — setting only `tr` to block
   leaves the cells in an anonymous table row and changes nothing.
   Column headers are gone at this width, so each cell carries its own label. */
@media (max-width: 620px) {
  .table-cards,
  .table-cards tbody,
  .table-cards tr,
  .table-cards td { display: block; width: 100%; }

  .table-cards thead { display: none; }

  .table-cards tr {
    border: 1px solid var(--n-150);
    border-radius: var(--r-md);
    padding: var(--s-3) var(--s-4);
    margin-bottom: var(--s-2);
    background: var(--n-0);
  }
  .table-cards tr:hover { background: var(--n-0); }
  .table-cards tr.is-now { background: var(--accent-soft); }

  .table-cards td {
    border: 0;
    padding: 3px 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    text-align: start;
  }
  .table-cards td::before {
    content: attr(data-label);
    flex-shrink: 0;
    font-size: 12px;
    color: var(--n-500);
  }
  /* An empty cell would otherwise render as a stray floating label. */
  .table-cards td:empty { display: none; }

  .table-cards .cell-actions { justify-content: flex-end; padding-top: var(--s-2); }
  .table-cards .cell-actions::before { content: none; }
}

@media print {
  .sidebar, .topbar, .btn, .cell-actions { display: none !important; }
  body { background: #fff; }
  .card { box-shadow: none; border-color: #ddd; }
}

/* ==========================================================================
   Dashboard
   --------------------------------------------------------------------------
   The day, laid out by priority: what needs a decision sits beside what is
   happening now, and the numbers that explain both sit above. Density is the
   point — a clinic screen is read at a glance between patients — so the
   restraint is in the colour and the borders, never in the whitespace.
   ========================================================================== */

.metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(170px, 100%), 1fr));
  gap: var(--s-3);
}
.metric {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-4);
  background: var(--n-0);
  border: 1px solid var(--n-150);
  border-radius: var(--r-md);
}
.metric-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  font-size: 13px;
  color: var(--n-600);
}
/* The icon is a quiet landmark for scanning, not decoration competing with
   the number it sits beside. */
.metric-ico {
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: 8px;
  background: var(--n-50);
  font-size: 13px;
  color: var(--n-500);
}
.metric b { font-size: 28px; line-height: 1.25; font-weight: 650; }
.metric-sub { font-size: 12px; color: var(--n-500); }

/* Only the cards that can demand action carry a tone. */
.metric.ok     .metric-ico { background: var(--ok-soft);     color: var(--ok); }
.metric.warn   .metric-ico { background: var(--warn-soft);   color: var(--warn); }
.metric.danger .metric-ico { background: var(--danger-soft); color: var(--danger); }
.metric.warn   b { color: var(--warn); }
.metric.danger b { color: var(--danger); }

.dash-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.9fr) minmax(300px, 1fr);
  gap: var(--s-4);
  align-items: start;
}
.dash-pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--s-4);
}

/* --- timeline ------------------------------------------------------------ */

.timeline { list-style: none; margin: 0; padding: 0; position: relative; }
/* One continuous rule behind the dots: the day reads as a single thread
   rather than a stack of unrelated rows. */
.timeline::before {
  content: "";
  position: absolute;
  inset-inline-start: 78px;
  top: 18px; bottom: 18px;
  width: 2px;
  background: var(--n-150);
}

.tl-row {
  display: grid;
  grid-template-columns: 62px 18px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-2);
  border-radius: var(--r-md);
  position: relative;
}
.tl-row + .tl-row { margin-top: 2px; }
.tl-row:hover { background: var(--n-25); }

.tl-time { font-size: 13px; color: var(--n-500); text-align: end; }
.tl-dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--n-0);
  border: 2px solid var(--n-300);
  justify-self: center;
  position: relative;
  z-index: 1;
}

.tl-body {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
.tl-who { display: flex; flex-direction: column; min-width: 0; }
.tl-name {
  font-weight: 550;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tl-type { font-size: 12px; color: var(--n-500); }
.tl-now {
  margin-inline-start: var(--s-2);
  color: var(--accent);
  font-weight: 600;
}

.tl-tags { display: flex; align-items: center; gap: var(--s-2); }
.tl-go {
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: 8px;
  color: var(--n-400);
  text-decoration: none;
  font-size: 18px;
}
.tl-row:hover .tl-go { background: var(--n-100); color: var(--n-700); }

/* The appointment in the room right now. A left rule and a tinted ground —
   enough to find instantly, not enough to shout. */
.tl-row.is-current {
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent);
}
.tl-row.is-current .tl-dot { border-color: var(--accent); background: var(--accent); }
.tl-row.is-current .tl-time { color: var(--accent); font-weight: 600; }
.tl-row.is-next .tl-dot { border-color: var(--accent); }
.tl-row.is-done { opacity: .55; }
.tl-row.is-slot .tl-dot { border-style: dashed; }

.risk-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--n-300);
  display: inline-block;
  flex: none;
}
.risk-dot.ok     { background: var(--ok); }
.risk-dot.warn   { background: var(--warn); }
.risk-dot.danger { background: var(--danger); }

/* --- avatars ------------------------------------------------------------- */

.avatar {
  width: 34px; height: 34px;
  flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .3px;
}
.avatar.sm { width: 26px; height: 26px; font-size: 10px; }
.avatar.warn  { background: var(--warn-soft);  color: var(--warn); }
.avatar.ghost { background: var(--n-100); color: var(--n-500); font-size: 16px; }

.stack-avatars { display: flex; }
.stack-avatars .avatar { border: 2px solid var(--n-0); }
.stack-avatars .avatar + .avatar { margin-inline-start: -10px; }

/* --- side panels --------------------------------------------------------- */

.attn-list { list-style: none; margin: 0; padding: 0; }
.attn { display: flex; gap: var(--s-3); padding: var(--s-3) 0; }
.attn + .attn { border-top: 1px solid var(--n-100); }
.attn-body { min-width: 0; flex: 1; }
.attn-body a { text-decoration: none; color: inherit; }
.attn-body a:hover { color: var(--accent); }

.msg-grid, .sum-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(80px, 100%), 1fr));
  gap: var(--s-3);
}
.msg-grid > div, .sum-grid > div { display: flex; flex-direction: column; }
.msg-grid b { font-size: 20px; }
.msg-grid span { font-size: 12px; color: var(--n-500); }
.sum-grid b { font-size: 22px; }
.msg-grid .is-bad b { color: var(--danger); }
.is-bad  { color: var(--danger); }
.is-good { color: var(--ok); }

/* --- charts -------------------------------------------------------------- */

.spark {
  display: flex;
  align-items: flex-end;
  gap: var(--s-2);
  height: 132px;
}
.spark-col {
  flex: 1;
  height: 100%;
  display: grid;
  grid-template-rows: 14px 1fr 16px;
  justify-items: center;
  align-items: end;
  gap: 4px;
}
.spark-bar {
  width: 100%;
  max-width: 34px;
  align-self: end;
  background: var(--accent-soft);
  border-radius: 6px 6px 0 0;
  min-height: 4px;
}
.spark-bar.today { background: var(--accent); }
.spark-val { color: var(--n-500); }

/* A conic gradient rather than SVG: three shares, no library, and the shape
   stays correct at any size. */
.ring {
  --low: 0; --med: 0;
  width: 116px; height: 116px;
  flex: none;
  border-radius: 50%;
  background: conic-gradient(
    var(--ok)     0 calc(var(--low) * 1%),
    var(--n-300)  0 calc((var(--low) + var(--med)) * 1%),
    var(--danger) 0 100%
  );
  display: grid;
  place-items: center;
}
.ring-hole {
  width: 78px; height: 78px;
  border-radius: 50%;
  background: var(--n-0);
  display: grid;
  place-items: center;
  text-align: center;
  line-height: 1.2;
}
.ring-hole b { font-size: 20px; }

.legend { list-style: none; margin: 0; padding: 0; font-size: 13px; }
.legend li {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 3px 0;
}
.legend b { margin-inline-start: auto; }

/* --- narrower screens ---------------------------------------------------- */

@media (max-width: 1100px) {
  /* What needs a decision comes first when the columns collapse: on a phone
     between patients, the exceptions are the reason the screen was opened. */
  .dash-grid { grid-template-columns: 1fr; }
  .dash-aside { order: -1; }
  .dash-pair  { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  .metric b { font-size: 24px; }
  .timeline::before { inset-inline-start: 62px; }
  .tl-row {
    grid-template-columns: 48px 14px minmax(0, 1fr);
    row-gap: var(--s-2);
  }
  .tl-tags { grid-column: 3; justify-content: flex-start; }
  .tl-go { display: none; }
  .risk-label { display: none; }
  .ring { width: 96px; height: 96px; }
  .ring-hole { width: 64px; height: 64px; }
}

/* --- theme toggle -------------------------------------------------------- */

/* A segmented control rather than a switch: there are three states, and
   "follow the system" is not the off position of anything. */
.theme-toggle {
  display: flex;
  gap: 2px;
  padding: 2px;
  background: var(--n-100);
  border-radius: var(--r-sm);
}
.theme-toggle button {
  width: 28px; height: 26px;
  display: grid; place-items: center;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--n-500);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  transition: background .12s ease, color .12s ease;
}
.theme-toggle button:hover { color: var(--n-800); }
.theme-toggle button.active {
  background: var(--n-0);
  color: var(--accent);
  box-shadow: var(--shadow-card);
}

@media (max-width: 640px) {
  /* The label is in the title attribute; on a phone the icons carry it. */
  .theme-toggle button { width: 26px; }
}

/* ==========================================================================
   Sidebar: collapsed and mobile states
   --------------------------------------------------------------------------
   Three layouts from one markup:

     wide            248px, icons + labels
     collapsed       68px, icons only, label on hover
     narrow (phone)  off-canvas, opened by the hamburger

   Everything is expressed with logical properties, so the drawer slides in
   from the right in Arabic and from the left in English without a second rule.
   ========================================================================== */

.brand-mark { display: block; }
.brand-mark:hover { color: inherit; }

/* --- collapsed (desktop) -------------------------------------------------- */

/* Collapsing is a request for more room to read in, so the column widens to
   use what the sidebar gave back instead of banking it as margin. */
/* Collapsed, the sidebar becomes a rail: narrower, and the current section
   marked by a filled square rather than a tint. At 56px an icon has to carry
   the meaning on its own, so the square has to be unmistakable. */
.app.is-collapsed { --sidebar-w: 56px; --content-max: 1440px; }

.app.is-collapsed .nav a.active {
  background: var(--accent);
  border-radius: 9px;
}
.app.is-collapsed .nav a.active .ico,
.app.is-collapsed .nav a.active .ic { color: #fff; opacity: 1; }
.app.is-collapsed .nav a.active .count {
  background: rgba(255, 255, 255, .24);
  color: #fff;
}

.app.is-collapsed .nav a > span:not(.ico):not(.count),
.app.is-collapsed .sidebar-foot,
.app.is-collapsed .clinic-switch,
.app.is-collapsed .sidebar-collapse .lbl { display: none; }

/* Collapsed, the wordmark goes and the mark stays. The old rule did this with
   font-size:0 on the anchor and a size back on the inner span, which left the
   word "Clinic" sitting alone in the rail. */
.app.is-collapsed .brand { padding: var(--s-4) 0 var(--s-3); }
.app.is-collapsed .brand-mark { display: flex; justify-content: center; }
.app.is-collapsed .brand-full { display: none; }
.app.is-collapsed .brand-only { display: block; }

/* At 56px there is no honest way to show a clinic's name — clipping it to one
   character works in Latin and produces nonsense in Arabic, where the first
   glyph of "عيادة نوري" says nothing. The rail is icons only; the name is one
   click away when it expands. */
.app.is-collapsed .clinic-here { display: none; }

.app.is-collapsed .nav { padding: var(--s-2) 6px; gap: 3px; }
.app.is-collapsed .nav a {
  justify-content: center;
  padding: 0;
  block-size: 40px;
  position: relative;
}
.app.is-collapsed .nav a .ico { width: auto; font-size: 17px; }

/* The count still has to be visible when there is no room for it inline. */
.app.is-collapsed .nav a .count {
  position: absolute;
  top: 3px;
  inset-inline-end: 8px;
  margin: 0;
  padding: 0 4px;
  font-size: 10px;
  min-width: 15px;
  text-align: center;
}

/* The label becomes a tooltip. Without it a collapsed sidebar is a column of
   symbols nobody can learn. */
.app.is-collapsed .nav a::after {
  content: attr(data-label);
  position: absolute;
  inset-inline-start: calc(100% + 8px);
  top: 50%;
  transform: translateY(-50%);
  padding: 5px 10px;
  background: var(--n-900);
  color: var(--n-0);
  font-size: 12px;
  white-space: nowrap;
  border-radius: var(--r-sm);
  opacity: 0;
  pointer-events: none;
  transition: opacity .12s ease;
  z-index: 40;
}
.app.is-collapsed .nav a:hover::after,
.app.is-collapsed .nav a:focus-visible::after { opacity: 1; }

.app.is-collapsed .sidebar-collapse { justify-content: center; }
.app.is-collapsed .sidebar-collapse .chev { transform: scaleX(-1); }

/* --- the collapse control ------------------------------------------------- */

.sidebar-collapse {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  padding: var(--s-3) var(--s-5);
  border: 0;
  border-top: 1px solid var(--n-150);
  background: transparent;
  color: var(--n-500);
  font-size: 13px;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.sidebar-collapse:hover { background: var(--n-100); color: var(--n-800); }
.sidebar-collapse .chev {
  font-size: 16px;
  line-height: 1;
  display: inline-block;
  transition: transform .16s ease;
}

/* --- hamburger and scrim -------------------------------------------------- */

.hamburger {
  display: none;                   /* desktop has a permanent sidebar */
  width: 36px; height: 36px;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--n-200);
  border-radius: var(--r-sm);
  background: var(--n-0);
  color: var(--n-700);
  font-size: 16px;
  cursor: pointer;
  flex: none;
}
.hamburger:hover { background: var(--n-100); }

.sidebar-scrim {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 45;
  border: 0;
  padding: 0;
  background: var(--backdrop);
  cursor: pointer;
}

/* --- narrow --------------------------------------------------------------- */

@media (max-width: 900px) {
  .hamburger { display: flex; }

  /* Off-canvas rather than the old horizontal strip: a scrolling row of seven
     items hides most of them, and hidden navigation is navigation nobody uses. */
  .sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    z-index: 50;
    width: min(82vw, 300px);
    height: 100dvh;
    transform: translateX(0);        /* open is the resting position */
    transition: transform .2s ease;
    box-shadow: var(--shadow-overlay);
    border-bottom: 0;
  }

  /* Hiding is stated per direction rather than through a custom property.
     The property version broke when the language switch changed dir on a live
     document: the variable kept its old value and the closed panel sat on top
     of the page in English. Two plain rules cannot go stale that way. */
  [dir="rtl"] .app:not(.is-open) .sidebar { transform: translateX(100%); }
  [dir="ltr"] .app:not(.is-open) .sidebar { transform: translateX(-100%); }
  .app.is-open .sidebar-scrim { display: block; }

  /* The collapsed desktop state has no meaning here. */
  .app.is-collapsed { --sidebar-w: 248px; }
  .app.is-collapsed .brand small,
  .app.is-collapsed .nav a > span:not(.ico):not(.count),
  .app.is-collapsed .sidebar-foot { display: revert; }
  .app.is-collapsed .brand,
  .app.is-collapsed .brand-mark { font-size: revert; text-align: start; }
  .app.is-collapsed .nav a { justify-content: flex-start; padding: 9px var(--s-3); }
  .app.is-collapsed .nav a::after { content: none; }
  .app.is-collapsed .nav a .count { position: static; }

  .sidebar-collapse { display: none; }
  .nav { flex-direction: column; overflow: visible; padding: var(--s-2) var(--s-3); }
  .sidebar-foot { display: block; }
  .brand small { display: block; }
}

/* Someone who has asked for less motion gets the panel without the slide. */
@media (prefers-reduced-motion: reduce) {
  .sidebar { transition: none; }
  .sidebar-collapse .chev { transition: none; }
}

/* --- appearance picker ---------------------------------------------------- */

.choice-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(170px, 100%), 1fr));
  gap: var(--s-3);
}
.choice {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--n-200);
  border-radius: var(--r-md);
  background: var(--n-0);
  color: inherit;
  text-align: start;
  cursor: pointer;
  font: inherit;
  transition: border-color .12s ease, background .12s ease;
}
.choice:hover { border-color: var(--n-300); }
.choice.active {
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: 0 0 0 1px var(--accent);
}
.choice-ico   { font-size: 16px; line-height: 1.4; }
.choice-label { font-weight: 600; font-size: 14px; }
.choice-hint  { font-size: 12px; color: var(--n-500); }
.choice.active .choice-hint { color: var(--n-600); }

.swatches { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.swatch {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: var(--s-3);
  width: 84px;
  border: 1px solid var(--n-200);
  border-radius: var(--r-md);
  background: var(--n-0);
  color: var(--n-600);
  cursor: pointer;
  font: inherit;
  transition: border-color .12s ease;
}
.swatch:hover { border-color: var(--n-300); }
.swatch.active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

/* The dot paints itself with the accent the button represents, because each
   button carries its own data-accent — the swatch previews the colour rather
   than describing it. */
.swatch-dot {
  width: 28px; height: 28px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .06);
}

/* --- language switch ------------------------------------------------------ */

.lang-toggle button {
  height: 30px;
  min-width: 34px;
  padding: 0 var(--s-2);
  border: 1px solid var(--n-200);
  border-radius: var(--r-sm);
  background: var(--n-0);
  color: var(--n-600);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.lang-toggle button:hover { background: var(--n-100); color: var(--n-900); }

/* --- message template editor --------------------------------------------- */

.tpl {
  border: 1px solid var(--n-150);
  border-radius: var(--r-md);
  background: var(--n-0);
}
.tpl summary {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  cursor: pointer;
  list-style: none;
}
.tpl summary::-webkit-details-marker { display: none; }
.tpl summary::before {
  content: "A";
  color: var(--n-400);
  transition: transform .12s ease;
}
.tpl[open] summary::before { transform: rotate(90deg); }
.tpl[open] summary { border-bottom: 1px solid var(--n-150); }
.tpl-body { padding: var(--s-4); }

/* The preview is shaped like the thing it previews: a chat bubble, so the
   doctor is judging a message rather than a form field. */
.tpl-preview { margin-bottom: var(--s-4); }
.bubble {
  margin-top: var(--s-2);
  padding: var(--s-3) var(--s-4);
  background: var(--ok-soft);
  border-radius: 14px 14px 14px 4px;
  max-width: 460px;
  font-size: 14px;
  line-height: 1.7;
}
.bubble-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin: var(--s-2) 0;
  max-width: 460px;
}
.bubble-buttons span {
  flex: 1;
  min-width: 90px;
  text-align: center;
  padding: 6px var(--s-3);
  border: 1px solid var(--n-200);
  border-radius: 8px;
  font-size: 13px;
  color: var(--accent);
  background: var(--n-0);
}

.tpl-vars { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: var(--s-2); }
.chip {
  padding: 2px 8px;
  border: 1px solid var(--n-200);
  border-radius: 20px;
  background: var(--n-50);
  color: var(--n-600);
  font-size: 12px;
  font-family: ui-monospace, monospace;
  cursor: pointer;
}
.chip:hover { border-color: var(--accent); color: var(--accent); }

/* --- return-visit recap --------------------------------------------------- */

/* Shown at the top of the consultation drawer on a follow-up. Reads as a quote
   of the previous visit rather than as more fields to fill: no input styling,
   and a tinted ground so it is clearly a reference, not a form. */
.recap {
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--n-200);
  border-radius: var(--r-sm);
  background: var(--accent-soft);
}
.recap + .recap { margin-top: var(--s-3); }
.recap-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s-3);
  margin-bottom: var(--s-2);
}
.recap-row {
  display: flex;
  gap: var(--s-3);
  font-size: 13px;
  padding-block: 2px;
}
.recap-row > span {
  flex: 0 0 88px;
  color: var(--n-500);
}
.recap-row > b { font-weight: 600; }
.recap.warn-tone { border-inline-start-color: var(--warn); }

/* ==========================================================================
   Role boards
   --------------------------------------------------------------------------
   Three dashboards, one vocabulary. The pieces below are what makes them
   read as different screens rather than the same screen with rows hidden:
   each board opens with ONE hero number in its own idiom — the month's money
   for the admin, the shape of the day for reception, the patient in the room
   for the doctor — and everything after it is deliberately quieter.

   Nothing here introduces a colour. The accent shows up in a hero figure, an
   active nav item and a primary button, and that is the whole budget.
   ========================================================================== */

/* --- hero: one number, one line, nothing else ----------------------------- */

.hero-stat {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-6);
  padding: var(--s-5) var(--s-5);
  flex-wrap: wrap;
}
.hero-stat .hero-figure { min-width: 0; }
.hero-stat .v {
  font-size: 40px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -.03em;
  margin: 10px 0 12px;
  font-variant-numeric: tabular-nums;
}
.hero-stat .v small {
  font-size: 18px;
  font-weight: 400;
  color: var(--n-500);
  letter-spacing: 0;
}
.hero-stat .hero-side { flex: none; }

/* The label above a hero number. Arabic has no capitals, so the "this is a
   caption, not a sentence" signal has to come from size, weight and tracking. */
.eyebrow {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .07em;
  color: var(--n-400);
  display: block;
}

/* --- stat strip: several numbers, ONE card ------------------------------- */
/* Four separate boxes read as four unrelated facts. Hairline-divided cells
   inside one card read as one statement about the same period, which is what
   they are. */

.stat-strip { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.stat-strip.q4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.stat-strip > div {
  padding: var(--s-4) var(--s-5);
  border-inline-start: 1px solid var(--n-100);
  min-width: 0;
}
.stat-strip > div:first-child { border-inline-start: 0; }
.stat-strip b {
  display: block;
  font-size: 23px;
  font-weight: 600;
  letter-spacing: -.02em;
  margin: 6px 0 5px;
  font-variant-numeric: tabular-nums;
}

/* --- delta chip ---------------------------------------------------------- */
/* Direction and tone are set separately on purpose: more visits going up is
   good news and more no-shows going up is not, and a chip that colours both
   the same teaches the reader to stop trusting the colour. */

.delta {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  font-weight: 500;
  padding: 3px 9px;
  border-radius: 20px;
  background: var(--n-100);
  color: var(--n-600);
  white-space: nowrap;
}
.delta.good { background: var(--ok-soft);     color: var(--ok); }
.delta.bad  { background: var(--danger-soft); color: var(--danger); }
.delta .was { color: var(--n-400); font-weight: 400; }

/* --- trend chart --------------------------------------------------------- */

.trendchart { display: block; }
.trendchart .line { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.trendchart .area { fill: var(--accent); opacity: .10; }
.trendchart .head { fill: var(--n-0); stroke: var(--accent); stroke-width: 2.2; }
.trend-axis {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--n-400);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}

/* --- the day spine (reception) ------------------------------------------- */
/* An hour rail rather than a flat list: a receptionist is not reading rows,
   they are reading where the gaps are, and a list cannot show a gap. */

.spine { display: block; }
.spine-hour { display: flex; gap: var(--s-3); }
.spine-hour .t {
  width: 46px;
  flex: none;
  font-size: 11.5px;
  color: var(--n-400);
  font-variant-numeric: tabular-nums;
  padding-top: 1px;
}
.spine-hour .lane {
  flex: 1;
  min-width: 0;
  border-top: 1px solid var(--n-100);
  margin-top: 7px;
  padding-bottom: var(--s-3);
  min-height: 34px;
}
.spine-hour:last-child .lane { padding-bottom: 0; }

.appt-chip {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-top: 9px;
  padding: 9px var(--s-3);
  border-radius: var(--r-md);
  background: var(--n-25);
  box-shadow: inset 0 0 0 1px var(--n-150);
  color: inherit;
  text-decoration: none;
  min-width: 0;
}
.appt-chip:hover { background: var(--n-100); color: inherit; }
.appt-chip .who { flex: 1; min-width: 0; }
.appt-chip .nm  { font-weight: 500; font-size: 13.5px; }
.appt-chip .mt  { font-size: 11.5px; color: var(--n-500); margin-top: 1px; }
/* The one appointment happening right now. Ringed, not filled — a filled row
   would out-shout the "now" line directly under it. */
.appt-chip.live { background: var(--n-0); box-shadow: 0 0 0 1.5px var(--accent), var(--shadow-card); }
.appt-chip.past { opacity: .5; }
.appt-chip.gap  { background: transparent; box-shadow: inset 0 0 0 1px var(--n-100); color: var(--n-500); }

.nowline { display: flex; align-items: center; gap: var(--s-2); margin: var(--s-3) 0 2px; }
.nowline .d {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); flex: none;
  box-shadow: 0 0 0 3.5px var(--accent-ring);
}
.nowline .ln { flex: 1; height: 1px; background: var(--accent); opacity: .45; }
.nowline .lb { font-size: 11px; font-weight: 500; color: var(--accent); flex: none; font-variant-numeric: tabular-nums; }

/* --- the patient in the room (doctor) ------------------------------------ */

.pt-hero { display: flex; align-items: center; gap: var(--s-4); padding: var(--s-5); flex-wrap: wrap; }
.pt-hero .who { flex: 1; min-width: 220px; }
.pt-hero .nm { font-size: 19px; font-weight: 600; letter-spacing: -.02em; }
.pt-hero .facts { color: var(--n-500); font-size: 12.5px; margin-top: 4px; }
.pt-hero .tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.pt-hero .acts { display: flex; gap: var(--s-2); flex-wrap: wrap; }

.vitals { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.vitals > div { padding: 0 var(--s-4); border-inline-start: 1px solid var(--n-100); min-width: 0; }
.vitals > div:first-child { border-inline-start: 0; padding-inline-start: 0; }
.vitals b {
  display: block;
  font-size: 17px;
  font-weight: 600;
  margin-top: 4px;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

/* --- small indicators ---------------------------------------------------- */

/* Three bars instead of a coloured word: the doctor is scanning a column of
   these, and a shape is faster to compare down a list than a label is. */
.meter { display: inline-flex; gap: 2px; align-items: flex-end; height: 12px; flex: none; }
.meter i { width: 3px; border-radius: 1px; background: var(--n-150); }
.meter i:nth-child(1) { height: 5px; }
.meter i:nth-child(2) { height: 8px; }
.meter i:nth-child(3) { height: 12px; }
.meter.mid i.on  { background: var(--warn); }
.meter.high i.on { background: var(--danger); }

.load-bar { height: 5px; border-radius: 99px; background: var(--n-100); overflow: hidden; }
.load-bar i { display: block; height: 100%; border-radius: 99px; background: var(--accent); }
.load-bar.warn i { background: var(--warn); }

.state-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--ok); }
.state-dot.idle { background: var(--n-200); }
.state-dot.warn { background: var(--warn); }

/* A row of people or facts, separated by air and one hairline. */
.board-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--n-100);
  min-width: 0;
}
.board-row:first-child { padding-top: 0; }
.board-row:last-child  { border-bottom: 0; padding-bottom: 0; }
.board-row .nm { font-weight: 500; font-size: 13.5px; }
.board-row .mt { font-size: 11.5px; color: var(--n-500); margin-top: 2px; }
.board-row .grow { flex: 1; min-width: 0; }

/* Says out loud what this role does not get to see. Without it the shorter
   sidebar reads as a bug rather than as a boundary. */
.board-note {
  padding: 0 var(--s-3) var(--s-3);
  font-size: 11.5px;
  line-height: 1.65;
  color: var(--n-400);
}

/* --- board layout -------------------------------------------------------- */

.board-cols { display: grid; gap: var(--s-4); margin-top: var(--s-4); align-items: start; }
.board-cols.wide-first  { grid-template-columns: minmax(0, 1.4fr) minmax(280px, 1fr); }
.board-cols.wider-first { grid-template-columns: minmax(0, 1.5fr) minmax(280px, 1fr); }
.board-stack { display: flex; flex-direction: column; gap: var(--s-4); min-width: 0; }

@media (max-width: 1100px) {
  .board-cols.wide-first,
  .board-cols.wider-first { grid-template-columns: 1fr; }
}

@media (max-width: 760px) {
  .stat-strip,
  .stat-strip.q4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* With two per row the vertical hairlines stop lining up with anything, so
     the cells fall back to being separated by a rule under each pair. */
  .stat-strip > div { border-inline-start: 0; border-top: 1px solid var(--n-100); }
  .stat-strip > div:nth-child(-n + 2) { border-top: 0; }
  .stat-strip > div:nth-child(2n) { border-inline-start: 1px solid var(--n-100); }

  .vitals { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--s-3); }
  .vitals > div:nth-child(2n + 1) { border-inline-start: 0; padding-inline-start: 0; }

  .hero-stat { gap: var(--s-4); }
  .hero-stat .hero-side { width: 100%; }
  .hero-stat .v { font-size: 32px; }
}

/* ==========================================================================
   Appearance axes — surface and density
   --------------------------------------------------------------------------
   Four independent axes now, not two:

     data-theme="light|dark"                 light or dark
     data-accent="nori|green|teal|…"         the one action colour
     data-surface="warm|cool|contrast"       the neutral ramp — the room
     data-density="comfortable|compact"      spacing only

   They are kept apart because they answer different questions and people
   combine them in ways no preset would predict: the cool ramp in dark with the
   rose accent is a real request, and a preset list would have to enumerate
   thirty of them to allow it.

   :root already IS warm + comfortable, so those two values match no block at
   all — the defaults cost nothing.
   ========================================================================== */

/* --- surface: cool --------------------------------------------------------
   The blue-grey ramp the app used before it was restyled. Kept as a choice
   rather than deleted: it is genuinely calmer under fluorescent light, which
   is what a lot of clinic rooms actually have. */

[data-surface="cool"] {
  --n-0:   #ffffff;
  --n-25:  #fcfcfd;
  --n-50:  #f6f7f9;
  --n-100: #f1f3f7;
  --n-150: #e8ebf1;
  --n-200: #dfe3ea;
  --n-300: #c6ccd8;
  --n-400: #9aa3b2;
  --n-500: #6b7482;
  --n-600: #4d5560;
  --n-700: #363c46;
  --n-800: #23272e;
  --n-900: #14171c;

  --surface-blur: rgba(255, 255, 255, .85);
  --backdrop:     rgba(20, 23, 28, .4);
  --warn-line:    #f0dcb8;
  --warn-tint:    #fffcf6;

  --shadow-card:    0 1px 2px rgba(20, 23, 28, .04), 0 2px 6px rgba(20, 23, 28, .03);
  --shadow-overlay: 0 16px 40px rgba(20, 23, 28, .14), 0 2px 8px rgba(20, 23, 28, .06);
}

/* --- surface: contrast ----------------------------------------------------
   The one appearance setting that is an accessibility control, not taste.
   Near-black text on ivory, borders instead of shadows — a shadow is exactly
   the cue that disappears first on a cheap panel and on weaker eyesight.

   It pins the accent. That deliberately overrides the user's colour choice:
   in a mode whose whole purpose is legibility, a pale accent on a bright
   ground is the one thing that must not be allowed through. The picker says
   so rather than letting it look like a bug. */

[data-surface="contrast"] {
  --n-0:   #ffffff;
  --n-25:  #fffdf7;
  --n-50:  #fffdf7;
  --n-100: #f4f0e4;
  --n-150: #d9d2c0;
  --n-200: #b9b09a;
  --n-300: #8f8877;
  --n-400: #6b6558;
  --n-500: #4a453b;
  --n-600: #332f28;
  --n-700: #211f1a;
  --n-800: #121110;
  --n-900: #000000;

  --accent:       #0a463f;
  --accent-hover: #073730;
  --accent-soft:  #dfeae7;
  --accent-ring:  rgba(10, 70, 63, .3);

  --ok:      #0d5546;  --ok-soft:     #dfeae7;
  --warn:    #6b5018;  --warn-soft:   #f5ecd6;
  --danger:  #8c3527;  --danger-soft: #f7e3df;

  --warn-line: #c8bb96;
  --shadow-card:    none;
  --shadow-overlay: 0 12px 32px rgba(0, 0, 0, .3);
}

/* Whatever the shadow was carrying, a border has to carry instead. */
[data-surface="contrast"] .card,
[data-surface="contrast"] .metric        { border: 1px solid var(--n-150); }
[data-surface="contrast"] .nav a.active  { box-shadow: inset 0 0 0 1px var(--n-200); }
[data-surface="contrast"] .search input,
[data-surface="contrast"] .switcher      { border-color: var(--n-200); box-shadow: none; }
[data-surface="contrast"] .card-head     { border-bottom: 1px solid var(--n-100); padding-bottom: var(--s-3); }

/* --- the dark half of each surface ---------------------------------------
   The dark ramp is never the light one inverted, and that is true per surface:
   each of these was picked against its own ground. Two attributes, so these
   win over the plain [data-theme="dark"] block on specificity rather than on
   source order. */

[data-theme="dark"][data-surface="cool"] {
  --n-0:   #15181e;
  --n-25:  #12151a;
  --n-50:  #0e1116;
  --n-100: #1b1f27;
  --n-150: #252a34;
  --n-200: #2f3540;
  --n-300: #414957;
  --n-400: #6b7484;
  --n-500: #929bab;
  --n-600: #b3bac6;
  --n-700: #ced4dd;
  --n-800: #e6eaf0;
  --n-900: #f5f7fa;

  --surface-blur: rgba(21, 24, 30, .85);
  --warn-line:    #3a2f18;
  --warn-tint:    #1d1810;
}

[data-theme="dark"][data-surface="contrast"] {
  --n-0:   #000000;
  --n-25:  #000000;
  --n-50:  #000000;
  --n-100: #141414;
  --n-150: #3d3d3d;
  --n-200: #5a5a5a;
  --n-300: #7d7d7d;
  --n-400: #a0a0a0;
  --n-500: #c0c0c0;
  --n-600: #d8d8d8;
  --n-700: #ededed;
  --n-800: #ffffff;
  --n-900: #ffffff;

  --accent:       #5fd9c3;
  --accent-hover: #7ee6d2;
  --accent-soft:  #10322c;
  --accent-ring:  rgba(95, 217, 195, .35);

  --ok:      #6ee7b7;  --ok-soft:     #10322c;
  --warn:    #fbbf24;  --warn-soft:   #33280a;
  --danger:  #fca5a5;  --danger-soft: #3a1414;

  --surface-blur: rgba(0, 0, 0, .9);
  --warn-line:    #4a3c12;
  --warn-tint:    #171308;
  --shadow-card:  none;
}

/* --- density: compact -----------------------------------------------------
   Spacing only. Nothing is hidden, no font shrinks below 14px, and no control
   goes under the touch minimum on a touch device — the coarse-pointer block
   further up sets 44px buttons and it deliberately still wins here, because a
   density preference is not a reason to make a target too small to hit. */

[data-density="compact"] {
  --s-1: 3px;  --s-2: 6px;  --s-3: 9px;  --s-4: 12px;
  --s-5: 16px; --s-6: 22px; --s-7: 34px; --s-8: 46px;

  --topbar-h: 52px;
  --ctl-h:    32px;
  --ctl-h-sm: 26px;
  --nav-y:    6px;
  --fs-base:  14px;
}

/* A handful of places carry their own spacing because they are not built from
   the scale. They are listed here rather than tokenised individually — this is
   the entire remainder of the density surface area.
 *
 * Desktop only, and that is load-bearing rather than tidy: these selectors are
 * two attributes deep, so they would outrank the single-class rules the mobile
 * and coarse-pointer blocks use to widen everything for a thumb. Density is a
 * choice about how much fits on a monitor; it has no business overriding a
 * touch target. The token block above stays global, because those variables
 * are what the mobile rules are built from and they compose correctly. */
@media (min-width: 901px) {
  [data-density="compact"] .brand        { padding: var(--s-4) var(--s-5) var(--s-3); font-size: 17px; }
  [data-density="compact"] h1            { font-size: 21px; }
  [data-density="compact"] h2            { font-size: 16px; }
  [data-density="compact"] .hero-stat .v { font-size: 33px; }
  [data-density="compact"] .stat-strip b { font-size: 20px; }
  [data-density="compact"] .avatar       { width: 28px; height: 28px; font-size: 11px; }
  [data-density="compact"] .avatar.sm    { width: 22px; height: 22px; font-size: 9.5px; }
  [data-density="compact"] .avatar.lg    { width: 44px; height: 44px; font-size: 15px; }
  [data-density="compact"] .spine-hour .lane { min-height: 28px; }
  [data-density="compact"] .appt-chip    { padding: 6px var(--s-3); margin-top: 7px; }
}

/* --- the owner-doctor's business strip ------------------------------------
   A whole card that is one link. It is the quietest thing on the clinical
   board on purpose: the numbers matter, but not more than the person in the
   room, so they get a single row at the bottom and a door to التحليلات rather
   than a section of their own. */

.business-strip { position: relative; }
.business-strip .business-go {
  position: absolute;
  inset-block-start: var(--s-3);
  inset-inline-end: var(--s-4);
  font-size: 11.5px;
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
}
.business-strip .business-go:hover { text-decoration: underline; }
@media (max-width: 760px) {
  /* With the cells stacked two-up the corner label overlaps the first one. */
  .business-strip .business-go { position: static; display: block; padding: 0 var(--s-4) var(--s-3); }
}

/* --- role brief ----------------------------------------------------------
   What a role can and cannot do, shown at the moment someone picks it. The
   denied half is not decoration: handing a person access to a medical record
   is a decision, and it needs both halves to be made honestly. */

.role-brief-list {
  margin: var(--s-1) 0 0;
  padding-inline-start: var(--s-4);
  font-size: 13px;
  color: var(--n-600);
  line-height: 1.75;
}
.role-brief-list li::marker { color: var(--ok); }
.role-brief-list.is-denied { color: var(--n-500); }
.role-brief-list.is-denied li::marker { color: var(--danger); }
.role-brief[hidden] { display: none; }

/* ==========================================================================
   Settings: a long page made navigable
   --------------------------------------------------------------------------
   A dozen sections and fifteen forms. The jump bar answers "where is the
   thing", folding answers "why is this page so long", and the save bar
   answers "which button saves what I just changed".
   ========================================================================== */

.sec-nav {
  position: sticky;
  /* The top bar is sticky at 0 with a higher z-index, so a nav at 0 would
     scroll underneath it and be invisible exactly when it is needed. */
  top: var(--topbar-h);
  z-index: 15;
  display: flex;
  /* Wraps rather than scrolls. It was a single scrolling row with the
     scrollbar hidden, which on a mouse meant the last chips existed but could
     not be reached by any means — a shortcut bar you cannot reach the end of
     is worse than no shortcut bar. Wrapping keeps every section one click
     away, which is the entire point of it. */
  flex-wrap: wrap;
  gap: 6px;
  padding: var(--s-2) 0 var(--s-3);
  margin-bottom: var(--s-4);
  background: var(--surface-blur);
  backdrop-filter: blur(8px);
}
.sec-nav a {
  flex: none;
  padding: 5px 11px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 500;
  color: var(--n-600);
  text-decoration: none;
  background: var(--n-0);
  box-shadow: var(--shadow-card);
  white-space: nowrap;
}
.sec-nav a:hover  { color: var(--n-900); }
.sec-nav a.active { background: var(--accent); color: #fff; box-shadow: none; }

@media (max-width: 900px) {
  /* Three wrapped rows plus the top bar is a third of a phone screen held
     permanently. On a narrow screen the bar scrolls away with the page and
     the folded sections do the navigating instead. */
  .sec-nav { position: static; }
}

/* --- folding ------------------------------------------------------------- */

.card[id] > .card-head { cursor: pointer; }
.sec-fold {
  margin-inline-start: auto;
  flex: none;
  width: 26px;
  height: 26px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--n-400);
  cursor: pointer;
  position: relative;
}
.sec-fold:hover { background: var(--n-100); color: var(--n-700); }
/* A chevron drawn from a border, so it needs no icon font and rotates cleanly. */
.sec-fold::before {
  content: '';
  position: absolute;
  inset-block-start: 8px;
  inset-inline-start: 9px;
  width: 7px;
  height: 7px;
  border-inline-end: 1.5px solid currentColor;
  border-block-end: 1.5px solid currentColor;
  transform: rotate(45deg);
  transition: transform .15s ease;
}
.sec-fold[aria-expanded="false"]::before { transform: rotate(-45deg); }

.card.is-folded > .card-body,
.card.is-folded > .table-wrap,
.card.is-folded > form { display: none; }
.card.is-folded > .card-head { padding-bottom: var(--s-4); }

/* --- the save bar -------------------------------------------------------- */

.save-bar {
  position: fixed;
  inset-block-end: var(--s-4);
  /* Pinned to the inline START: the right in Arabic, the left in English —
     the side the reader's eye is already on, either way. */
  inset-inline: var(--s-5) auto;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-lg);
  background: var(--n-900);
  color: var(--n-0);
  box-shadow: var(--shadow-overlay);
}
.save-bar[hidden] { display: none; }
.save-bar-txt { font-size: 13px; }
.save-bar-txt b { font-weight: 600; }
/* The bar sits on the inverted surface, so the buttons cannot use the page's
   own secondary style — it would vanish into the dark ground. */
.save-bar .btn.secondary {
  background: transparent;
  color: var(--n-300);
  border-color: var(--n-600);
}
.save-bar .btn.secondary:hover { color: var(--n-0); border-color: var(--n-400); }

@media (max-width: 640px) {
  .save-bar { inset-inline: var(--s-3) var(--s-3); justify-content: space-between; }
}

/* --- a number you can open ------------------------------------------------
   A count that names people should look openable and nothing more: no button
   chrome around a figure that has to keep reading as a figure. The hint under
   it appears on hover and focus so the affordance is discoverable without
   being permanent noise. */

.stat-open {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: start;
  cursor: pointer;
}
.stat-open b { display: block; }
.stat-open-hint {
  display: block;
  font-size: 11px;
  font-weight: 500;
  color: var(--accent);
  opacity: 0;
  transition: opacity .12s ease;
}
.stat-open:hover .stat-open-hint,
.stat-open:focus-visible .stat-open-hint { opacity: 1; }
.stat-open:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-ring); border-radius: var(--r-sm); }

/* A button that has to sit in running text and read as a link. */
.linkish {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 12px;
  color: var(--accent);
  cursor: pointer;
  text-align: start;
}
.linkish:hover { text-decoration: underline; }

/* The dialog is a list of people, so it needs room to breathe rather than the
   compact stack a form dialog wants. */
#who-missed .board-row:first-child { padding-top: 0; }
#who-missed .board-row .nm { text-decoration: none; }
#who-missed .board-row .nm:hover { text-decoration: underline; }

/* ==========================================================================
   The dark navigation rail  ·  data-sidebar="dark"
   --------------------------------------------------------------------------
   A deep desaturated slate — the colour clinical software reaches for because
   it reads as steady and unexcited, and because it is quiet enough not to
   argue with whichever accent the user picked. It is nearly neutral on
   purpose: a saturated navy would fight the green, and every accent has to
   survive sitting on top of this.

   The whole rail is recoloured from one small set of tokens, so the rule below
   is the only place that knows the rail is dark. Everything else — spacing,
   folding, the collapsed state, the mobile drawer — is untouched.
   ========================================================================== */

[data-sidebar="dark"] {
  --rail-bg:     #1b2334;
  --rail-line:   #2b3548;
  --rail-ink:    #c3cbd9;   /* resting label */
  --rail-strong: #ffffff;   /* the active one, and the brand */
  /* 5.0:1 on the rail. #7d879b read better as a "dim" grey but measured 4.35,
     and this text is a role and a count — small, and exactly the sort of thing
     that disappears first on a clinic monitor. */
  --rail-dim:    #8892a6;
  --rail-hover:  rgba(255, 255, 255, .06);
  --rail-active: rgba(255, 255, 255, .10);
  /* Any accent lifted until it reads on the slate. The user's colour survives
     the switch instead of being overridden by it. */
  --rail-accent: color-mix(in srgb, var(--accent) 45%, #ffffff);
}

[data-sidebar="dark"] .sidebar {
  background: var(--rail-bg);
  border-inline-end-color: var(--rail-line);
}

[data-sidebar="dark"] .brand,
[data-sidebar="dark"] .brand-mark,
[data-sidebar="dark"] .brand-mark:hover { color: var(--rail-strong); }
[data-sidebar="dark"] .brand span { color: var(--rail-accent); }
[data-sidebar="dark"] .brand small { color: var(--rail-dim); }

[data-sidebar="dark"] .clinic-here { color: var(--rail-ink); }
[data-sidebar="dark"] .clinic-switch select {
  background: var(--rail-hover);
  border-color: var(--rail-line);
  color: var(--rail-strong);
}

[data-sidebar="dark"] .nav a { color: var(--rail-ink); }
[data-sidebar="dark"] .nav a:hover { background: var(--rail-hover); color: var(--rail-strong); }
[data-sidebar="dark"] .nav a.active {
  /* A lifted panel rather than a shadow: a shadow does nothing on a dark
     ground, so the active row has to be carried by contrast instead. */
  background: var(--rail-active);
  color: var(--rail-strong);
  box-shadow: none;
}
[data-sidebar="dark"] .nav a .ico,
[data-sidebar="dark"] .nav a .ic       { color: inherit; }
[data-sidebar="dark"] .nav a.active .ico,
[data-sidebar="dark"] .nav a.active .ic,
[data-sidebar="dark"] .nav a.active .count { color: var(--rail-accent); }
[data-sidebar="dark"] .nav a .count { color: var(--rail-dim); }

[data-sidebar="dark"] .sidebar-foot { border-top-color: var(--rail-line); color: var(--rail-ink); }
[data-sidebar="dark"] .sidebar-foot .who  { color: var(--rail-strong); }
[data-sidebar="dark"] .sidebar-foot .role { color: var(--rail-dim); }
[data-sidebar="dark"] .sidebar-foot .muted { color: var(--rail-dim); }
[data-sidebar="dark"] .sidebar-foot .muted:hover { color: var(--rail-strong); }
[data-sidebar="dark"] .sidebar .avatar { background: var(--rail-active); color: var(--rail-accent); }

[data-sidebar="dark"] .sidebar-collapse {
  border-top-color: var(--rail-line);
  color: var(--rail-dim);
}
[data-sidebar="dark"] .sidebar-collapse:hover { background: var(--rail-hover); color: var(--rail-strong); }

/* The collapsed rail's hover label floats over the content, not the rail, so
   it keeps the page's own colours rather than the rail's. */
/* ==========================================================================
   Public site
   --------------------------------------------------------------------------
   White. The identity deck is presented on near-black; the product is not,
   and the decision is recorded in the token block at the top of this file.
   Brand colour is carried at full strength on a white ground: blue for
   action, cyan for the person and the live moment, green/amber/red for state.

   Same stylesheet as the app on purpose — the site and the product cannot
   drift apart, and a doctor who signs in already has it cached.
   ========================================================================== */

.site { background: var(--n-0); }
.site-wrap { width: min(100% - 2 * var(--s-5), var(--content-max)); margin-inline: auto; }
.site-wrap.narrow { max-width: 720px; }

/* --- header -------------------------------------------------------------- */
.site-nav {
  position: sticky;
  inset-block-start: 0;
  z-index: 40;
  background: var(--surface-blur);
  backdrop-filter: saturate(1.6) blur(12px);
  border-block-end: 1px solid var(--n-150);
}
.site-nav-inner { display: flex; align-items: center; gap: var(--s-6); min-height: 64px; }
.site-links { display: flex; gap: var(--s-5); flex: 1; font-size: 14px; }
.site-links a { color: var(--n-600); padding-block: var(--s-2); font-weight: 500; }
.site-links a:hover { color: var(--n-900); }
.site-links a[aria-current="page"] { color: var(--nori-blue); }
.site-nav-end { display: flex; align-items: center; gap: var(--s-3); }
.site-signin { font-size: 14px; font-weight: 500; color: var(--n-600); }
.site-signin:hover { color: var(--nori-blue); }
.site-lang {
  font-size: 12px; font-weight: 700; letter-spacing: .06em;
  color: var(--n-600); border: 1px solid var(--n-200);
  border-radius: var(--r-pill); padding: 5px 10px; line-height: 1;
}
.site-lang:hover { color: var(--nori-blue); border-color: var(--nori-blue); }
@media (max-width: 900px) { .site-links { display: none; } }

/* --- hero ---------------------------------------------------------------- */
.hero {
  position: relative;
  padding-block: clamp(40px, 6vw, 88px) clamp(48px, 7vw, 96px);
  background: var(--n-0);
  overflow: hidden;
}
/* One structural element instead of a wash: the mark's own cradle, drawn at
   architectural scale, cropped by the section edge. It is geometry, not
   atmosphere, and reading text never crosses it. */
.hero::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset-block-start: -18%;
  inset-inline-end: -14%;
  inline-size: min(680px, 58%);
  aspect-ratio: 1;
  border-radius: 50%;
  background:
    radial-gradient(closest-side, color-mix(in oklab, var(--nori-cyan) 13%, white), transparent 100%);
}

/* The slogan. Below the headline, never above it — a label set over a heading
   is the one page-scaffold habit that no brief earns back. */
.hero-slogan {
  font-size: clamp(14px, 1.4vw, 16px);
  font-weight: 600;
  color: var(--nori-blue);
  margin-block: calc(var(--s-4) * -1) var(--s-4);
  letter-spacing: var(--track-display);
}
[dir="rtl"] .hero-slogan { letter-spacing: normal; }
.hero-in { position: relative; z-index: 1; }
.hero-in {
  display: grid;
  grid-template-columns: minmax(0, .88fr) minmax(0, 1.12fr);
  gap: clamp(var(--s-6), 5vw, var(--s-8));
  align-items: center;
}
.hero-h1 {
  font-family: var(--font-display);
  font-size: clamp(36px, 5.4vw, 62px);
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: var(--track-display);
  color: var(--n-900);
  margin-block-end: var(--s-4);
  text-wrap: balance;
}
[dir="rtl"] .hero-h1 { letter-spacing: normal; line-height: 1.25; }
.hero-lead {
  font-size: clamp(16px, 1.6vw, 18px);
  line-height: 1.85;
  color: var(--n-600);
  max-width: 40ch;
  margin-block-end: var(--s-5);
}
.hero-cta { display: flex; align-items: center; gap: var(--s-5); flex-wrap: wrap; margin-block-end: var(--s-6); }
.hero-alt {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 15px; font-weight: 600; color: var(--nori-blue);
  text-decoration: none; text-underline-offset: 4px;
}
.hero-alt:hover { text-decoration: underline; }
.hero-alt .ic { transition: transform .2s cubic-bezier(.2,.7,.3,1); }
[dir="rtl"] .hero-alt .ic { transform: scaleX(-1); }
.hero-alt:hover .ic { transform: translateX(3px); }
[dir="rtl"] .hero-alt:hover .ic { transform: scaleX(-1) translateX(3px); }
.btn-lg { height: 48px; padding-inline: var(--s-6); font-size: 15px; border-radius: var(--r-pill); }
.hero-notes { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s-4); }
.hero-notes li {
  font-size: 12.5px; color: var(--n-500);
  padding-inline-end: var(--s-4);
  border-inline-end: 1px solid var(--n-150);
}
.hero-notes li:last-child { border-inline-end: 0; padding-inline-end: 0; }

/* --- the alternating board ----------------------------------------------
   Two real boards, one 16s loop. The clinic's whole day, then one doctor's
   own board — including the patient who has not replied to the automated
   follow-up. That row is the point: the automation removes the work, not the
   patient's free will, and the value is that they are the ONLY call to make.
   -------------------------------------------------------------------------- */
/* The window. Two shadows, both offset and blurred and both tinted with the
   brand ink: a tight contact shadow so it sits on the page, and a long soft
   one so it lifts off it. A zero-offset halo would be decoration. */
.appwin {
  position: relative;
  border-radius: 14px;
  background: var(--n-0);
  border: 1px solid var(--n-150);
  box-shadow:
    0 1px 2px rgba(10, 20, 36, .05),
    0 2px 8px rgba(10, 20, 36, .05),
    0 28px 64px -12px rgba(10, 20, 36, .18);
  overflow: hidden;
}
.appwin-bar {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 10px var(--s-4);
  border-block-end: 1px solid var(--n-150);
  background: var(--n-25);
}
.appwin-brand { display: inline-flex; }
.appwin-name { font-size: 12.5px; font-weight: 600; color: var(--n-700); }
.appwin-tabs { display: flex; gap: var(--s-3); margin-inline-start: auto; }
.appwin-tabs i {
  font-style: normal; font-size: 11.5px; color: var(--n-400);
  padding-block-end: 2px; border-block-end: 2px solid transparent;
}
.appwin-tabs i.on { color: var(--nori-blue); border-block-end-color: var(--nori-blue); }

.stage {
  position: relative;
  min-height: 402px;
  background: var(--n-0);
  overflow: hidden;
}
.board {
  position: absolute;
  inset: 0;
  padding: var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  animation: 16s ease-in-out infinite;
}
.board-a { animation-name: swap-a; }
.board-b { animation-name: swap-b; }
@keyframes swap-a { 0%,42% { opacity: 1; } 50%,92% { opacity: 0; } 100% { opacity: 1; } }
@keyframes swap-b { 0%,42% { opacity: 0; } 50%,92% { opacity: 1; } 100% { opacity: 0; } }

.board-top { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.board-title { display: flex; align-items: center; gap: var(--s-2); font-weight: 700; font-size: 15px; color: var(--n-900); }
.board-title .ic { color: var(--nori-blue); }
.board-date { font-size: 12px; color: var(--n-500); }

.tally { display: flex; gap: var(--s-5); padding-block: var(--s-2); border-block-end: 1px solid var(--n-150); }
.tally span { display: flex; flex-direction: column; gap: 2px; font-size: 11px; color: var(--n-500); }
.tally b { font-family: var(--font-mono); font-size: 22px; font-weight: 500; color: var(--n-900); }
.tally .t-ok { color: var(--ok); }
.tally .t-wait { color: var(--warn); }

.rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.rows li {
  display: grid;
  grid-template-columns: 58px 1fr auto;
  align-items: center;
  gap: var(--s-3);
  padding-block: var(--s-3);
  border-block-start: 1px solid var(--n-150);
  font-size: 14px;
}
.rows li:first-child { border-block-start: 0; }
.rows .t { font-family: var(--font-mono); font-style: normal; font-size: 12px; color: var(--n-500); }
.rows b { font-weight: 600; color: var(--n-800); }
.rows .live { background: rgba(79, 220, 236, .10); }
.rows .gap b { color: var(--n-500); font-weight: 500; }
.rows .flag { background: var(--danger-soft); }

/* Status pills. Colour AND a word, every time. */
.pill {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; font-weight: 700; line-height: 1;
  padding: 5px 9px; border-radius: var(--r-pill); white-space: nowrap;
}
.pill.ok      { background: var(--ok-soft);     color: var(--ok); }
.pill.wait    { background: var(--warn-soft);   color: var(--warn); }
.pill.danger  { background: var(--danger-soft); color: var(--danger); }
.pill.none    { background: var(--danger-soft); color: var(--danger); }
.pill.live    { background: rgba(79, 220, 236, .22); color: var(--nori-cyan-ink); }
.pill.arrived { background: var(--accent-soft); color: var(--nori-blue); }
.pill.go      { background: var(--accent-soft); color: var(--nori-blue); }

.board-foot {
  margin-block-start: auto;
  display: flex; align-items: flex-start; gap: var(--s-2);
  font-size: 12.5px; line-height: 1.6; color: var(--n-600);
  padding-block-start: var(--s-3); border-block-start: 1px solid var(--n-150);
}
.board-foot .ic { color: var(--ok); flex: none; margin-block-start: 2px; }

.stage-tag {
  position: absolute;
  inset-block-end: var(--s-3); inset-inline-end: var(--s-4);
  font-size: 10px; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--n-500);
}
[dir="rtl"] .stage-tag { letter-spacing: normal; text-transform: none; }

@media (max-width: 900px) {
  .hero-in { grid-template-columns: 1fr; }
  .hero-lead { max-width: none; }
  .stage { min-height: 470px; }
}

/* With motion suppressed the boards stop overlapping and stack, so the whole
   story is readable at once and nobody waits for a fade. */
@media (prefers-reduced-motion: reduce) {
  .board { position: relative; inset: auto; animation: none; opacity: 1 !important; }
  .board-b { border-block-start: 1px solid var(--n-150); }
  .stage { min-height: 0; }
}

/* --- sections ------------------------------------------------------------ */
.sec { padding-block: clamp(52px, 6.5vw, 92px); border-block-start: 1px solid var(--n-150); }
.sec-h {
  font-family: var(--font-display);
  font-size: clamp(24px, 3.2vw, 36px);
  font-weight: 700;
  letter-spacing: var(--track-display);
  color: var(--n-900);
  margin-block-end: var(--s-4);
  max-width: 24ch;
}
[dir="rtl"] .sec-h { letter-spacing: normal; }
.sec-lead { font-size: 17px; line-height: 1.85; color: var(--n-600); max-width: 62ch; }
.sec-small { font-size: 14px; line-height: 1.75; color: var(--n-500); margin-block-start: var(--s-3); max-width: 62ch; }
.sec-foot {
  margin-block-start: var(--s-6); padding-block-start: var(--s-4);
  border-block-start: 1px solid var(--n-150);
  font-size: 14px; color: var(--n-600); max-width: 66ch;
}
.split-in { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(var(--s-6), 5vw, var(--s-8)); align-items: center; }
.split-in.flip > *:first-child { order: 2; }
@media (max-width: 860px) { .split-in, .split-in.flip > *:first-child { grid-template-columns: 1fr; order: 0; } }

/* --- WhatsApp ------------------------------------------------------------ */
.wa-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: var(--s-4); margin-block-start: var(--s-6); }
.wa { border: 1px solid var(--n-150); border-radius: var(--r-lg); background: var(--n-0); overflow: hidden; display: flex; flex-direction: column; }
.wa-head {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  font-size: 13px; font-weight: 700; color: var(--n-900);
  border-block-end: 1px solid var(--n-150);
}
.wa-head .ic { color: var(--ok); }
.wa-body { padding: var(--s-4); background: var(--n-50); display: flex; flex-direction: column; gap: var(--s-2); flex: 1; }
.b { font-size: 13px; line-height: 1.65; padding: var(--s-2) var(--s-3); border-radius: var(--r-md); max-width: 88%; }
/* The clinic speaks in white; the patient answers in brand blue. */
.b.out { background: var(--n-0); border: 1px solid var(--n-150); color: var(--n-800); }
.b.in  { background: var(--nori-blue); color: #fff; margin-inline-start: auto; }
/* The choice chips are the whole point: the patient taps, they do not type. */
.choices { display: flex; flex-wrap: wrap; gap: 6px; margin-inline-start: auto; }
.choices span {
  font-size: 12px; font-weight: 600; padding: 6px 11px;
  border-radius: var(--r-pill); border: 1px solid var(--nori-blue);
  color: var(--nori-blue); background: var(--n-0);
}
.choices .y { background: var(--nori-blue); color: #fff; }
.wa-did {
  display: flex; align-items: flex-start; gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  font-size: 12.5px; line-height: 1.6; color: var(--n-600);
  border-block-start: 1px solid var(--n-150);
}
.wa-did .ic { color: var(--nori-blue); flex: none; margin-block-start: 2px; }

/* --- the research figure ------------------------------------------------- */
.sec-roi { text-align: center; }
.roi-fig { font-family: var(--font-mono); font-size: clamp(48px, 9vw, 96px); font-weight: 500; color: var(--nori-blue); line-height: 1; }
.roi-say { font-size: 18px; color: var(--n-700); margin-block: var(--s-4) var(--s-3); }
.roi-cite { font-size: 13px; color: var(--n-500); }
.roi-note { font-size: 12px; color: var(--n-400); margin-block-start: var(--s-2); }

/* --- demo cards ---------------------------------------------------------- */
.card-demo { border: 1px solid var(--n-150); border-radius: var(--r-lg); background: var(--n-0); box-shadow: var(--shadow-card); overflow: hidden; }
.cd-top { padding: var(--s-4); border-block-end: 1px solid var(--n-150); display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.cd-top b { font-size: 15px; margin-inline-end: var(--s-2); }
.cd-list { list-style: none; margin: 0; padding: 0; }
.cd-list li { display: flex; gap: var(--s-4); padding: var(--s-3) var(--s-4); border-block-start: 1px solid var(--n-150); font-size: 14px; color: var(--n-700); }
.cd-list i { font-family: var(--font-mono); font-style: normal; font-size: 12px; color: var(--n-400); flex: none; width: 46px; }

.res-rule { padding: var(--s-4); font-size: 14px; line-height: 1.9; color: var(--n-700); border-block-end: 1px solid var(--n-150); }
.res-rule b { color: var(--nori-blue); padding-inline: 4px; }
.res-out { padding: var(--s-6) var(--s-4); display: flex; align-items: baseline; gap: var(--s-3); background: var(--accent-soft); }
.res-out b { font-family: var(--font-mono); font-size: 44px; font-weight: 500; color: var(--nori-blue); }
.res-out span { font-size: 14px; color: var(--n-600); }
.res-exp { display: flex; align-items: center; gap: var(--s-2); padding: var(--s-3) var(--s-4); font-size: 13px; color: var(--n-600); border-block-start: 1px solid var(--n-150); }
.res-exp .ic { color: var(--nori-cyan-ink); }

/* --- finance ------------------------------------------------------------- */
.fin { margin-block-start: var(--s-6); border: 1px solid var(--n-150); border-radius: var(--r-lg); overflow-x: auto; background: var(--n-0); }
.fin-row {
  display: grid; grid-template-columns: minmax(140px, 1.6fr) repeat(3, minmax(76px, 1fr));
  gap: var(--s-3); padding: var(--s-3) var(--s-4);
  border-block-start: 1px solid var(--n-150);
  font-size: 14px; font-variant-numeric: tabular-nums;
}
.fin-head { border-block-start: 0; font-size: 11px; color: var(--n-500); font-weight: 600; }
.fin-row b { font-weight: 700; color: var(--n-900); }

/* --- roles --------------------------------------------------------------- */
.roles { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr)); gap: var(--s-4); margin-block-start: var(--s-6); }
.roles article { border: 1px solid var(--n-150); border-radius: var(--r-lg); padding: var(--s-5); background: var(--n-0); }
.roles .ic { color: var(--nori-blue); margin-block-end: var(--s-3); }
.roles h3 { font-size: 15px; font-weight: 700; margin-block-end: var(--s-2); }
.roles p { font-size: 14px; line-height: 1.7; color: var(--n-600); }

/* --- pricing ------------------------------------------------------------- */
.plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: var(--s-4); margin-block-start: var(--s-6); }
.plan { border: 1px solid var(--n-150); border-radius: var(--r-lg); padding: var(--s-5); background: var(--n-0); }
.plan.featured { border-color: var(--nori-blue); box-shadow: 0 0 0 3px var(--accent-soft); }
.plan h3 { font-size: 16px; font-weight: 700; }
.plan-price { font-family: var(--font-mono); font-size: 34px; font-weight: 500; color: var(--n-900); margin-block: var(--s-3); }
.plan-price s { font-size: 18px; color: var(--n-400); margin-inline-end: var(--s-2); }
.plan-tag { font-size: 14px; color: var(--n-600); margin-block-end: var(--s-4); }

/* --- close --------------------------------------------------------------- */
.close {
  padding-block: clamp(64px, 9vw, 112px);
  text-align: center;
  background:
    radial-gradient(700px 360px at 50% 0%, rgba(79, 220, 236, .16), transparent 65%),
    var(--n-50);
  border-block-start: 1px solid var(--n-150);
}
.close .brandmark { justify-content: center; margin-block-end: var(--s-4); }
.close-h {
  font-family: var(--font-display);
  font-size: clamp(28px, 4.4vw, 46px); font-weight: 800;
  letter-spacing: var(--track-display); color: var(--n-900);
  margin-block-end: var(--s-5);
}
[dir="rtl"] .close-h { letter-spacing: normal; }

/* --- footer -------------------------------------------------------------- */
.site-foot { border-block-start: 1px solid var(--n-150); padding-block: var(--s-7); background: var(--n-0); }
.site-foot-inner { display: flex; flex-wrap: wrap; gap: var(--s-6); justify-content: space-between; }
.site-foot-brand p { font-size: 14px; color: var(--n-500); margin-block-start: var(--s-2); }
.site-foot-links { display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5); font-size: 14px; }
.site-foot-links a { color: var(--n-600); }
.site-foot-links a:hover { color: var(--nori-blue); }
.site-flashes { padding-block: var(--s-4); }

/* --- reveal -------------------------------------------------------------- */
.js-reveal-ready .reveal { opacity: 0; transform: translateY(14px); }
.js-reveal-ready .reveal.in-view {
  opacity: 1; transform: none;
  transition: opacity .7s cubic-bezier(.16,1,.3,1), transform .7s cubic-bezier(.16,1,.3,1);
}
@media (prefers-reduced-motion: reduce) { .js-reveal-ready .reveal { opacity: 1; transform: none; } }

.skip-link { position: absolute; inset-inline-start: -9999px; padding: var(--s-3) var(--s-4); background: var(--nori-blue); color: #fff; z-index: 100; border-radius: var(--r-sm); }
.skip-link:focus { inset-inline-start: var(--s-4); inset-block-start: var(--s-3); }

/* ==========================================================================
   Homepage — additions
   ========================================================================== */

/* --- the stage now holds three states, on a faster loop -------------------
   11s rather than 16s: at the old pace a reader had usually scrolled past
   before the second board arrived, which meant the argument never landed. */
.board-c { animation-name: swap-c; }
.board, .board-a, .board-b, .board-c { animation-duration: 11s; }

@keyframes swap-a { 0%,26% { opacity: 1; } 32%,94% { opacity: 0; } 100% { opacity: 1; } }
@keyframes swap-b { 0%,28% { opacity: 0; } 34%,60% { opacity: 1; } 66%,100% { opacity: 0; } }
@keyframes swap-c { 0%,62% { opacity: 0; } 68%,94% { opacity: 1; } 100% { opacity: 0; } }

/* The action offered on the unconfirmed patient. It is the whole point of
   that row: the system has narrowed the day down to one phone call. */
.board-act {
  margin-inline-start: auto;
  flex: none;
  font-size: 12px;
  font-weight: 700;
  color: var(--nori-blue);
  border: 1px solid var(--nori-blue);
  border-radius: var(--r-pill);
  padding: 4px 12px;
}
.board-act:hover { background: var(--nori-blue); color: #fff; }
.board-foot { align-items: center; flex-wrap: wrap; }
.board-foot > span { flex: 1; min-width: 12ch; }

/* --- capture at the chairside -------------------------------------------- */
.cap-shot { display: flex; gap: var(--s-4); align-items: stretch; }
.cap-frame {
  flex: none;
  width: 92px;
  display: grid;
  place-items: center;
  border: 1px dashed var(--n-300);
  border-radius: var(--r-md);
  background: var(--n-50);
  color: var(--n-400);
}
.cap-read { flex: 1; min-width: 0; }
.cap-lbl {
  font-size: 11px; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--n-500); margin-block-end: var(--s-2);
}
[dir="rtl"] .cap-lbl { letter-spacing: normal; text-transform: none; }
.cap-read ul { list-style: none; margin: 0; padding: 0; }
.cap-read li {
  display: flex; align-items: center; gap: var(--s-2);
  padding-block: var(--s-2);
  border-block-start: 1px solid var(--n-150);
  font-size: 13px;
}
.cap-read li:first-child { border-block-start: 0; }
.cap-read b { font-weight: 600; color: var(--n-700); flex: 1; }
.cap-read i { font-family: var(--font-mono); font-style: normal; color: var(--n-800); }
.cap-read i.hi { color: var(--warn); font-weight: 500; }

/* Approve / amend. Rendered as the real pair of controls, because "nothing is
   saved until the physician approves" is a promise about consent, and showing
   only the approve button would quietly misstate it. */
.cap-btns { display: flex; gap: var(--s-2); flex: none; margin-inline-start: auto; }
.cap-btns b, .cap-btns em {
  font-style: normal; font-size: 12px; font-weight: 700;
  padding: 5px 12px; border-radius: var(--r-pill);
}
.cap-btns b { background: var(--nori-blue); color: #fff; }
.cap-btns em { border: 1px solid var(--n-200); color: var(--n-600); font-weight: 600; }

/* --- gain and loss -------------------------------------------------------- */
.val-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: var(--s-4);
  margin-block-start: var(--s-6);
}
.val { border: 1px solid var(--n-150); border-radius: var(--r-lg); padding: var(--s-5); background: var(--n-0); }
.val-lbl {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: 13px; font-weight: 700; margin-block-end: var(--s-4);
}
.val ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.val li { font-size: 14.5px; line-height: 1.8; color: var(--n-700); padding-inline-start: var(--s-4); position: relative; }
.val li::before {
  content: ''; position: absolute; inset-inline-start: 0; inset-block-start: .72em;
  width: 6px; height: 6px; border-radius: 50%;
}
.gain .val-lbl { color: var(--ok); }
.gain li::before { background: var(--ok); }
/* The loss column is stated plainly and never dressed as alarm: no red panel,
   no countdown, no scarcity. A cost the reader already pays does not need
   decoration to be persuasive. */
.loss .val-lbl { color: var(--n-600); }
.loss li::before { background: var(--n-300); }
.loss li { color: var(--n-600); }

/* --- the research figure -------------------------------------------------- */
.sec-roi { text-align: center; background: var(--n-50); }
.roi-fig { font-family: var(--font-mono); font-size: clamp(56px, 11vw, 116px); font-weight: 500; color: var(--nori-blue); line-height: 1; letter-spacing: -.03em; }
.roi-say { font-size: clamp(16px, 1.8vw, 19px); line-height: 1.8; color: var(--n-700); margin-block: var(--s-4) var(--s-3); }
.roi-cite { font-size: 13px; color: var(--n-600); font-family: var(--font-mono); }
.roi-note { font-size: 12.5px; line-height: 1.7; color: var(--n-500); margin-block-start: var(--s-3); }

/* --- what the owner controls ---------------------------------------------- */
.settings {
  margin-block-start: var(--s-6);
  border: 1px solid var(--n-150);
  border-radius: var(--r-lg);
  padding: var(--s-5);
  background: var(--n-50);
}
.set-lbl { display: flex; align-items: center; gap: var(--s-2); font-size: 13px; font-weight: 700; color: var(--n-900); margin-block-end: var(--s-4); }
.set-lbl .ic { color: var(--nori-blue); }
.settings ul { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr)); gap: var(--s-3); }
.settings li {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-3); background: var(--n-0);
  border: 1px solid var(--n-150); border-radius: var(--r-md);
  font-size: 13px;
}
.settings b { font-weight: 600; color: var(--n-700); }
.settings i { font-style: normal; font-family: var(--font-mono); font-size: 12px; color: var(--nori-blue); flex: none; }
.set-foot { margin-block-start: var(--s-4); font-size: 13px; color: var(--n-600); }

/* --- the value-of-data note ----------------------------------------------- */
/* No coloured side border. A 2px rule down the edge of a callout is the
   single most recognisable tell of a generated interface, and it carried no
   information the surface and ink do not. */
.sec-value-note {
  margin-block-start: var(--s-5);
  padding: var(--s-4) var(--s-5);
  background: var(--n-25);
  border: 1px solid var(--n-150);
  border-radius: var(--r-lg);
  font-size: 14.5px;
  line-height: 1.95;
  color: var(--n-700);
  max-width: 62ch;
}

/* --- record / structured entry, alternating ------------------------------- */
.flip-stage { position: relative; min-height: 300px; }
.flip-stage .card-demo { position: absolute; inset: 0; animation: 11s ease-in-out infinite; }
/* Three panels now: the stored record, structured entry, and capture by
   photo. Same 11s clock as the hero so the two alternating areas of the page
   stay in step rather than fighting each other for the eye. */
.fs-a { animation-name: swap-fa; }
.fs-b { animation-name: swap-fb; }
.fs-c { animation-name: swap-fc; }
@keyframes swap-fa { 0%,26% { opacity: 1; } 32%,94% { opacity: 0; } 100% { opacity: 1; } }
@keyframes swap-fb { 0%,28% { opacity: 0; } 34%,60% { opacity: 1; } 66%,100% { opacity: 0; } }
@keyframes swap-fc { 0%,62% { opacity: 0; } 68%,94% { opacity: 1; } 100% { opacity: 0; } }

.cd-list.entry li { align-items: center; gap: var(--s-3); }
.cd-list.entry b { flex: 1; font-weight: 600; color: var(--n-800); }
.cd-list .code {
  font-family: var(--font-mono); font-size: 11px;
  background: var(--accent-soft); color: var(--nori-blue);
  padding: 3px 7px; border-radius: var(--r-sm);
}
.cd-foot { padding: var(--s-3) var(--s-4); border-block-start: 1px solid var(--n-150); font-size: 12.5px; color: var(--n-600); }

.res-exp { display: flex; flex-wrap: wrap; gap: var(--s-4); }
.res-exp span { display: flex; align-items: center; gap: 6px; }

/* ==========================================================================
   Tablet
   --------------------------------------------------------------------------
   Between 700 and 1000 the hero must not stay two-up — the board becomes too
   narrow to read a row — but it must not fall to the phone layout either.
   One column, board at full width, and the type stays large.
   ========================================================================== */
@media (min-width: 700px) and (max-width: 1000px) {
  .hero-in { grid-template-columns: 1fr; gap: var(--s-7); }
  .hero-h1 { font-size: clamp(42px, 6.4vw, 56px); }
  .hero-lead { max-width: 56ch; }
  .stage { min-height: 400px; }
  .split-in { grid-template-columns: 1fr; }
  .split-in.flip > *:first-child { order: 0; }
  .wa-grid { grid-template-columns: repeat(2, 1fr); }
  .wa:last-child { grid-column: 1 / -1; }
  .val-grid { grid-template-columns: 1fr; }
  .roles { grid-template-columns: repeat(2, 1fr); }
}

/* ==========================================================================
   Phone
   --------------------------------------------------------------------------
   Not a squeezed desktop. The board keeps its full row structure — time,
   name, state — because dropping the state pill on a phone would remove the
   one thing the panel exists to show. Type scales, padding tightens, and
   every tap target stays at 44px.
   ========================================================================== */
@media (max-width: 699px) {
  .site-wrap { width: calc(100% - 2 * var(--s-4)); }

  .hero { padding-block: var(--s-6) var(--s-7); }
  .hero-h1 { font-size: clamp(30px, 8.6vw, 40px); line-height: 1.22; }
  .hero-lead { font-size: 15.5px; line-height: 1.8; }
  .hero-cta { flex-direction: column; align-items: stretch; }
  .hero-cta .btn { width: 100%; justify-content: center; }
  .hero-notes { gap: var(--s-2) var(--s-4); }
  .hero-notes li { font-size: 12.5px; }

  .stage { min-height: 400px; border-radius: var(--r-md); }
  .board { padding: var(--s-4); gap: var(--s-2); }
  .rows li { grid-template-columns: 46px 1fr; row-gap: 6px; padding-block: var(--s-2); }
  /* The state pill drops to its own line rather than being dropped: it is
     the information the row is for. */
  .rows li .pill { grid-column: 2; justify-self: start; }
  .rows b { font-size: 13.5px; }
  .tally { gap: var(--s-4); }
  .tally b { font-size: 19px; }
  .board-foot { font-size: 12px; }
  .board-act { padding: 6px 14px; min-height: 32px; display: inline-flex; align-items: center; }

  .cap-shot { flex-direction: column; }
  .cap-frame { width: 100%; height: 74px; }
  .cap-btns { width: 100%; }
  .cap-btns b, .cap-btns em { flex: 1; text-align: center; padding-block: 9px; }

  .sec { padding-block: var(--s-7); }
  .sec-h { font-size: clamp(21px, 6vw, 27px); }
  .sec-lead { font-size: 15.5px; }
  .val { padding: var(--s-4); }
  .settings { padding: var(--s-4); }
  .settings ul { grid-template-columns: 1fr; }
  .roles { grid-template-columns: 1fr 1fr; gap: var(--s-3); }
  .roles article { padding: var(--s-4); }
  .flip-stage { min-height: 280px; }

  .roi-fig { font-size: clamp(52px, 17vw, 76px); }

  /* 44px floor on everything tappable, phone included. */
  .btn-lg { height: 48px; }
  .site-lang, .site-signin { min-height: 44px; display: inline-flex; align-items: center; }
}

/* Very narrow — the board stacks rather than compressing to unreadable. */
@media (max-width: 380px) {
  .rows li { grid-template-columns: 1fr; }
  .rows .t { font-size: 11px; }
  .roles { grid-template-columns: 1fr; }
}

/* Reduced motion: every alternating panel stops overlapping and stacks, so
   the whole argument is readable at once. */
@media (prefers-reduced-motion: reduce) {
  .flip-stage { min-height: 0; }
  .flip-stage .card-demo { position: relative; inset: auto; animation: none; opacity: 1 !important; }
  .flip-stage .fs-b,
  .flip-stage .fs-c { margin-block-start: var(--s-4); }
  .board-c { position: relative; inset: auto; animation: none; opacity: 1 !important; }
}

/* --- touch targets on the phone -----------------------------------------
   PRODUCT.md sets a 44px floor on primary actions that density may not
   override. Three things were slipping under it on a phone: the header CTA
   (.btn.sm inherits a 38px compact height), the demo board's own action, and
   the footer links, which were 22px of text with nothing around them.
   Inline links inside a paragraph are exempt; a standalone link in a list is
   not — it is a target, and on a phone it is a thumb. */
@media (max-width: 699px) {
  .site-nav-end .btn.sm { height: 44px; padding-inline: var(--s-4); }
  .board-act { min-height: 44px; padding-inline: var(--s-4); }
  .site-foot-links a,
  .site-links a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* ==========================================================================
   Homepage — dashboards, capture, sheet, footnote
   ========================================================================== */

/* The manager's board. Same row grammar as the other two so the three read as
   one system seen from three chairs, not as three different products. */
.rows-c li { grid-template-columns: 26px 1fr auto; }
.rows-c .t { display: inline-flex; align-items: center; color: var(--n-400); }
.rows-c .num {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  color: var(--n-900);
}
.rows-c .on b { color: var(--n-600); font-weight: 500; }
.board-foot .delta {
  flex: none;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  color: var(--ok);
  background: var(--ok-soft);
  padding: 3px 9px;
  border-radius: var(--r-pill);
}

/* --- not shipped yet ------------------------------------------------------
   A quiet, permanent marker on the two capabilities the page shows but the
   product does not yet do: capture-by-photo and AI import/cleaning. It is
   deliberately legible rather than hidden — showing a roadmap is honest,
   implying it ships today is not, and the difference is one small word. */
.soon {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--n-500);
  background: var(--n-100);
  border: 1px solid var(--n-200);
  border-radius: var(--r-pill);
  padding: 3px 8px;
  margin-inline-start: auto;
  white-space: nowrap;
}
[dir="rtl"] .soon { letter-spacing: normal; text-transform: none; }
i.soon { font-style: normal; margin-inline-start: 6px; }

/* --- capture by photo, in the record section ------------------------------ */
.lab { display: flex; gap: var(--s-4); padding: var(--s-4); align-items: stretch; }
/* --- the captured sheet --------------------------------------------------
   A printed report photographed on a desk: paper turned a few degrees, a
   contact shadow under it, and the viewfinder brackets over the top.
   ------------------------------------------------------------------------- */
.lab-shot {
  flex: none;
  inline-size: 132px;
  position: relative;
  border-radius: var(--r-md);
  background: linear-gradient(160deg, var(--n-100), var(--n-50));
  overflow: hidden;
  padding: var(--s-3);
  display: flex; flex-direction: column; justify-content: flex-end;
}
.shot-paper {
  position: absolute;
  inset-block-start: 12px; inset-inline-start: 14px;
  inline-size: 104px; block-size: 116px;
  background: #fff;
  border-radius: 3px;
  transform: rotate(-4.5deg);
  box-shadow: 0 1px 2px rgba(10,20,36,.14), 0 8px 18px -6px rgba(10,20,36,.28);
  padding: 9px 10px;
  display: flex; flex-direction: column; gap: 4px;
  direction: ltr;
}
.shot-hd { font-size: 6.5px; font-weight: 700; letter-spacing: .06em; color: var(--n-700); }
.shot-rule { display: block; block-size: 1.5px; background: var(--n-150); border-radius: 2px; }
.shot-rule.short { inline-size: 62%; margin-block-end: 3px; }
.shot-paper u {
  display: flex; align-items: baseline; justify-content: space-between;
  text-decoration: none;
  border-block-end: 1px solid var(--n-100);
  padding-block-end: 2.5px;
}
.shot-paper u span { font-size: 6.5px; font-weight: 600; color: var(--n-600); letter-spacing: .04em; }
.shot-paper u em {
  font-style: normal; font-family: var(--font-mono);
  font-size: 7.5px; font-weight: 500; color: var(--n-900);
}
/* Viewfinder: four brackets and a soft vignette. Corners only — a full
   rectangle would read as a border, not a lens. */
.shot-frame { position: absolute; inset: 7px; pointer-events: none; }
.shot-frame::before, .shot-frame::after {
  content: ""; position: absolute; inline-size: 13px; block-size: 13px;
  border: 2px solid rgba(255,255,255,.92);
  filter: drop-shadow(0 0 1px rgba(10,20,36,.35));
}
.shot-frame::before { inset-block-start: 0; inset-inline-start: 0; border-inline-end: 0; border-block-end: 0; border-start-start-radius: 3px; }
.shot-frame::after  { inset-block-end: 0; inset-inline-end: 0; border-inline-start: 0; border-block-start: 0; border-end-end-radius: 3px; }
.shot-cap {
  position: relative;
  display: flex; align-items: center; gap: 4px;
  font-style: normal; font-size: 9.5px; line-height: 1.3; color: var(--n-0);
  background: rgba(10, 20, 36, .72);
  padding: 3px 6px; border-radius: var(--r-sm);
}
.lab-vals { flex: 1; min-width: 0; list-style: none; margin: 0; padding: 0; }
.lab-vals li {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: center;
  gap: var(--s-2);
  padding-block: 7px;
  border-block-start: 1px solid var(--n-150);
  font-size: 13px;
}
.lab-vals li:first-child { border-block-start: 0; }
/* Test names stay Latin and left-to-right even on the Arabic page: ALT is ALT
   in every clinic in the world, and flipping it would be a translation nobody
   asked for. */
.lab-vals b { font-weight: 600; color: var(--n-700); direction: ltr; unicode-bidi: isolate; }
.lab-vals i { font-family: var(--font-mono); font-style: normal; color: var(--n-900); }
.lab-vals i.hi { color: var(--warn); font-weight: 500; }
.lab-vals em { font-style: normal; font-size: 11px; color: var(--n-500); }

.cap-foot { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.cap-foot > span:first-of-type { flex: 1; min-width: 14ch; }
.cap-foot .ic { color: var(--ok); flex: none; }

/* --- the cohort as a sheet ------------------------------------------------ */
.sheet-wrap { overflow-x: auto; border-block-end: 1px solid var(--n-150); }
.sheet {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-mono);
  font-size: 12px;
  /* A spreadsheet is read left-to-right whatever the page direction: the
     column order is the export's order, and mirroring it would misrepresent
     the file the researcher actually receives. */
  direction: ltr;
}
.sheet th, .sheet td {
  border: 1px solid var(--n-150);
  padding: 6px 9px;
  text-align: left;
  white-space: nowrap;
}
.sheet thead th {
  background: var(--n-100);
  color: var(--n-600);
  font-weight: 500;
  position: sticky;
  inset-block-start: 0;
}
.sheet td { color: var(--n-800); background: var(--n-0); }
/* The row-number gutter, which is what makes a table read as a sheet. */
/* The row-number gutter. --n-400 on the tinted gutter measured 3.03:1 — row
   numbers are minor, but they are still text and the floor does not bend for
   unimportant text. */
.sheet .rn, .sheet thead th:first-child {
  background: var(--n-100);
  color: var(--n-500);
  text-align: center;
  width: 30px;
}
.res-exp .dim { color: var(--n-500); }

/* --- the permissions footnote --------------------------------------------- */
.roles-note {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  margin-block-start: var(--s-5);
  font-size: 13px;
  line-height: 1.8;
  color: var(--n-500);
  max-width: 76ch;
}
.roles-note .ic { color: var(--n-400); flex: none; margin-block-start: 4px; }

/* --- phone ---------------------------------------------------------------- */
@media (max-width: 699px) {
  .lab { flex-direction: column; padding: var(--s-3); }
  .lab-shot { inline-size: 100%; block-size: 128px; padding: var(--s-2); }
  /* Centred with auto margins, not a translate: translateX flips meaning
     between LTR and RTL and pushed the sheet outside its own frame. */
  .shot-paper {
    inset-block-start: 10px;
    inset-inline: 0;
    margin-inline: auto;
    transform: rotate(-3deg);
    inline-size: 116px; block-size: 106px;
  }
  .lab-vals li { grid-template-columns: 1fr auto auto; row-gap: 4px; }
  .lab-vals li .pill { grid-column: 3; }
  .rows-c li { grid-template-columns: 22px 1fr auto; }
  .sheet { font-size: 11px; }
  .sheet th, .sheet td { padding: 5px 7px; }
}

/* ==========================================================================
   Sign-in
   --------------------------------------------------------------------------
   A split shell, not a card floating in grey. Signing in is the moment a
   physician hands a system their patients' records; the aside answers "who
   sees what" in three sentences, which settles more than polish on the form.

   `.auth` is left alone — /forgot, /reset and the platform login share it.
   ========================================================================== */

/* --- the brand lockup -------------------------------------------------------
   views/_brand.php emits these classes and, until now, nothing styled them:
   the mark and the wordmark stacked as plain blocks at weight 400 wherever the
   partial was used — the landing header and footer included. The wordmark is
   Latin, so it stays LTR in both locales, and it keeps the identity's split:
   Nori carries the weight, Clinic recedes.
   -------------------------------------------------------------------------- */
.brandmark {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  line-height: 1;
}
.brandmark-svg { flex: none; display: block; }
.brandmark-word {
  direction: ltr;
  unicode-bidi: isolate;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.06em;
  letter-spacing: -.015em;
  color: var(--n-900);
  white-space: nowrap;
}
.brandmark-word span { font-weight: 500; color: var(--n-500); }
/* On a dark ground the mark rides currentColor, so the word must follow it. */
.brandmark-mono .brandmark-word,
.brandmark-mono .brandmark-word span { color: currentColor; }

/* --- sign-in ---------------------------------------------------------------
   A single panel on a sunken ground. The dark brand slab that used to sit
   beside it is gone: this screen has one job, and the person opening it has
   already decided to come. The product argument belongs on the landing page.
   -------------------------------------------------------------------------- */
.signin-page { margin: 0; background: var(--n-50); }
.signin {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-5);
  padding: var(--s-6) var(--s-4);
}
.signin-panel {
  inline-size: 100%;
  max-inline-size: 400px;
  background: var(--n-0);
  border: 1px solid var(--n-150);
  border-radius: 14px;
  padding: clamp(var(--s-5), 4vw, var(--s-7));
  box-shadow: 0 1px 2px rgba(10, 20, 36, .04), 0 12px 32px -14px rgba(10, 20, 36, .14);
}
.signin-mark { display: flex; justify-content: center; margin-block-end: var(--s-6); }
.signin-panel h1 {
  font-size: 21px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--n-900);
  text-align: center;
}
.signin-sub {
  margin-block: var(--s-2) var(--s-6);
  font-size: 13.5px;
  color: var(--n-500);
  text-align: center;
}
.signin-panel .btn.block { width: 100%; justify-content: center; }
.signin-alt { margin-block-start: var(--s-5); font-size: 13.5px; text-align: center; }
.signin-alt a { color: var(--nori-blue); font-weight: 600; }
.signin-foot { font-size: 13px; }
.signin-foot a { color: var(--n-500); }
.signin-foot a:hover { color: var(--nori-blue); }

/* A sign-in error is the one message on this site a user is most likely to be
   stuck on, so it is stated in the danger ink with a full border rather than
   as a tinted whisper. */
.signin-flash {
  margin-block-end: var(--s-5);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  font-size: 14px;
  line-height: 1.6;
  border: 1px solid var(--n-200);
  background: var(--n-50);
  color: var(--n-700);
}
.signin-flash.error   { border-color: var(--danger); background: var(--danger-soft); color: var(--danger); }
.signin-flash.success { border-color: var(--ok);     background: var(--ok-soft);     color: var(--ok); }

@media (pointer: coarse) {
  .signin-alt a, .signin-foot a { display: inline-flex; align-items: center; min-block-size: 44px; }
}

/* Below 380 the header cannot hold a wordmark, a language switch, a sign-in
   link and a CTA. The sign-in link goes: the CTA leads to the same place for
   a new visitor, and a signed-up user reaches /login from the footer. */
@media (max-width: 380px) {
  .site-nav-inner { gap: var(--s-3); }
  .site-nav-end { gap: var(--s-2); min-width: 0; }
  .site-signin { display: none; }
  .site-nav-end .btn.sm { padding-inline: var(--s-3); font-size: 13px; }
}

/* --- the owner's board ---------------------------------------------------
   Today reads first and largest, the month settles underneath it, and the
   physician rows are a real grid so hours, counts and money line up in
   columns instead of drifting on auto margins.
   ------------------------------------------------------------------------ */
.mgr-today {
  display: flex; gap: var(--s-5);
  padding-block-end: var(--s-3);
  border-block-end: 1px solid var(--n-150);
}
.mgr-today span { display: flex; flex-direction: column; gap: 2px; font-size: 11px; color: var(--n-500); }
.mgr-today b {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 24px; font-weight: 500; color: var(--n-900);
}
.mgr-today .t-ok { color: var(--ok); }
.mgr-today .t-wait { color: var(--warn); }

.mgr-docs { list-style: none; margin: 0; padding: 0; }
.mgr-docs li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto auto;
  align-items: center; gap: var(--s-3);
  padding-block: 11px;
  border-block-end: 1px solid var(--n-100);
}
.mgr-docs .d-nm { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--n-800); }
.mgr-docs .d-nm .ic { color: var(--n-400); }
.mgr-docs .d-hr, .mgr-docs .d-ct { font-size: 12px; color: var(--n-500); }
.mgr-docs .d-hr { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.mgr-docs .d-sm {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 14px; font-weight: 500; color: var(--n-900); min-inline-size: 6ch; text-align: end;
}

.mgr-month {
  display: flex; align-items: baseline; gap: var(--s-5);
  margin-block-start: auto; padding-block-start: var(--s-3);
}
.mgr-month span { display: flex; flex-direction: column; gap: 1px; }
.mgr-month i { font-style: normal; font-size: 11px; color: var(--n-500); }  /* n-400 measured 3.5:1 — below the floor */
.mgr-month b {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 15px; font-weight: 500; color: var(--n-800);
}
.mgr-delta {
  margin-inline-start: auto; align-self: center;
  font-size: 12px; font-weight: 600; color: var(--ok);
  background: var(--ok-soft); padding: 4px 9px; border-radius: var(--r-pill);
}
@media (max-width: 699px) {
  .mgr-docs li { grid-template-columns: minmax(0, 1fr) auto; row-gap: 2px; }
  .mgr-docs .d-hr { grid-column: 1; font-size: 11px; }
  .mgr-docs .d-ct { display: none; }
}

/* --- the day axis -----------------------------------------------------------
   The doctor's day drawn to scale. Two things follow from that and neither is
   decoration: free time is visible as empty space, because on an axis a gap
   has a size; and a long day scrolls INSIDE this window, so the page under it
   never moves and the hours column stays where the eye left it.
   -------------------------------------------------------------------------- */
.axis {
  max-block-size: 520px;
  overflow-y: auto;
  overscroll-behavior: contain;      /* the page must not scroll when this ends */
  padding: var(--s-4) var(--s-5) var(--s-5);
}
.axis-in { position: relative; display: flex; gap: var(--s-2); }

.axis-hours { inline-size: 46px; flex: none; position: relative; }
.axis-hours span {
  position: absolute;
  inset-inline-end: 0;
  transform: translateY(-50%);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  color: var(--n-400);
}
.axis-hours span:nth-child(1) { top: 0; }
.axis-hours span:nth-child(2) { top: var(--hour); }
.axis-hours span:nth-child(3) { top: calc(var(--hour) * 2); }
.axis-hours span:nth-child(4) { top: calc(var(--hour) * 3); }
.axis-hours span:nth-child(5) { top: calc(var(--hour) * 4); }
.axis-hours span:nth-child(6) { top: calc(var(--hour) * 5); }
.axis-hours span:nth-child(7) { top: calc(var(--hour) * 6); }
.axis-hours span:nth-child(8) { top: calc(var(--hour) * 7); }
.axis-hours span:nth-child(9) { top: calc(var(--hour) * 8); }
.axis-hours span:nth-child(10){ top: calc(var(--hour) * 9); }
.axis-hours span:nth-child(11){ top: calc(var(--hour) * 10); }
.axis-hours span:nth-child(12){ top: calc(var(--hour) * 11); }
.axis-hours span:nth-child(13){ top: calc(var(--hour) * 12); }
.axis-hours span:nth-child(14){ top: calc(var(--hour) * 13); }
.axis-hours span:nth-child(15){ top: calc(var(--hour) * 14); }

.axis-track {
  flex: 1;
  min-inline-size: 0;
  position: relative;
  border-inline-start: 1px solid var(--n-150);
}
.axis-rule {
  position: absolute;
  inset-inline: 0;
  block-size: 0;
  border-block-start: 1px dashed var(--n-100);
}

.axis-slot {
  position: absolute;
  /* inline-size and inset-inline-start come from the layout pass in the view:
     overlapping appointments share the track in columns. */
  inset-inline-end: auto;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 0 var(--s-3);
  border: 1px solid var(--n-150);
  border-radius: var(--r-md);
  background: var(--n-0);
  text-decoration: none;
  color: inherit;
  overflow: hidden;
}
.axis-slot:hover { border-color: var(--n-300); background: var(--n-25); }

/* The slot is a container, not a link, so it can hold a real start button.
   The link is stretched across the whole block instead: the text spans are not
   interactive, so covering them costs nothing and the entire slot stays
   clickable. Anything that IS interactive has to be lifted above it. */
.axis-slot .s-hit { position: absolute; inset: 0; z-index: 1; text-decoration: none; }
.axis-slot .s-hit:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-md);
}
.axis-slot .s-go { position: relative; z-index: 2; display: flex; margin-inline-start: var(--s-2); }
/* A 20-minute appointment is a 26px block and .btn.sm is 30px, so the default
   would be clipped by the slot's overflow:hidden. Sized to the shortest block
   the timeline can draw rather than to the button scale. */
.axis-slot .s-go .btn {
  block-size: 20px;
  padding-inline: var(--s-2);
  font-size: 11px;
  border-radius: var(--r-sm);
}
/* Revealed on hover or keyboard focus: a button on every row would turn the
   day into a wall of buttons and bury the one that matters. */
.axis-slot .s-go .btn { opacity: 0; transition: opacity .12s ease; }
.axis-slot:hover .s-go .btn,
.axis-slot .s-go .btn:focus-visible { opacity: 1; }
@media (pointer: coarse) {
  /* Nothing to hover with — a permanently hidden action is an absent one. */
  .axis-slot .s-go .btn { opacity: 1; }
}
.axis-slot .s-t {
  font-family: var(--font-mono); font-size: 11.5px; color: var(--n-500); flex: none;
}
.axis-slot .s-n { font-weight: 600; font-size: 13.5px; color: var(--n-800); flex: none; }
.axis-slot .s-r {
  font-size: 12px; color: var(--n-500);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-inline-size: 0;
}
.axis-slot .s-e { margin-inline-start: auto; flex: none; display: flex; align-items: center; }

.axis-slot.is-now  { border-color: rgba(79, 220, 236, .55); background: rgba(79, 220, 236, .10); }
.axis-slot.is-done { opacity: .55; }
.axis-slot.is-miss { border-style: dashed; }
/* Cancelled. Still drawn, because the hour is still on the doctor's day and
   the gap is the point — but struck out, so it is never read as someone who
   is coming. */
.axis-slot.is-cancel { opacity: .6; border-style: dashed; }
.axis-slot.is-cancel .s-n { text-decoration: line-through; }

/* The single mark at the end of a slot that is still ahead: confirmed, or
   cancelled. Doubt keeps the three-bar .meter instead. Sized to sit in the
   26px block the shortest appointment draws. */
.s-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 14px;
  font-size: 12px;
  line-height: 1;
  font-weight: 700;
  flex: none;
}
.s-mark.ok        { color: var(--ok); }
.s-mark.cancelled { color: var(--danger); }

/* Now. The one moving thing on the screen, and the only place cyan is spent
   here — the identity reserves it for what is live. */
.axis-now {
  position: absolute;
  inset-inline: 0;
  block-size: 0;
  border-block-start: 2px solid var(--nori-cyan);
  z-index: 3;
}
.axis-now::after {
  content: attr(data-label);
  position: absolute;
  inset-inline-start: 0;
  transform: translateY(-50%);
  background: var(--nori-cyan-ink);
  color: #fff;
  font-size: 9.5px;
  font-weight: 600;
  padding: 1px 7px;
  border-radius: var(--r-pill);
}

@media (max-width: 699px) {
  .axis { max-block-size: 440px; padding: var(--s-3); }
  .axis-hours { inline-size: 38px; }
  .axis-slot { gap: var(--s-2); padding: 0 var(--s-2); }
  .axis-slot .s-r { display: none; }
}

/* --- reception's worksheet --------------------------------------------------
   Dense on purpose: this desk works a row at a time, hundreds of times a day.
   The free hours are rows in the same table rather than a separate panel —
   "where can I put someone" has to be answerable without changing screens.
   -------------------------------------------------------------------------- */
.desk-find {
  display: flex; align-items: center; gap: var(--s-2);
  margin: var(--s-4) var(--s-5) 0;
  padding: 0 var(--s-3);
  border: 1px solid var(--n-200);
  border-radius: var(--r-md);
  background: var(--n-0);
}
.desk-find:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring); }
.desk-find .ic { color: var(--n-400); flex: none; }
.desk-find input {
  flex: 1; min-inline-size: 0;
  border: 0; background: none; outline: none;
  block-size: 38px; font: inherit; font-size: 13.5px; color: var(--n-800);
}
.desk-find input::-webkit-search-cancel-button { filter: grayscale(1) opacity(.5); }
.desk-count { font-size: 11.5px; color: var(--n-500); flex: none; }

.desk-wrap { margin: var(--s-3) var(--s-5) var(--s-5); overflow-x: auto; }
.desk { inline-size: 100%; border-collapse: collapse; font-size: 13px; }
.desk thead th {
  text-align: start;
  font-size: 10.5px; font-weight: 600; letter-spacing: .05em;
  color: var(--n-500);
  padding: 8px 10px;
  border-block-end: 1px solid var(--n-150);
  white-space: nowrap;
}
.desk tbody td {
  padding: 9px 10px;
  border-block-end: 1px solid var(--n-100);
  vertical-align: middle;
}
.desk tbody tr:last-child td { border-block-end: 0; }
.desk tbody tr:hover td { background: var(--n-25); }
.desk .c-t { inline-size: 74px; white-space: nowrap; color: var(--n-500); font-size: 12px; }
.desk .c-n a { font-weight: 600; color: var(--n-800); text-decoration: none; display: block; }
.desk .c-n a:hover { color: var(--accent); text-decoration: underline; }
.desk .c-r { font-size: 11.5px; color: var(--n-500); }
.desk .c-d { inline-size: 116px; color: var(--n-600); white-space: nowrap; }
.desk .c-s { inline-size: 116px; white-space: nowrap; }
.desk .c-p { inline-size: 128px; color: var(--n-500); font-size: 12px; white-space: nowrap; }
.desk .c-a { inline-size: 56px; text-align: end; white-space: nowrap; }
.desk .c-a a { font-size: 12px; color: var(--accent); text-decoration: none; font-weight: 600; }
.desk .c-a a:hover { text-decoration: underline; }

.desk tr.past td { opacity: .55; }
.desk tr.live td { background: rgba(79, 220, 236, .10); }
.desk tr.live:hover td { background: rgba(79, 220, 236, .16); }

/* A free hour is money, so it reads as a slot to fill, not as a blank row. */
.desk tr.is-gap td {
  background: var(--n-25);
  color: var(--n-500);
  font-size: 12px;
}
.desk tr.is-gap:hover td { background: var(--n-50); }

.desk-none { padding: var(--s-5); text-align: center; color: var(--n-500); font-size: 13px; }

@media (max-width: 900px) {
  .desk .c-p, .desk .c-d { display: none; }
}

/* --- the desk's one suggestion ----------------------------------------------
   A count that opens into the people behind it, each with the three things the
   desk can actually do: call, message, or confirm on their behalf. Built on
   <details> so it works with no JavaScript and is keyboard-reachable, and
   deliberately not a modal — the day stays on screen behind it.
   -------------------------------------------------------------------------- */
.nudge {
  margin: var(--s-4) var(--s-5) 0;
  border: 1px solid var(--warn-line, #e6d29a);
  border-radius: var(--r-md);
  background: var(--warn-tint, #fdf8ec);
}
.nudge > summary {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 10px var(--s-4);
  cursor: pointer;
  list-style: none;
  font-size: 13px;
  color: var(--warn);
  min-block-size: 44px;
}
.nudge > summary::-webkit-details-marker { display: none; }
.nudge > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--r-md); }
.nudge > summary .ic { flex: none; }
.nudge-say { min-inline-size: 0; }
.nudge-say b { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.nudge-do {
  margin-inline-start: auto; flex: none;
  font-size: 12px; font-weight: 600; color: var(--nori-blue);
  text-decoration: underline; text-underline-offset: 3px;
}
.nudge[open] .nudge-do::after { content: ""; }

.nudge-list {
  list-style: none; margin: 0; padding: 0;
  border-block-start: 1px solid var(--warn-line, #e6d29a);
}
.nudge-list li {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  padding: 10px var(--s-4);
  border-block-start: 1px solid rgba(230, 210, 154, .55);
}
.nudge-list li:first-child { border-block-start: 0; }
.n-who { min-inline-size: 0; flex: 1; }
.n-who a { font-weight: 600; font-size: 13.5px; color: var(--n-800); text-decoration: none; display: block; }
.n-who a:hover { color: var(--nori-blue); text-decoration: underline; }
.n-meta { font-size: 11.5px; color: var(--n-600); }
.n-acts { display: flex; align-items: center; gap: 6px; flex: none; flex-wrap: wrap; }
.n-acts form { margin: 0; }
.n-acts .btn.sm { gap: 5px; }

/* "we rang and nobody answered", carried on the row itself. Warm rather than
   red: an unanswered call is a note to the next shift, not an alarm. */
.n-tried { color: var(--warn); font-weight: 500; }

.nudge-foot {
  margin: 0;
  padding: 9px var(--s-4);
  border-block-start: 1px solid rgba(230, 210, 154, .55);
  font-size: 12px;
  color: var(--n-600);
}

@media (max-width: 699px) {
  .nudge { margin-inline: var(--s-3); }
  .nudge-list li { align-items: flex-start; }
  .n-acts { inline-size: 100%; }
}

/* --- AI suggestions ---------------------------------------------------------
   Each card carries the number it rests on and how sure the model was, both
   visible without hovering. A suggestion about a clinic's money that hides its
   evidence is worse than no suggestion.
   -------------------------------------------------------------------------- */
.insight .card-head h2 { display: flex; align-items: center; gap: 7px; }
.insight .card-head .ic { color: var(--accent); }
.insight-refresh {
  background: none; border: 0; cursor: pointer;
  color: var(--n-500); font: inherit; font-size: 12px;
  text-decoration: underline; text-underline-offset: 3px;
  padding: 6px; margin: -6px;
}
.insight-refresh:hover { color: var(--accent); }
.insight-refresh:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: var(--r-sm); }

.insight-loading, .insight-msg { margin: 0; font-size: 13px; color: var(--n-500); }

.insight-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.insight-item {
  padding: var(--s-4);
  border: 1px solid var(--n-150);
  border-radius: var(--r-md);
  background: var(--n-0);
}
.i-head { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.i-head b { font-size: 14px; color: var(--n-900); }
.i-area {
  font-size: 10.5px; font-weight: 600; color: var(--n-600);
  background: var(--n-100); padding: 2px 8px; border-radius: var(--r-pill);
}
.i-conf { margin-inline-start: auto; font-size: 10.5px; font-weight: 600; padding: 2px 8px; border-radius: var(--r-pill); }
.conf-high   .i-conf { background: var(--ok-soft);     color: var(--ok); }
.conf-medium .i-conf { background: var(--n-100);       color: var(--n-600); }
.conf-low    .i-conf { background: var(--warn-soft);   color: var(--warn); }

.i-find { margin: var(--s-2) 0 0; font-size: 13px; line-height: 1.7; color: var(--n-600); }
.i-act  { margin: var(--s-2) 0 0; font-size: 13.5px; line-height: 1.7; color: var(--n-800); font-weight: 500; }
.i-metric {
  display: inline-block; margin-block-start: var(--s-3);
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 11.5px; color: var(--n-600);
  background: var(--n-50); border: 1px solid var(--n-150);
  padding: 3px 9px; border-radius: var(--r-sm);
}

.insight-foot {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin: var(--s-4) 0 0; padding-block-start: var(--s-3);
  border-block-start: 1px solid var(--n-150);
  font-size: 11.5px; color: var(--n-500);
}
.insight-foot .ic { color: var(--ok); flex: none; }
.insight-when { color: var(--n-400); }

/* --- voice dictation --------------------------------------------------------
   The live state has to be unmistakable: a doctor who thinks the microphone is
   off while it is listening is the one failure this control must not have.
   -------------------------------------------------------------------------- */
.dictate { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; margin-block-end: var(--s-4); }
.dictate .btn { gap: 7px; }
.dictate-ic { display: inline-flex; }
.dictate-state { font-size: 12px; color: var(--n-500); min-inline-size: 0; }

.dictate .btn.is-live {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}
.dictate .btn.is-live .ic { color: #fff; }
.dictate .btn.is-live .dictate-ic::after {
  content: "";
  display: inline-block;
  inline-size: 7px; block-size: 7px;
  margin-inline-start: 6px;
  border-radius: 50%;
  background: #fff;
  animation: dictate-pulse 1.1s ease-in-out infinite;
}
@keyframes dictate-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) {
  .dictate .btn.is-live .dictate-ic::after { animation: none; }
}

/* What was heard, kept beside what was filled in. */
.dictate-heard {
  margin-block-end: var(--s-4);
  padding: var(--s-3) var(--s-4);
  background: var(--n-25);
  border: 1px solid var(--n-150);
  border-radius: var(--r-md);
}
.dh-label { font-size: 10.5px; font-weight: 600; letter-spacing: .06em; color: var(--n-400); }
.dh-text { margin: 4px 0 0; font-size: 13px; line-height: 1.75; color: var(--n-700); }
.dh-unclear { list-style: none; margin: var(--s-3) 0 0; padding: var(--s-3) 0 0; border-block-start: 1px solid var(--n-150); }
.dh-unclear li {
  font-size: 12px; color: var(--warn);
  padding-block: 2px;
}
.dh-unclear li::before { content: "؟ "; font-weight: 700; }

/* --- handwriting pad --------------------------------------------------------
   touch-action:none inside the canvas is not a detail: without it a palm
   resting on a tablet scrolls the drawer out from under the hand mid-word.
   -------------------------------------------------------------------------- */
.pad { margin-block-end: var(--s-4); border: 1px solid var(--n-150); border-radius: var(--r-md); }
.pad > summary {
  display: flex; align-items: center; gap: 7px;
  padding: 10px var(--s-4); cursor: pointer; list-style: none;
  font-size: 13px; font-weight: 600; color: var(--n-700);
  min-block-size: 44px;
}
.pad > summary::-webkit-details-marker { display: none; }
.pad > summary .ic { color: var(--n-400); }
.pad[open] > summary { border-block-end: 1px solid var(--n-150); }
.pad > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--r-md); }

.pad-body { padding: var(--s-3); }
.pad-canvas {
  display: block;
  inline-size: 100%;
  block-size: 260px;
  background: var(--n-0);
  border: 1px dashed var(--n-200);
  border-radius: var(--r-sm);
  touch-action: none;          /* the palm must not scroll the drawer */
  cursor: crosshair;
}
.pad-bar { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; margin-block-start: var(--s-3); }
.pad-state { font-size: 12px; color: var(--n-500); min-inline-size: 0; }

@media (max-width: 699px) { .pad-canvas { block-size: 200px; } }

/* --- scanning a sheet into the record ------------------------------------- */

/* The picker is one target, sized to be hit with a thumb while holding a
   phone over a piece of paper. */
.scan-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-block-size: 190px;
  padding: var(--s-5);
  text-align: center;
  cursor: pointer;
  color: var(--n-600);
  background: var(--n-25);
  border: 1px dashed var(--n-200);
  border-radius: var(--r-md);
}
.scan-drop:hover { border-color: var(--accent); color: var(--n-800); }
.scan-drop .ic { color: var(--n-400); }
.scan-drop:hover .ic { color: var(--accent); }
.scan-drop-title { font-weight: 600; font-size: 14px; }

/* Keyboard focus lands on the visually hidden input, so the label is what has
   to show it. */
.scan-pick:focus-within .scan-drop { outline: 2px solid var(--accent); outline-offset: 2px; }

.scan-shot {
  display: flex;
  gap: var(--s-4);
  align-items: flex-start;
  margin-block-end: var(--s-4);
}
.scan-shot img {
  inline-size: 168px;
  block-size: 168px;
  object-fit: cover;
  border-radius: var(--r-md);
  border: 1px solid var(--n-150);
  background: var(--n-50);
}
.scan-side { display: flex; flex-direction: column; gap: var(--s-3); min-inline-size: 0; }
.scan-state { font-size: 13px; color: var(--n-600); margin: 0; }

.scan-summary { margin-block-end: var(--s-4); }
.scan-note {
  margin: 0 0 var(--s-2);
  font-size: 13px;
  color: var(--n-600);
}
.scan-note.is-warn {
  padding: 8px var(--s-3);
  color: var(--warn);
  background: var(--warn-soft);
  border: 1px solid var(--warn-line);
  border-radius: var(--r-sm);
}

.scan-unclear {
  margin: var(--s-3) 0 0;
  padding-inline-start: var(--s-4);
  font-size: 12.5px;
  color: var(--n-600);
}
.scan-unclear-head { list-style: none; margin-inline-start: calc(var(--s-4) * -1); font-weight: 600; }

.scan-head { margin-block-end: var(--s-4); }

/* Column headings for the rows below. Hidden on narrow screens, where each
   row stacks and carries its own placeholders instead. */
.scan-legend {
  display: grid;
  grid-template-columns: 28px 2.2fr 1fr .8fr .7fr .7fr;
  gap: var(--s-3);
  padding-block-end: 6px;
  border-block-end: 1px solid var(--n-150);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--n-500);
}
.scan-legend span:first-child { grid-column: 2; }

.scan-rows { max-block-size: 46vh; overflow-y: auto; }

.scan-row.intake-row {
  grid-template-columns: 28px 2.2fr 1fr .8fr .7fr .7fr;
  align-items: center;
  padding: 7px 0;
}
.scan-row input[type="text"] { min-inline-size: 0; }
.scan-use { display: flex; align-items: center; justify-content: center; }

/* A row whose test did not match the clinic's terminology. Marked on the
   edge rather than by colouring the field, so the row stays readable and the
   mark survives a value being typed into it. */
.scan-row.is-unmatched { border-inline-start: 2px solid var(--warn); padding-inline-start: 6px; }

.scan-keep { display: flex; align-items: center; gap: 7px; margin-inline-end: auto; font-size: 13px; }

@media (max-width: 700px) {
  .scan-legend { display: none; }
  .scan-shot img { inline-size: 108px; block-size: 108px; }
  .scan-row.intake-row {
    grid-template-columns: 28px 1fr;
    align-items: start;
    gap: var(--s-2);
  }
  .scan-row .scan-test { grid-column: 2; }
  .scan-row > input, .scan-row [data-name] { grid-column: 2; }
  .scan-rows { max-block-size: none; }
}
