/* The resident portal's stylesheet — mobile-first, and small on purpose.
 *
 * ────────────────────────────────────────────────────────────────────────────
 * WHY THIS IS NOT styles.css
 * ────────────────────────────────────────────────────────────────────────────
 * The M8 plan records the decision under "Rejected alternatives": styles.css is
 * a ~4000-line DESKTOP-FIRST sheet with a single 900px breakpoint, written for
 * a sidebar application. A phone-first page would spend its length overriding
 * it rather than inheriting it, and every future staff layout change would land
 * on a resident's phone. So the tokens this page needs are COPIED below rather
 * than imported, and nothing else comes across.
 *
 * The copy is small and deliberate: the staff palette (Phase 11 Q5: brand, page,
 * card, borders, text, the status text tints and the chip tints), the radii, and
 * one font stack. Colour VALUES are the same as styles.css's (body.light-theme
 * and body.dark-theme) so the two surfaces look like one product; only the names
 * keep the --client- prefix. If they ever have to diverge, they can, which is
 * the other half of what a separate sheet buys.
 *
 * ────────────────────────────────────────────────────────────────────────────
 * THEME
 * ────────────────────────────────────────────────────────────────────────────
 * The DEVICE decides, and the resident may overrule it FOR THIS TAB ONLY.
 *
 * The staff app toggles `body.dark-theme` / `body.light-theme` and remembers
 * the choice in localStorage. THIS PAGE HAS NO DURABLE STORAGE AT ALL — the
 * plan's no-PHI-at-rest rule bans the API outright and a guard fails the build
 * if it appears — so there is nothing here to remember a preference in, and the
 * ban is not worth weakening for a colour. A resident's phone may be shared,
 * lost or handed to staff; what may be written to it is the one question this
 * surface never reopens.
 *
 * So the default is `prefers-color-scheme`, which needs no storage and is what
 * a phone user has already set, and the toggle writes
 * `data-client-theme="dark|light"` onto <html> in module memory. It survives
 * every screen change, because the attribute is on the document rather than on
 * anything the portal re-renders, and it does NOT survive a reload — which is
 * the honest consequence of storing nothing, not an oversight.
 *
 * That makes the palette three-state, and the selectors below are load-bearing:
 *
 *   :root                                        light — the floor
 *   @media dark + :root:not([data-client-theme="light"])   device dark, unless overruled
 *   :root[data-client-theme="dark"]              overruled INTO dark
 *
 * The dark tokens are therefore written twice, deliberately. A single
 * `:root[data-client-theme="dark"], @media …` cannot be spelled in CSS, and the
 * alternative — dropping the media query and defaulting to light — would break
 * the promise that a device already set to dark opens dark.
 *
 * ────────────────────────────────────────────────────────────────────────────
 * TOUCH
 * ────────────────────────────────────────────────────────────────────────────
 * Every interactive target is at least 44px on its short axis — the floor the
 * M3 mobile-hardening rows settled on. On this surface it is not a nicety:
 * these forms are filled in on a phone, one-handed, often in a car park before
 * a meeting.
 */

:root {
  color-scheme: light;
  --client-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Shared traffic-light base hues, byte-identical to styles.css's, used for
     dots, borders and chip tints. The rest of the palette below is the staff
     palette too (Q5), not only these three. */
  --client-success: rgb(16, 185, 129);
  --client-warning: rgb(245, 158, 11);
  --client-danger: rgb(244, 63, 94);

  /* ── The danger colour, SPLIT IN TWO ──────────────────────────────────────
     --client-danger is a DOT AND A BORDER colour: a verification pip, the rule
     round a notice. It is the staff sheet's rose and it stays that, because the
     two surfaces must report the same verification state in the same colour.

     --client-danger-text is what a REFUSAL IS WRITTEN IN, and it is a different
     value for a measured reason. rgb(244, 63, 94) on this page's light
     background contrasts at 3.51:1 (and 3.67:1 on a white card) — below WCAG
     2.1 AA's 4.5:1 for body text, which is the audit's portal finding. Every
     field error and every failure sentence on the sign-in screen was set in it,
     which is to say the least legible text on the page was the text somebody was
     stuck on, read on a phone, outdoors.

     rgb(190, 18, 60) is the same hue two steps darker (the staff light
     --text-danger, #be123c): 5.55:1 on --client-bg and 6.29:1 on a white card. Pinned by a test that computes the ratio rather than
     comparing the string, because the number is the requirement and the hex is
     an implementation of it.

     1.4.1 is why the split is not simply "make the dot darker too": colour is
     never the only channel here (the pips carry text, the notices carry a
     sentence), and a border has no AA text requirement to meet. */
  --client-danger-text: rgb(190, 18, 60);

  --client-radius-sm: 4px;
  --client-radius-md: 6px;
  --client-radius-lg: 8px;
  --client-radius-full: 9999px;

  /* The minimum touch target, named so a later row cannot quietly shrink one. */
  --client-touch: 44px;

  /* Light, and the default: a phone with no declared preference is far more
     often outdoors than a nursing station is. */
  --client-bg: #eef1f5;
  --client-surface: #ffffff;
  --client-border: #cfd6df;
  --client-border-light: #e3e8ee;
  --client-text: #1a222c;
  --client-text-muted: #5b6675;
  --client-text-faint: #646e7e;
  --client-input-bg: #ffffff;
  --client-chip-neutral-bg: #f6f8fa;
  --client-brand: #0e7490;
  --client-brand-text: #ffffff;
  --client-brand-hover: #0b5f75;
  --client-focus: rgba(14, 116, 144, 0.35);

  /* Status TEXT tints (staff --text-success / --text-warning). Like
     --client-danger-text, they are what a sentence is written in; the base hues
     above stay the dot and border colours. Light warning text is 4.43:1 on
     --client-bg and 5.02:1 on --client-surface, which is why every notice sits
     on the surface (.client-notice below). */
  --client-success-text: #047857;
  --client-warning-text: #b45309;

  /* Chip tints: the tone's base hue at 12%, as the staff .chip-* rules paint
     them. Only the brand tint differs by theme. */
  --client-success-tint: rgba(16, 185, 129, 0.12);
  --client-warning-tint: rgba(245, 158, 11, 0.12);
  --client-danger-tint: rgba(244, 63, 94, 0.12);
  --client-brand-tint: rgba(14, 116, 144, 0.12);
}

/* Device dark — unless the resident has overruled it to light for this tab. */
@media (prefers-color-scheme: dark) {
  :root:not([data-client-theme="light"]) {
    color-scheme: dark;
    --client-bg: #0f1216;
    --client-surface: #181d24;
    --client-border: #2a313b;
    --client-border-light: #262d37;
    --client-text: #e8ecf1;
    --client-text-muted: #98a2b3;
    --client-text-faint: #838e9e;
    --client-input-bg: #14181e;
    --client-chip-neutral-bg: #1e242c;
    /* The staff dark --text-danger: 6.98:1 on the background and 6.29:1 on a
       card. The darkened light-mode value would not pass here (it is darker than
       the surface it would sit on). The token exists so each theme can carry the
       value that passes in it. */
    --client-danger-text: #fb7185;
    --client-success-text: #34d399;
    --client-warning-text: #f5b544;
    --client-brand: #3aa6cc;
    --client-brand-text: #0f1216;
    --client-brand-hover: #2f93b6;
    --client-focus: rgba(58, 166, 204, 0.35);
    --client-brand-tint: rgba(58, 166, 204, 0.12);
  }
}

/* Overruled INTO dark, on a device that is light or has no preference. Same
   values; the duplication is explained in the THEME note at the top. */
:root[data-client-theme="dark"] {
  color-scheme: dark;
  --client-bg: #0f1216;
  --client-surface: #181d24;
  --client-border: #2a313b;
  --client-border-light: #262d37;
  --client-text: #e8ecf1;
  --client-text-muted: #98a2b3;
  --client-text-faint: #838e9e;
  --client-input-bg: #14181e;
  --client-chip-neutral-bg: #1e242c;
  /* Same value, same reason as the media-query block above. */
  --client-danger-text: #fb7185;
  --client-success-text: #34d399;
  --client-warning-text: #f5b544;
  --client-brand: #3aa6cc;
  --client-brand-text: #0f1216;
  --client-brand-hover: #2f93b6;
  --client-focus: rgba(58, 166, 204, 0.35);
  --client-brand-tint: rgba(58, 166, 204, 0.12);
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body.client-body {
  font-family: var(--client-font);
  background-color: var(--client-bg);
  color: var(--client-text);
  min-height: 100vh;
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
  /* 16px is the floor at which iOS Safari stops zooming the page when a field
     takes focus. Anything smaller on an input is a layout jump on every tap. */
  font-size: 16px;
  line-height: 1.5;
}

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

.client-main {
  /* One column, comfortably readable, centred once the viewport outgrows it.
     There is no second breakpoint on purpose: this page is a single column at
     390px and a single column at 1400px, and a desktop layout for it would be
     a surface nobody uses. */
  width: 100%;
  max-width: 34rem;
  margin: 0 auto;
  padding: 16px 16px calc(24px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* ── The regions ──────────────────────────────────────────────────────────── */

.client-notice-root[hidden],
.client-app-root[hidden] {
  display: none;
}

.client-card {
  background: var(--client-surface);
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-lg);
  padding: 20px 16px;
}

.client-card-header {
  margin-bottom: 16px;
}

.client-title {
  font-size: 1.375rem;
  font-weight: 650;
  letter-spacing: -0.01em;
}

.client-subtitle,
.client-body-text {
  color: var(--client-text-muted);
  font-size: 0.9375rem;
}

.client-subtitle {
  margin-top: 4px;
}

.client-body-text {
  margin-bottom: 16px;
}

/* ── Notices ──────────────────────────────────────────────────────────────── */

/* Every notice sits on the surface: light warning text is 4.43:1 on the page
   background and 5.02:1 on the surface, so status text is only measured there. */
.client-notice {
  background: var(--client-surface);
  font-size: 0.9375rem;
  border-radius: var(--client-radius-md);
  padding: 10px 12px;
  margin-bottom: 14px;
}

/* A notice that follows a line of text gets the same 16px the other blocks do. */
p + .client-notice {
  margin-top: 16px;
}

.client-notice-error {
  color: var(--client-danger-text);
  border: 1px solid var(--client-danger);
}

.client-notice-warning {
  color: var(--client-warning-text);
  border: 1px solid var(--client-warning);
  margin-bottom: 0;
}

/* ── Chips ────────────────────────────────────────────────────────────────── */
/* The staff chip, in the portal's tokens: a word on a tint, never colour alone.
   Each tone's text token is measured on its tint over the surface. */

.client-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: var(--client-radius-sm);
  font-size: 11px;
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
}

.client-chip[hidden] {
  display: none;
}

.client-chip-neutral {
  color: var(--client-text-muted);
  background: var(--client-chip-neutral-bg);
}

.client-chip-brand {
  color: var(--client-brand);
  background: var(--client-brand-tint);
}

.client-chip-success {
  color: var(--client-success-text);
  background: var(--client-success-tint);
}

.client-chip-warning {
  color: var(--client-warning-text);
  background: var(--client-warning-tint);
}

.client-chip-danger {
  color: var(--client-danger-text);
  background: var(--client-danger-tint);
}

.client-chip-muted {
  color: var(--client-text-faint);
  background: transparent;
}

/* For a long word that has to wrap inside a narrow cell. */
.client-chip-wrap {
  white-space: normal;
  overflow-wrap: anywhere;
  min-width: 0;
  max-width: 100%;
}

/* ── Forms ────────────────────────────────────────────────────────────────── */

.client-label {
  display: block;
  color: var(--client-text-muted);
  font-size: 0.875rem;
  margin: 14px 0 6px;
}

.client-card input[type="email"],
.client-card input[type="text"],
.client-card input[type="password"] {
  width: 100%;
  min-height: var(--client-touch);
  padding: 10px 12px;
  background: var(--client-input-bg);
  color: var(--client-text);
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-md);
}

.client-card input:focus-visible,
.client-btn:focus-visible,
.client-link-btn:focus-visible,
.client-password-toggle:focus-visible {
  outline: 2px solid var(--client-brand);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--client-focus);
}

/* The password field and its eye. The input keeps the full width and the
   button sits inside its right-hand padding, so the touch target overlaps no
   text the user is trying to read. */
.client-password-field {
  position: relative;
}

.client-password-field input {
  padding-right: calc(var(--client-touch) + 4px);
}

.client-password-toggle {
  position: absolute;
  top: 0;
  right: 0;
  width: var(--client-touch);
  height: 100%;
  min-height: var(--client-touch);
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  color: var(--client-text-muted);
  cursor: pointer;
}

/* Which eye shows is driven entirely by aria-pressed, so the button's accessible
   state and its icon cannot disagree — there is only one source for both. */
.client-password-toggle .client-eye-off,
.client-password-toggle[aria-pressed="true"] .client-eye {
  display: none;
}

.client-password-toggle[aria-pressed="true"] .client-eye-off {
  display: block;
}

/* ── Buttons ──────────────────────────────────────────────────────────────── */

.client-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: var(--client-touch);
  margin-top: 20px;
  padding: 12px 16px;
  border: 1px solid transparent;
  border-radius: var(--client-radius-md);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.client-btn-primary {
  background: var(--client-brand);
  color: var(--client-brand-text);
}

@media (hover: hover) {
  .client-btn-primary:hover {
    background: var(--client-brand-hover);
  }
}

.client-btn-danger {
  background: var(--client-danger-text);
  color: #fff;
}

@media (hover: hover) {
  .client-btn-danger:hover {
    filter: brightness(0.92);
  }
}

.client-btn-secondary {
  width: auto;
  margin-top: 0;
  background: transparent;
  color: var(--client-text);
  border-color: var(--client-border);
}

.client-btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* An inline "switch to the other form" control. It is a BUTTON rather than a
   link because it navigates nowhere — but it has to look like the sentence it
   sits in, and it still has to be tappable, hence the padding rather than a
   height (a 44px inline-block would break the line box). */
.client-link-btn {
  background: none;
  border: none;
  padding: 12px 2px;
  color: var(--client-brand);
  font-weight: 600;
  text-decoration: underline;
  cursor: pointer;
}

.client-aside {
  margin-top: 16px;
  color: var(--client-text-muted);
  font-size: 0.9375rem;
}

/* ── The signed-in portal ─────────────────────────────────────────────────── */

.client-app-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 2px 16px;
  border-bottom: 1px solid var(--client-border);
}

/* v1.46: a 12-hour range ("9:00 AM–5:00 PM") or a day ("Tue, Oct 6") wraps as one piece. */
.client-work-time,
.client-nowrap {
  white-space: nowrap;
}

.client-eyebrow {
  color: var(--client-text-muted);
  font-size: 0.8125rem;
}

.client-name {
  font-size: 1.125rem;
  font-weight: 650;
  /* A name is free text and a phone is 390px wide. It wraps rather than
     pushing Sign out off the screen. */
  overflow-wrap: anywhere;
}

.client-portal-body {
  padding-top: 16px;
}

/* Respect a device that has asked for less motion. Nothing here animates today;
   the rule is here so the next row that adds a transition inherits the answer
   rather than deciding it again. */
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ── M8-7: the resident's calendar of their own attendance ─────────────────── */
/*
 * 390px FIRST, and the constraint that shapes everything below: seven columns
 * on a 390px screen is ~48px per day. That is not a width a meeting name fits
 * in, so the grid is a DENSITY view — the name and time are in the cell, clipped
 * where they must be, and the full record for a day is one tap away in the
 * detail panel. Clipping is honest here in a way that hiding is not: the text is
 * present, addressable and read by a screen reader, and the panel below shows it
 * whole.
 *
 * Every column is `minmax(0, 1fr)` rather than `1fr`. A bare `1fr` is
 * `minmax(auto, 1fr)`, and `auto` refuses to shrink below its content — one long
 * meeting name would push the grid wider than the viewport and give the whole
 * page a horizontal scrollbar. This is the same fix the M3 mobile rows applied
 * to the staff grids, for the same reason.
 *
 * NO COLOUR CARRIES MEANING ON ITS OWN. The verification and credit words are
 * words. The traffic-light tokens tint a border, never replace the text, and
 * nothing on the check-in evidence line is coloured at all — the plan is
 * explicit that a geotag is evidence, and a green anything beside it would be
 * the claim this row exists to avoid making.
 */

.client-calendar {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* ── The week strip ───────────────────────────────────────────────────────── */

.client-week {
  background-color: var(--client-surface);
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-lg);
  padding: 14px;
}

.client-week-title {
  font-size: 0.8125rem;
  font-weight: 650;
  color: var(--client-text-muted);
  margin-bottom: 10px;
}

.client-week-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.client-week-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* A requirement name is free text from the facility's own configuration. */
  overflow-wrap: anywhere;
}

.client-week-name {
  font-weight: 600;
}

.client-week-count {
  font-size: 1.0625rem;
  font-weight: 650;
}

.client-week-pending {
  font-size: 0.875rem;
  color: var(--client-text-muted);
}

.client-week-foot {
  margin-top: 10px;
  font-size: 0.8125rem;
  color: var(--client-text-muted);
}

/* ── Month navigation ─────────────────────────────────────────────────────── */

.client-month-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.client-month-step {
  /* The 44px floor, on both axes, because these two sit at the top of a grid of
     much smaller targets and are the ones a thumb reaches for most. */
  min-width: var(--client-touch);
  min-height: var(--client-touch);
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-md);
  background-color: var(--client-surface);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.client-month-step:focus-visible,
.client-cal-cell:focus-visible {
  outline: 3px solid var(--client-focus);
  outline-offset: 2px;
}

.client-month-label {
  font-size: 1.0625rem;
  font-weight: 650;
  text-align: center;
  flex: 1 1 auto;
}

/* ── The grid ─────────────────────────────────────────────────────────────── */

.client-cal-head,
.client-cal-grid {
  display: grid;
  /* See the header: minmax(0, 1fr), never a bare 1fr. */
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
}

.client-cal-head {
  margin-bottom: 2px;
}

.client-cal-dow {
  text-align: center;
  font-size: 0.6875rem;
  font-weight: 650;
  color: var(--client-text-muted);
  /* Three letters into ~48px: the initial is what actually renders on a phone. */
  overflow: hidden;
}

.client-cal-cell {
  /* A `button` and a `div` share this rule, so the button's UA styling is reset
     rather than worked around. */
  appearance: none;
  -webkit-appearance: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-items: stretch;
  text-align: left;
  min-width: 0;
  min-height: var(--client-touch);
  padding: 3px;
  border: 1px solid transparent;
  border-radius: var(--client-radius-md);
  background-color: var(--client-surface);
  color: inherit;
  font: inherit;
}

/* A day that carries meetings is the one that is tappable, and it says so. */
button.client-cal-cell {
  cursor: pointer;
  border-color: var(--client-border);
}

.client-cal-cell[data-cal-in-month="false"] {
  /* Still a real day carrying real meetings — dimmed, never hidden. */
  opacity: 0.55;
}

.client-cal-cell[data-cal-today="true"] .client-cal-daynum {
  background-color: var(--client-brand);
  color: var(--client-brand-text);
  border-radius: var(--client-radius-full);
}

.client-cal-daynum {
  font-size: 0.75rem;
  font-weight: 650;
  align-self: flex-start;
  min-width: 1.25rem;
  padding: 1px 4px;
  text-align: center;
}

.client-cal-entry {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  padding: 2px;
  border-radius: var(--client-radius-sm);
  /* No stripe and no fill: the status and credit are chips with words, and they
     sit on the cell surface their contrast is measured against. */
  border-left: none;
  background-color: transparent;
  font-size: 0.6875rem;
  line-height: 1.25;
}

.client-cal-entry-name,
.client-cal-entry-time,
.client-cal-badge {
  /* Clipped, not hidden. The whole line is in the detail panel one tap below. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: none;
}

.client-cal-entry-name { font-weight: 650; }
.client-cal-entry-time { color: var(--client-text-muted); }

/* A chip in a ~48px phone cell: tighter padding, as wide as its word and never
   wider than the cell. A block so a word too long for the cell ends in an
   ellipsis rather than past the edge. */
.client-cal-entry .client-chip {
  display: block;
  align-self: flex-start;
  max-width: 100%;
  padding: 1px 4px;
}

.client-cal-entry .client-chip[hidden] {
  display: none;
}

/* ── The day detail panel ─────────────────────────────────────────────────── */

.client-day-detail {
  background-color: var(--client-surface);
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-lg);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.client-day-detail[hidden] { display: none; }

.client-day-detail-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.client-day-detail-title {
  font-size: 1rem;
  font-weight: 650;
  overflow-wrap: anywhere;
}

.client-detail-entry {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-top: 12px;
  border-top: 1px solid var(--client-border-light);
}

.client-detail-name {
  font-size: 1rem;
  font-weight: 650;
  /* A meeting name is free text the resident typed on a phone. */
  overflow-wrap: anywhere;
}

.client-detail-sub,
.client-detail-line,
.client-detail-reason {
  font-size: 0.875rem;
  overflow-wrap: anywhere;
}

.client-detail-sub { color: var(--client-text-muted); }

.client-detail-reason {
  /* The reason staff gave. Set apart because it is the one line on this screen
     written BY a person TO this resident. */
  padding: 8px 10px;
  border-radius: var(--client-radius-md);
  background-color: var(--client-bg);
  border: 1px solid var(--client-border);
}

/*
 * The three factor lines.
 *
 * DELIBERATELY UNDECORATED. No icon, no tick, no colour — on this surface
 * exactly as on the staff one. A geotag with no anchor to compare it against is
 * evidence that somebody pressed a button somewhere, and a pass-the-phone
 * signature is the digital equivalent of a paper court card. Both are shown, and
 * neither is dressed as a verification.
 */
.client-evidence-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 2px 0;
}

.client-evidence {
  font-size: 0.8125rem;
  color: var(--client-text-muted);
  /* Coordinates and a facilitator's name, on a 390px screen. */
  overflow-wrap: anywhere;
}

.client-evidence[data-evidence-present="false"] {
  font-style: italic;
}

/* ── Above a phone ────────────────────────────────────────────────────────── */
/*
 * ONE breakpoint, and it buys room rather than a different layout: the same
 * single column, with cells tall enough to show a meeting name unclipped. There
 * is no desktop design for this page and there should not be one.
 */
@media (min-width: 560px) {
  .client-cal-cell { min-height: 76px; padding: 4px; }
  .client-cal-entry { font-size: 0.6875rem; }
  .client-cal-entry-name { white-space: normal; overflow-wrap: anywhere; }
  /* The column stays 34rem, so a cell is about 70px here, not the 48px the
     ellipsis was written for. The status word wraps to a second line instead of
     being cut to "Pending…"; the time still fits on one. */
  .client-cal-entry .client-chip { white-space: normal; overflow-wrap: anywhere; }
  .client-cal-daynum { font-size: 0.8125rem; }
}

/* ── M8-6: logging a meeting, and its three factors ─────────────────────────── */
/*
 * 390px FIRST. This form is filled in on a phone, one-handed, standing outside a
 * church hall — the M3 mobile rows' floor of 44px on every touch target is not a
 * nicety here, and the signature box is sized for a finger rather than a stylus.
 *
 * NOTHING ON THIS SURFACE IS GREEN. The check-in evidence line and the signature
 * state are undecorated text, on this surface exactly as on M8-1's staff one and
 * M8-7's calendar: a geotag with no anchor to compare it against is evidence
 * that somebody pressed a button somewhere, and a tick beside it would be a
 * claim about a check nobody ran. The traffic-light tokens are deliberately
 * unused below.
 *
 * NO FIXED WIDTHS. Every box is `width: 100%` inside the page's own max-width,
 * so a 320px phone gets the same layout narrower rather than a scrollbar.
 */

.client-log-meeting {
  display: block;
  margin-bottom: 24px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--client-border);
}

.client-log-title {
  font-size: 1.125rem;
  font-weight: 650;
}

.client-log-form {
  display: block;
}

/* The one input rule this page's forms outside a card need. Same geometry as
   the card's, so a resident does not meet two different text boxes. */
.client-input {
  width: 100%;
  min-height: var(--client-touch);
  padding: 10px 12px;
  background: var(--client-input-bg);
  color: var(--client-text);
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-md);
  font: inherit;
}

.client-input:focus-visible {
  outline: 2px solid var(--client-brand);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--client-focus);
}

.client-optional {
  color: var(--client-text-muted);
  font-weight: 400;
}

/* A refusal, attached to the field it is about. Empty by default and it takes no
   space when empty, so the form does not jump as errors clear. */
.client-field-error {
  color: var(--client-danger-text);
  font-size: 0.8125rem;
  margin-top: 6px;
  overflow-wrap: anywhere;
}

.client-field-error:empty {
  display: none;
}

.client-step-title {
  font-size: 1rem;
  font-weight: 650;
  margin-bottom: 6px;
}

.client-step-lead {
  font-size: 0.9375rem;
  margin-bottom: 6px;
}

.client-step-note {
  color: var(--client-text-muted);
  font-size: 0.8125rem;
  margin-top: 8px;
  overflow-wrap: anywhere;
}

.client-step-note:empty {
  display: none;
}

.client-checkin,
.client-signature {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--client-border);
}

/* EVIDENCE, and styled as evidence: the same muted text as every other factor
   line in this app, with no colour and no icon whichever state it is in. */
.client-checkin-evidence {
  margin-top: 10px;
  font-size: 0.875rem;
  color: var(--client-text);
  overflow-wrap: anywhere;
}

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

/* The pad. `touch-action: none` is load-bearing rather than cosmetic: without it
   a finger dragged across this box scrolls the page and the browser cancels the
   pointer stream mid-signature, so the mark simply stops. 200px is the floor a
   signature needs to be legible at 390px wide. */
.client-sig-pad {
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  width: 100%;
  min-height: 200px;
  margin-top: 12px;
  background: var(--client-input-bg);
  border: 1px dashed var(--client-border);
  border-radius: var(--client-radius-md);
  overflow: hidden;
}

.client-sig-ink {
  display: block;
  width: 100%;
  height: 200px;
  color: var(--client-text);
}

.client-sig-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 8px;
}

.client-sig-foot .client-step-note {
  margin-top: 0;
}

.client-notice-ok {
  color: var(--client-text);
  border: 1px solid var(--client-border);
  margin-top: 14px;
  margin-bottom: 0;
}

.client-notice-ok:empty,
.client-log-form .client-notice-error:empty {
  display: none;
}

/* Above a phone: a taller box to sign in, and nothing else different. There is
   no desktop design for this page and there should not be one. */
@media (min-width: 560px) {
  .client-sig-pad { min-height: 240px; }
  .client-sig-ink { height: 240px; }
}

/* ──────────────────────────────────────────────────────────────────────────
   THE PAGE CHROME — the theme toggle at the top, the version at the foot
   ──────────────────────────────────────────────────────────────────────────
   Both are static markup in client.html rather than anything the portal
   renders, and that placement is the point: portal.js EMPTIES
   #client-auth-root and #client-app-root on every screen change and on
   sign-out, so a control living inside either would be destroyed by a state
   transition. Out here they are present on the sign-in screen, the invite
   redemption, the refusal and the portal alike — wired once at boot.

   Both are quiet on purpose. Neither is what a resident opened the page to do:
   the theme is a preference they set once, and the version is small print for
   the person they ring when something looks wrong.

   NOTE ON THE TWO ICONS: both ship in the button and CSS hides one, keyed off
   a `data-theme-icon` attribute that footer.js sets on the BUTTON.

   The obvious approach — `svg.hidden = true` — silently does nothing, and this
   shipped that way for one commit's worth of drafting. `hidden` is an
   HTMLElement property; an `<svg>` is an SVGElement and does not implement it,
   so the assignment creates an expando that reflects to no attribute and
   matches no `[hidden]` rule. Both icons rendered side by side, and reading the
   property back returned the expando, so the test asserting on it passed.
   Measured in Chrome: hasAttribute('hidden') false, computed display `block`.

   Doing it in CSS off an attribute is deterministic, and the pair below is
   mutually exclusive by construction rather than by two assignments agreeing. */

[data-client-theme-toggle][data-theme-icon="sun"] .client-icon-moon,
[data-client-theme-toggle][data-theme-icon="moon"] .client-icon-sun {
  display: none;
}
.client-topbar {
  display: flex;
  /* Alone on its row and pushed to the trailing edge, where a phone's thumb
     reaches and where the staff shell puts the same control. */
  justify-content: flex-end;
  align-items: center;
  min-height: var(--client-touch);
}

.client-theme-toggle {
  /* Square, and at least the shared touch floor on BOTH axes — an icon-only
     control has no text to grow the target for it. */
  min-height: var(--client-touch);
  min-width: var(--client-touch);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-md);
  background: var(--client-surface);
  color: var(--client-text-muted);
  cursor: pointer;
}

@media (hover: hover) {
  .client-theme-toggle:hover {
    color: var(--client-text);
    border-color: var(--client-brand);
  }
}

.client-theme-toggle:focus-visible {
  outline: 2px solid var(--client-brand);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--client-focus);
}

/* ── Sign out, in the header beside the name ─────────────────────────────── */
.client-app-header .client-btn {
  flex: none;
}

/* ── The version, directly under it ───────────────────────────────────────── */
.client-footer {
  /* Sits under whatever is on screen — under the tab body on the portal,
     under the card on the sign-in screen. NOT pinned to the bottom of the
     viewport: .client-main is content-sized (the body is not a flex container,
     so there is no spare height for a `margin-top: auto` to absorb), and a
     footer floating below a short sign-in card would look detached anyway. */
  padding-top: 4px;
  text-align: center;
}

/* The same small-print treatment .client-week-foot uses, so the version reads
   as an aside rather than as content. */
.client-version {
  font-size: 0.8125rem;
  color: var(--client-text-muted);
}

/* ──────────────────────────────────────────────────────────────────────────
   THE TAB BAR
   ──────────────────────────────────────────────────────────────────────────
   One row, scrollable sideways rather than wrapping: five tabs at a 44px
   minimum on a 390px viewport do not fit on one line, and a bar that wrapped to
   two lines would push the feature below the fold on the screen this app is
   designed for. A horizontal scroller keeps the first tab where the thumb
   expects it and the rest a swipe away. */

.client-tabs {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  /* The scroll happens here and nowhere else; without this the row would widen
     .client-main and put the whole page on a horizontal scrollbar. */
  max-width: 100%;
  border-bottom: 1px solid var(--client-border-light);
  margin-bottom: 14px;
  /* Room for the focus ring below, which would otherwise be clipped by the
     overflow rule above. */
  padding: 2px 2px 0;
}

/* v1.46: on a touch screen the row is swiped, and the browser's grey scroll
   bar under it read as a second underline. A mouse keeps its scroll bar. */
@media (pointer: coarse) {
  .client-tabs {
    scrollbar-width: none;
  }

  .client-tabs::-webkit-scrollbar {
    display: none;
  }
}

/* The six tabs total about 400px. Below that the row swipes, and a fade at the
   right edge says so (the staff app's strips do the same). The padding after
   the last tab is as wide as the fade, so Help is clear of it at the end. */
@media (max-width: 440px) {
  .client-tabs {
    padding-right: 28px;
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent);
  }
}

.client-tab {
  /* The shared touch floor, like every other target on this page. */
  min-height: var(--client-touch);
  padding: 10px 12px;
  display: inline-flex;
  align-items: center;
  /* A tab must not shrink to its share of a crowded row — a 20px-wide "Rides"
     is a target nobody hits. It stays its own width and the row scrolls. */
  flex: 0 0 auto;
  border: none;
  background: none;
  color: var(--client-text-muted);
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
}

@media (hover: hover) {
  .client-tab:hover {
    color: var(--client-text);
  }
}

/* SELECTED IS NOT ONLY A COLOUR. The underline carries it as a second channel
   (WCAG 1.4.1), and `aria-selected` carries it to anything that is not looking. */
.client-tab[aria-selected="true"] {
  color: var(--client-text);
  box-shadow: inset 0 -2px 0 var(--client-brand);
}

.client-tab:focus-visible {
  outline: 2px solid var(--client-brand);
  outline-offset: -2px;
  border-radius: var(--client-radius-md);
}

/* The tab's icon (tabs.js TAB_ICONS) is for the bottom bar below; the desktop
   row is text only. */
.client-tab-icon {
  display: none;
}

/* More and its menu belong to the bottom bar (tabs.js BAR_COLUMNS); the desktop
   row shows every tab and has neither. */
.client-tab-more,
.client-tab-more-menu {
  display: none;
}

/* ── THE BOTTOM BAR (v1.87) ──────────────────────────────────────────────────
   On a phone or a tablet the same tablist is a bar fixed to the bottom of the
   screen, one equal column per tab, an icon over each label, the way a phone
   app is navigated. Below 1280px, or on any touch screen without hover (a large
   tablet in landscape); a laptop with a mouse keeps the row above. The page
   reserves the bar's height under its last line, so the version footer and the
   last button of a form are never under it. */
@media (max-width: 1279px), (hover: none) and (pointer: coarse) {
  :root {
    --client-tabbar-h: 66px;
  }

  .client-main {
    padding-bottom: calc(24px + var(--client-tabbar-h) + env(safe-area-inset-bottom, 0px));
  }

  html {
    scroll-padding-bottom: calc(var(--client-tabbar-h) + env(safe-area-inset-bottom, 0px));
  }

  .client-tabs {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 80;
    display: grid;
    grid-template-columns: repeat(var(--tab-count, 6), minmax(0, 1fr));
    gap: 2px;
    max-width: none;
    margin: 0;
    /* Full-width bar; on a wide tablet the tabs gather under the content
       column instead of spreading across the whole screen. */
    padding: 4px max(4px, calc((100% - 40rem) / 2)) calc(4px + env(safe-area-inset-bottom, 0px));
    overflow: visible;
    background: var(--client-surface);
    border-top: 1px solid var(--client-border-light);
    border-bottom: none;
    -webkit-mask-image: none;
    mask-image: none;
  }

  .client-tab {
    flex-direction: column;
    justify-content: center;
    gap: 1px;
    min-width: 0;
    min-height: 56px;
    padding: 4px 2px;
    font-size: 12px;
    line-height: 1.2;
    white-space: normal;
    text-align: center;
  }

  .client-tab-label {
    max-width: 100%;
    overflow-wrap: anywhere;
  }

  .client-tab-icon {
    display: block;
    flex: none;
    width: 22px;
    height: 22px;
    /* The pill behind the chosen tab's icon is drawn on this box. */
    padding: 4px 14px;
    box-sizing: content-box;
    border-radius: var(--client-radius-full);
  }

  /* SELECTED, TWO WAYS (WCAG 1.4.1): a tinted pill behind the icon in the brand
     colour, and a semibold label. The underline belongs to the row above. */
  .client-tab[aria-selected="true"] {
    box-shadow: none;
    font-weight: 600;
  }

  .client-tab[aria-selected="true"] .client-tab-icon {
    color: var(--client-brand);
    background: var(--client-brand-tint);
  }

  /* MORE (walk 2026-10-23): past five tabs the bar shows four and More, and
     the rest sit in More's menu. More reads as chosen while one of them is open. */
  .client-tab[data-tab-overflow] {
    display: none;
  }

  .client-tab-more {
    display: inline-flex;
  }

  .client-tab-more[data-more-current] {
    color: var(--client-text);
    font-weight: 600;
  }

  .client-tab-more[data-more-current] .client-tab-icon {
    color: var(--client-brand);
    background: var(--client-brand-tint);
  }

  .client-tab-more-menu {
    position: absolute;
    bottom: calc(100% + 6px);
    right: max(8px, calc((100% - 40rem) / 2));
    min-width: 200px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 6px;
    background: var(--client-surface);
    border: 1px solid var(--client-border);
    border-radius: var(--client-radius-lg);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  }

  /* Restated: the display above would beat the UA [hidden] rule. */
  .client-tab-more-menu[hidden] {
    display: none;
  }

  .client-tab-more-item {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: var(--client-touch);
    padding: 8px 12px;
    border: none;
    border-radius: var(--client-radius-md);
    background: none;
    color: var(--client-text);
    font: inherit;
    font-size: 15px;
    text-align: left;
    cursor: pointer;
  }

  .client-tab-more-item .client-tab-icon {
    padding: 0;
  }

  .client-tab-more-item[aria-current] {
    color: var(--client-brand);
    font-weight: 600;
    background: var(--client-brand-tint);
  }

  .client-tab-more-item:focus-visible {
    outline: 2px solid var(--client-brand);
    outline-offset: -2px;
  }

  /* No tabs yet (the notice gate is up): no empty bar. */
  .client-tabs:empty {
    display: none;
  }
}

/* While the resident types, the on-screen keyboard is up and, with
   interactive-widget=resizes-content (client.html), a fixed bar would ride up
   above it and cover the form. It steps aside until the field loses focus, as
   the staff app's pinned bar does (styles.css, G2-124). Touch screens only:
   a narrow desktop window has no on-screen keyboard. Below the bar's rule. */
@media (hover: none) and (pointer: coarse) {
  body:has(.client-main :is(input:not([type="checkbox"]):not([type="radio"]):not([type="button"]):not([type="submit"]), textarea):focus) .client-tabs {
    display: none;
  }
}

/* The panel is a plain container: the feature inside it brings its own layout.
   `[hidden]` is restated because a `display` on a hidden panel would beat the
   UA rule — the trap the theme-icon note above this file's chrome section
   records, in the one place a later edit would most plausibly hit it. */
.client-tab-panel[hidden] {
  display: none;
}

/* ──────────────────────────────────────────────────────────────────────────
   THE DAY DETAIL, AS A DIALOG
   ────────────────────────────────────────────────────────────────────────── */

/* Focus is MOVED here when a day opens (calendar.js), and a panel that took
   focus with nothing to show for it would look like a tap that did nothing. */
.client-day-detail:focus-visible {
  outline: 2px solid var(--client-brand);
  outline-offset: 2px;
}

/* ──────────────────────────────────────────────────────────────────────────
   "A NEW VERSION IS AVAILABLE" (decision D11)
   ──────────────────────────────────────────────────────────────────────────
   Top of .client-main, above everything including the notice strip, because it
   is the one message on the page that explains why the others might be wrong.
   Created by views.js only when the server names a version this client is not —
   it is never in the document on an ordinary day, which is why there is no
   hidden state to get wrong here. */

.client-update-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  margin-bottom: 14px;
  border: 1px solid var(--client-brand);
  border-radius: var(--client-radius-lg);
  background: var(--client-surface);
}

.client-update-text {
  /* Takes the row and pushes the two buttons onto the next line on a narrow
     phone, rather than squeezing all three onto one. */
  flex: 1 1 100%;
  font-size: 0.9375rem;
}

/* ──────────────────────────────────────────────────────────────────────────
   THE NOTICE GATE (phase 2h, decision D5)
   ──────────────────────────────────────────────────────────────────────────
   What a resident sees INSTEAD OF the tabs until they have accepted the notice
   their programme publishes. It is rendered into #client-portal-body by
   notice.js, so the shell above it — their own name, and Log out — stays
   reachable: a gate that also hid the way out would be a trap rather than a
   notice.

   BOTH THEMES COME FOR FREE and that is deliberate rather than lucky: every
   colour below is one of the tokens declared at the top of this file, which the
   two dark blocks redefine. There is no second copy of this block under a media
   query to keep in step. */

.client-notice-gate {
  background: var(--client-surface);
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-lg);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.client-notice-gate-title {
  margin: 0;
  font-size: 1.25rem;
  line-height: 1.3;
  color: var(--client-text);
}

.client-notice-gate-lead {
  margin: 0;
  font-size: 0.9375rem;
  color: var(--client-text-muted);
}

/* THE OPERATOR'S TEXT, AS THE OPERATOR TYPED IT.

   `pre-wrap` is load-bearing: notice.js writes the body with textContent (never
   innerHTML — it is operator free text off the wire), so the blank lines between
   the paragraphs are real newline characters and nothing else would show them.

   The height cap plus `overflow-y: auto` keeps the checkbox and the button on
   screen on a phone. A resident who has to scroll a whole page to find the
   control is a resident who taps Accept without reaching the end; scrolling the
   TEXT and leaving the control in place is the version that can be read. */
.client-notice-gate-body {
  max-height: 46vh;
  overflow-y: auto;
  padding: 12px;
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-md);
  background: var(--client-bg);
  color: var(--client-text);
  font-size: 0.9375rem;
  line-height: 1.55;
  white-space: pre-wrap;
  /* A long unbroken string an operator pasted must not push the panel sideways. */
  overflow-wrap: anywhere;
}

/* The body is a scrollable region and takes focus (tabindex="0"), so it needs a
   visible ring for the same reason the day detail does. */
.client-notice-gate-body:focus-visible {
  outline: 2px solid var(--client-brand);
  outline-offset: 2px;
}

/* The refusal line. --client-danger-text rather than --client-danger, because
   this is TEXT: the rose passes at 3.5:1 and AA wants 4.5:1 for body copy — the
   split argued at the top of this file. */
.client-notice-gate-error {
  margin: 0;
  font-size: 0.9375rem;
  color: var(--client-danger-text);
}

.client-notice-gate-error[hidden] {
  display: none;
}

/* THE WHOLE ROW IS THE TARGET, not the 13px box: this is a phone, and the
   minimum touch target is a token in this file for exactly this reason. */
.client-notice-gate-check {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--client-touch);
  font-size: 1rem;
  color: var(--client-text);
  cursor: pointer;
}

.client-notice-gate-check input[type="checkbox"] {
  width: 22px;
  height: 22px;
  accent-color: var(--client-brand);
  flex: none;
}

.client-notice-gate-check input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--client-brand);
  outline-offset: 2px;
}

/* Which version this is. Small, last, and present so that a resident and a
   member of staff can say the same number out loud. */
.client-notice-gate-version {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--client-text-muted);
}

/* ── Schedule tab — the resident's work schedule (phase 5) ──────────────────
   Every colour is a theme token, so both themes follow without a second
   block. Touch targets keep --client-touch; nothing has a fixed width. */

.client-work {
  display: block;
}

.client-work-title {
  font-size: 1.125rem;
  font-weight: 650;
  margin-bottom: 4px;
}

.client-work-subtitle {
  font-size: 1rem;
  font-weight: 650;
  margin: 0;
  overflow-wrap: anywhere;
}

.client-work-section {
  margin: 16px 0 20px;
}

/* List cards (Phase 11 P3b): a list is one card on the surface with the
   gutter on the container, and its rows share one grid-row rule with a
   hairline between them (mirrors the staff .list-card / .list-row). */
.client-work-week,
.client-work-list,
.client-house-lines {
  list-style: none;
  margin: 8px 0;
  padding: 0 16px;
  background: var(--client-surface);
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-lg);
}

.client-work-day,
.client-work-list > li,
.client-house-line {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 6px 10px;
  min-height: var(--client-touch);
  padding: 8px 0;
  border-bottom: 1px solid var(--client-border-light);
  overflow-wrap: anywhere;
}

/* A shift's hours stay on one line: a narrow phone cell may break the row at the
 * separator, never between "2:00" and "PM". */
.client-work-range {
  white-space: nowrap;
  overflow-wrap: normal;
}

.client-work-day:last-child,
.client-work-list > li:last-child,
.client-house-line:last-child {
  border-bottom: none;
}

.client-work-day[hidden],
.client-work-list > li[hidden],
.client-house-line[hidden] {
  display: none;
}

/* The week keeps its day-name column. */
.client-work-day {
  grid-template-columns: 5.5rem minmax(0, 1fr);
}

.client-work-day-label {
  color: var(--client-text-muted);
  font-size: 0.875rem;
}

.client-work-day-items {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  overflow-wrap: anywhere;
}

.client-work-off,
.client-work-none {
  color: var(--client-text-muted);
}

/* A job is a section of the tab, not a card inside it: a hairline above
   each one but the first. */
.client-work-job {
  margin: 16px 0;
  padding-top: 16px;
  border-top: 1px solid var(--client-border-light);
}

.client-work-job:first-child {
  padding-top: 0;
  border-top: none;
}

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

.client-work-label {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--client-text-muted);
  margin: 14px 0 4px;
}

.client-work-actions {
  display: flex;
  gap: 12px;
  flex: none;
}

.client-work-actions .client-link-btn,
.client-work-list li > .client-link-btn,
.client-rent-what .client-link-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--client-touch);
  padding: 0 12px;
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-md);
  background: var(--client-surface);
  color: var(--client-text);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}

.client-work-form {
  margin-top: 16px;
  padding: 14px;
  border: 1px solid var(--client-brand);
  border-radius: var(--client-radius-lg);
  background: var(--client-surface);
}

.client-work-days {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  border: 0;
  margin: 0;
  padding: 0;
}

.client-work-daybox,
.client-work-check {
  display: flex;
  margin-top: 8px;
  align-items: center;
  gap: 6px;
  min-height: var(--client-touch);
  color: var(--client-text);
}

.client-work-daybox input,
.client-work-check input {
  flex: none;
  width: 22px;
  height: 22px;
  accent-color: var(--client-brand);
}

.client-work-form-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 16px;
}

/* .client-btn carries a 20px top margin for a button under a field. In this
   row it pushed Save below Cancel, so both start level at the touch height. */
.client-work-form-actions .client-btn {
  margin-top: 0;
}

.client-work-confirmed {
  color: var(--client-success-text);
  border: 1px solid var(--client-success);
}

/* ── Rent tab — the resident's rent (phase 7) ─────────────────────────────────
   Money lines up in tabular figures; a reversed entry stays in the list,
   struck through, beside the correction that took it out. */

.client-rent-standing {
  font-weight: 600;
}

/* The standing's tone comes from the attribute rent.js already writes;
   'behind' keeps .client-notice-error. Text tokens are measured on the surface. */
.client-rent-standing[data-rent-standing="owing"] {
  color: var(--client-warning-text);
  border: 1px solid var(--client-warning);
}

.client-rent-standing[data-rent-standing="paid"],
.client-rent-standing[data-rent-standing="credit"] {
  color: var(--client-success-text);
  border: 1px solid var(--client-success);
}

.client-rent-facts {
  margin: 12px 0;
}

.client-rent-facts > div {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0;
  border-bottom: 1px solid var(--client-border-light);
}

.client-rent-facts dt {
  color: var(--client-text-muted);
}

.client-rent-facts dd {
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* The row rule draws the hairline; a ledger line tops-aligns its columns. */
.client-rent-lines > li {
  align-items: start;
}

.client-rent-what {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.client-rent-what .client-link-btn {
  align-self: flex-start;
}

.client-rent-amounts {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex: none;
  font-variant-numeric: tabular-nums;
}

.client-rent-voided .client-rent-what > span:first-child,
.client-rent-voided .client-rent-amounts > span:first-child {
  text-decoration: line-through;
}

/* Promissory notes (phase 7 step 6c). The note is a legal document read on a
   phone: its own scroll box, the line breaks the office wrote, and text large
   enough to read. The e-sign wording sits apart from it, so what is agreed to
   is not mistaken for part of the note. */
.client-rent-note-text,
.client-rent-consent {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  padding: 12px;
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-md);
  background: var(--client-surface);
  line-height: 1.55;
}

.client-rent-note-text {
  max-height: 55vh;
  overflow-y: auto;
}

.client-rent-note-text:focus-visible {
  outline: 2px solid var(--client-brand);
  outline-offset: 2px;
}

.client-rent-consent {
  font-size: 0.9375rem;
}

.client-rent-signed {
  margin: 16px 0;
}

.client-rent-signature {
  display: block;
  width: 100%;
  max-width: 360px;
  height: auto;
  color: var(--client-text);
  border-bottom: 1px solid var(--client-border);
}

[data-rent-idle][hidden],
[data-rent-sign-notice][hidden] {
  display: none;
}

/* ── House tab (house.js) — going out and back home (phase 8) ────────────────
   Tokens only, so both themes follow. The two actions are full-width touch
   targets; nothing has a fixed width, so it holds at 390px. */

.client-house {
  display: block;
}

.client-house-status {
  margin: 12px 0 16px;
  padding: 12px;
  background: var(--client-surface);
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-lg);
}

.client-house-now {
  margin: 0 0 4px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.client-house-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 20px;
}

.client-house-form .client-btn {
  min-height: var(--client-touch);
  width: 100%;
}

.client-house-heading {
  font-size: 1rem;
  font-weight: 650;
  margin: 8px 0 4px;
}

/* The list card and row rule above draw these; a house or ride line keeps
   its two lines stacked. */
.client-house-line {
  grid-template-columns: minmax(0, 1fr);
  gap: 2px;
}

.client-house-line > span:first-child {
  color: var(--client-text-muted);
  font-size: 0.875rem;
}

/* ── Help tab (help.js) ───────────────────────────────────────────────────
   Tokens only, so both themes follow. Each question is a full-width
   <summary> at least --client-touch tall: the whole row is the target. */

.client-help-group {
  margin: 20px 0 8px;
}

.client-help-entry {
  border-bottom: 1px solid var(--client-border-light);
}

.client-help-question {
  /* display stays list-item: a flex <summary> loses its disclosure triangle
     in Chromium, and the triangle is what says the row opens. */
  min-height: var(--client-touch);
  box-sizing: border-box;
  padding: 12px 2px;
  font-weight: 600;
  cursor: pointer;
  overflow-wrap: anywhere;
}

.client-help-answer {
  margin: 0 2px 14px;
  color: var(--client-text-muted);
  overflow-wrap: anywhere;
}

/* Outside links under an answer (help.js linksMarkup): one per row, each at
   least --client-touch tall so a thumb lands on it. */
.client-help-links {
  list-style: none;
  margin: 0 2px 14px;
  padding: 0;
}

.client-help-link {
  display: flex;
  align-items: center;
  min-height: var(--client-touch);
  box-sizing: border-box;
  overflow-wrap: anywhere;
}

/* The state block (charter 7): a title, one sentence, one action. The
   sentence keeps the notice styling it had before the title joined it. */
.client-state-title {
  font-size: 1rem;
  font-weight: 600;
  margin: 0 0 8px;
}

/* A field the page refused: red border as well as the message under it. */
.client-input[aria-invalid="true"],
.client-sig-pad[aria-invalid="true"] {
  border-color: var(--client-danger);
}

/* Several refusals in one box (formErrors.js). */
.client-notice .form-error-list { margin: 0; padding-left: 16px; }

/* ── Standing tab (standing.js) ───────────────────────────────────────────
   Tokens only, so both themes follow. Every button is a .client-btn, which
   carries the --client-touch floor; the response box is as tall as a finger. */

.client-standing-card {
  margin: 8px 0 16px;
  padding: 4px 16px 16px;
  background: var(--client-surface);
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-lg);
}

.client-standing-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 4px 0 8px;
}

.client-standing-next {
  margin: 0;
  overflow-wrap: anywhere;
}

.client-standing-dates {
  margin-top: 4px;
}

.client-standing-lines {
  margin: 8px 0 0;
  padding-left: 20px;
  overflow-wrap: anywhere;
}

.client-standing-state {
  margin: 12px 0 0;
  font-weight: 600;
}

.client-standing-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}

.client-standing-actions .client-btn,
.client-standing-row .client-btn {
  width: 100%;
  min-height: var(--client-touch);
  margin-top: 0;
}

.client-standing-row {
  grid-template-columns: minmax(0, 1fr);
  gap: 6px;
  padding: 12px 0;
}

.client-standing-row > span {
  overflow-wrap: anywhere;
}

.client-standing-when {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.client-standing-text {
  min-height: calc(var(--client-touch) * 2);
  resize: vertical;
}

/* The sheet. A phone: a bottom sheet over a dimmed page, above the tab bar
   (z-index 80). Wider: a card in the flow, like the rent signing view. */
.client-standing-backdrop {
  display: none;
}

.client-standing-sheet {
  margin: 8px 0 16px;
  padding: 4px 16px 16px;
  background: var(--client-surface);
  border: 1px solid var(--client-border);
  border-radius: var(--client-radius-lg);
}

@media (max-width: 559px) {
  .client-standing-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 100;
    background: rgba(0, 0, 0, 0.45);
  }

  .client-standing-sheet {
    position: fixed;
    left: 0;
    right: 0;
    /* The card's own rounded corners run off the bottom of the screen. */
    bottom: calc(var(--client-radius-lg) * -1);
    z-index: 101;
    max-height: 90dvh;
    margin: 0;
    overflow-y: auto;
    padding-bottom: calc(max(16px, env(safe-area-inset-bottom)) + var(--client-radius-lg));
  }
}
