/* ============================================================================
   Deacon Scheduler

   Built on the Coptic Orthodox design system (vendored in ./coptic/). The page
   is parchment carrying a faint ink dot-texture; sanguine (oxblood) is the one
   emphasis colour and the single primary action per view; vespers (a deep
   incense blue) is structure — the header, nav and headings; gilt is the focus
   ring and nothing else. There are no gradients and shadows are warm and
   shallow — separation comes from a hairline border. Corners are a deliberate
   departure from the design system's near-square rule: softened back to the
   rounder scale the app had before, tuned in the --radius-* tokens below.
   Headings are Cormorant Garamond, body is Alegreya Sans, and section labels
   are set as red-ink "rubrics".

   The app's own components are styled here against the design system's semantic
   aliases (--surface-card, --text-sacred, --accent-primary, …). The legacy
   variable names below (--bg, --accent, --danger, …) are kept as a thin mapping
   layer so every existing selector keeps working.
   ========================================================================= */

@import url("coptic/tokens/fonts.css");
@import url("coptic/tokens/colors.css");
@import url("coptic/tokens/typography.css");
@import url("coptic/tokens/spacing.css");
@import url("coptic/tokens/elevation.css");
@import url("coptic/tokens/motion.css");
@import url("coptic/patterns/base.css");

:root {
  color-scheme: light;

  /* -- surfaces --------------------------------------------------------- */
  --bg: var(--surface-page);
  --bg-far: var(--surface-page);          /* no gradient — kept for callers */
  --surface: var(--surface-card);
  --surface-2: var(--parchment-200);      /* zebra rows, table heads, insets */
  --hover: color-mix(in oklab, var(--ink-900) 7%, transparent);

  /* -- lines and type ------------------------------------------------------- */
  --border: var(--border-hairline);
  --text: var(--text-body);
  --text-soft: var(--ink-700);
  --muted: var(--text-muted);

  /* -- accents ------------------------------------------------------------ */
  --accent: var(--accent-secondary);      /* vespers — structure, not action */
  --accent-deep: var(--accent-secondary-active);
  --accent-soft: var(--vespers-100);
  --accent-line: var(--vespers-300);
  --ring: var(--gilt-500);

  --gold: var(--gilt-700);
  --gold-soft: var(--gilt-100);

  --warn: var(--gilt-700);
  --warn-soft: var(--gilt-100);
  --danger: var(--sanguine-700);
  --danger-soft: var(--sanguine-100);
  --ok: var(--olive-700);
  --ok-soft: var(--olive-100);

  /* -- geometry --------------------------------------------------------- */
  /* Overrides the design system's near-square scale (2/3/6px). Dial these to
     taste; --radius-pill (999px) stays for nav and badges. */
  --radius-sm: 8px;                       /* controls, inputs, list items */
  --radius-md: 12px;                      /* cards, popovers, toast */
  --radius-lg: 16px;                      /* the login panel */
  --radius: var(--radius-sm);

  --shadow-sm: var(--shadow-1);
  --shadow: var(--shadow-1);
  --shadow-lg: var(--shadow-3);

  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%236B6154' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* This app is light-only. `color-scheme: light` above pins the form controls,
   scrollbars and canvas to the light palette even when the OS is set to dark,
   so there is nothing here that responds to prefers-color-scheme. The design
   system's vendored [data-theme="night"] scope is inert — no element ever
   carries that attribute. */

* { box-sizing: border-box; }

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

body {
  margin: 0;
  min-height: 100vh;
  background-color: var(--bg);
  /* The whole background system: a 3px ink dot-grid at 3.5% on parchment. */
  background-image: var(--texture-parchment);
  background-size: var(--texture-parchment-size);
  background-attachment: fixed;
  color: var(--text);
  font: var(--text-sm)/1.6 var(--font-body);
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--sanguine-100); color: var(--ink-900); }

/* One focus treatment everywhere: the gilt double ring, never a browser
   outline. */
:focus-visible {
  outline: none;
  box-shadow: var(--ring-gilt);
  border-radius: var(--radius-sm);
}

a { color: var(--text-link); text-underline-offset: 2px; }
a:hover { color: var(--text-link-hover); }

/* ---------------------------------------------------------------- loading -- */

.loading {
  padding: 3.5rem 1rem;
  text-align: center;
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
}

.loading::before {
  content: "";
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  border: 2px solid var(--border-strong);
  border-top-color: var(--accent);
  animation: spin 0.7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

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

header.app {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 0.7rem 1.25rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  position: sticky;
  top: 0;
  z-index: 10;
}

header.app h1 {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-display);
  color: var(--text-heading);
  margin: 0;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* The app icon stands in as the logo, so the header and the home-screen tile
   read as the same thing. */
header.app h1::before {
  content: "";
  width: 1.5em;
  height: 1.5em;
  flex: 0 0 auto;
  border-radius: 22%;
  background: url("/apple-touch-icon.png") center / cover no-repeat;
}

nav { display: flex; flex-wrap: wrap; gap: 0.2rem; flex: 1; }
.nav-wrap { display: flex; flex: 1; min-width: 0; }

nav a {
  padding: 0.35rem 0.7rem;
  border-radius: var(--radius-pill);
  color: var(--muted);
  text-decoration: none;
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  transition: var(--transition-color);
}

nav a:hover { background: var(--hover); color: var(--text); }

nav a.active {
  background: var(--accent-secondary);
  color: var(--text-on-dark);
  box-shadow: none;
}

main {
  max-width: 1000px;
  margin: 0 auto;
  padding: 1.75rem 1.25rem 4rem;
}

h2 {
  font-family: var(--font-display);
  font-size: 1.65rem;
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-display);
  color: var(--text-heading);
  margin: 0 0 0.2rem;
}

/* Section labels are set as red-ink rubrics — the instruction voice from
   manuscript margins. */
h3 {
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-rubric);
  text-transform: uppercase;
  color: var(--text-sacred);
  margin: 1.75rem 0 0.6rem;
}

.subtitle {
  color: var(--muted);
  margin: 0 0 1.5rem;
  max-width: 62ch;
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-1);
  padding: 1.1rem 1.3rem;
  margin-bottom: 1.25rem;
}

/* A card whose first child is a heading shouldn't start with a gap. */
.card > h3:first-child { margin-top: 0; }

.row { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
.row.between { justify-content: space-between; }
.spacer { flex: 1; }

/* --------------------------------------------------------------- controls -- */

button {
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  padding: 0.5rem 0.9rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--accent-secondary);
  cursor: pointer;
  box-shadow: none;
  transition: var(--transition-color),
              box-shadow var(--duration-fast) var(--ease-standard),
              transform var(--duration-instant) var(--ease-standard);
}

button:hover:not(:disabled) {
  border-color: var(--accent-secondary);
  color: var(--accent-secondary);
  background: color-mix(in oklab, var(--vespers-700) 8%, transparent);
}

button:active:not(:disabled) { transform: translateY(1px); }
button:disabled { opacity: 0.42; cursor: not-allowed; }
button:focus-visible { outline: none; box-shadow: var(--ring-gilt); }

/* The one sanguine action per view. */
button.primary {
  background: var(--accent-primary);
  border-color: var(--sanguine-800);
  color: var(--text-on-sacred);
}

button.primary:hover:not(:disabled) {
  background: var(--accent-primary-hover);
  border-color: var(--sanguine-800);
  color: var(--text-on-sacred);
}

button.primary:active:not(:disabled) { background: var(--accent-primary-active); }

button.danger {
  color: var(--danger);
  border-color: color-mix(in oklab, var(--danger) 45%, transparent);
  background: transparent;
}

button.danger:hover:not(:disabled) {
  background: var(--danger-soft);
  border-color: var(--danger);
  color: var(--danger);
}

button.small { padding: 0.3rem 0.6rem; font-size: 11px; }

button.link {
  border: none;
  background: none;
  color: var(--text-link);
  text-transform: none;
  letter-spacing: normal;
  text-decoration: underline;
  text-underline-offset: 2px;
  padding: 0;
  box-shadow: none;
  font-weight: var(--weight-medium);
  font-size: inherit;
}

button.link:hover:not(:disabled) { color: var(--text-link-hover); background: none; }

input[type="text"], input[type="password"], input[type="date"],
input[type="number"], input[type="search"], select, textarea {
  font-family: var(--font-body);
  /* 16px is the threshold below which iOS Safari zooms the page on focus and
     then leaves it zoomed. Never go under it on a form control. */
  font-size: 16px;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--border-hairline);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text);
  box-shadow: var(--shadow-inset-sunken);
  max-width: 100%;
  transition: var(--transition-color);
}

input:hover:not(:disabled), select:hover:not(:disabled), textarea:hover:not(:disabled) {
  border-color: var(--border-strong);
}

/* A confirmed service goes read-only by disabling its controls rather than
   swapping them for plain text: the layout does not shift, and the deacon
   already in a box is still shown where you would look for him. */
input:disabled, select:disabled, textarea:disabled {
  background: var(--surface-2);
  color: var(--muted);
  border-color: var(--border-hairline);
  opacity: 0.6;
  cursor: not-allowed;
}

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent-secondary);
  border-bottom-color: var(--gilt-500);
  background: var(--surface);
  box-shadow: var(--ring-gilt);
}

/* The native select chevron is inconsistent across platforms and ignores the
   dark theme, so it is replaced with one that follows the ink tone. */
select {
  appearance: none;
  -webkit-appearance: none;
  background-image: var(--chevron);
  background-repeat: no-repeat;
  background-position: right 0.6rem center;
  padding-right: 1.9rem;
}

input[type="checkbox"], input[type="radio"] { accent-color: var(--accent-primary); }

::placeholder { color: var(--text-faint); }

label.inline {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  border-radius: var(--radius-sm);
}

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

.table-wrap {
  overflow-x: auto;
  border-radius: var(--radius-sm);
  /* The scroll container is also the clip, so a striped row never bleeds past
     the rounded corner. */
  border: 1px solid var(--border);
  background: var(--surface);
}

/* Inside a card the table already sits on a surface; a second border would
   double up. */
.card > .table-wrap { border: none; border-radius: 0; background: none; }

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

th, td {
  text-align: left;
  padding: 0.6rem 0.7rem;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

thead th {
  background: var(--surface-2);
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--text-sacred);
  border-bottom: 1px solid var(--border-strong);
  white-space: nowrap;
}

/* Zebra and hover are carried on a custom property so the sticky first column
   below can pick up the same colour instead of punching a hole in the row. */
tbody tr { --row-bg: transparent; background: var(--row-bg); }
tbody tr:nth-child(even) { --row-bg: var(--surface-2); }
tbody tr:hover { --row-bg: var(--hover); }
tbody tr:last-child td { border-bottom: none; }

td.numeric, th.numeric { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--font-mono); }
tr.inactive td { opacity: 0.55; }

/* ----------------------------------------------------------------- notes -- */

/* A tinted panel with a solid rule down the leading edge - the stripe is what
   makes the tone readable at a glance without relying on hue alone. */
.note {
  padding: 0.65rem 0.9rem;
  border-radius: var(--radius-sm);
  border-left: 3px solid currentColor;
  margin: 0.6rem 0;
}

.note.warn { background: var(--warn-soft); color: var(--warn); }
.note.error { background: var(--danger-soft); color: var(--danger); }
.note.ok { background: var(--ok-soft); color: var(--ok); }
.note.muted { background: var(--surface-2); color: var(--muted); border-left-color: var(--border-strong); }

.reason { font-size: 0.85rem; color: var(--muted); }

/* The character counter on an announcement, once it has gone past the limit. */
.reason.over { color: var(--danger); }

/* A notice typed with line breaks in it keeps them. Both halves of the app show
   announcements this way - the scheduler's list and the deacon's card. */
.announcement-text { white-space: pre-line; margin: 0; }

.empty {
  color: var(--muted);
  font-style: italic;
  padding: 1.25rem 0;
}

/* The design system's Badge is uppercase, but the app reuses .badge for prose
   chips ("You said you can serve") and for the signed-in deacon's name, so it
   is kept sentence-case here. */
.badge {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  font-style: normal;
  letter-spacing: var(--tracking-wide);
  padding: 0.14rem 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--muted);
  border: 1px solid var(--border);
  white-space: nowrap;
}

.badge.warn { background: var(--warn-soft); color: var(--warn); border-color: color-mix(in oklab, var(--warn) 30%, transparent); }
.badge.ok { background: var(--ok-soft); color: var(--ok); border-color: color-mix(in oklab, var(--ok) 30%, transparent); }
.badge.danger { background: var(--danger-soft); color: var(--danger); border-color: color-mix(in oklab, var(--danger) 30%, transparent); }

/* ---------------------------------------------------- the deacon portal -- */

/* A small heading inside a card, under the card's own h3. */
.section-title {
  margin: 1.1rem 0 0.5rem;
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--muted);
}

.portal-replies > p:first-child { margin-top: 0.9rem; }

/* Who is away, and why, one person per line. Names in a column of their own so
   the eye can run down them; the reason sits beside the name it belongs to and
   wraps under it on a narrow screen. */
.away-list,
.swap-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--border);
}

.away-item {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.75rem;
  padding: 0.45rem 0.7rem;
  background: var(--surface);
}

.away-item + .away-item { border-top: 1px solid var(--border); }
.away-item:nth-child(odd) { background: var(--surface-2); }

.away-name {
  font-weight: 600;
  flex: 0 0 11rem;
  max-width: 100%;
}

.away-note {
  color: var(--text-soft);
  font-size: 0.9rem;
  min-width: 0;
}

.away-note.none { color: var(--muted); font-style: italic; }

/* The swap trail. The stripe down the left says at a glance what happened, so
   a page of them can be read without reading every badge. */
.swap-item {
  padding: 0.5rem 0.7rem 0.5rem 0.85rem;
  background: var(--surface);
  border-left: 3px solid var(--border-strong);
}

.swap-item + .swap-item { border-top: 1px solid var(--border); }
.swap-item.auto { border-left-color: var(--ok); }
.swap-item.manual { border-left-color: var(--accent); }
.swap-item.denied { border-left-color: var(--danger); }
.swap-item.pending { border-left-color: var(--warn); }

.swap-item .row { flex-wrap: nowrap; gap: 0.75rem; align-items: baseline; }
.swap-line { min-width: 0; }
.swap-item .reason { margin-top: 0.1rem; }

/* ----------------------------------------------------------------- login -- */

.login {
  max-width: 23rem;
  margin: 14vh auto;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border-hairline);
  /* "sacred" frame — one per view. */
  border-top: 4px solid var(--accent-primary);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-3);
  padding: 2rem 1.75rem;
}

.login h1 {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: var(--weight-regular);
  letter-spacing: var(--tracking-display);
  color: var(--text-heading);
  margin: 0 0 0.25rem;
}

/* The app icon, sized up, standing in for a logo above the title. */
.login h1::before {
  content: "";
  display: block;
  width: 4.5rem;
  height: 4.5rem;
  border-radius: 22%;
  margin: 0 auto 0.7rem;
  background: url("/apple-touch-icon.png") center / cover no-repeat;
}

.login p { color: var(--muted); margin-top: 0; }
.login form { display: flex; flex-direction: column; gap: 0.7rem; margin-top: 1.4rem; }
.login form label.inline { justify-content: center; }

/* ------------------------------------------------------ qualification grid -- */

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

.grid th.role {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  height: 9rem;
  /* Long names wrap into a second vertical line rather than being clipped -
     there is no hover on a phone to recover a truncated label. */
  white-space: normal;
  line-height: 1.15;
  vertical-align: bottom;
  padding-bottom: 0.6rem;
}

.grid td.check { text-align: center; }
.grid td.name { white-space: nowrap; font-weight: 550; }
.grid input[type="checkbox"] { width: 1.05rem; height: 1.05rem; cursor: pointer; }

/* Keep the name visible while the role columns scroll sideways - without this
   you lose track of whose row you are ticking. The row's own --row-bg is used
   so the sticky cell stays in step with the stripe and the hover. */
.sticky-name th:first-child,
.sticky-name td:first-child {
  position: sticky;
  left: 0;
  background-color: var(--surface);
  background-image: linear-gradient(var(--row-bg, transparent), var(--row-bg, transparent));
  z-index: 1;
  box-shadow: 1px 0 0 var(--border);
}

.sticky-name thead th:first-child { background-color: var(--surface-2); z-index: 2; }

.scroll-hint {
  font-size: 0.8rem;
  color: var(--muted);
  margin: 0.5rem 0 0;
}

/* Long checkbox lists (61 deacons) - columns on wide screens, capped height so
   the list never runs longer than the screen it sits on. */
.checklist {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: 0.1rem 0.75rem;
  max-height: 20rem;
  overflow-y: auto;
  padding: 0.25rem;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}

.checklist label.inline { padding: 0.15rem 0.4rem; }
.checklist label.inline:hover { background: var(--surface); }

details.collapsible > summary {
  cursor: pointer;
  font-weight: 600;
  padding: 0.4rem 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style-position: inside;
  border-radius: var(--radius-sm);
}

details.collapsible > summary:hover { color: var(--accent); }
details.collapsible > summary::-webkit-details-marker { color: var(--muted); }
details.collapsible[open] > summary { margin-bottom: 0.6rem; }

/* One row of the special-roles editor: a lighter frame than a full .card, so
   several stacked don't read as several cards. */
.special-role {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  padding: 0.7rem 0.85rem;
  margin-bottom: 0.6rem;
}
.special-role .combo-name { flex: 1 1 12rem; }

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

/* In a table cell the column would otherwise size itself to the longest name
   and leave a box too cramped to type in. */
.combo { position: relative; min-width: min(15rem, 100%); }

/* It reads as a dropdown, so it gets the same chevron as a <select>. */
.combo input {
  width: 100%;
  background-image: var(--chevron);
  background-repeat: no-repeat;
  background-position: right 0.6rem center;
  padding-right: 1.9rem;
}

/* Fixed so the results float over everything: the card below must not jump down
   and back up as you type, and inside a scrolling .table-wrap an absolute list
   would be clipped to one table row. Placed by combobox() in dom.js. */
.combo-list {
  position: fixed;
  z-index: 20;
  margin: 0;
  padding: 0.3rem;
  list-style: none;
  max-height: 16rem;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-3);
}

.combo-list li {
  padding: 0.45rem 0.55rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* The numbers that explain the order. Right-aligned and quiet: they are read
   after the name, and they are not what typing searches. */
.combo-list .combo-meta {
  margin-left: auto;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  text-align: right;
}

.combo-list li.combo-group {
  cursor: default;
  color: var(--text-sacred);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  padding: 0.55rem 0.55rem 0.25rem;
}

.combo-list li.combo-group:not(:first-child) {
  border-top: 1px solid var(--border);
  margin-top: 0.25rem;
}

/* One highlight for both the pointer and the arrow keys, so there is never a
   second, competing "selected" row. */
.combo-list li.active { background: var(--accent-soft); color: var(--text); }
.combo-list li.empty { cursor: default; font-style: italic; color: var(--muted); padding: 0.6rem 0.55rem; }

/* ----------------------------------------------------------------- toast -- */

.toast {
  position: fixed;
  bottom: 1.5rem;
  left: 50%;
  transform: translateX(-50%);
  background: var(--vespers-900);
  color: var(--parchment-100);
  border: 1px solid rgba(246, 243, 232, 0.22);
  border-left: 4px solid var(--gilt-500);
  padding: 0.7rem 1.1rem;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-3);
  z-index: 100;
  font-size: 0.92rem;
  animation: toast-in 0.2s ease-out;
}

.toast.ok { border-left-color: var(--olive-500); }
.toast.error, .toast.danger { border-left-color: var(--sanguine-500); }
.toast.warn { border-left-color: var(--gilt-500); }

@keyframes toast-in {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}

/* --------------------------------------------------------------- scrollbars -- */

.table-wrap, .grid-wrap, .checklist, .combo-list, nav {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}

/* ---------------------------------------------------------------- mobile -- */

@media (max-width: 700px) {
  /* Header: was 212px (a quarter of a phone screen) because the nav wrapped
     onto three lines. Now one compact title row plus a single scrolling row
     of nav pills. */
  header.app {
    padding: 0.5rem max(0.75rem, env(safe-area-inset-right)) 0.4rem
             max(0.75rem, env(safe-area-inset-left));
    gap: 0.5rem 0.75rem;
    align-items: center;
  }

  header.app h1 { font-size: 1.1rem; flex: 1 1 auto; }
  header.app > button { flex: 0 0 auto; }

  .nav-wrap {
    order: 3;
    flex: 1 0 100%;
    position: relative;
    min-width: 0;
  }

  /* The items together are wider than any phone, so the row scrolls. This fade
     is what tells you there is more beyond the right edge. */
  .nav-wrap::after {
    content: "";
    position: absolute;
    right: 0;
    top: 0;
    bottom: 0;
    width: 2rem;
    background: linear-gradient(to right, transparent, var(--surface));
    pointer-events: none;
  }

  nav {
    flex: 1;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    scroll-behavior: smooth;
  }
  nav::-webkit-scrollbar { display: none; }
  /* Tightened so the whole nav fits a 390px screen without scrolling; the
     overflow above is the fallback for narrower phones. */
  nav a { white-space: nowrap; padding: 0.35rem 0.55rem; }

  main {
    padding: 1.1rem max(0.75rem, env(safe-area-inset-right))
             calc(3rem + env(safe-area-inset-bottom))
             max(0.75rem, env(safe-area-inset-left));
  }

  .card { padding: 0.95rem 1rem; }
  h2 { font-size: 1.4rem; }
  .subtitle { margin-bottom: 1.1rem; }

  /* Comfortable touch targets - 34px links and 24px buttons are fiddly. */
  nav a,
  button,
  .stack a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  button.small { min-height: 44px; padding: 0.35rem 0.75rem; }
  button.link { min-height: 0; display: inline; }

  input[type="text"], input[type="password"], input[type="date"],
  input[type="number"], input[type="search"], select, textarea {
    min-height: 44px;
  }

  label.inline { min-height: 44px; }
  /* A long name plus its numbers will not fit on one phone line, so the numbers
     drop underneath rather than squeezing the name. */
  .combo-list li { min-height: 44px; flex-wrap: wrap; }
  .combo-list .combo-meta { flex-basis: 100%; text-align: left; margin-left: 0; }
  input[type="checkbox"] { width: 1.35rem; height: 1.35rem; }

  /* The grid's checkboxes sit alone in a cell rather than inside a label, so
     without this the tap target is the 22px box itself. */
  .grid td.check label {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    min-width: 44px;
    margin: 0 auto;
    cursor: pointer;
  }
  .grid input[type="checkbox"] { width: 1.5rem; height: 1.5rem; }

  /* Tables that read better as stacked blocks than as columns. A phone cannot
     show role + deacon + lock side by side, and the lock ended up scrolled off
     the right edge entirely. */
  .stack, .stack tbody, .stack tr, .stack td { display: block; width: 100%; }
  .stack thead { display: none; }

  /* Stripes make sense across a row of columns, not down a stack of blocks. */
  .stack tbody tr { --row-bg: transparent; }

  .stack tr {
    padding: 0.9rem 0;
    border-bottom: 1px solid var(--border);
  }
  .stack tr:last-child { border-bottom: none; }

  .stack td {
    border: none;
    padding: 0.2rem 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
  }

  .stack td[data-label]::before {
    content: attr(data-label);
    color: var(--text-sacred);
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
    flex: 0 0 auto;
  }

  /* Cells that should own the full width: selects, button rows, text inputs. */
  .stack td.block { display: block; }
  .stack td.block > select,
  .stack td.block > .combo,
  .stack td.block > input[type="text"] { width: 100%; }
  .stack td.block .row { justify-content: flex-start; }

  /* A row's headline (the role name, the service date). */
  .stack td.headline {
    font-family: var(--font-display);
    font-size: 1.2rem;
    font-weight: var(--weight-medium);
    letter-spacing: var(--tracking-display);
    color: var(--text-heading);
    padding-bottom: 0.4rem;
  }
  .stack td.headline a { text-decoration: none; }

  /* An empty notes cell shouldn't leave a stray label behind. */
  .stack td:empty { display: none; }

  .table-wrap table:not(.stack) { min-width: max-content; }

  /* A table-wrap that is not already inside a card draws its own frame, so the
     stacked rows need a gutter or they sit flush against it. */
  .table-wrap { padding: 0 0.9rem; }
  .card > .table-wrap { padding: 0; }

  .login {
    margin: 8vh auto;
    padding: 1.5rem 1.25rem;
  }

  .toast {
    bottom: calc(1rem + env(safe-area-inset-bottom));
    left: 0.75rem;
    right: 0.75rem;
    transform: none;
    text-align: center;
    /* Never let a toast swallow a tap meant for the row underneath it. */
    pointer-events: none;
  }

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

/* ------------------------------------------------------- reduced motion -- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* The spinner is the one exception: without it the loading state has no
     visible sign of life at all. */
  .loading::before { animation: spin 1.4s linear infinite !important; }
}
