/* Design System for Accountability */

/* Fonts and Theme Tokens */
:root {
  --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --sidebar-width: 232px;

  /* Shared Colors */
  --compliant-rgb: 16, 185, 129; /* Emerald Green */
  --marginal-rgb: 245, 158, 11;  /* Amber Yellow */
  --non-compliant-rgb: 244, 63, 94; /* Rose Red */

  --color-success: rgb(var(--compliant-rgb));
  --color-warning: rgb(var(--marginal-rgb));
  --color-danger: rgb(var(--non-compliant-rgb));

  --transition-fast: 0.15s ease;
  --transition-normal: 0.25s ease;
  --transition-slow: 0.4s cubic-bezier(0.16, 1, 0.3, 1);

  /* Flat-clinical radii. Small, and only three of them: 4 for chips and inline
     badges, 6 for controls and rows, 8 for cards and panels.
     --border-radius-full survives for the two things that are genuinely round —
     the sidebar status dot and the settings switch knob. */
  --border-radius-sm: 4px;
  --border-radius-md: 6px;
  --border-radius-lg: 8px;
  --border-radius-full: 9999px;

  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);

  /* --shadow-md and --shadow-lg are declared PER THEME below, because they are
     no longer the same idea in both: a flat dark card casts nothing, while a
     white card on a near-white ground needs a 1px lift to have an edge at all.
     print.css re-declares both on `body.dark-theme, body.light-theme` — equal
     specificity, later source order — so print still wins either way. */

  /* Kept as a NAME rather than deleted. print.css neutralises it, and any
     var(--shadow-glow) that outlives this restyle must resolve to nothing
     rather than to an unset custom property. */
  --shadow-glow: none;
}

/* ─────────────────────────────────────────────────────────────────────────────
   THE PALETTES — "flat clinical"
   ─────────────────────────────────────────────────────────────────────────────
   Opaque surface STEPS and 1px hairlines, not blur and glow. The three steps
   are --bg-app (the page) then --bg-sidebar (chrome) then --bg-card (content),
   with --bg-input one step BELOW the card so a field reads as a well rather
   than a raised tile. Nothing here is translucent: a translucent card takes its
   contrast from whatever happens to be behind it, which is why the old light
   theme had almost none.

   STATUS COLOUR COMES IN TWO GRADES and they are not interchangeable:
     - --color-success/-warning/-danger (from the *-rgb triples) are the BASE
       hues. They are for FILLS: progress bars, dots, the small status squares,
       where the colour sits on its own and carries the meaning by itself.
     - --text-success/-warning/-danger are the READABLE TINTS, for status as
       TEXT on a surface. The base rose on #181d24 is about 3:1; the tint is
       about 5:1. On white the relationship inverts, which is why the light
       values are darker rather than lighter.

   --color-brand-fg is the foreground ON a brand fill. It exists because
   .btn-primary used to hard-code #ffffff, which was fine on the old vivid cyan
   and is not on the toned one.
   ───────────────────────────────────────────────────────────────────────────── */

/* Dark Theme (Default) */
body.dark-theme {
  --bg-app: #0f1216;
  --bg-sidebar: #14181e;
  --sidebar-text: #e8ecf1;
  --sidebar-text-muted: #98a2b3;
  --sidebar-border: #262d37;
  --sidebar-item-hover: #1b2129;

  --bg-card: #181d24;
  --bg-card-hover: #1b2129;
  --bg-input: #14181e;
  --bg-input-focus: #1e242c;
  --border-color: #2a313b;
  --border-color-light: #262d37;

  --text-primary: #e8ecf1;
  --text-secondary: #98a2b3;
  --text-muted: #6b7686;

  --text-success: #34d399;
  --text-warning: #f5b544;
  --text-danger: #fb7185;

  --color-brand: #3aa6cc; /* Toned teal-cyan */
  --color-brand-hover: #2f93b6;
  --color-brand-glow: rgba(58, 166, 204, 0.12); /* a TINT now, never a shadow */
  /* The same colour as bare channels, so a rule can choose its own alpha —
     the idiom --compliant-rgb and its siblings already use at the top of this
     file. The guide's spotlight fades a ring from opaque to nothing, which one
     fixed rgba() value cannot express. Keep in step with --color-brand above. */
  --color-brand-rgb: 58, 166, 204;
  --color-brand-fg: #0f1216;

  --bg-overlay: rgba(6, 8, 11, 0.7);
  --glass-blur: none;
  --glass-border: 1px solid #262d37;

  --shadow-md: none;
  --shadow-lg: 0 20px 40px rgba(0, 0, 0, 0.4); /* modals only */
}

/* Light Theme */
body.light-theme {
  --bg-app: #eef1f5;
  --bg-sidebar: #ffffff;
  --sidebar-text: #1a222c;
  --sidebar-text-muted: #5b6675;
  --sidebar-border: #d5dbe3;
  --sidebar-item-hover: #f1f4f8;

  --bg-card: #ffffff;
  --bg-card-hover: #fafbfc;
  --bg-input: #ffffff;
  --bg-input-focus: #f6f8fa;
  --border-color: #cfd6df;
  --border-color-light: #e3e8ee;

  --text-primary: #1a222c;
  --text-secondary: #5b6675;
  --text-muted: #8a94a3;

  --text-success: #047857;
  --text-warning: #b45309;
  --text-danger: #be123c;

  --color-brand: #0e7490; /* Clinical Teal */
  --color-brand-hover: #0b5f75;
  --color-brand-glow: rgba(14, 116, 144, 0.10);
  /* Channels for --color-brand, per the note in the dark theme above. */
  --color-brand-rgb: 14, 116, 144;
  --color-brand-fg: #ffffff;

  --bg-overlay: rgba(16, 24, 40, 0.55);
  --glass-blur: none;
  /* The card border is --sidebar-border (#d5dbe3), NOT --border-color-light.
     This one value is what gives light mode its definition: at
     --border-color-light a white card on #eef1f5 has no visible edge. */
  --glass-border: 1px solid #d5dbe3;

  --shadow-md: 0 1px 2px rgba(16, 24, 40, 0.04);
  --shadow-lg: 0 20px 40px rgba(16, 24, 40, 0.18);
}

/* Global Reset & Base Styling */
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* Every checkbox and radio the app has NOT given a custom UI — the medication
   filters, the picker rows — was rendering as the operating system's default
   white box, which on the dark theme is the brightest thing on the screen and
   on the light one is the only unthemed control. One line, both themes. */
input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--color-brand);
}

body {
  font-family: var(--font-family);
  background-color: var(--bg-app);
  color: var(--text-primary);
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  transition: background-color var(--transition-normal), color var(--transition-normal);
  -webkit-font-smoothing: antialiased;
}

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

/* ── The auth gate (LoginView) — M3-6 owns its real appearance ────────────────
   The M1-7 stopgap gave the bare inputs a readable surface so they were not
   white-on-white; M3-6 turns the whole gate into a coherent centered card. Every
   rule here is SCOPED to the [data-auth-screen] attribute LoginView sets on
   #auth-root, so none of it can reach the signed-in app behind the gate. It is
   token-based throughout, so dark and light both stay correct, and it is
   appearance only: the markup, the inputs and the submit handlers are
   LoginView's and unchanged. */

/* The centered card the gate mounts into (LoginView's shell()). */
.auth-card {
  width: 100%;
  max-width: 420px;
  margin: 8vh auto;
  padding: 28px;
}

/* Readable inputs — the surface the M1-7 note describes, now full-width and
   consistently spaced so the form reads as one column. */
[data-auth-screen] input {
  width: 100%;
  background-color: var(--bg-input);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 4px 0 16px;
  font-size: 14px;
  transition: background-color var(--transition-fast), border-color var(--transition-fast);
}
[data-auth-screen] input:focus {
  background-color: var(--bg-input-focus);
  border-color: var(--color-brand);
  outline: none;
}

/* Password field with a show/hide eye. The wrapper takes over the input's own
   vertical margin so the absolutely-positioned button centres on the input box,
   and the input gains right padding so its text never runs under the eye. Rules
   sit AFTER [data-auth-screen] input so the equal-specificity overrides win. */
.password-field {
  position: relative;
  display: flex;
  align-items: center;
  margin: 4px 0 16px;
}
.password-field input {
  margin: 0;
  padding-right: 44px;
}
.password-toggle {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--text-secondary);
  cursor: pointer;
  transition: color var(--transition-fast);
}
.password-toggle:hover {
  color: var(--text-primary);
}
.password-toggle:focus-visible {
  outline: 2px solid var(--color-brand);
  outline-offset: 1px;
}
/* Default (aria-pressed="false") = password hidden → show the plain eye
   ("reveal"); pressed = visible → show the eye-with-slash ("hide"). */
.password-toggle .eye-off-icon { display: none; }
.password-toggle[aria-pressed="true"] .eye-icon { display: none; }
.password-toggle[aria-pressed="true"] .eye-off-icon { display: flex; }

/* The primary action fills the card width; .btn-primary already carries the
   brand colour, glow and hover, so this only sizes and centres it. */
[data-auth-screen] .btn-primary {
  width: 100%;
  justify-content: center;
  margin-top: 4px;
}

/* The "Set up your account" / "Sign in" affordance is a text link, not a button
   surface — but it earns a real focus ring for keyboard users. */
.auth-link-btn {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--color-brand);
  text-decoration: underline;
  cursor: pointer;
}
.auth-link-btn:hover {
  color: var(--color-brand-hover);
}
.auth-link-btn:focus-visible {
  outline: 2px solid var(--color-brand);
  outline-offset: 2px;
  border-radius: 2px;
}

/* The signed-in footer control (boot.js renderSessionControls): the user's name
   and a styled Sign out laid along the sidebar-footer strip. */
#session-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.session-signout-btn {
  font-size: 11px;
  padding: 6px 12px;
  flex-shrink: 0;
}

button {
  cursor: pointer;
  border: none;
  background: none;
}

/* Scrollbar Customization */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background: var(--border-color);
  border-radius: var(--border-radius-full);
}
::-webkit-scrollbar-thumb:hover {
  background: var(--text-secondary);
}

/* Application Layout Layout */
.app-layout {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  /* ONE ROW, AND IT MAY NOT GROW. This is the load-bearing line for keeping the
     sidebar's Sign out button on screen, and it is easy to think `height:
     100dvh` below already does the job. It does not.

     Without an explicit row, the two columns land in an IMPLICIT row, and
     implicit rows are `auto` — content-sized. The row therefore grew to the
     tallest item, which is the sidebar's content, and the grid overflowed its
     own 100dvh height: measured live at a 551px page height, the row resolved
     to 613px and the sidebar's footer rendered 50px past the bottom edge, with
     no scrollbar anywhere to reach it.

     `minmax(0, 1fr)` pins the row to the container's height and — the half that
     `1fr` alone would miss — gives it a zero MINIMUM. A bare `1fr` is
     `minmax(auto, 1fr)`, whose automatic minimum is its content, which is the
     same growth by another name. Exactly the trap recorded for `flex: 1 1 0` vs
     `flex: 1` in the roster section of this sheet, one layout module over. */
  grid-template-rows: minmax(0, 1fr);
  height: 100vh;
  height: 100dvh;
  width: 100vw;
}

/* Sidebar Navigation Styling */
/* THREE ROWS: header, menu, footer.

   The overflow that hid Sign out was NOT caused here — it was .app-layout's
   implicit auto row growing to this element's content, which is fixed at its
   own rule above. This structure is what decides WHICH part gives once the
   sidebar is correctly constrained: as three rows, the footer is laid out
   inside the box and the middle row absorbs the squeeze, rather than the footer
   being the tail of a column that runs off the end.

   The middle row is `minmax(0, 1fr)` and .sidebar-menu carries `min-height: 0`
   for the same reason spelled out on .app-layout: a track or a child that keeps
   its automatic content minimum cannot shrink, and something that cannot shrink
   pushes its siblings out.

   MEASURED on the shared web build at 1280x551 (a 1366x768 laptop, once the
   browser chrome and the taskbar are taken out), where the menu carries EIGHT
   items because Admin and Send Feedback are both revealed: the sidebar wanted
   613px in a 551px viewport and Sign out sat 50px below the fold.

   Constraining the rows alone would trade one bug for another — with the roomy
   spacing the menu then clips 23px, cutting Send Feedback in half — so the
   short-viewport block further down compacts it enough that nothing is cut off
   at the heights a laptop actually has. Both halves were measured separately;
   neither is sufficient alone. */
.app-sidebar {
  background-color: var(--bg-sidebar);
  color: var(--sidebar-text);
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  padding: 20px 12px;
  border-right: 1px solid var(--sidebar-border);
}

.sidebar-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 20px;
  padding: 0 2px;
}

/* The mark is a CSS square with a letter in it, not an icon. The glowing person
   glyph it replaces was the single most decorative thing in the chrome, and a
   product mark that is also a picture of a user is a small lie in an app whose
   users are staff and whose subjects are residents. No SVG, no asset. */
.logo-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  border-radius: var(--border-radius-md);
  background-color: var(--color-brand);
  color: var(--color-brand-fg);
  font-size: 13px;
  font-weight: 800;
  line-height: 1;
}

.logo-text h2 {
  font-size: 14px;
  font-weight: 650;
  line-height: 1.2;
}

/* The facility name. Not uppercase: it is a proper noun somebody chose, and
   small caps make a real name read like a category label. */
.logo-text span {
  display: block;
  font-size: 11px;
  color: var(--sidebar-text-muted);
  font-weight: 500;
  line-height: 1.3;
  margin-top: 1px;
}

.sidebar-menu {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* `flex: 1` used to stretch this; the parent's `1fr` row does that now. What
     is load-bearing here is min-height: 0 — without it this row inherits the
     automatic minimum of its content and grows the sidebar past its box again,
     which is the whole defect the row structure exists to prevent. */
  min-height: 0;
}

.menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--border-radius-md);
  font-size: 13px;
  font-weight: 500;
  color: var(--sidebar-text-muted);
  text-align: left;
  transition: color var(--transition-fast), background-color var(--transition-fast);
}

.menu-item:hover {
  color: var(--sidebar-text);
  background-color: var(--sidebar-item-hover);
}

.menu-item.active {
  color: var(--text-primary);
  background-color: var(--color-brand-glow);
  box-shadow: inset 3px 0 0 var(--color-brand);
}

.menu-item.active .menu-icon {
  color: var(--color-brand);
}

/* The rule between the destinations and the two entries below them. It used to
   ride .menu-item-action (Send Feedback), which put the line UNDER Settings and
   left Settings grouped with Admin — the wrong side. Settings and Send Feedback
   are both things you do rather than places you are, so the line goes above the
   pair. */
#nav-settings {
  margin-top: 8px;
  padding-top: 16px;
  border-top: 1px solid var(--border-color-light);
}

/* ── An ACTION in the menu, not a destination ────────────────────────────────
   Send feedback lives in the menu because that is where people look for it, but
   it opens a modal rather than swapping the view — so it must not look like a
   seventh place you can BE. A rule above it separates the two kinds, and it
   never takes .active (nothing sets it: app.js returns early for a menu item
   with no data-view, before the active class is moved).

   It no longer carries the separator or gets pushed to the bottom: the rule
   moved up to #nav-settings (above), and margin-top:auto is gone because the
   flat design groups Settings and Send Feedback together under that one line
   rather than stranding Feedback at the foot of the menu. It keeps a padding-top
   of its own so the pair still breathes under the rule — and so the
   short-viewport block below still has a value to compact. */
.menu-item-action {
  padding-top: 8px;
  font-weight: 500;
}

.menu-item-action:hover {
  /* The separator would otherwise disappear under the hover fill. */
  background-color: transparent;
  color: var(--color-brand);
}

.menu-icon {
  flex-shrink: 0;
}

/* Role-gated sidebar chrome (the admin nav entry and the feedback button) ships
   hidden and is revealed by src/js/boot.js only on the shared backend — admin
   for #nav-admin, any authenticated user for #feedback-btn. .menu-item sets an
   explicit `display: flex` that beats the UA `[hidden] { display: none }` rule,
   so hiding them needs this. */
#nav-admin[hidden],
#feedback-btn[hidden] {
  display: none !important;
}


.sidebar-footer {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--sidebar-border);
}

/* A plain row, not a badge. The box around it made a passive status read like a
   control. The padding stays (trimmed) because the short-viewport block below
   compacts it, and a rule with nothing to compact is a rule that silently
   stops meaning anything. */
.backup-status-widget {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--sidebar-text-muted);
  padding: 6px 0;
}

.status-indicator {
  width: 7px;
  height: 7px;
  flex-shrink: 0;
  border-radius: var(--border-radius-full);
}

.status-indicator.warning {
  background-color: var(--color-warning);
}

.status-indicator.success {
  background-color: var(--color-success);
}

/* NOTE ON PLACEMENT: this block sits BELOW every rule it overrides
   (.app-sidebar, .sidebar-header, .sidebar-menu, .menu-item, .menu-item-action,
   .sidebar-footer, .backup-status-widget) and must stay there. A media query
   adds no specificity, so a compaction rule placed above the plain rule it
   means to override simply loses to it — silently, and only for the selectors
   that happen to be declared further down. That is not hypothetical: the first
   version of this block was placed higher up, where .sidebar-footer and
   .backup-status-widget are declared later, and both of those overrides were
   dead on arrival. */
/* ── A shorter viewport gets a shorter sidebar ───────────────────────────────
   WHERE 700px COMES FROM. Measured: at the default spacing, eight menu items
   plus the header and the footer occupy 590px. Add headroom for a footer that
   is taller than the one measured — a long signed-in email wraps to a second
   line — and the default stops fitting somewhere under ~660px. 700px is the
   round number above that, so the compaction switches on just before it is
   needed and never while the roomy spacing still fits.

   It is deliberately NOT set at the 551px this was reported from. A threshold
   tuned to one laptop leaves every slightly shorter window broken, and the
   compaction has to be in force BEFORE the sidebar runs out of room, not at the
   exact height where it already has.

   WHAT IT APPLIES TO. Any viewport at least 901px wide and at most 700px tall —
   which includes a short Electron window on the desktop build, not only the
   browser. That is intended: both are mouse-driven and both benefit. The
   desktop build shows six menu items rather than eight (Admin and Send Feedback
   need the server and stay hidden), so it was never over its box; it simply
   gets the tighter spacing too.

   WHAT IT DOES NOT APPLY TO. Anything under 901px wide — the off-canvas drawer.
   Phones are narrow but TALL, so they have this space to spare, and their
   targets must stay at the 44px floor the M3 mobile rows settled on. A
   height-only query would have compacted a phone's menu to 36px rows to solve a
   laptop's problem, so the width clause is load-bearing rather than decorative.

   Nothing here is a new mechanism: every value is the existing one, smaller. */
@media (min-width: 901px) and (max-height: 700px) {
  .app-sidebar {
    padding: 10px 12px;
  }

  .sidebar-header {
    margin-bottom: 12px;
  }

  .sidebar-menu {
    gap: 1px;
  }

  /* ~30px rows rather than ~33px. This is the mouse-driven layout — the touch
     floor is enforced on the drawer, which this query excludes. */
  .menu-item {
    padding: 5px 10px;
  }

  .menu-item-action {
    padding-top: 4px;
  }

  .sidebar-footer {
    gap: 8px;
    padding-top: 8px;
  }

  .backup-status-widget {
    padding: 3px 0;
  }
}

.theme-toggle-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px;
  background-color: var(--sidebar-item-hover);
  border: 1px solid var(--sidebar-border);
  border-radius: var(--border-radius-md);
  color: var(--sidebar-text);
  font-size: 13px;
  font-weight: 550;
  transition: all var(--transition-fast);
}

.theme-toggle-btn:hover {
  background-color: var(--sidebar-item-hover);
  color: #ffffff;
}

/* Main Content Workspace Layout */
.app-main {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

/* Header Styling */
.app-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 28px;
  border-bottom: 1px solid var(--border-color-light);
  /* The header is CHROME, so it takes the chrome surface and reads as one
     continuous band with the sidebar rather than as a very wide card. */
  background: var(--bg-sidebar);
  z-index: 10;
}

/* Mobile sidebar drawer (M3-1) — desktop-inert half.
   Both the hamburger and the drawer backdrop are display:none at >=900px, so
   the grid renders the fixed 260px sidebar exactly as before and no hamburger
   shows. Everything that reshapes the layout lives in the @media block below. */
.sidebar-toggle {
  display: none;            /* shown only below the 900px breakpoint */
  align-items: center;
  justify-content: center;
  width: 44px;              /* the >=44x44 touch target the a11y guideline wants */
  height: 44px;
  flex-shrink: 0;
  margin-right: 4px;
  border-radius: var(--border-radius-md);
  color: var(--text-secondary);
  transition: all var(--transition-fast);
}

.sidebar-toggle:hover {
  background-color: var(--bg-input-focus);
  color: var(--text-primary);
}

.sidebar-backdrop {
  display: none;            /* only an open drawer, on a phone, reveals it */
}

.header-title-section h1 {
  font-size: 18px;
  font-weight: 650;
  letter-spacing: -0.2px;
}

.header-title-section p {
  font-size: 12px;
  margin-top: 2px;
}

.muted {
  color: var(--text-secondary);
}

/* Week Navigator UI */
.week-navigator {
  display: flex;
  align-items: stretch;
  height: 34px;
  background-color: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
  overflow: hidden;
}

/* 32x32 at desktop. The 44x44 touch floor for these two arrows lives in the
   max-width:900px block further down and is untouched by this rule. */
.nav-arrow-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  flex-shrink: 0;
  color: var(--text-secondary);
  transition: color var(--transition-fast), background-color var(--transition-fast);
}

/* Hairlines BETWEEN the three parts rather than a border around each: the
   navigator is one control, not three buttons that happen to be adjacent. */
#prev-week-btn {
  border-right: 1px solid var(--border-color);
}

#next-week-btn {
  border-left: 1px solid var(--border-color);
}

.nav-arrow-btn:hover {
  background-color: var(--bg-input-focus);
  color: var(--text-primary);
}

/* Right-side header control cluster: week navigator + theme toggle. */
.header-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Icon-only header button (theme toggle). Sized to sit level with the week
   navigator pill; the sun/moon swap below picks which glyph shows. */
.header-icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
  background-color: var(--bg-card);
  color: var(--text-secondary);
  transition: color var(--transition-fast), background-color var(--transition-fast);
}

.header-icon-btn:hover {
  background-color: var(--bg-card-hover);
  color: var(--text-primary);
}

/* Show the sun in dark mode (click to brighten → light), the moon in light
   mode (click → dark). Default markup order shows the sun; the light theme
   class swaps to the moon. */
#theme-toggle .moon-icon { display: none; }
#theme-toggle .sun-icon { display: block; }
body.light-theme #theme-toggle .sun-icon { display: none; }
body.light-theme #theme-toggle .moon-icon { display: block; }

.week-picker-trigger {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 14px;
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  user-select: none;
  min-width: 180px;
  justify-content: center;
}

/* The ISO week number, muted, after the date range. Two people comparing a
   client-week across a phone call say "W36", not "the week of August 31st". */
.week-iso-suffix {
  color: var(--text-muted);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.hidden-week-input {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

/* Views Wrapper */
.view-content {
  flex: 1;
  padding: 32px;
  overflow-y: auto;
  position: relative;
}

/* Card Elements & Glassmorphism */
/* THE HUB.
   The class name is historical: there is no glass left in it. Every card in
   every view is .glass-card plus a role class, so this one rule is where "flat"
   is actually decided — an opaque surface, one hairline, an 8px radius, and a
   shadow that is none on dark and a 1px lift on light. It keeps the name
   because renaming it would touch every view for nothing a user can see. */
.glass-card {
  background: var(--bg-card);
  border: var(--glass-border);
  border-radius: var(--border-radius-lg);
  padding: 16px;
  box-shadow: var(--shadow-md);
  /* No transform here any more: the lift-on-hover went with the glow. A card
     that moves under the pointer is a marketing affordance, and this is a
     surface somebody reads a medication count off. */
  transition: box-shadow var(--transition-fast), background-color var(--transition-fast);
}

.glass-card:hover {
  background: var(--bg-card-hover);
}

/* Grid Layouts */
/* Four equal columns, and EQUAL is the point: auto-fit/minmax reflowed to three
   or five depending on how wide the window happened to be, so the same four
   numbers landed in a different arrangement every time somebody resized. */
.grid-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 24px;
}

/* Two columns on a phone rather than four. Four 90px tiles would put the status
   split's three swatch-and-count pairs into a horizontal scroll, and
   test/e2e/geometry.test.js measures that the page never scrolls sideways at
   390px. A media query adds no specificity, so this must stay BELOW the rule it
   overrides. */
@media (max-width: 900px) {
  .grid-stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* LABEL ABOVE VALUE. The old tile led with a 24px number and explained it
   underneath, which reads fine when you already know what the tile is and not
   at all when you are scanning four of them. .stat-info keeps its place in the
   markup for the views that use the tile; the ordering is now the markup's. */
.stat-card {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 14px 16px;
}

.stat-info {
  display: flex;
  flex-direction: column;
}

.stat-value {
  font-size: 26px;
  font-weight: 650;
  line-height: 1.1;
  letter-spacing: -0.5px;
  margin-top: 6px;
  /* Lining figures, so a column of counts stays a column. */
  font-variant-numeric: tabular-nums;
}

.stat-label {
  font-size: 11px;
  color: var(--text-secondary);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}

/* Status tint for a tile's number. The BASE hue is for fills; a 26px numeral is
   text, so it takes the readable tint. */
.stat-value.is-compliant { color: var(--text-success); }
.stat-value.is-marginal { color: var(--text-warning); }
.stat-value.is-non-compliant { color: var(--text-danger); }

/* The status-split tile: three small squares and their counts on one row. */
.stat-split {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 8px;
  font-size: 15px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

.stat-split > span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* A SQUARE, not a dot. The round dot in the sidebar means "connection state";
   reusing it here for compliance bands would make two unrelated things look
   like one vocabulary. */
.stat-swatch {
  width: 8px;
  height: 8px;
  flex-shrink: 0;
  border-radius: 2px;
}

.stat-swatch.is-compliant { background-color: var(--color-success); }
.stat-swatch.is-marginal { background-color: var(--color-warning); }
.stat-swatch.is-non-compliant { background-color: var(--color-danger); }

/* The pending-meetings tile puts its count and its link on one baseline. */
.stat-value-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.stat-link {
  color: var(--color-brand);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

.stat-link:hover {
  color: var(--color-brand-hover);
  text-decoration: underline;
}

/* Section Header */
.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}

/* ── Segmented control ───────────────────────────────────────────────────────
   One bordered box with two halves, both visible. It replaced a single button
   labelled "Switch to List View", which is a control that describes what will
   happen rather than what is true — you had to read the button to find out
   which view you were looking at.

   Its handler PICKS rather than flips (see DashboardView), so the halves are
   real options and not two labels on one toggle. The active half is filled with
   the hairline colour rather than the brand: this chooses a layout, and a brand
   fill here would make it compete with the primary action on the same row. */
.segmented {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
  overflow: hidden;
}

.segmented-option {
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  background-color: transparent;
  transition: color var(--transition-fast), background-color var(--transition-fast);
}

.segmented-option + .segmented-option {
  border-left: 1px solid var(--border-color);
}

.segmented-option:hover {
  color: var(--text-primary);
}

.segmented-option[aria-pressed="true"] {
  background-color: var(--border-color-light);
  color: var(--text-primary);
}

.section-header h3 {
  font-size: 14px;
  font-weight: 650;
}

.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background-color: var(--color-brand);
  color: var(--color-brand-fg);
  padding: 7px 12px;
  border-radius: var(--border-radius-md);
  font-size: 13px;
  font-weight: 600;
  transition: background-color var(--transition-fast);
}
/* No translateY on hover. A button that jumps a pixel under the pointer is a
   marketing flourish; the colour step is the whole feedback now. */
.btn-primary:hover {
  background-color: var(--color-brand-hover);
}

.btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background-color: var(--bg-card);
  border: 1px solid var(--border-color);
  color: var(--text-primary);
  padding: 7px 12px;
  border-radius: var(--border-radius-md);
  font-size: 13px;
  font-weight: 500;
  transition: background-color var(--transition-fast), border-color var(--transition-fast);
}
.btn-secondary:hover {
  background-color: var(--bg-card-hover);
  border-color: var(--text-muted);
}

.btn-danger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background-color: var(--color-danger);
  color: #ffffff;
  padding: 10px 20px;
  border-radius: var(--border-radius-md);
  font-size: 14px;
  font-weight: 600;
  transition: all var(--transition-fast);
}
.btn-danger:hover {
  opacity: 0.9;
}

/* Client Dashboard Grid */
.dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 12px;
}

/* M3-2: the List View compliance table is wider than a phone (six columns, a
   score badge, a progress column). Give it its OWN horizontal scroller in its
   card rather than clipping the columns with overflow:hidden (which hid real
   data) or letting the page scroll sideways (orchestrator decision 2). Inert at
   desktop, where the table fits the content width and no scrollbar appears; the
   card still clips vertically to its rounded corners exactly as before. */
.glass-card.dashboard-list-scroll {
  padding: 0;
  overflow-x: auto;
}

/* Sortable list-view column headers. The header cells carry inline padding/align
   from the renderer; these token-based rules add the click affordance and the
   active-column arrow, so both themes stay correct. */
/* The list header band. Was an inline rgba(0,0,0,.1) on the row, which is a
   wash rather than a surface: on the light theme it read as a grey smear over
   white instead of the chrome step the rest of the app uses. */
.dashboard-list-head {
  background-color: var(--bg-sidebar);
  text-align: left;
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
}

.dashboard-list-scroll th[data-sort-key] {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition: color var(--transition-fast);
}

.dashboard-list-scroll th[data-sort-key]:hover {
  color: var(--text-primary);
}

.dashboard-list-scroll th[data-sort-key] .th-sort-arrow {
  margin-left: 4px;
  font-size: 10px;
  color: var(--color-brand);
}

.client-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.client-card-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
}

.client-info-block {
  min-width: 0;
}

.client-info-block h4 {
  font-size: 14px;
  font-weight: 650;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Room, bed and admission date on one muted line, with the WORKING tag inline
   after them. It used to be a pill floating beside the name, competing with the
   compliance chip on the other side of the same row for the eye that was
   looking for the client name. */
.client-meta-line {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 3px;
  font-size: 11px;
  color: var(--text-secondary);
}

.client-working-tag {
  display: inline-flex;
  align-items: center;
  padding: 0 5px;
  border: 1px solid rgba(58, 166, 204, 0.4);
  border-radius: 3px;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  color: var(--color-brand);
}

body.light-theme .client-working-tag {
  border-color: rgba(14, 116, 144, 0.4);
}

.badge-compliance {
  display: inline-flex;
  align-items: center;
  padding: 3px 8px;
  border-radius: var(--border-radius-sm);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--text-secondary);
  background-color: var(--bg-input-focus);
}

.badge-compliance.is-compliant {
  color: var(--text-success);
  background-color: rgba(var(--compliant-rgb), 0.12);
}

.badge-compliance.is-marginal {
  color: var(--text-warning);
  background-color: rgba(var(--marginal-rgb), 0.12);
}

.badge-compliance.is-non-compliant {
  color: var(--text-danger);
  background-color: rgba(var(--non-compliant-rgb), 0.12);
}

/* On the dashboard client-card header the compliance pill sits beside the
   name/Working block; keep it a single line and stop it collapsing so it
   never wraps ("0% - Non-\nCompliant") on a narrow phone. Scoped to the card
   header so the shared list-view and modal badges are untouched. */
.client-card-header .badge-compliance {
  white-space: nowrap;
  flex-shrink: 0;
}

.compliance-progress-section {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.progress-labels {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  font-size: 11px;
  font-weight: 500;
  color: var(--text-secondary);
}

/* Keep the right-hand count ("0 / 17 tasks met") on one line; let the
   left label shrink first if the row ever runs short. */
.progress-labels span:last-child {
  white-space: nowrap;
  flex-shrink: 0;
}

/* A 4px rule, not an 8px capsule. It is a reading, and the FILL is the whole
   signal — which is why the fill keeps the base status hue rather than the text
   tint: nothing is written on it. */
.progress-track {
  width: 100%;
  height: 4px;
  background-color: var(--border-color-light);
  border-radius: 2px;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background-color: var(--card-status-color, var(--color-brand));
  border-radius: 2px;
  width: 0%;
  transition: width var(--transition-slow);
}

.client-tasks-mini-list {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding-top: 10px;
  border-top: 1px solid var(--border-color-light);
  font-size: 12px;
}

.mini-task-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
}

.mini-task-name {
  font-weight: 400;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mini-task-stats {
  font-weight: 600;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

.mini-task-stats.warning-state {
  color: var(--text-warning);
}
.mini-task-stats.danger-state {
  color: var(--text-danger);
}
.mini-task-stats.success-state {
  color: var(--text-success);
}

.client-card-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px solid var(--border-color-light);
  font-size: 11px;
  color: var(--text-secondary);
}

.client-card-foot .log-details-btn {
  flex-shrink: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--color-brand);
  white-space: nowrap;
}

.client-card-foot .log-details-btn:hover {
  color: var(--color-brand-hover);
  text-decoration: underline;
}

/* Modal UI elements */
.modal-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  background-color: var(--bg-overlay);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  transition: opacity var(--transition-normal);
}

.modal-container {
  background: var(--bg-card);
  border: var(--glass-border);
  border-radius: var(--border-radius-lg);
  box-shadow: var(--shadow-lg);
  max-width: 680px;
  width: 90%;
  max-height: 85vh;
  overflow-y: auto;
  padding: 32px;
  position: relative;
  transform: translateY(20px);
  transition: transform var(--transition-normal), opacity var(--transition-normal);
}

.modal-backdrop:not(.hidden) {
  opacity: 1;
}

.modal-backdrop:not(.hidden) .modal-container {
  transform: translateY(0);
}

.hidden {
  display: none !important;
  opacity: 0 !important;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 24px;
}

.modal-header h2 {
  font-size: 16px;
  font-weight: 650;
}

/* ── The client-detail modal ─────────────────────────────────────────────────
   SCOPED, and that is the whole point of the class. #modal-container is the
   shared shell for eight different dialogs; the rules below give this one a
   card with NO padding of its own, so each band inside it can carry its own
   padding and its own hairline and read as a document rather than as one
   32px box with four margins stacked in it. ClientDetailsModal adds the class
   on open and removes it on close. */
.client-detail-modal {
  max-width: 560px;
  padding: 0;
  border-radius: 10px;
  border: 1px solid var(--border-color);
}

.client-detail-modal .modal-header {
  padding: 18px 20px 14px;
  margin-bottom: 0;
}

/* The score strip: chrome-coloured and hairlined top and bottom, so the one
   number somebody opened this modal to change is banded off from the header
   above it and the tabs below. */
.client-detail-modal #modal-live-score-panel {
  margin: 0;
  padding: 12px 20px;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  border-top: 1px solid var(--border-color-light);
  border-bottom: 1px solid var(--border-color-light);
  background: var(--bg-sidebar);
  box-shadow: none;
}

/* The second header line: what this window is, and the period it covers. */
.modal-subline {
  font-size: 12px;
  margin-top: 3px;
}

/* The score strip's contents: figure, band chip, bar and count on one row. */
.client-detail-modal #modal-live-score-panel {
  display: flex;
  align-items: center;
  gap: 14px;
}

.modal-score-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.modal-score-val {
  font-size: 22px;
  font-weight: 650;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.modal-score-val.is-compliant { color: var(--text-success); }
.modal-score-val.is-marginal { color: var(--text-warning); }
.modal-score-val.is-non-compliant { color: var(--text-danger); }

.modal-score-count {
  font-size: 11px;
  color: var(--text-secondary);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* The bar takes the leftover width between the figure and the count. */
.client-detail-modal .modal-score-track {
  flex: 1 1 auto;
  min-width: 40px;
}

/* The two carry-forward states. Both keep the .glass-card surface; the border
   says which one it is, and the 20px gutter comes from the modal scope. */
.carry-forward-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 14px;
  margin-bottom: 16px;
}

.carry-forward-banner.is-offer { border-color: var(--color-brand); }
.carry-forward-banner.is-loaded { border-color: var(--color-success); }
.carry-forward-banner.is-loaded .carry-forward-title { color: var(--text-success); }

.carry-forward-title {
  font-size: 13px;
  font-weight: 600;
}

.carry-forward-note {
  font-size: 12px;
  margin-top: 2px;
}

.client-detail-modal .modal-tabs {
  padding: 0 20px;
  margin-bottom: 0;
}

.client-detail-modal .modal-body {
  padding: 16px 20px;
  gap: 16px;
}

.client-detail-modal .modal-footer {
  margin-top: 0;
  padding: 12px 20px;
  background: var(--bg-sidebar);
  border-top: 1px solid var(--border-color-light);
}

/* The carry-forward offer sits between the strip and the tabs and needs the
   same 20px gutter the bands around it use. */
.client-detail-modal .carry-forward-banner {
  margin: 12px 20px 0;
}

.modal-close-btn {
  color: var(--text-secondary);
  transition: color var(--transition-fast);
}

.modal-close-btn:hover {
  color: var(--text-primary);
}

/* ── Dynamically-created dialogs (feedback / invite) ──────────────────────────
   These reuse .modal-container for the card + reveal transform; the rules below
   style their internals with the shared tokens. The forms are compact, so they
   want a narrower card than the 680px default. */
.invite-dialog {
  max-width: 460px;
}

/* The feedback card is a short form built out of full-width controls, so it
   wants a wider measure than the compact invite dialog. */
.feedback-dialog {
  max-width: 540px;
}

.feedback-header,
.invite-header,
.consent-header,
.meeting-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
}

.feedback-header h3,
.invite-header h3,
.consent-header h3,
.meeting-header h3 {
  font-size: 18px;
  font-weight: 700;
}

/* The × close button in these headers (markup uses .modal-close, not the
   .modal-close-btn above). */
.modal-close {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 24px;
  line-height: 1;
  padding: 0 4px;
  color: var(--text-secondary);
  transition: color var(--transition-fast);
}

.modal-close:hover {
  color: var(--text-primary);
}

.feedback-form,
.invite-form,
.consent-form,
.meeting-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.feedback-form label,
.invite-form label,
.consent-form label,
.meeting-form label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
}

.invite-form input,
.feedback-form textarea,
.consent-form input,
.consent-form textarea,
.consent-form select,
.meeting-form input,
.meeting-form select {
  width: 100%;
  background-color: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
  padding: 10px 14px;
  font-size: 14px;
  color: var(--text-primary);
  font-family: inherit;
  transition: border-color var(--transition-fast);
}

.invite-form input:focus,
.feedback-form textarea:focus,
.consent-form input:focus,
.consent-form textarea:focus,
.consent-form select:focus,
.meeting-form input:focus,
.meeting-form select:focus {
  outline: none;
  border-color: var(--color-brand);
}

.feedback-form textarea {
  min-height: 132px;
  padding: 14px 16px;
  line-height: 1.5;
  resize: vertical;
}

/* ── The feedback card's own layout ───────────────────────────────────────────
   A stack of labelled sections: a small capitalised eyebrow, optional helper
   copy, then one full-width control. The header leads with a back chevron
   rather than a trailing ×, so the title reads first. */
.feedback-header {
  justify-content: flex-start;
  gap: 16px;
}

.feedback-header h3 {
  font-size: 20px;
}

.feedback-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  padding: 0;
  background: none;
  border: none;
  border-radius: var(--border-radius-sm);
  cursor: pointer;
  color: var(--text-primary);
  transition: background-color var(--transition-fast), color var(--transition-fast);
}

.feedback-back svg {
  width: 22px;
  height: 22px;
}

.feedback-back:hover {
  background-color: var(--sidebar-item-hover);
  color: var(--color-brand);
}

.feedback-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Scoped to the form so it outranks the shared `.feedback-form label` rule
   above — the note's eyebrow IS a <label>, and a bare class would lose. */
.feedback-form .feedback-field-label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* Helper copy under an eyebrow: normal sentence case, tighter to the control
   it explains than the section gap would leave it. */
.feedback-hint {
  margin: -2px 0 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-secondary);
}

.feedback-toggle {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.feedback-toggle-btn {
  justify-content: center;
  width: 100%;
  padding: 14px 12px;
  text-align: center;
}

/* The selected toggle in each group — the previously-invisible active state. */
.feedback-toggle-btn.active,
.feedback-toggle-btn[aria-pressed="true"] {
  background-color: var(--color-brand);
  border-color: var(--color-brand);
  color: #fff;
}

.feedback-submit {
  justify-content: center;
  width: 100%;
  margin-top: 4px;
  padding: 14px 20px;
  font-size: 15px;
}

.feedback-errors,
.invite-errors,
.consent-errors,
.meeting-errors {
  background: rgba(var(--non-compliant-rgb), 0.1);
  border: 1px solid var(--color-danger);
  color: var(--color-danger);
  padding: 10px 12px;
  border-radius: var(--border-radius-md);
  font-size: 13px;
}

.consent-errors ul,
.meeting-errors ul {
  margin: 0;
  padding-left: 18px;
}

/* ── Consent modal internals (§2.31 capture/list/revoke) ─────────────────────
   The dialog reuses .modal-container (styled card) and the shared form/input/error
   rules above; these style the parts unique to consent — the local-storage notice,
   the stored-consent list, and the capture form's section headings. */
.consent-local-notice {
  font-size: 12px;
  color: var(--text-muted);
  background: var(--bg-input);
  border-radius: var(--border-radius-md);
  padding: 8px 12px;
  margin: 0 0 16px;
}

.consent-form h4 {
  font-size: 15px;
  font-weight: 700;
  margin: 4px 0 0;
}

.consent-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 20px;
}

.consent-empty {
  font-size: 13px;
  color: var(--text-muted);
  margin: 0;
}

.consent-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 14px;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
}

.consent-row-main {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}

.consent-recipient {
  font-weight: 600;
  font-size: 14px;
  color: var(--text-primary);
}

.consent-row-detail {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 12px;
  color: var(--text-secondary);
}

.consent-row-revocation {
  color: var(--text-muted);
}

.consent-row-actions {
  display: flex;
  justify-content: flex-end;
}

.consent-revoked-note {
  font-size: 12px;
  color: var(--color-danger);
}

/* The consent status pill, coloured by computed state; mirrors .status-badge. */
.consent-status-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border-radius: var(--border-radius-full);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  background: var(--bg-input);
  color: var(--text-secondary);
}

.consent-status-badge[data-consent-status="active"] {
  background: rgba(var(--compliant-rgb), 0.12);
  color: var(--color-success);
}

.consent-status-badge[data-consent-status="verify-expiry"] {
  background: rgba(var(--marginal-rgb), 0.12);
  color: var(--color-warning);
}

.consent-status-badge[data-consent-status="expired"],
.consent-status-badge[data-consent-status="revoked"] {
  background: rgba(var(--non-compliant-rgb), 0.12);
  color: var(--color-danger);
}

.consent-expiry {
  display: flex;
  flex-direction: column;
  gap: 12px;
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
  padding: 12px 14px;
  margin: 0;
}

.consent-expiry legend {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  padding: 0 6px;
}

.consent-revocation-note {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.5;
  margin: 0;
}

/* ---------------------------------------------------------------------------
   The governing-consent picker (src/js/reports/print.js) — the dialog that asks
   what a client-scoped print is for before the report is rendered.

   It builds its own backdrop rather than using modal.js (it is a three-way
   choice with conditional fields, not an alert/confirm/prompt), so it reuses
   .modal-backdrop / .modal-container / .modal-header / .modal-body /
   .modal-footer and only the parts unique to it are styled here. The form is
   compact, so the card is narrower than the 680px default.
   --------------------------------------------------------------------------- */
.disclosure-picker {
  max-width: 520px;
}

.disclosure-picker-choices {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 16px;
}

/* A radio and its label on one line, with a 44px-tall hit area: this is pressed
   on a tablet at a med window, not only with a mouse. */
.disclosure-picker-option {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  font-size: 14px;
  color: var(--text-primary);
  cursor: pointer;
}

.disclosure-picker-option input {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  accent-color: var(--color-brand);
  cursor: pointer;
}

/* The conditional fields under a choice — indented so they read as belonging to
   the option above them. */
.disclosure-picker-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-left: 28px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
}

.disclosure-picker-field input,
.disclosure-picker-field select {
  width: 100%;
  min-height: 44px;
  background-color: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
  padding: 10px 14px;
  font-size: 14px;
  font-weight: 400;
  color: var(--text-primary);
  font-family: inherit;
  transition: border-color var(--transition-fast);
}

.disclosure-picker-field input:focus,
.disclosure-picker-field select:focus {
  outline: none;
  border-color: var(--color-brand);
}

.disclosure-picker-error {
  margin: 14px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--color-danger);
}

/* Invite "created" state — the one-time link row. */
.admin-link-warning {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0 0 12px;
}

.admin-link-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}

.admin-link-value {
  flex: 1 1 240px;
  min-width: 0;
  background-color: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-sm);
  padding: 8px 10px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  color: var(--text-primary);
  word-break: break-all;
}

.admin-copy-btn {
  white-space: nowrap;
}

/* ── Admin dashboard — evaluator cards ──────────────────────────────────────── */
.admin-eval-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Match .grid-stats' bottom margin so the "Feedback" heading below sits the
     same distance from this list as "Evaluators & sign-ins" does from the tiles
     above it — consistent section spacing down the page. */
  margin-bottom: 32px;
}

.admin-eval-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px 20px;
}

.admin-eval-main {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.admin-eval-email {
  font-weight: 600;
  font-size: 15px;
  color: var(--text-primary);
  word-break: break-word;
}

.admin-eval-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-secondary);
}

.admin-eval-sep { color: var(--text-muted); }
.admin-eval-you { color: var(--color-brand); }

.admin-eval-source {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  color: var(--text-muted);
}

.admin-eval-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
}

/* The one-time link, when revealed, drops to its own row under the buttons. */
.admin-eval-actions .admin-link-reveal { flex-basis: 100%; }

/* Status pill — colour-coded by account state; a leading dot in the same colour.
   Falls back to a neutral grey for any state not named here (never blank). */
.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: var(--border-radius-full);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  background: var(--bg-input);
  color: var(--text-secondary);
}

.status-badge::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}

.status-badge[data-status="active"] {
  background: rgba(var(--compliant-rgb), 0.12);
  color: var(--color-success);
}

.status-badge[data-status="invited"],
.status-badge[data-status="pending-password"] {
  background: rgba(var(--marginal-rgb), 0.12);
  color: var(--color-warning);
}

.status-badge[data-status="disabled"] {
  background: rgba(var(--non-compliant-rgb), 0.12);
  color: var(--color-danger);
}

/* ── Admin dashboard — feedback cards ────────────────────────────────────────
   The feedback list gets the same card treatment as the evaluator cards above:
   one card per note, a head row of pills (sentiment + kind) and dotted meta
   (author · screen · time), then the note. */
.admin-feedback-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.admin-feedback-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px 20px;
}

.admin-feedback-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-secondary);
}

/* Sentiment pill — liked reads success-green, disliked reads danger-red, with a
   leading dot in the same colour; a neutral fallback for any other value. */
.feedback-sentiment {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: var(--border-radius-full);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  background: var(--bg-input);
  color: var(--text-secondary);
}

.feedback-sentiment::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}

.feedback-sentiment[data-sentiment="like"] {
  background: rgba(var(--compliant-rgb), 0.12);
  color: var(--color-success);
}

.feedback-sentiment[data-sentiment="dislike"] {
  background: rgba(var(--non-compliant-rgb), 0.12);
  color: var(--color-danger);
}

/* Kind chip — a quieter, outlined tag (bug / suggestion). */
.feedback-kind {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border-radius: var(--border-radius-full);
  border: 1px solid var(--border-color);
  font-size: 11px;
  font-weight: 600;
  text-transform: capitalize;
  color: var(--text-secondary);
}

.feedback-author {
  font-weight: 600;
  color: var(--text-primary);
  word-break: break-word;
}

.feedback-screen {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  color: var(--text-muted);
}

/* Push the timestamp to the right edge of the head row. */
.feedback-at {
  margin-left: auto;
  white-space: nowrap;
}

.admin-feedback-note {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-primary);
  white-space: pre-wrap;
  word-break: break-word;
}

/* ── Release notes ──────────────────────────────────────────────────────────
   Deliberately the same card/list shape as the feedback section above: both are
   a reverse-chronological list of short entries, and giving them one visual
   language means the dashboard reads as one page rather than a stack of
   unrelated widgets. */
.admin-release-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.admin-release-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px 20px;
}

.admin-release-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
}

.admin-release-version {
  font-size: 15px;
  font-weight: 650;
  color: var(--text-primary);
}

.admin-release-date {
  font-size: 13px;
}

.admin-release-changes {
  /* The list keeps its markers, indented far enough to clear the card padding
     rather than hanging outside it. */
  margin: 0;
  padding-left: 20px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* The running version, beside the section heading. Small print: an admin who
   wants it is checking which build they are on, and everyone else is reading
   the notes underneath. */
.admin-release-current {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
}

.modal-body {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Modal Form Controls */
.modal-row-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border-color-light);
}

.modal-row-item:last-child {
  border-bottom: none;
}

.requirement-detail-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 600;
}

.requirement-detail-title label {
  font-size: 15px;
}

.requirement-badge {
  font-size: 11px;
  background-color: var(--bg-input);
  border: 1px solid var(--border-color);
  padding: 2px 8px;
  border-radius: var(--border-radius-full);
  color: var(--text-secondary);
}

/* Daily Checklist (M-Su) UI */
.daily-checkbox-group {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
  margin-top: 8px;
}

.day-checkbox-label {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-width: 0;
  font-size: 10px;
  font-weight: 500;
  color: var(--text-muted);
  cursor: pointer;
}

.day-checkbox-input {
  display: none;
}

.custom-checkbox-ui {
  width: 100%;
  height: 36px;
  border-radius: 5px;
  background-color: var(--bg-input-focus);
  border: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  justify-content: center;
  color: transparent;
  transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

@media (min-width: 901px) {
  .custom-checkbox-ui {
    height: 30px;
  }
}

.day-checkbox-label:hover .custom-checkbox-ui {
  border-color: var(--color-brand);
}

.day-checkbox-input:checked + .custom-checkbox-ui {
  background-color: var(--color-brand);
  border-color: var(--color-brand);
  color: var(--color-brand-fg);
}

.day-checkbox-input:checked + .custom-checkbox-ui svg {
  opacity: 1;
}

.custom-checkbox-ui svg {
  opacity: 0;
  transition: opacity var(--transition-fast);
}

/* Count-Based target input */
.count-input-group {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
}

/* The minus, the value and the plus share one border and one radius, with
   hairlines between them. The mobile block further down still grows the two
   buttons to 44px; only the desktop size came down. */
.count-input-group .counter-btn:first-child {
  border-radius: var(--border-radius-md) 0 0 var(--border-radius-md);
}

.count-input-group .count-plus {
  border-radius: 0 var(--border-radius-md) var(--border-radius-md) 0;
  border-left: 1px solid var(--border-color);
}

.counter-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background-color: var(--bg-card);
  border: 1px solid var(--border-color);
  font-size: 16px;
  font-weight: 600;
  transition: background-color var(--transition-fast);
}

.counter-btn:hover {
  background-color: var(--bg-card-hover);
}

.counter-value {
  width: 40px;
  height: 30px;
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-left: 0;
  border-right: 0;
}

.count-fraction-label {
  font-size: 12px;
  font-weight: 400;
  color: var(--text-secondary);
}

/* Shown beside the counter once the week's target is met. */
.count-met-label {
  margin-left: auto;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-success);
}

/* Text Area clinical notes */
.notes-input-wrapper {
  margin-top: 8px;
}

.text-area-notes {
  width: 100%;
  min-height: 44px;
  background-color: var(--bg-sidebar);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
  padding: 8px 10px;
  font-size: 12px;
  resize: vertical;
  transition: border-color var(--transition-fast);
}

.text-area-notes:focus {
  outline: none;
  border-color: var(--color-brand);
}

.modal-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px solid var(--border-color-light);
}

/* A quiet text button on the left of the footer, for the action that changes
   nothing. It used to be a .btn-secondary sitting beside Save at the same
   weight, which is three equal-looking buttons for one decision. */
.modal-footer-link {
  margin-right: auto;
  font-size: 12px;
  color: var(--text-secondary);
  transition: color var(--transition-fast);
}

.modal-footer-link:hover {
  color: var(--text-primary);
  text-decoration: underline;
}

.modal-footer-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* In-app dialog (components/modal.js) touch sizing — lifted out of the inline
   styles the component used to set on every open. The confirm/cancel/dismiss
   buttons all carry [data-modal-confirm] or [data-modal-cancel], so this pair
   is the whole button set; the ≥44×44 minimum is kept at ALL widths (the
   destructive confirm/cancel must be comfortable on desktop too, exactly as the
   old inline min-width/min-height did). */
[data-modal-confirm],
[data-modal-cancel] {
  min-width: 44px;
  min-height: 44px;
}

/* The promptDialog text field, styled off the hook the component sets rather
   than inline. Full width, a comfortable box, and the same 44px min touch
   height the inline rule guaranteed. */
[data-modal-input] {
  width: 100%;
  padding: 10px 12px;
  min-height: 44px;
  /* Explicit tokens, not inheritance: without these the input keeps the UA's
     white background while inheriting the dark theme's white text — an
     unreadable white-on-white box (reported live on the Confirm Count modal). */
  background-color: var(--bg-input);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
}

/* Modal Tabs */
.modal-tabs {
  display: flex;
  gap: 18px;
  border-bottom: 1px solid var(--border-color-light);
  margin-bottom: 24px;
  padding-bottom: 0;
}

.modal-tab-btn {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  padding: 10px 0;
  transition: color var(--transition-fast), box-shadow var(--transition-fast);
  white-space: nowrap;
}

.modal-tab-btn:hover {
  color: var(--text-primary);
}

/* An INSET underline rather than a border-bottom, so the rule the tabs sit on
   stays one unbroken hairline and the active marker is drawn on top of it.
   The label goes to the primary text colour: brand-coloured tab text made the
   whole strip read as four links to somewhere else. */
.modal-tab-btn.active {
  color: var(--text-primary);
  box-shadow: inset 0 -2px 0 var(--color-brand);
}

.modal-tab-content {
  display: none;
  animation: slideIn 0.2s ease;
}

.modal-tab-content.active {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.report-section-title {
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  color: var(--text-secondary);
  letter-spacing: 0.4px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--border-color-light);
  padding-bottom: 6px;
}

/* The work-hours panel's own header row, matching that title treatment. */
.work-schedule-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.work-schedule-head .report-section-title {
  margin-bottom: 0;
  border-bottom: 0;
  padding-bottom: 0;
}

.work-hours-total {
  font-size: 13px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

.work-hours-total-label {
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-muted);
  margin-right: 6px;
}

.report-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.schedule-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}

.compliance-banner {
  background: rgba(var(--non-compliant-rgb), 0.1);
  border: 1px solid var(--color-danger);
  color: var(--text-danger);
  padding: 12px;
  border-radius: var(--border-radius-md);
  font-size: 12px;
  font-weight: 600;
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin-bottom: 16px;
}

/* The same banner in the warning band — a medication note, not a refusal. */
.compliance-banner.is-warning {
  background: rgba(var(--marginal-rgb), 0.1);
  border-color: var(--color-warning);
  color: var(--text-warning);
}

/* Client Roster List / Form Layouts */

/* SHARED BY TWO VIEWS — the client roster and the requirements library. Keep
   that in mind before changing anything here: the requirements library still
   wants the form beside its list, and only the client roster stacks. */
.roster-layout {
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: 24px;
}

/* ── The client roster stacks: Add New Client ON TOP of the roster ───────────
   The shared `320px 1fr` read badly on the roster specifically, because the
   roster is the thing that view is FOR: the form was squeezed into a narrow
   left rail while the list — thirteen clients, each with a row of actions — was
   pushed aside and given the leftovers.

   Applied through a modifier rather than to `.roster-layout` itself so the
   requirements library, which shares every class in this section, keeps the
   side-by-side layout it was designed for.

   The panel is COLLAPSED by default (ClientManagerView resolves that), and this
   layout is why it can now be collapsed on desktop too: with the form above
   rather than beside, an open form pushes the list down instead of sitting
   harmlessly alongside it. */
.roster-layout-stacked {
  grid-template-columns: 1fr;
}

/* The single 1fr column defaults to minmax(auto, 1fr), so its automatic minimum
   is its widest child's min-content — the same hazard the mobile block below
   documents, which now applies at EVERY width here because the column is 1fr at
   every width. min-width:0 lets the children shrink to the column instead. */
.roster-layout-stacked > * {
  min-width: 0;
}

/* The open form is a band across the top rather than a 320px rail, so its
   fields flow into as many columns as fit instead of one tall stack. auto-fit
   with a 220px floor gives four columns on a wide desktop and one on a phone,
   with no breakpoint of its own. The .form-group children already carry
   min-width:0, which is what lets a <select> shrink into its track. */
#add-client-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0 16px;
  align-items: start;
}

/* AND THEREFORE THIS. The panel is collapsed by setting `form.hidden = true`
   and nothing else — ClientManagerView's toggle sets the attribute, the class
   on the card only turns the chevron. That works on a plain <form> because the
   UA stylesheet carries `[hidden] { display: none }`.

   It stops working the moment the form is given a display of its own: an AUTHOR
   declaration beats a UA one outright, whatever the specificity, so the
   `display: grid` above kept the collapsed form on screen — the chevron
   rotated and the form did not move. This sheet already records the same
   hazard twice, for .menu-item and .theme-toggle-btn, whose own `display: flex`
   is why #nav-admin[hidden] and #feedback-btn[hidden] need an explicit rule.

   No jsdom test can see this: the dom layer asserts the ATTRIBUTE, and jsdom
   applies no stylesheet cascade. test/e2e/geometry.test.js drives the real
   toggle in Chromium and asserts the form is actually not displayed. */
#add-client-form[hidden] {
  display: none;
}

/* The things that are not one field: the locker's lock/combination pair (itself
   a nested group) and the submit button both take the full width. */
#add-client-form > #new-client-lock-fields,
#add-client-form > .btn-primary {
  grid-column: 1 / -1;
}

/* Sized to its content and left-aligned, because the form is now as wide as the
   page: a full-width primary button across four columns reads as a banner, not
   a button. It carried an inline width:100% while the form was a 320px rail,
   where that was the right answer — which is why the sizing moved here, into a
   rule that can have a breakpoint. */
#add-client-form > .btn-primary {
  justify-content: center;
  justify-self: start;
  margin-top: 10px;
}

/* One column on a phone, where a full-width button is the right answer again. */
@media (max-width: 900px) {
  #add-client-form > .btn-primary {
    justify-self: stretch;
  }
}

/* The Add-New-Client card collapses (collapsed by default on mobile). Its header
   is a full-width button with a chevron that rotates when the panel is open. */
.form-card-toggle {
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  margin: 0 0 16px 0;             /* gap to the form while expanded */
  width: 100%;
  cursor: pointer;
  color: inherit;
  font: inherit;
  text-align: left;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  user-select: none;
}
/* Collapsed: no form below, so drop the header's bottom gap. */
.form-card.collapsed .form-card-toggle {
  margin-bottom: 0;
}
.form-card-title {
  font-size: 16px;
  font-weight: 700;
}
.form-card-chevron {
  flex: none;
  width: 9px;
  height: 9px;
  margin-right: 2px;
  border-right: 2px solid var(--text-secondary);
  border-bottom: 2px solid var(--text-secondary);
  transform: rotate(-135deg);     /* points up when expanded */
  transition: transform var(--transition-fast);
}
.form-card.collapsed .form-card-chevron {
  transform: rotate(45deg);       /* points down when collapsed */
}
.form-card-toggle:focus-visible {
  outline: 2px solid var(--color-brand);
  outline-offset: 2px;
  border-radius: var(--border-radius-sm);
}

.form-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 16px;
  /* An input's intrinsic min-width (its default `size`) otherwise forces a grid
     track wider than its share and overruns the panel border. */
  min-width: 0;
}

.form-group label {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
}

.form-input {
  background-color: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
  padding: 10px 14px;
  font-size: 14px;
  transition: border-color var(--transition-fast);
  min-width: 0;
  max-width: 100%;
}

.form-input:focus {
  outline: none;
  border-color: var(--color-brand);
}

.roster-card {
  display: flex;
  flex-direction: column;
  /* Allow the card to shrink below its content so the list inside it can own the
     scroll and grow to the card's full height (see the desktop fill rules below). */
  min-height: 0;
}

.tabs-header {
  display: flex;
  gap: 18px;
  border-bottom: 1px solid var(--border-color-light);
  margin-bottom: 16px;
}

/* The roster/history tab strip. Same inset-underline pattern as the modal's
   tabs, so the app has one way of saying which tab you are on rather than two
   that differ only by which file they were written in. */
.tab-btn {
  padding: 10px 2px;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  transition: color var(--transition-fast), box-shadow var(--transition-fast);
}

.tab-btn:hover {
  color: var(--text-primary);
}

.tab-btn.active {
  color: var(--text-primary);
  box-shadow: inset 0 -2px 0 var(--color-brand);
}

.roster-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-height: 60vh;
  overflow-y: auto;
}

/* ── Active Clients card matches the Add-New-Client form height ───────────────
   The list used to cap at a fixed 60vh, so it showed only a couple of cards while
   the surrounding card sat much taller — wasted space, and the view double-scrolled
   (the view-content scroller AND the list).

   The desktop rule below removes that cap and lets the client list fill its card.
   The card takes its height from the grid row, which the taller column — the
   full-height add-client form — drives; grid align-items:stretch then makes the
   card exactly as tall as the form. .roster-layout is unique to this view, so these
   rules are self-scoped and touch no other view.

   Dead ends (do not resurrect): forcing height:100% on #view-container itself
   (overrode its flex height to a full 100dvh; the overflow:hidden .app-main then
   clipped the bottom); capping the grid to the viewport with height:100% +
   grid-template-rows:minmax(0,1fr) plus a form `overflow` (pinned both columns to
   the window and made the FORM scroll). Both fought the form's natural height
   instead of following it.

   SCOPED AWAY FROM THE STACKED LAYOUT. All of this exists to make two SIDE-BY-SIDE
   columns agree on a height, which the client roster no longer has — with one
   column there is no second column to match, the card takes its own content
   height, and .roster-list's base 60vh cap is the right one. The requirements
   library still stacks its form beside its list, so the rules stay, addressed to
   the layouts that are actually two columns wide. */
@media (min-width: 901px) {
  /* The add-client form is shown at its FULL natural height with no internal
     scroll, so it — the taller of the two columns — sets the grid row height. The
     Active Clients card then stretches to that SAME height (grid default
     align-items:stretch, and the card carries no explicit height), and the client
     list fills the card and scrolls inside it. Net effect, which is the actual
     requirement: form full-size, card == form height, and the list shows as many
     clients as fit instead of a fixed 60vh window.
       Do NOT reintroduce a viewport cap here (height:100% + grid-template-rows
     minmax(0,1fr)) or a form `overflow`: every earlier attempt did some version of
     that and it pinned both columns to the window height and made the FORM scroll —
     the exact opposite of what's wanted. The grid row is left at its default `auto`
     so the form's own content drives it. Verified live: form 695px with no internal
     scroll, card 695px (card == form), list scrolls internally. */
  .roster-layout:not(.roster-layout-stacked) {
    align-items: stretch;
  }

  /* flex-basis MUST be a definite 0 (`0`, not the `0%` that `flex: 1` expands to).
     The card has no fixed height, so a percentage basis resolves as `content` and
     the list demands its full content height — which stretches the card AND the
     form to the whole 13-client list (~2100px). A definite `0` basis lets
     min-height:0 collapse the list's intrinsic contribution, so the form drives
     the row and the list scrolls within the form-height card. */
  .roster-layout:not(.roster-layout-stacked) .roster-list {
    flex: 1 1 0;
    min-height: 0;
    max-height: none;
  }

  /* Safety floor: the form column normally drives the grid-row height, but it
     is now a collapsible panel (button + hidden form). If a desktop user
     collapses it, keep the roster card usably tall instead of shrinking to the
     header row. When the form is open and taller, grid-stretch wins and this
     floor is inert. */
  .roster-layout:not(.roster-layout-stacked) .roster-card {
    min-height: 460px;
  }
}

.roster-item-card {
  display: flex;
  flex-direction: column;
  background-color: var(--bg-input-focus);
  border: 1px solid var(--border-color-light);
  border-radius: var(--border-radius-md);
  padding: 16px;
  transition: all var(--transition-fast);
}

.roster-item-card:hover {
  background-color: var(--bg-input-focus);
}

/* The client card stacks its identity ABOVE its actions at every width — the
   layout M3 built for mobile, now the default. Side-by-side (the old desktop
   layout) squeezed the identity into a narrow wrapping column while the action
   buttons fought it for width and wrapped their own labels; stacking gives the
   identity the full card width on top and the buttons the full width below. */
.roster-item-main {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}

/* Identity as a horizontal, wrapping row so name · room/bed · admitted spread
   across the available width instead of stacking in a cramped left column. */
.roster-item-identity {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 14px;
}

.roster-client-name {
  font-size: 15px;
  font-weight: 700;
}

.roster-client-date {
  font-size: 11px;
  color: var(--text-secondary);
}

/* The actions use the full card width and wrap only if they must (they fit on
   one row on desktop now that they no longer share it with the identity). */
.roster-item-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* ══════════════════════════════════════════════════════
   MOBILE CLIENT ROSTER (M3-7)
   Below 900px the admit form and the roster list stack (the grid was 320px+1fr).
   The card's own identity-above-actions stacking is now the DEFAULT (see
   .roster-item-main above), so only the grid collapse and the intrinsic-min fix
   remain width-specific here.
   ══════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  .roster-layout {
    grid-template-columns: 1fr;
  }

  /* The single 1fr column defaults to minmax(auto,1fr), so its automatic minimum
     is the widest grid child's min-content — and the create-requirement <select>
     (option "Weekly Target Count (Staff enters number, Target is variable)") has a
     min-content near 473px. That blew the track past the ~318px container, the
     card and its action row stretched to fill it, and .view-content scrolled
     sideways. min-width:0 lets each grid child shrink to the column instead, the
     same intrinsic-min fix already applied to .form-group above; the <select>'s
     max-width:100% then caps it and .roster-item-actions can actually wrap. */
  .roster-layout > * {
    min-width: 0;
  }

  /* Reclaim horizontal width on a phone. The shell scroller kept its 32px desktop
     gutter while the header trimmed to 16px, so every card sat inset from the
     header with dead space either side. Trim the scroller to match, and trim the
     roster card's own 24px inner padding, so the card and its list fill the width.
     App-wide on mobile (dashboard cards benefit too); desktop keeps 32px/24px. */
  .view-content {
    padding: 16px 12px;
  }

  .roster-card {
    padding: 16px;
  }

  /* Give the list rows a touch more of the reclaimed width too. */
  .roster-item-card {
    padding: 14px;
  }
}

.btn-icon-label {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 500;
  padding: 6px 10px;
  border-radius: var(--border-radius-sm);
  background-color: var(--bg-card);
  border: 1px solid var(--border-color);
  transition: background-color var(--transition-fast), border-color var(--transition-fast);
}

.btn-icon-label:hover {
  background-color: var(--bg-card-hover);
  border-color: var(--text-muted);
}

.client-config-panel {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px dashed var(--border-color);
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.client-config-panel h5 {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
}

.config-req-row {
  display: grid;
  grid-template-columns: 1fr 120px 80px;
  align-items: center;
  gap: 12px;
  background-color: var(--bg-input);
  padding: 8px 12px;
  border-radius: var(--border-radius-sm);
  border: 1px solid var(--border-color-light);
}

.config-req-name {
  font-size: 13px;
  font-weight: 600;
}

.config-req-target {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
}

.config-req-target input {
  width: 45px;
  background-color: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-sm);
  padding: 4px;
  text-align: center;
  font-weight: bold;
}

.config-toggle-switch {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

/* Custom CSS Toggle Switch */
.switch-input {
  opacity: 0;
  width: 0;
  height: 0;
  position: absolute;
}

.switch-label {
  position: relative;
  display: inline-block;
  width: 40px;
  height: 20px;
  background-color: var(--border-color);
  border-radius: var(--border-radius-full);
  cursor: pointer;
  transition: background-color var(--transition-fast);
}

.switch-label::after {
  content: '';
  position: absolute;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background-color: #ffffff;
  top: 2px;
  left: 2px;
  transition: transform var(--transition-fast);
}

.switch-input:checked + .switch-label {
  background-color: var(--color-brand);
}

.switch-input:checked + .switch-label::after {
  transform: translateX(20px);
}

/* ── Toggle rows ─────────────────────────────────────────────────────────────
   A bordered row: what it is on the left, a switch on the right. It replaced
   native checkboxes wearing transform:scale(), which enlarged the PAINT and
   not the hit box — so on every one of them the thing you could see was
   bigger than the thing you could click.

   The whole row is the <label>, so the text is part of the target rather than
   decoration beside it. The switch itself is a <span>.switch-label rather than
   a nested <label> (which would be invalid inside one) and reuses the roster
   drawer's switch, so the app has ONE toggle and not two that drift. */
.toggle-row-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

@media (max-width: 900px) {
  .toggle-row-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 10px;
  background-color: var(--bg-input-focus);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
  cursor: pointer;
  /* The whole row is the tap target; 44 is the floor a finger needs and it is
     kept at every width because these rows are never crowded. */
  min-height: 44px;
}

.toggle-row-label {
  font-size: 12px;
  color: var(--text-secondary);
  user-select: none;
}

.toggle-row-note {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  color: var(--text-muted);
}

.toggle-row-wide {
  margin-bottom: 16px;
}

.modal-home-group {
  margin-top: 10px;
  margin-bottom: 0;
}

/* A single boolean requirement: the tick and its label on one row, on the
   same inset surface the day cells use. */
.boolean-checkbox-row {
  flex-direction: row;
  gap: 12px;
  width: fit-content;
  padding: 8px 12px;
  min-height: 44px;
  background-color: var(--bg-input-focus);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
}

.boolean-checkbox-row .custom-checkbox-ui {
  width: 26px;
  flex-shrink: 0;
}

/* A panel nested INSIDE a card. One step in, never a black wash. */
.inset-panel {
  padding: 12px;
  background-color: var(--bg-input-focus);
}

.empty-inset {
  text-align: center;
  padding: 15px;
  background-color: var(--bg-input-focus);
  border: 1px dashed var(--border-color-light);
  border-radius: var(--border-radius-md);
}

/* The work-hours calendar. It used to paint itself the old cyan at 5% with a
   20% cyan border, both hard-coded, so it stayed the PREVIOUS brand colour
   after the palette moved and read as a differently-branded panel. */
.work-schedule-calendar {
  padding: 12px;
  width: 100%;
  box-sizing: border-box;
  background-color: var(--bg-input-focus);
  border-color: var(--border-color);
}

.work-hour-input {
  padding: 4px;
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  width: 100%;
  box-sizing: border-box;
}

/* Settings Sliders & Form Configurations */
.settings-grid {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.settings-row-slider {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 20px;
}

.slider-labels {
  display: flex;
  justify-content: space-between;
  font-weight: 600;
  font-size: 14px;
}

.threshold-slider-group {
  position: relative;
  display: flex;
  align-items: center;
  gap: 16px;
  background-color: var(--bg-input);
  padding: 12px;
  border-radius: var(--border-radius-md);
  border: 1px solid var(--border-color-light);
}

.slider-labels label,
.slider-labels > span:first-child {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
}

.threshold-num-display {
  font-size: 15px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  min-width: 50px;
  text-align: right;
}

/* The band lives on the VALUE the slider moves, not on the label beside it. */
.threshold-num-display.is-compliant { color: var(--text-success); }
.threshold-num-display.is-marginal { color: var(--text-warning); }

.settings-num-input {
  width: 72px;
  padding: 4px 8px;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-md);
}

/* An explanatory line under a group of controls. Three views use it. */
.inline-notice {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 12px;
  padding: 10px 12px;
  background-color: var(--bg-input-focus);
  border: 1px solid var(--border-color-light);
  border-radius: var(--border-radius-md);
}

/* Two settings cards that mark themselves: one migration, one destructive. */
.settings-card-brand { border-color: var(--color-brand); }
.settings-card-danger { border-color: var(--color-danger); }

.slider-input {
  flex: 1;
  -webkit-appearance: none;
  appearance: none;
  height: 6px;
  border-radius: var(--border-radius-full);
  background: var(--border-color);
  outline: none;
}

.slider-input::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--color-brand);
  cursor: pointer;
  transition: transform var(--transition-fast);
}

.slider-input::-webkit-slider-thumb:hover {
  transform: scale(1.25);
}

/* Firefox renders the thumb unstyled without its own pseudo-element, and a
   ::-webkit-/::-moz- selector LIST is dropped whole by both engines, so these
   are declared as their own rules rather than folded into the ones above. */
.slider-input::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border: none;
  border-radius: 50%;
  background: var(--color-brand);
  cursor: pointer;
  transition: transform var(--transition-fast);
}

.slider-input::-moz-range-thumb:hover {
  transform: scale(1.25);
}

.settings-btn-row {
  display: flex;
  gap: 12px;
  margin-top: 10px;
}

/* System Notification Toast Alert */
.toast-notification {
  position: fixed;
  bottom: 24px;
  right: 24px;
  background-color: var(--bg-sidebar);
  color: #ffffff;
  border: 1px solid var(--color-brand);
  padding: 12px 24px;
  border-radius: var(--border-radius-md);
  box-shadow: var(--shadow-lg);
  font-size: 14px;
  font-weight: 550;
  z-index: 999;
  display: flex;
  align-items: center;
  gap: 10px;
  animation: slideIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

/* History Heatmap Analytics */
.history-layout {
  display: flex;
  flex-direction: column;
  gap: 28px;
}

/* One client's twelve weeks, wrapping.
   This replaced a clients x weeks <table> whose first column was position:sticky
   — on a phone that column slid over the week cells as they scrolled under it.
   There is no horizontal scroller here and nothing is sticky: the client is
   chosen with #history-client-select and the weeks reflow to whatever width
   there is, which is what makes the view work at 390px.

   auto-FIT, not auto-fill, and a 56px floor. Both were MEASURED in the real
   Electron renderer (the repo's own CDP harness), not reasoned:
     390px  -> 4 columns x 3 rows, 61px per cell, no horizontal overflow
     1280px -> 12 columns x 1 row,  64px per cell
     1920px -> 12 columns x 1 row, 117px per cell
   auto-fill would leave the empty tail tracks in place, so at 1920 the twelve
   weeks would sit at their minimum against the left edge with dead space
   beside them; auto-fit collapses the empty tracks and the twelve stretch to
   fill the row. The floor is 56px because the desktop card's content box
   measures ~873px: a 72px floor fits only ten tracks there and a 64px floor
   eleven, either of which orphans the tail weeks onto a second row for no
   reason. 56px fits all twelve on one row on a desktop and still leaves four
   readable columns on a phone. */
.heatmap-week-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(56px, 1fr));
  gap: 10px;
  border: 1px solid var(--border-color-light);
  border-radius: var(--border-radius-lg);
  padding: 16px;
  background-color: var(--bg-input-focus);
}

/* min-width: 0 so a long label can never push a track wider than its share and
   overflow the card — the 390px-audit discipline. */
.heatmap-week {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.heatmap-week-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.heatmap-cell {
  width: 100%;
  height: 38px;
  border-radius: var(--border-radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  color: #ffffff;
  cursor: pointer;
  transition: transform var(--transition-fast);
}

.heatmap-cell:hover {
  transform: scale(1.08);
  box-shadow: 0 0 0 1px var(--color-brand);
}

.heatmap-cell.compliant-cell {
  background-color: rgba(var(--compliant-rgb), 0.85);
  border: 1px solid rgba(var(--compliant-rgb), 1);
}

.heatmap-cell.marginal-cell {
  background-color: rgba(var(--marginal-rgb), 0.85);
  border: 1px solid rgba(var(--marginal-rgb), 1);
}

.heatmap-cell.non-compliant-cell {
  background-color: rgba(var(--non-compliant-rgb), 0.85);
  border: 1px solid rgba(var(--non-compliant-rgb), 1);
}

.heatmap-cell.empty-cell {
  background-color: var(--bg-input);
  border: 1px dashed var(--border-color);
  color: var(--text-muted);
}

/* Empty State Styling */
.empty-state-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 20px;
  text-align: center;
}

.empty-state-icon {
  color: var(--text-muted);
  margin-bottom: 16px;
}

.empty-state-container h4 {
  font-size: 16px;
  font-weight: 700;
  margin-bottom: 6px;
}

.empty-state-container p {
  font-size: 13px;
  color: var(--text-secondary);
  max-width: 300px;
}

/* Keyframe Animations */
/* Respect a device that has asked for less motion. The flat restyle removed the
   pulse, the hover lift and the checkbox bounce, so what is left is short colour
   and background fades and the one slideIn below — but a staff member with
   vestibular sensitivity should not have to take that on trust. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@keyframes slideIn {
  from {
    transform: translateY(20px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

/* ══════════════════════════════════════════════════════
   HISTORY BACKFILL MODAL
   ══════════════════════════════════════════════════════ */

.backfill-modal {
  background: var(--bg-card);
  border: var(--glass-border);
  border-radius: var(--border-radius-lg);
  box-shadow: var(--shadow-lg);
  width: 96vw;
  max-width: 1200px;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: slideIn 0.3s ease;
}

.backfill-modal-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  padding: 28px 32px 20px;
  border-bottom: 1px solid var(--border-color-light);
  flex-shrink: 0;
  gap: 16px;
}

/* Date range controls strip */
.backfill-date-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 14px;
  flex-wrap: wrap;
}

.backfill-date-group {
  display: flex;
  align-items: center;
  gap: 8px;
}

.backfill-date-label {
  display: flex;
  align-items: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
}

.backfill-date-input {
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-sm);
  color: var(--text-primary);
  font-size: 13px;
  padding: 6px 10px;
  height: 34px;
  transition: border-color var(--transition-fast);
  cursor: pointer;
}

.backfill-date-input:focus {
  outline: none;
  border-color: var(--color-brand);
}

/* Suppress webkit date picker button styles */
.backfill-date-input::-webkit-calendar-picker-indicator {
  opacity: 0.5;
  cursor: pointer;
  filter: invert(0.5);
}

.backfill-week-count {
  font-size: 12px;
  white-space: nowrap;
}

/* Row that already has data saved — subtle highlight */
.backfill-week-row.backfill-has-data {
  background-color: rgba(var(--compliant-rgb), 0.06);
}

.backfill-title {
  font-size: 20px;
  font-weight: 700;
  display: flex;
  align-items: center;
  color: var(--text-primary);
  margin-bottom: 8px;
}

.backfill-subtitle {
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.5;
  max-width: 680px;
}

.backfill-legend {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 32px;
  background-color: var(--bg-input-focus);
  border-bottom: 1px solid var(--border-color-light);
  flex-shrink: 0;
  flex-wrap: wrap;
}

.backfill-legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
}

.backfill-legend-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
}

.backfill-table-wrapper {
  overflow: auto;
  flex: 1;
  padding: 0;
}

.backfill-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.backfill-th {
  padding: 10px 12px;
  text-align: left;
  font-weight: 700;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-secondary);
  background: var(--bg-card);
  border-bottom: 2px solid var(--border-color);
  position: sticky;
  top: 0;
  z-index: 2;
  white-space: nowrap;
}

.backfill-week-th {
  min-width: 180px;
}

.backfill-type-badge {
  display: inline-block;
  font-size: 10px;
  font-weight: 600;
  color: var(--color-brand);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-top: 2px;
}

.backfill-td {
  padding: 10px 12px;
  vertical-align: middle;
  border-bottom: 1px solid var(--border-color-light);
}

.backfill-week-row:hover {
  background-color: var(--bg-card-hover);
}

.backfill-week-row.backfill-current-week {
  background-color: var(--color-brand-glow);
}

.backfill-week-label {
  min-width: 180px;
}

.backfill-week-name {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-primary);
}

.backfill-week-range {
  font-size: 11px;
  color: var(--text-secondary);
  margin-top: 2px;
}

.backfill-score-wrap {
  margin-top: 6px;
}

.backfill-score-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  color: #ffffff;
  padding: 2px 8px;
  border-radius: var(--border-radius-full);
}

.backfill-no-data {
  font-size: 11px;
  color: var(--text-muted);
  font-style: italic;
}

.backfill-current-badge {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  color: var(--color-brand);
  background: var(--color-brand-glow);
  padding: 2px 6px;
  border-radius: var(--border-radius-full);
  margin-top: 4px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

/* Daily checkbox group inside backfill table */
.backfill-daily-group {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.backfill-day-chk {
  display: flex;
  flex-direction: column;
  align-items: center;
  cursor: pointer;
}

.backfill-day-chk input[type="checkbox"] {
  display: none;
}

.backfill-day-ui {
  width: 26px;
  height: 26px;
  border-radius: 6px;
  background-color: var(--bg-input);
  border: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  color: var(--text-secondary);
  transition: all var(--transition-fast);
  user-select: none;
}

.backfill-day-chk:hover .backfill-day-ui {
  border-color: var(--color-brand);
  color: var(--color-brand);
}

.backfill-day-chk input:checked + .backfill-day-ui {
  background-color: var(--color-brand);
  border-color: var(--color-brand);
  color: var(--color-brand-fg);
}

.backfill-target-info {
  font-size: 10px;
  color: var(--text-muted);
  margin-top: 4px;
  text-align: center;
}

/* Count input group in backfill table */
.backfill-count-group {
  display: flex;
  align-items: center;
  gap: 6px;
}

.backfill-counter-btn {
  width: 28px;
  height: 28px;
  border-radius: var(--border-radius-sm);
  background-color: var(--bg-input);
  border: 1px solid var(--border-color);
  font-size: 16px;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all var(--transition-fast);
  flex-shrink: 0;
}

.backfill-counter-btn:hover {
  background-color: var(--bg-input-focus);
  border-color: var(--color-brand);
  color: var(--color-brand);
}

.backfill-count-input {
  width: 52px;
  height: 28px;
  text-align: center;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-sm);
  font-size: 14px;
  font-weight: 700;
  color: var(--text-primary);
  padding: 0 4px;
  transition: border-color var(--transition-fast);
}

.backfill-count-input:focus {
  outline: none;
  border-color: var(--color-brand);
}

/* Hide number spinners */
.backfill-count-input::-webkit-outer-spin-button,
.backfill-count-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
}

/* Notes cell */
.backfill-notes-td {
  min-width: 180px;
}

.backfill-notes-input {
  width: 100%;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-sm);
  color: var(--text-primary);
  font-size: 12px;
  padding: 6px 8px;
  resize: vertical;
  min-height: 44px;
  transition: border-color var(--transition-fast);
}

.backfill-notes-input:focus {
  outline: none;
  border-color: var(--color-brand);
}

/* Action cell */
.backfill-action-td {
  white-space: nowrap;
}

.backfill-save-week-btn {
  font-size: 12px;
  padding: 6px 12px;
  gap: 4px;
}

.backfill-empty-notice {
  text-align: center;
  padding: 40px;
  color: var(--text-secondary);
  font-style: italic;
}

/* Job Tracker UI Styles */
.job-tracker-container {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 10px;
}

.job-cards-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.job-application-card {
  background: var(--bg-input-focus);
  border: 1px solid var(--border-color-light);
  border-radius: var(--border-radius-md);
  padding: 12px;
  transition: all var(--transition-fast);
}

.job-application-card:hover {
  background: var(--bg-card-hover);
  border-color: var(--border-color);
}

.job-checkbox-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  user-select: none;
  color: var(--text-secondary);
}

.job-checkbox-label input {
  width: 16px;
  height: 16px;
  accent-color: var(--color-brand);
}

.job-checkbox-label:hover {
  color: var(--text-primary);
}

.btn-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px;
  border-radius: var(--border-radius-sm);
  transition: all var(--transition-fast);
}

.btn-icon:hover {
  background: var(--bg-card-hover);
}


/* ==========================================================================
   MEDICATION CALENDAR
   ========================================================================== */

/* The calendar owns the full content width; medication management and dose
   logging both live in the day modal. */
.med-day-modal {
  max-width: 820px;
}

.med-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

.med-month-nav {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}

.med-month-label {
  font-size: 18px;
  font-weight: 700;
  min-width: 170px;
  text-align: center;
}

/* The only 7-column grid in the app */
.med-grid-dowhead,
.med-month-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
}

.med-grid-dowhead {
  margin-bottom: 6px;
}

.med-grid-dowhead span {
  text-align: center;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.med-day-cell {
  /* Roomier now that the calendar spans the full content width. */
  min-height: 104px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px;
  background: var(--bg-input);
  border: 1px solid var(--border-color-light);
  border-radius: var(--border-radius-md);
  transition: border-color var(--transition-fast);
}

.med-day-cell:hover {
  border-color: var(--border-color);
}

.med-day-cell.out-of-month {
  background: transparent;
  border-color: transparent;
  opacity: 0.35;
}

.med-day-cell.is-today {
  border-color: var(--color-brand);
  box-shadow: 0 0 0 1px var(--color-brand);
}

.med-day-cell.is-future {
  opacity: 0.55;
}

.med-day-cell.is-future .med-pip {
  cursor: not-allowed;
}

.med-day-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.med-day-num {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-secondary);
}

.med-day-cell.is-today .med-day-num {
  color: var(--color-brand);
}

.med-day-ratio {
  font-size: 10px;
  font-weight: 800;
  color: var(--text-muted);
  background: var(--bg-card);
  border-radius: var(--border-radius-full);
  padding: 1px 6px;
}

.med-day-ratio.is-complete {
  color: var(--color-success);
}

.med-day-none {
  font-size: 11px;
}

/* Compact dose dots: one per scheduled dose that day, status only. Editing
   happens in the day modal, so the grid stays readable at any regimen size. */
.med-day-dots {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  align-items: center;
  flex: 1;
  align-content: flex-start;
  min-width: 0;
}

.med-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--border-radius-full);
  border: 1px solid var(--border-color);
  background: transparent;
  flex-shrink: 0;
}

.med-dot.is-taken {
  background: var(--color-success);
  border-color: var(--color-success);
}

.med-dot.is-missed {
  border-color: var(--color-danger);
  background: var(--color-danger);
  opacity: 0.55;
}

.med-dot.is-pending {
  border-color: var(--border-color);
  background: transparent;
}

.med-dot-more {
  font-size: 9px;
  font-weight: 800;
  color: var(--text-muted);
}

.med-day-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  margin-top: auto;
}

.med-prn-chip {
  font-size: 9px;
  font-weight: 700;
  padding: 1px 6px;
  border: 1px dashed var(--border-color);
  border-radius: var(--border-radius-full);
  color: var(--text-secondary);
  white-space: nowrap;
}

/* The + affordance that opens the day modal */
.med-day-open {
  margin-left: auto;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  opacity: 0;
  transition: all var(--transition-fast);
}

.med-day-cell:hover .med-day-open,
.med-day-cell.is-today .med-day-open {
  opacity: 1;
}

.med-day-open:hover {
  border-color: var(--color-brand);
  color: var(--color-brand);
  background: var(--color-brand-glow);
}

.med-day-open:disabled {
  opacity: 0 !important;
  cursor: not-allowed;
}

.med-day-cell[data-date]:not(.no-meds):not(.is-future) {
  cursor: pointer;
}

/* ---- Day logging modal ---- */
.med-modal-card {
  background: var(--bg-input-focus);
  border: 1px solid var(--border-color-light);
  border-radius: var(--border-radius-md);
  padding: 14px;
}

.med-modal-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 10px;
  flex-wrap: wrap;
}

.med-modal-name {
  font-size: 15px;
  font-weight: 700;
}

.med-dose-time {
  font-size: 10px;
  color: var(--text-muted);
  font-weight: 600;
}

.day-checkbox-label.is-missed .custom-checkbox-ui {
  border-color: var(--color-danger);
  background-image: linear-gradient(135deg, transparent 45%, var(--color-danger) 45%, var(--color-danger) 55%, transparent 55%);
}

.med-missed-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  flex-wrap: wrap;
}

.med-missed-label {
  font-size: 11px;
  font-weight: 700;
  color: var(--color-danger);
}

.med-prn-entry {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border-color-light);
  font-size: 12px;
}

.med-prn-time {
  font-weight: 800;
  color: var(--text-secondary);
  white-space: nowrap;
}

.med-prn-note {
  flex: 1;
  color: var(--text-secondary);
  overflow-wrap: anywhere;
}

.med-prn-add {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  align-items: center;
}

.med-prn-add .form-input {
  flex: 1;
}

.med-legend {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--border-color-light);
  font-size: 11px;
  color: var(--text-secondary);
}

.med-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.med-legend-swatch {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
}

.med-legend-swatch.is-taken {
  background: var(--color-success);
  border-color: var(--color-success);
}

.med-legend-swatch.is-missed {
  border-color: var(--color-danger);
}

.med-legend-swatch.is-prn {
  border-style: dashed;
}

/* Medication definition cards (mirrors .job-application-card) */
.med-def-card {
  background: var(--bg-input-focus);
  border: 1px solid var(--border-color-light);
  border-radius: var(--border-radius-md);
  padding: 12px;
  margin-bottom: 10px;
  transition: all var(--transition-fast);
}

.med-def-card:hover {
  background: var(--bg-card-hover);
  border-color: var(--border-color);
}

.med-def-card.is-inactive {
  opacity: 0.55;
}

.med-def-name {
  font-size: 14px;
  font-weight: 700;
}

.med-def-meta {
  font-size: 11px;
  color: var(--text-secondary);
  margin-top: 2px;
  overflow-wrap: anywhere;
}

.med-def-actions {
  display: flex;
  gap: 6px;
  margin-top: 8px;
  flex-wrap: wrap;
}

/* Dose time editor rows in the add/edit form */
.med-dose-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 120px 64px 32px;
  gap: 6px;
  align-items: center;
  margin-bottom: 6px;
}

@media (max-width: 900px) {
  /* M3-9: the month-nav is a single nowrap row (prev / label / next / Today /
     Print MAR / a right-floated filter select). On a phone Print MAR and the
     filter spill off the card's right edge, so let it wrap onto extra rows and
     trim the gap. Desktop keeps the single-row nav. */
  .med-month-nav {
    flex-wrap: wrap;
    gap: 8px;
  }

  /* M3-9: reclaim horizontal space per cell — a tighter grid gap and cell
     padding give each of the seven columns more width so the dots, the
     now-bottom ratio and the PRN/count chips fit without clipping or forcing
     page overflow. The day-of-week header shares the gap so its columns stay
     aligned with the cells. */
  .med-grid-dowhead,
  .med-month-grid {
    gap: 3px;
  }

  .med-day-cell {
    min-height: 84px;
    padding: 4px;
  }

  /* M3-9: the foot now carries the ratio too (ratio + optional PRN/count chips +
     the open button). In a narrow phone cell that can be more than one row's
     worth, so let it wrap rather than clip or push the cell wider. */
  .med-day-foot {
    flex-wrap: wrap;
  }

  /* M3-9: the ratio pill's default 6px side padding makes it a couple of px wider
     than the tightened cell can hold; trim it so the pill fits the narrow foot. */
  .med-day-ratio {
    padding: 1px 4px;
  }
}

/* ══════════════════════════════════════════════════════
   MOBILE SIDEBAR DRAWER (M3-1)
   Reuses the app's existing 900px breakpoint. Below it the 260px sidebar leaves
   the grid and becomes an off-canvas drawer that the hamburger opens over a
   backdrop; at >=900px none of this applies and the desktop layout is unchanged.
   ══════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  /* The grid collapses to a single column: the main content spans the full
     viewport width because the sidebar is now position:fixed, out of flow. */
  .app-layout {
    grid-template-columns: 1fr;
  }

  /* The drawer: fixed, full-height, translated fully off the left edge when
     closed so it steals none of the viewport. */
  .app-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: var(--sidebar-width);
    max-width: 85vw;
    z-index: 200;
    /* Off-canvas to the left. A token-width pixel translate rather than -100%:
       max-width:85vw only ever SHRINKS the drawer below 260px, so translating by
       the full --sidebar-width always clears the viewport at any phone width. */
    transform: translateX(calc(-1 * var(--sidebar-width)));
    transition: transform var(--transition-normal);
    box-shadow: var(--shadow-lg);
  }

  /* Open state — app.js toggles .sidebar-open on .app-layout — slides it in. */
  .app-layout.sidebar-open .app-sidebar {
    transform: translateX(0);
  }

  /* The hamburger appears in the header. */
  .sidebar-toggle {
    display: flex;
  }

  /* The backdrop sits under the open drawer and closes it on click. */
  .app-layout.sidebar-open .sidebar-backdrop {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    background-color: var(--bg-overlay);
    z-index: 150;
  }
}

/* ══════════════════════════════════════════════════════
   MOBILE CLIENT DETAIL MODAL (M3-3)
   Reuses the app's existing 900px breakpoint, so at >=900px none of this applies
   and the desktop modal is byte-identical. Below it the 4-tab, ~40-field editor
   in .modal-container reflows for a phone:
     • the four tab buttons (white-space:nowrap) become their OWN horizontal
       scroller instead of spilling out and forcing the modal to scroll sideways
       to reach the fourth tab;
     • the 32px padding is trimmed to reclaim width;
     • the height becomes dvh-aware (vh fallback first) so mobile browser chrome
       does not clip it, still scrolling INTERNALLY via the base overflow-y:auto;
     • the multi-column report/schedule grids stack to one column so every field
       stays reachable rather than being squeezed off the edge.
   ══════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  .modal-container {
    /* dvh-aware, vh first as the fallback for engines without dvh. The base rule
       already sets overflow-y:auto, so the container scrolls internally. */
    max-height: 85vh;
    max-height: 85dvh;
    padding: 20px;
  }

  /* The tab strip is the scroller: its nowrap buttons scroll WITHIN the strip
     rather than widening the modal. This is what keeps the container from
     scrolling sideways at 390px. */
  .modal-tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    gap: 14px;
  }

  /* Keep each label on one line and let the strip — not the modal — do the
     scrolling. */
  .modal-tab-btn {
    flex-shrink: 0;
  }

  /* Field grids collapse to a single column so nothing is pushed off-screen.
     !important because the component sets grid-template-columns inline on several
     of these grids, and an inline value otherwise outranks this sheet. */
  .report-grid,
  .schedule-grid {
    grid-template-columns: 1fr !important;
  }
}

/* ══════════════════════════════════════════════════════
   MOBILE TOUCH TARGETS (M3-4) — WCAG 2.5.5
   Reuses the app's 900px breakpoint. Below it the small tappable controls are
   grown to the 44×44 CSS-px minimum a finger needs; at >=900px none of this
   applies and the desktop sizes (32/36/28px) are byte-identical. Each rule sizes
   the TAP TARGET — the button that receives the pointer event — not the
   decorative glyph inside it.

   NOT here: the medications day-modal dose toggle (.day-checkbox-label). Its
   wrapping label already measures 62×76 at 390px (an inline min-width:62px plus
   the stacked glyph/time), so it is a compliant tap target as-is — and the same
   class is reused by the client-detail daily-requirement day boxes, where a
   44px floor would push that 7-across row wider than the phone modal (the M3-3
   surface). It is left untouched by design.
   ══════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  /* Week navigator arrows — the 32×32 round buttons in the header pill.
     flex-shrink:0 so the crowded header row cannot squeeze them back under 44. */
  .nav-arrow-btn {
    width: 44px;
    height: 44px;
    flex-shrink: 0;
  }

  /* Counter/stepper +/− buttons in the client-detail count requirement (36×36)
     and the history-backfill count cells (28×28). The value field between them
     keeps its own width; only the buttons grow. */
  .counter-btn,
  .backfill-counter-btn {
    width: 44px;
    height: 44px;
  }
}

/* ══════════════════════════════════════════════════════
   MOBILE INNER-OVERFLOW FIXES (M3-11)
   Reuses the app's 900px breakpoint, so at >=900px none of this applies and the
   desktop layout is byte-identical. Two space-between flex rows whose non-shrinking
   child pushed their min-content wider than the phone box — the document did NOT
   scroll (an ancestor clips overflow-x) so the excess hid inside, taking a control
   off-screen.
   ══════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  /* Issue B — the history-backfill modal header. Its flex:1 left block (> div)
     kept min-width:auto and refused to shrink, so its min-content (~412px) pushed
     #backfill-close-btn ~30px past the 96vw modal's right edge. Trim the fixed
     28/32px padding and let the left block shrink; the close button returns
     on-screen. */
  .backfill-modal-header {
    padding: 16px;
  }
  .backfill-modal-header > div {
    min-width: 0;
  }

  /* Issue A — the medications month .section-header (shared class). At >=900px this
     rule does not apply, so other .section-header uses are untouched; below it the
     header may wrap and #med-client-select (inline min-width:220px) may shrink, so
     the ~353px min-content clears the ~318px box instead of clipping ~35px. */
  .section-header {
    flex-wrap: wrap;
    gap: 8px;
  }
  /* The history heatmap's client picker is the same control with the same inline
     min-width:220px, sitting in the same shared .section-header, so it needs the
     same permission to shrink or it clips at 390px exactly as the medication
     one did. */
  #med-client-select,
  #history-client-select {
    min-width: 0;
  }
}

/* ══════════════════════════════════════════════════════
   MOBILE HEADER — unified across every view
   Reuses the app's 900px breakpoint, so at >=900px the desktop header is
   byte-identical. Below 900px EVERY view gets the same shape so the app feels
   consistent page to page:
     Row 1:  [menu] [title + subtitle] ················· [theme toggle]
     Row 2:  [ ◀   date selector (centered pill)   ▶ ]   (dashboard/history only)
   header-actions groups the week-navigator + theme toggle on desktop; on mobile
   it becomes `display: contents`, dissolving the wrapper so those two children
   join the header's own flex flow and can split across the two rows — the theme
   toggle up on row 1 (right edge), the date pill on its own full-width row 2.
   On views with no week (roster/requirements/settings) the pill is display:none,
   so row 2 collapses and the header is just row 1 — same layout, one row shorter.
   (The forced Card view + hidden view toggle live in DashboardView.js.)
   ══════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  .app-header {
    flex-wrap: wrap;
    align-items: center;       /* menu, title line and theme toggle sit level */
    row-gap: 10px;
    column-gap: 12px;
    padding: 12px 16px;
  }

  /* Dissolve BOTH wrappers so the menu, title, theme toggle, subtitle and date
     pill are all direct flex items of the header — then `order` + a 100% basis
     lay them onto the rows above regardless of their DOM nesting. */
  .header-actions,
  .header-title-section {
    display: contents;
  }

  .sidebar-toggle { order: 0; }

  /* Title: next to the menu, growing to push the theme toggle to the right edge.
     min-width:0 lets a long title shrink rather than force a sideways scroll. */
  #view-title {
    order: 1;
    flex: 1 1 auto;
    min-width: 0;
  }

  /* Guide and theme toggle: same row, right edge, level with the title.
     BOTH need an explicit order. .header-actions is `display: contents` here,
     so its children are laid out by the header's own `order` values — and a
     child without one defaults to order 0, which would put the `?` ahead of
     the hamburger at the far left rather than beside the theme toggle. Sharing
     order 2 is deliberate: equal orders fall back to DOM order, so the pair
     stays in the sequence index.html declares them in. */
  #help-btn,
  #theme-toggle {
    order: 2;
    flex: 0 0 auto;
  }

  /* Subtitle: its own full-width row directly under the title (row-gap owns the
     spacing, so drop the desktop margin). */
  #view-subtitle {
    order: 3;
    flex: 0 0 100%;
    margin-top: 0;
  }

  /* Date selector: full-width row below — a pill with the label centered and the
     prev/next arrows pinned to each end. Hidden (display:none, set per-view in
     app.js) on views without a week, so its row simply collapses there. */
  .week-navigator {
    order: 4;
    flex: 0 0 100%;
    justify-content: space-between;
  }
}

/* ---- Pill counts ---- */
.med-count-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 9px;
  font-weight: 800;
  padding: 1px 5px;
  border-radius: var(--border-radius-full);
  border: 1px solid var(--border-color);
  white-space: nowrap;
}

.med-count-chip.is-ok {
  color: var(--color-success);
  border-color: var(--color-success);
}

.med-count-chip.is-bad {
  color: var(--color-danger);
  border-color: var(--color-danger);
  background: rgba(var(--non-compliant-rgb), 0.12);
}

.med-count-card {
  background: var(--bg-input-focus);
  border: 1px solid var(--border-color-light);
  border-left: 3px solid var(--border-color);
  border-radius: var(--border-radius-md);
  padding: 14px;
}

.med-count-card.is-ok    { border-left-color: var(--color-success); }
.med-count-card.is-short { border-left-color: var(--color-danger); }
.med-count-card.is-over  { border-left-color: var(--color-warning); }

.med-count-math {
  font-size: 12px;
  color: var(--text-secondary);
  margin-bottom: 10px;
  line-height: 1.5;
}

.med-count-entry {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: 10px;
  align-items: end;
}

.med-count-entry .form-input {
  padding: 8px 10px;
  font-size: 13px;
}

.med-count-entry .btn-primary {
  padding: 9px 16px;
  font-size: 13px;
  white-space: nowrap;
}

.med-count-feedback {
  margin-top: 10px;
  font-size: 12px;
  font-weight: 600;
  padding: 8px 10px;
  border-radius: var(--border-radius-sm);
}

.med-count-feedback.is-ok {
  color: var(--color-success);
  background: rgba(var(--compliant-rgb), 0.1);
}

.med-count-feedback.is-short {
  color: var(--color-danger);
  background: rgba(var(--non-compliant-rgb), 0.12);
}

.med-count-feedback.is-over {
  color: var(--color-warning);
  background: rgba(var(--marginal-rgb), 0.12);
}

/* M4-6: offline on the shared backend, the pill-count entry is disabled and this
   reason is shown in its place. Reuses the danger tint the "short" feedback uses. */
.med-count-offline {
  margin-top: 10px;
  font-size: 12px;
  font-weight: 600;
  padding: 8px 10px;
  border-radius: var(--border-radius-sm);
  color: var(--color-danger);
  background: rgba(var(--non-compliant-rgb), 0.12);
}

.med-count-explain {
  margin-top: 10px;
}

.med-count-explain label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  display: block;
  margin-bottom: 4px;
}

.med-count-result {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}

.med-count-big {
  font-size: 26px;
  font-weight: 850;
}

.med-count-flag {
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  padding: 3px 10px;
  border-radius: var(--border-radius-full);
}

.med-count-flag.is-ok    { color: var(--color-success); background: rgba(var(--compliant-rgb), 0.12); }
.med-count-flag.is-short { color: var(--color-danger);  background: rgba(var(--non-compliant-rgb), 0.14); }
.med-count-flag.is-over  { color: var(--color-warning); background: rgba(var(--marginal-rgb), 0.14); }

.med-count-note {
  font-size: 12px;
  color: var(--text-secondary);
  font-style: italic;
  padding: 8px 10px;
  background: var(--bg-input);
  border-radius: var(--border-radius-sm);
  margin-top: 8px;
  overflow-wrap: anywhere;
}

.med-count-history {
  margin-top: 10px;
  font-size: 12px;
}

.med-count-history summary {
  cursor: pointer;
  color: var(--text-secondary);
  font-weight: 600;
}

.med-count-hist-row {
  display: grid;
  grid-template-columns: 100px minmax(0, 1fr) auto;
  gap: 8px;
  padding: 5px 0;
  border-bottom: 1px solid var(--border-color-light);
  color: var(--text-secondary);
}

/* ── Inventory: where the pills are, and when they run out ─────────────────
   Every colour below comes from an existing token, so both themes follow the
   same swap the rest of the med block does — nothing here defines a literal.
   The rows WRAP rather than scroll: each item is a short standalone phrase, so
   at 390px they stack into a readable block instead of forcing the page sideways. */
.med-inventory-panel {
  margin-bottom: 20px;
}

.med-inventory-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.med-inventory-row {
  display: flex;
  align-items: center;
  gap: 8px 12px;
  flex-wrap: wrap;
  padding: 10px 0;
  border-top: 1px solid var(--border-color-light);
  font-size: 12px;
  color: var(--text-secondary);
  /* A long medication name must break rather than push the row wide. */
  overflow-wrap: anywhere;
}

.med-inventory-row:first-of-type {
  border-top: none;
}

.med-inventory-name {
  font-size: 13px;
  font-weight: 700;
  color: var(--text-primary);
}

.med-inventory-onhand {
  font-weight: 700;
  color: var(--text-primary);
}

.med-inventory-days {
  font-weight: 600;
}

.med-inventory-verdict {
  margin-left: auto;
  font-style: italic;
}

/* Never counted is not "fine" — it reads as unresolved, not as a quiet pass. */
.med-inventory-verdict.is-unknown {
  font-style: normal;
  font-weight: 700;
  color: var(--color-warning);
}

/* The refill flag, shaped like .med-count-flag so the two read as one family. */
.med-refill-flag {
  margin-left: auto;
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  padding: 3px 10px;
  border-radius: var(--border-radius-full);
  color: var(--color-danger);
  background: rgba(var(--non-compliant-rgb), 0.14);
  white-space: nowrap;
}

/* On a med-def card the flag sits inline with the name, not pushed to the edge. */
.med-def-name .med-refill-flag {
  margin-left: 6px;
  vertical-align: middle;
}

.med-inventory-row.is-low {
  border-left: 3px solid var(--color-danger);
  padding-left: 10px;
}

/* The refills tile, matching the inline danger treatment the pill-count tile uses. */
.med-stat-danger {
  border-color: var(--color-danger);
}

/* A one-shot line above the medication list — e.g. "record the starting count". */
.med-panel-notice {
  margin-bottom: 12px;
  font-size: 12px;
  font-weight: 600;
  padding: 8px 10px;
  border-radius: var(--border-radius-sm);
  color: var(--color-warning);
  background: rgba(var(--marginal-rgb), 0.12);
}

/* Storage-location inputs in the medication form. */
.med-loc-row {
  display: grid;
  grid-template-columns: 140px minmax(0, 1fr) minmax(0, 1fr);
  gap: 8px;
}

.med-loc-row.is-lock {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

.med-loc-row.is-lock label {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary);
  display: block;
  margin-bottom: 4px;
}

@media (max-width: 900px) {
  /* One field per line on a phone — three inputs across a 390px screen is how a
     grid blows the page out sideways (the M3-9 pattern). */
  .med-loc-row,
  .med-loc-row.is-lock {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Nothing is pushed to the right edge once the row has stacked. */
  .med-inventory-verdict,
  .med-inventory-row .med-refill-flag {
    margin-left: 0;
  }
}

/* Dose-row header in the medication form (label / time / qty) */
.med-dose-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 120px 64px 32px;
  gap: 6px;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-muted);
  margin-bottom: 4px;
}

/* ── The direct storage-location affordance and its editor ─────────────────
   The trigger sits inline beside the location badge on an inventory row and on
   a med card. Tokens only, so both themes follow the same swap as the rest of
   the .med-* block. */
.med-loc-trigger {
  flex-shrink: 0;
  color: var(--text-secondary);
}

.med-loc-trigger:hover {
  color: var(--color-brand);
}

/* The empty case has to advertise itself — a pencil beside nothing is invisible
   — so the unset trigger is a labelled, outlined chip rather than a bare icon. */
.med-loc-trigger.is-unset {
  font-size: 11px;
  font-weight: 700;
  padding: 2px 10px;
  border: 1px dashed var(--border-color);
  border-radius: var(--border-radius-full);
  white-space: nowrap;
}

.med-loc-trigger.is-unset:hover {
  border-color: var(--color-brand);
  border-style: solid;
}

/* A narrow dialog: it holds five short fields and nothing else, and a
   full-width modal card would make a two-line task look like a form. */
.med-loc-dialog {
  max-width: 520px;
}

@media (max-width: 900px) {
  /* One field per line on a phone, the same stack .med-loc-row takes. */
  .med-loc-dialog .med-loc-grid {
    grid-template-columns: minmax(0, 1fr) !important;
  }

  /* WCAG 2.5.5: the icon trigger is a ~13px glyph — give it a real tap box.
     It sits in a wrapping flex row, so a 44px box grows the row, never the page. */
  .med-loc-trigger {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  .med-loc-trigger.is-unset {
    padding: 2px 14px;
  }
}

/* ══════════════════════════════════════════════════════
   MOBILE TOUCH TARGETS (M3-12) — WCAG 2.5.5, the worst offenders
   Reuses the app's 900px breakpoint, so at >=900px none of this applies and the
   desktop sizes (22/24/29/32px) are byte-identical. Each rule grows the TAP TARGET
   — the element that receives the pointer event — to the 44×44 CSS-px minimum a
   finger needs, without clipping text, overlapping siblings, or re-introducing
   overflow (the med "+" is the delicate one; see its note).
   ══════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  /* Modal header "X" (24×27) on every modal — client-detail #modal-close-btn, the
     med day-modal #med-day-close and the backfill #backfill-close-btn all share
     this class. Center the 24px glyph inside a 44×44 box. */
  .modal-close-btn {
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }

  /* Roster + requirements action buttons (~29px tall). They already wrap below
     900px (M3-7 / M3-10), so a taller row simply grows down. */
  .btn-icon-label {
    min-height: 44px;
  }

  /* The ▲/▼ requirement reorder buttons (32×29). They carry .btn-icon-label so the
     rule above already gives them the 44px height; they are narrow, so add the
     matching min-width. */
  .move-req-btn {
    min-width: 44px;
  }

  /* The month-calendar "+" open button (22×22). It lives in a ~36px-wide phone cell
     (M3-9 tightened the grid to seven columns), so a 44px-wide button cannot sit in
     the cell's flow without pushing the cell — and the page — wider. On a phone the
     button only shows on the TODAY cell (no hover), so scope the enlarge to that one
     cell and lift the button out of flow: pinned to the cell's bottom-right, a 44×44
     box grows LEFT and UP over the cell's own area. Right-pinned means it never
     overflows the cell's right edge, so the cell's scrollWidth is unchanged and the
     page does not scroll sideways; out-of-flow means .med-day-foot's own fit (the
     M3-9 assertion) is untouched. The other cells' buttons stay hidden (opacity:0)
     and unchanged. */
  .med-day-cell.is-today {
    position: relative;
  }
  .med-day-cell.is-today .med-day-open {
    position: absolute;
    right: 0;
    bottom: 0;
    width: 44px;
    height: 44px;
    margin: 0;
  }
}

/* ── M8-1: meeting attendance (the staff surfaces) ─────────────────────────
   Two surfaces are styled here: the per-client occurrence modal, and the
   read-only derived counter that replaces the +/- control in ClientDetailsModal
   for a `verifiedBy: 'meetings'` requirement.

   THE ONE RULE THAT IS NOT COSMETIC: the check-in factor gets NO success
   colouring, NO tick and NO icon — it is deliberately styled exactly like the
   other evidence lines. v1 has no location anchor, so a geotag says a phone was
   somewhere; painting it green would make the interface assert something the
   data cannot support, and staff act on that assertion when they decide credit.
   The dom suite asserts the absence of any success token in that factor's
   markup, so this is a pinned contract, not a preference.

   Every colour comes from an existing token, so both themes follow the same
   swap the rest of the sheet does. Rows WRAP rather than scroll: each item is a
   short standalone phrase, so at 390px they stack into a readable block instead
   of pushing the page sideways. */
.meeting-dialog {
  max-width: 720px;
}

.meeting-evidence-notice {
  font-size: 12px;
  color: var(--text-muted);
  background: var(--bg-input);
  border-radius: var(--border-radius-md);
  padding: 8px 12px;
  margin: 0 0 16px;
}

.meeting-hint {
  font-size: 12px;
  color: var(--text-muted);
  margin: 0;
}

.meeting-form h4 {
  font-size: 15px;
  font-weight: 700;
  margin: 4px 0 0;
}

/* The shared-backend notice: the fallback form cannot store a record here until
   M8-8 gives the delta protocol a `meetingAttendance` vocabulary. Warning
   coloured, because it explains a control that is greyed out. */
.meeting-form-notice {
  font-size: 12px;
  color: var(--color-warning);
  background: rgba(var(--marginal-rgb), 0.1);
  border: 1px solid var(--color-warning);
  border-radius: var(--border-radius-md);
  padding: 8px 12px;
  margin: 0;
}

.meeting-form input:disabled,
.meeting-form select:disabled,
.meeting-form button:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* A field-level complaint, under the box it is about. */
.meeting-field-error {
  font-size: 12px;
  font-weight: 600;
  color: var(--color-danger);
}

.meeting-facilitators {
  margin-bottom: 18px;
}

.meeting-facilitators h4 {
  font-size: 13px;
  font-weight: 700;
  margin: 0 0 4px;
}

.meeting-facilitator-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.meeting-facilitator {
  font-size: 12px;
  color: var(--text-secondary);
  background: var(--bg-input);
  border: 1px solid var(--border-color-light);
  border-radius: 999px;
  padding: 3px 10px;
  overflow-wrap: anywhere;
}

/* Seen exactly once. Outlined, not filled: this is a prompt to ask a question,
   not a verdict on the client. */
.meeting-facilitator[data-facilitator-once="true"] {
  color: var(--color-warning);
  border-color: var(--color-warning);
}

.meeting-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 20px;
}

.meeting-empty {
  font-size: 13px;
  color: var(--text-muted);
  margin: 0;
}

.meeting-row {
  background: var(--bg-input);
  border: 1px solid var(--border-color-light);
  border-radius: var(--border-radius-md);
  padding: 10px 12px;
}

.meeting-row-main {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.meeting-when {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-secondary);
}

.meeting-name {
  font-size: 13px;
  font-weight: 700;
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

.meeting-kind {
  font-size: 11px;
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.meeting-status-badge,
.meeting-credit-badge {
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border-radius: 999px;
  padding: 2px 8px;
  border: 1px solid var(--border-color);
  color: var(--text-secondary);
}

/* 'Verified' means all THREE factors are present — it is a statement about
   completeness of evidence, never about the truth of any one factor, and it is
   never applied to a factor on its own. */
.meeting-status-badge[data-meeting-status-badge="verified"] {
  color: var(--color-success);
  border-color: var(--color-success);
}

.meeting-status-badge[data-meeting-status-badge="partial"] {
  color: var(--color-warning);
  border-color: var(--color-warning);
}

.meeting-credit-badge[data-meeting-credit="granted"] {
  color: var(--color-success);
  border-color: var(--color-success);
}

.meeting-credit-badge[data-meeting-credit="denied"] {
  color: var(--color-danger);
  border-color: var(--color-danger);
}

.meeting-credit-badge[data-meeting-unsaved="true"] {
  color: var(--color-danger);
  border-color: var(--color-danger);
}

/* The three factor lines. Identical treatment for all three — see the header. */
.meeting-factors {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 6px;
  font-size: 12px;
  color: var(--text-secondary);
  overflow-wrap: anywhere;
}

.meeting-factor[data-factor-present="false"] {
  color: var(--text-muted);
  font-style: italic;
}

.meeting-row-detail {
  margin-top: 6px;
  font-size: 12px;
  color: var(--text-secondary);
  overflow-wrap: anywhere;
}

/* ── The read-only derived counter (ClientDetailsModal) ────────────────────
   No input and no +/- buttons exist in this row at all. `completed` for an
   opted-in requirement is `count(granted occurrences in this ISO week)`, written
   by the credit transaction (M8-3) and its local mirror (M8-4) — never by this
   modal. A disabled input would suggest the number is merely locked; there is
   nothing to unlock. */
.meeting-derived-group {
  flex-wrap: wrap;
  gap: 8px 12px;
}

.meeting-derived-value {
  font-size: 14px;
  font-weight: 700;
  color: var(--text-primary);
}

.meeting-pending-badge {
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border-radius: 999px;
  padding: 2px 8px;
  color: var(--color-warning);
  border: 1px solid var(--color-warning);
}

/* ── M8-4: the review queue ────────────────────────────────────────────────
   The evidence panel and the decision are two blocks of ONE row, stacked so a
   reviewer reads what happened and then what to do about it in a single glance.
   The panel is given a quiet left rule rather than a colour: the whole point of
   the graded model is that the system does not grade FOR the reviewer, and a
   green-tinted "verified" panel beside a Grant button is a recommendation drawn
   in CSS. Nothing here colours by status except the badges, which name it. */
.meeting-evidence {
  margin-top: 6px;
  padding-left: 10px;
  border-left: 2px solid var(--border-color-light);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

.meeting-source-marker,
.meeting-facilitator-signal {
  font-size: 12px;
  color: var(--text-secondary);
  overflow-wrap: anywhere;
}

/* A name seen exactly once. Marked, not accused — it is the shape a made-up
   signature has AND the shape a one-off visit to another group has. */
.meeting-facilitator-signal[data-facilitator-once="true"] {
  color: var(--color-warning);
  font-weight: 700;
}

.meeting-decision {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--border-color-light);
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.meeting-decision-detail {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 12px;
  color: var(--text-secondary);
  overflow-wrap: anywhere;
}

.meeting-decision-who {
  font-weight: 700;
  color: var(--text-primary);
}

.meeting-reason-label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
}

.meeting-reason-input {
  width: 100%;
  max-width: 100%;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border-color);
  background-color: var(--bg-input);
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
}

.meeting-reason-input:focus {
  outline: none;
  border-color: var(--color-brand);
}

.meeting-decision-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Deny is NOT painted as the destructive option and Grant is NOT painted as the
   happy path. Either can be the right answer, both are equally reversible, and a
   button pair that reads approve/reject would be the recommendation this surface
   is not allowed to make. */
.meeting-grant-btn,
.meeting-deny-btn {
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 6px 12px;
}

/* A row someone else adjudicated while our decision was in flight. Dimmed and
   dashed rather than coloured like an error: nothing went wrong and nothing was
   lost — the record simply moved, and this row is showing a version of it that
   is out of date until a sync brings the real decision in. */
.meeting-row[data-meeting-stale="true"] .meeting-decision {
  opacity: 0.72;
}

.meeting-stale-notice {
  margin: 0;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px dashed var(--color-warning);
  font-size: 12px;
  font-weight: 700;
  color: var(--color-warning);
}

.meeting-queue-count {
  margin: 8px 0 0;
  font-size: 12px;
  font-weight: 700;
  color: var(--color-warning);
}

/* ── M8-4: the roster's entry points ───────────────────────────────────────
   A badge on the client's own Meetings button, and one facility-wide summary
   above the list. Both are ABSENT at zero — see the render comments. */
.roster-pending-badge {
  margin-left: 6px;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border-radius: 999px;
  padding: 1px 7px;
  color: var(--color-warning);
  border: 1px solid var(--color-warning);
}

.roster-pending-bar {
  padding: 8px 16px 0;
}

.roster-pending-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  justify-content: center;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--color-warning);
  background: transparent;
  color: var(--color-warning);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.3px;
  cursor: pointer;
}

.roster-pending-btn:hover {
  background-color: var(--bg-input);
}

@media (max-width: 900px) {
  /* At 390px the occurrence header is four short phrases; let them stack rather
     than force the dialog wide. */
  .meeting-row-main {
    gap: 6px;
  }

  /* The fallback form's date/time controls are the usual iOS-narrow suspects —
     keep them inside the card. */
  .meeting-form input,
  .meeting-form select {
    max-width: 100%;
  }

  /* The decision buttons go full width at phone size: they are the two things a
     reviewer taps, and a 44px target matters more here than a tidy row. */
  .meeting-decision-actions {
    flex-direction: column;
  }

  .meeting-grant-btn,
  .meeting-deny-btn {
    width: 100%;
    justify-content: center;
    min-height: 44px;
  }
}

/* ─────────────────────────────────────────────────────────────────────────────
   Residents & portal access (admin dashboard)

   Mirrors the .admin-eval-* block above rather than inventing a second card
   language — the two lists sit on the same screen and a resident card that
   looked different would read as a different KIND of thing, which it is not.
   No backdrop-filter here, so no -webkit- sibling is required (the guard in
   test/meta/css-prefix.test.js only fires on that property).
   ───────────────────────────────────────────────────────────────────────────── */

.admin-resident-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 32px;
}

.admin-resident-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px 20px;
}

.admin-resident-name {
  font-weight: 700;
  color: var(--text-primary);
}

.admin-resident-email {
  color: var(--text-secondary);
  word-break: break-all;
}

/* More than one account bound to the same resident — legitimate (disabling is a
   stamp, not a delete), so it is a note rather than a warning colour. */
.admin-resident-extra {
  color: var(--text-secondary);
  font-weight: 600;
}

.admin-resident-orphan-note,
.admin-resident-capped-note {
  font-size: 13px;
  margin: 0 0 12px;
}

/* The typeable invite code, sitting under the link it was parsed out of. */
.admin-code-hint {
  font-size: 13px;
  margin: 0 0 8px;
}

.admin-link-code .admin-link-value {
  letter-spacing: 0.04em;
}
