/* Rosterly. Mobile first, because volunteers read this on a phone in a pew. */

:root {
  --accent: #4f46e5;
  --accent-soft: #eef2ff;
  --accent-text: #ffffff;
  --bg: #f4f4f7;
  --surface: #ffffff;
  --surface-2: #fafafc;
  --border: #e4e4ea;
  --text: #17181c;
  --text-muted: #6b7280;
  --ok: #047857;
  --ok-soft: #ecfdf5;
  --warn: #b45309;
  --warn-soft: #fffbeb;
  /* Solid attention block: amber that stays legible in both themes. */
  --attn: #b45309;
  --attn-text: #ffffff;
  --danger: #b91c1c;
  --danger-soft: #fef2f2;
  --radius: 14px;
  --radius-sm: 9px;
  --shadow: 0 1px 2px rgba(16, 18, 26, .06), 0 8px 24px rgba(16, 18, 26, .06);
  --tabbar: 62px;
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-top: env(safe-area-inset-top, 0px);
}

@media (prefers-color-scheme: dark) {
  :root {
    --accent: #818cf8;
    --accent-soft: #272a45;
    --accent-text: #15162b;
    --bg: #0e0f13;
    --surface: #17191f;
    --surface-2: #1d2027;
    --border: #2a2d36;
    --text: #f2f3f6;
    --text-muted: #9aa1ad;
    --ok: #34d399;
    --ok-soft: #16281f;
    --warn: #fbbf24;
    --attn: #fbbf24;
    --attn-text: #15162b;
    --warn-soft: #2a2314;
    --danger: #f87171;
    --danger-soft: #2b1718;
    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .3);
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

body { min-height: 100dvh; }

h1, h2, h3 { line-height: 1.25; margin: 0; font-weight: 650; letter-spacing: -0.01em; }
h1 { font-size: 1.4rem; }
h2 { font-size: 1.08rem; }
h3 { font-size: .96rem; }
p { margin: 0 0 .75rem; }
p:last-child { margin-bottom: 0; }
a { color: var(--accent); }

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

.app { min-height: 100dvh; display: flex; flex-direction: column; }

.topbar {
  position: sticky; top: 0; z-index: 30;
  padding: calc(var(--safe-top) + 12px) 16px 12px;
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 12px;
}
.topbar__title { flex: 1; min-width: 0; }
.topbar__title small { display: block; color: var(--text-muted); font-size: .76rem; font-weight: 500; }
.topbar__title strong { display: block; font-size: 1.12rem; font-weight: 650; }

/* The home masthead: the app's name, and a verse that changes every fifteen
   seconds. Centred, with the account button floated out of the flow so the
   wordmark stays in the middle of the screen rather than the middle of what
   is left beside the button. */
.topbar--home { display: block; position: relative; padding-bottom: 14px; }
.masthead { text-align: center; }
.masthead__org {
  display: block; color: var(--text-muted); font-size: .72rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
}
.masthead__name {
  margin: 2px 0 0; font-size: clamp(1.9rem, 8vw, 2.4rem); font-weight: 750;
  letter-spacing: -.03em; line-height: 1.05; color: var(--accent);
}
.masthead__verse {
  margin: 6px auto 0; max-width: 34rem;
  transition: opacity .3s ease, transform .3s ease;
}
.masthead__verse--out { opacity: 0; transform: translateY(-4px); }
.masthead__verse p {
  margin: 0; font-size: .86rem; line-height: 1.45; color: var(--text);
  text-wrap: balance;
}
.masthead__verse cite {
  display: block; margin-top: 2px; font-style: normal;
  font-size: .74rem; font-weight: 600; color: var(--text-muted);
}
.masthead__me { position: absolute; top: calc(var(--safe-top) + 12px); right: 16px; }
@media (prefers-reduced-motion: reduce) {
  .masthead__verse { transition: none; }
}

.main {
  flex: 1;
  padding: 16px 16px calc(var(--tabbar) + var(--safe-bottom) + 28px);
  max-width: 820px; width: 100%; margin: 0 auto;
}

.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: flex; padding-bottom: var(--safe-bottom);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  border-top: 1px solid var(--border);
}
.tab {
  flex: 1; border: 0; background: none; cursor: pointer;
  padding: 8px 2px 10px; color: var(--text-muted);
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  font-size: .67rem; font-weight: 600; letter-spacing: .01em;
  font-family: inherit; position: relative;
}
.tab svg { width: 22px; height: 22px; }
/* Six tabs fit a narrow phone only if each one gives up a little room. */
@media (max-width: 400px) {
  .tab { font-size: .62rem; padding-left: 0; padding-right: 0; }
  .tab svg { width: 20px; height: 20px; }
}
.tab--on { color: var(--accent); }
.tab__dot {
  position: absolute; top: 5px; right: 50%; transform: translateX(16px);
  min-width: 17px; height: 17px; padding: 0 4px; border-radius: 9px;
  background: var(--danger); color: #fff; font-size: .62rem; line-height: 17px;
}

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

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px; box-shadow: var(--shadow);
  margin-bottom: 14px;
}
.card--flat { box-shadow: none; }
.card__head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 10px; }
.card__head h2 { flex: 1; }
.card__sub { color: var(--text-muted); font-size: .85rem; margin: -4px 0 12px; }

.section-title {
  font-size: .74rem; text-transform: uppercase; letter-spacing: .07em;
  color: var(--text-muted); font-weight: 700; margin: 22px 0 10px;
}
.section-title:first-child { margin-top: 4px; }

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

.btn {
  appearance: none; border: 1px solid transparent; border-radius: var(--radius-sm);
  padding: 11px 16px; font: inherit; font-weight: 600; font-size: .92rem;
  cursor: pointer; background: var(--accent); color: var(--accent-text);
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 44px; transition: filter .12s ease, opacity .12s ease;
}
.btn:active { filter: brightness(.92); }
.btn[disabled] { opacity: .5; cursor: default; }
.btn--ghost { background: transparent; color: var(--text); border-color: var(--border); }
.btn--soft { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.btn--danger { background: var(--danger-soft); color: var(--danger); }
.btn--ok { background: var(--ok); color: #fff; }
.btn--sm { padding: 7px 11px; min-height: 36px; font-size: .84rem; }
.btn--block { width: 100%; }
.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }
.btn-row--split > .btn { flex: 1; }

.link-btn {
  background: none; border: 0; padding: 0; font: inherit; color: var(--accent);
  cursor: pointer; font-weight: 600; font-size: .86rem;
}

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

label.field { display: block; margin-bottom: 13px; }
label.field > span {
  display: block; font-size: .8rem; font-weight: 600; margin-bottom: 5px; color: var(--text-muted);
}
input[type=text], input[type=email], input[type=tel], input[type=date],
input[type=number], select, textarea {
  width: 100%; padding: 11px 12px; font: inherit; color: var(--text);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); min-height: 44px;
}
textarea { min-height: 84px; resize: vertical; }
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent;
}
.field-hint { font-size: .78rem; color: var(--text-muted); margin-top: 5px; }
.field-row { display: flex; gap: 10px; }
.field-row > * { flex: 1; }

.switch { display: flex; align-items: center; gap: 12px; padding: 12px 0; }
.switch + .switch { border-top: 1px solid var(--border); }
.switch__text { flex: 1; }
.switch__text strong { display: block; font-weight: 600; font-size: .94rem; }
.switch__text small { color: var(--text-muted); font-size: .8rem; }
.switch input { width: 46px; height: 28px; flex: none; accent-color: var(--accent); }

/* ------------------------------------------------------------------- lists */

.list { list-style: none; margin: 0; padding: 0; }
.list__item {
  display: flex; align-items: center; gap: 12px; padding: 12px 0;
  border-bottom: 1px solid var(--border);
}
.list__item:last-child { border-bottom: 0; padding-bottom: 0; }
.list__item:first-child { padding-top: 0; }
.list__body { flex: 1; min-width: 0; }
.list__body strong { display: block; font-weight: 600; font-size: .95rem; }
.list__body small { color: var(--text-muted); font-size: .82rem; display: block; }

.avatar {
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font-weight: 700; font-size: .82rem;
  background: var(--accent-soft); color: var(--accent);
}

/* ------------------------------------------------------------------- chips */

.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 999px; font-size: .75rem; font-weight: 650;
  background: var(--surface-2); color: var(--text-muted); border: 1px solid var(--border);
  white-space: nowrap;
}
.chip--ok { background: var(--ok-soft); color: var(--ok); border-color: transparent; }
.chip--warn { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.chip--danger { background: var(--danger-soft); color: var(--danger); border-color: transparent; }
.chip--accent { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.chip--soft { background: var(--surface-2); color: var(--text); }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }

.pill-select { display: flex; flex-wrap: wrap; gap: 8px; }
.pill {
  padding: 8px 13px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface-2); font-size: .85rem; font-weight: 600; cursor: pointer;
  font-family: inherit; color: var(--text); min-height: 38px;
}
.pill--on { background: var(--accent); color: var(--accent-text); border-color: transparent; }

/* ------------------------------------------------------------- roster items */

.duty { display: flex; gap: 13px; padding: 14px 0; border-bottom: 1px solid var(--border); }
.duty:last-child { border-bottom: 0; }
.duty__date {
  flex: none; width: 52px; text-align: center; border-radius: var(--radius-sm);
  background: var(--accent-soft); color: var(--accent); padding: 7px 2px;
}
.duty__date b { display: block; font-size: 1.25rem; line-height: 1.1; }
.duty__date small { display: block; font-size: .66rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 700; }
.duty__body { flex: 1; min-width: 0; }
.duty__body h3 { margin-bottom: 2px; }
.duty__meta { color: var(--text-muted); font-size: .83rem; }

.grid-day { border-bottom: 1px solid var(--border); padding: 13px 0; }
.grid-day:last-child { border-bottom: 0; }
.grid-day__head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; }
.grid-day__head strong { font-size: .95rem; }
.grid-slot {
  display: flex; align-items: baseline; gap: 9px; padding: 6px 0;
  font-size: .88rem;
}
.grid-slot__role { color: var(--text-muted); width: 42%; flex: none; }
.grid-slot__who { flex: 1; font-weight: 550; min-width: 0; }
.grid-slot--empty .grid-slot__who { color: var(--warn); font-weight: 600; }

/* Names like "Welcome & Announcements" and "Tithe & Offering Call" squeeze the
   people column on a narrow phone, so stack them instead of wrapping both. */
@media (max-width: 430px) {
  .grid-slot { display: block; padding: 7px 0; }
  .grid-slot + .grid-slot { border-top: 1px solid var(--border); }
  .grid-slot__role { display: block; width: auto; font-size: .78rem; margin-bottom: 1px; }
  .grid-slot__who { display: block; }
}

/* ------------------------------------------------------------------ matrix */
/* The planning grid: positions down the side, dates across the top. It is wide
   by nature, so it scrolls sideways with both the first column and the header
   row pinned. */

.matrix-wrap {
  overflow: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); max-height: calc(100dvh - 230px);
}
.matrix { border-collapse: separate; border-spacing: 0; font-size: .85rem; min-width: 100%; }

.matrix th, .matrix td {
  border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--border);
  padding: 7px 10px; text-align: left; vertical-align: top;
}
.matrix tr:last-child th, .matrix tr:last-child td { border-bottom: 0; }

/* Date headers across the top. */
.matrix thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--surface-2); font-weight: 650; white-space: nowrap;
  box-shadow: inset 0 -1px 0 var(--border);
}
.matrix thead th small { display: block; font-weight: 500; color: var(--text-muted); font-size: .72rem; }

/* Position names down the left. */
.matrix .m-role {
  position: sticky; left: 0; z-index: 2;
  background: var(--surface); color: var(--text-muted); font-weight: 550;
  min-width: 132px; max-width: 168px;
}
.matrix thead .m-role { z-index: 4; background: var(--surface-2); }

.matrix .m-team {
  position: sticky; left: 0; z-index: 2;
  background: var(--surface-2); font-weight: 700; font-size: .72rem;
  text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted);
}
.matrix tr.m-team-row td { background: var(--surface-2); }

.matrix td.m-cell { min-width: 150px; cursor: pointer; }
.matrix td.m-cell:hover { background: var(--accent-soft); }
.matrix td.m-none { background: repeating-linear-gradient(
  45deg, transparent, transparent 6px, var(--surface-2) 6px, var(--surface-2) 12px); cursor: default; }
.matrix td.m-none:hover { background: repeating-linear-gradient(
  45deg, transparent, transparent 6px, var(--surface-2) 6px, var(--surface-2) 12px); }

/* Each name is its own tap target: the whole row, not a tiny cross. */
.m-person {
  display: flex; align-items: center; gap: 6px; line-height: 1.35;
  padding: 4px 4px; margin: -2px -4px; border-radius: 6px;
  cursor: pointer; text-align: left; width: 100%;
}
.m-person:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.m-person:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.m-recorded { font-size: .62rem; color: var(--text-muted); }
.m-person + .m-person { border-top: 1px solid var(--border); margin-top: 3px; padding-top: 5px; }
.m-mark { flex: none; width: 15px; height: 15px; display: grid; place-items: center;
  border-radius: 50%; font-size: .6rem; font-weight: 800; color: #fff; }
.m-mark--yes { background: var(--ok); }
.m-mark--wait { background: var(--warn); }
.m-mark--no { background: var(--danger); }
/* Penciled in, nobody asked yet. */
.m-mark--draft { background: var(--text-muted); }
.m-name--no { text-decoration: line-through; color: var(--text-muted); }
/* Double-booked: needed somewhere else at the same time. */
.m-clash {
  flex: none; display: grid; place-items: center;
  width: 15px; height: 15px; border-radius: 4px;
  background: var(--danger); color: #fff; font-size: .62rem; font-weight: 800;
  cursor: help;
}

.m-needed {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--warn); font-weight: 650; font-size: .8rem;
}
.m-needed b {
  background: var(--warn-soft); border-radius: 4px; padding: 0 5px; font-size: .72rem;
}
.m-add { color: var(--accent); font-weight: 600; font-size: .78rem; }

.matrix-legend {
  display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px;
  font-size: .76rem; color: var(--text-muted);
}
.matrix-legend span { display: inline-flex; align-items: center; gap: 5px; }

/* -------------------------------------------------------------------- chat */

/* Height is set in JS from the element's own position, because what sits above
   it varies: a service chat carries the day's details and the list of who is
   on, a team chat carries neither, and a fixed subtraction leaves the composer
   below the fold on one of them. */
.chat { display: flex; flex-direction: column; height: calc(100dvh - var(--tabbar) - var(--safe-bottom) - 118px); }
.chat__scroll { flex: 1; overflow-y: auto; padding: 4px 0 12px; -webkit-overflow-scrolling: touch; }
.bubble { max-width: 82%; margin-bottom: 10px; }
.bubble__who { font-size: .74rem; color: var(--text-muted); margin: 0 0 3px 11px; font-weight: 600; }
.bubble__body {
  padding: 9px 13px; border-radius: 17px; background: var(--surface);
  border: 1px solid var(--border); white-space: pre-wrap; overflow-wrap: anywhere;
  font-size: .93rem;
}
.bubble--mine { margin-left: auto; }
.bubble--mine .bubble__body { background: var(--accent); color: var(--accent-text); border-color: transparent; }
.bubble--mine .bubble__who { text-align: right; margin: 0 11px 3px 0; }
.bubble__time { font-size: .68rem; color: var(--text-muted); margin-top: 3px; padding: 0 11px; }
.bubble--mine .bubble__time { text-align: right; }

.composer { display: flex; gap: 8px; padding-top: 10px; border-top: 1px solid var(--border); }
.composer textarea { min-height: 44px; max-height: 120px; resize: none; }

/* --------------------------------------------------------------- assistant */

.assistant__log { min-height: 120px; }
.assistant__turn { margin-bottom: 14px; }
.assistant__turn--user .bubble__body { background: var(--accent); color: var(--accent-text); border-color: transparent; }
.assistant__actions {
  margin-top: 8px; padding: 9px 12px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm); font-size: .8rem;
}
.assistant__actions li { margin: 2px 0; }
.suggestion-row { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 12px; }

/* ------------------------------------------------------------------ states */

.empty { text-align: center; padding: 30px 18px; color: var(--text-muted); }
.empty h3 { color: var(--text); margin-bottom: 6px; }
.empty p { font-size: .88rem; }

.notice {
  padding: 12px 14px; border-radius: var(--radius-sm); font-size: .87rem;
  background: var(--accent-soft); color: var(--accent); margin-bottom: 14px;
}
.notice--warn { background: var(--warn-soft); color: var(--warn); }
.notice--danger { background: var(--danger-soft); color: var(--danger); }
.notice--ok { background: var(--ok-soft); color: var(--ok); }
.notice strong { display: block; margin-bottom: 3px; }

.boot { min-height: 70dvh; display: grid; place-items: center; gap: 14px; color: var(--text-muted); }
.boot__spinner, .spinner {
  width: 26px; height: 26px; border-radius: 50%;
  border: 2.5px solid var(--border); border-top-color: var(--accent);
  animation: spin .7s linear infinite;
}
.spinner--inline { width: 15px; height: 15px; border-width: 2px; display: inline-block; vertical-align: -2px; }
@keyframes spin { to { transform: rotate(360deg); } }

.toasts {
  position: fixed; left: 12px; right: 12px; z-index: 90;
  bottom: calc(var(--tabbar) + var(--safe-bottom) + 12px);
  display: flex; flex-direction: column; gap: 8px; pointer-events: none;
}
.toast {
  background: #17181c; color: #fff; padding: 12px 15px; border-radius: var(--radius-sm);
  font-size: .88rem; box-shadow: var(--shadow); animation: rise .2s ease;
  max-width: 560px; margin: 0 auto; width: 100%;
}
.toast--bad { background: var(--danger); }
.toast--good { background: var(--ok); color: #fff; }
@keyframes rise { from { transform: translateY(10px); opacity: 0; } }

/* Counts a group leader scans before reading any detail. Each one scrolls to
   its own card, so the summary and the list are never out of step. */
.tiles {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
  margin-top: 4px;
}
.tile {
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 12px 14px;
  font: inherit; color: var(--text); cursor: pointer; text-align: left;
}
.tile:hover { border-color: var(--accent); }
.tile strong {
  font-size: 1.5rem; line-height: 1.1; font-weight: 650;
  font-variant-numeric: tabular-nums;
}
.tile span { font-size: .8rem; color: var(--text-muted); }
.tile--warn { background: var(--warn-soft); border-color: var(--warn); }
.tile--warn strong, .tile--warn span { color: var(--warn); }

/* Somebody else's request for cover, waiting on an answer from this person. */
.ask { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 14px; }
.ask + .ask { margin-top: 10px; }
.ask__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.ask__head small { display: block; color: var(--text-muted); }

/* What has happened lately. A stripe carries the kind, so the list reads at a
   glance without a badge on every row. */
.feed { list-style: none; margin: 0; padding: 0; }
.feed__item {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
  padding: 10px 0 10px 12px; border-left: 3px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.feed__item:last-child { border-bottom: 0; padding-bottom: 0; }
.feed__item strong { display: block; font-weight: 550; font-size: .9rem; }
.feed__item small { font-size: .78rem; }
.feed__item--accepted { border-left-color: var(--ok); }
.feed__item--declined { border-left-color: var(--danger); }
.feed__item--covered  { border-left-color: var(--accent); }
.feed__item--swapped  { border-left-color: var(--accent); }

/* A band heading that folds its rows away, so somebody can look at just the
   music without the rest of the grid in the way. */
.m-fold {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
}
.m-fold__mark {
  display: inline-grid; place-items: center; width: 15px; height: 15px;
  border-radius: 3px; background: var(--surface); color: var(--text-muted);
  font-size: .8rem; line-height: 1;
}
.m-fold:hover .m-fold__mark { background: var(--accent); color: #fff; }
.m-fold__n {
  font-size: .68rem; font-weight: 600; color: var(--text-muted);
  background: var(--surface); border-radius: 8px; padding: 1px 6px;
}

/* The four planning rows that sit above the roster in the grid, so a presiding
   leader sees what the service is about and who is on it in one glance. */
.m-team-row--plan th { background: var(--accent-soft); color: var(--accent); }
.m-plan { cursor: pointer; vertical-align: middle; }
.m-plan:hover { background: var(--accent-soft); }
.m-plan__value { font-size: .8rem; line-height: 1.3; display: block; }
.m-plan__empty { color: var(--text-muted); opacity: .55; }
/* A fixed frame, so every graphic occupies the same space whatever shape it
   arrived in. Without this the cell was as tall as the tallest image in the row
   and each one left a different gap beside it. The framing values come from the
   graphic itself, set once and used on every week of the series. */
.m-art {
  display: block; width: 100%; aspect-ratio: 16 / 10;
  overflow: hidden; border-radius: 4px; border: 1px solid var(--border);
  background: var(--surface-2);
}
.m-art img {
  display: block; width: 100%; height: 100%;
  object-fit: var(--art-fit, cover);
  object-position: var(--art-x, 50%) var(--art-y, 50%);
  /* Zooming around the focal point, so the thing being framed stays put. */
  transform: scale(var(--art-zoom, 1));
  transform-origin: var(--art-x, 50%) var(--art-y, 50%);
}

/* Framing an image: a live frame at the grid's own shape, dragged to reposition. */
.art-frame {
  width: 100%; max-width: 320px; margin: 0 auto;
  aspect-ratio: 16 / 10; overflow: hidden;
  border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--surface-2); position: relative;
  cursor: grab; touch-action: none; user-select: none;
}
.art-frame:active { cursor: grabbing; }
.art-frame img {
  display: block; width: 100%; height: 100%; pointer-events: none;
  object-fit: var(--art-fit, cover);
  object-position: var(--art-x, 50%) var(--art-y, 50%);
  transform: scale(var(--art-zoom, 1));
  transform-origin: var(--art-x, 50%) var(--art-y, 50%);
}
/* A crosshair showing what is being kept in view while dragging. */
.art-frame__dot {
  position: absolute; width: 18px; height: 18px; margin: -9px 0 0 -9px;
  border: 2px solid #fff; border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .45), 0 1px 3px rgba(0, 0, 0, .4);
  pointer-events: none; opacity: 0; transition: opacity .15s;
}
.art-frame--dragging .art-frame__dot { opacity: 1; }
.art-hint { text-align: center; font-size: .78rem; color: var(--text-muted); margin: 8px 0 0; }
.art-range { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.art-range input[type="range"] { flex: 1; accent-color: var(--accent); }
.art-range span {
  font-size: .78rem; color: var(--text-muted); min-width: 3.2rem;
  font-variant-numeric: tabular-nums; text-align: right;
}

/* Picking or uploading artwork for a series. */
.art-current {
  display: flex; gap: 12px; align-items: center;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px; margin-bottom: 14px;
}
/* The thumbnail is now a framed box, so it is sized rather than the image. */
.art-current .m-art { width: 104px; flex: none; }
.art-grid {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
}
.art-pick {
  display: flex; flex-direction: column; gap: 4px; align-items: stretch;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 8px; cursor: pointer;
  font: inherit; color: var(--text); text-align: left;
}
.art-pick:hover { border-color: var(--accent); }
.art-pick--on { border-color: var(--accent); background: var(--accent-soft); }
.art-pick .m-art { width: 100%; border: 0; border-radius: 3px; }
.art-pick span { font-size: .78rem; font-weight: 550; line-height: 1.25; }
.art-pick small { font-size: .7rem; color: var(--text-muted); }

/* Help is grouped, so somebody scanning for their problem reads seven headings
   rather than twenty-three titles. */
.help-section + .help-section { margin-top: 22px; }
.help-section__title {
  margin: 0 0 8px; padding: 0 2px;
  font-size: .76rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-muted);
}
.help-section .card + .card { margin-top: 8px; }

/* What the day is about, above a service conversation. */
.plan-strip {
  display: flex; gap: 12px; align-items: flex-start;
  margin: 8px 0 4px; padding-top: 10px; border-top: 1px solid var(--border);
}
.plan-strip__art { width: 84px; flex: none; }
.plan-strip__list {
  margin: 0; flex: 1; min-width: 0;
  display: grid; grid-template-columns: auto 1fr; gap: 2px 10px; align-items: baseline;
}
.plan-strip__list dt {
  font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-muted); white-space: nowrap;
}
.plan-strip__list dd { margin: 0; font-size: .86rem; line-height: 1.35; }

/* Who is in a week's service chat, and what each of them is doing that day. */
.who { display: inline; }
.who:not(:last-child)::after { content: ', '; color: var(--text-muted); }
.who__role { color: var(--text-muted); font-size: .92em; }
.who__role--none { font-style: italic; }
.bubble__who .who__role { font-weight: 400; }

/* The first-time tour. Cards rather than an overlay pointing at live buttons:
   the tab bar differs per person and the screen re-renders underneath, so an
   anchored tooltip is the thing that breaks. */
.card--invite { border: 1px solid var(--accent); background: var(--accent-soft); }
.tour__dots { display: flex; gap: 5px; margin-bottom: 14px; }
.tour__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--border); }
.tour__dot--on { background: var(--accent); width: 18px; border-radius: 3px; }
.tour__tab {
  margin: 0 0 10px; font-size: .78rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--accent);
}
.tour__body { margin: 0; font-size: .95rem; line-height: 1.55; }
.tour__point {
  margin: 12px 0 0; padding: 10px 12px; border-left: 3px solid var(--accent);
  background: var(--surface-2); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: .87rem; color: var(--text-muted);
}

/* One collapsible answer in the help index. */
.topic {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; background: none; border: 0; padding: 0;
  font: inherit; color: var(--text); text-align: left; cursor: pointer;
}
.topic strong { font-weight: 600; font-size: .95rem; line-height: 1.35; }
.topic__chev {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  background: var(--surface-2); color: var(--text-muted);
  display: grid; place-items: center; font-size: 1rem; line-height: 1;
}
.topic:hover .topic__chev { background: var(--accent-soft); color: var(--accent); }

/* The follow-up message, shown in full before the channel is chosen, so nobody
   sends something they have not read. */
.msg-preview {
  background: var(--surface-2); border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); padding: 12px 14px; margin: 14px 0 4px;
  font-size: .88rem; line-height: 1.5; white-space: pre-wrap;
}
/* Ways of sending a follow-up, any number of them ticked at once. Each is a
   toggle rather than a link, because they go one at a time: a tap can only
   open one app, so the sheet stays put and offers the next one. */
.pick-list { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.pick {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 12px 14px; text-align: left; cursor: pointer;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; font-size: .95rem;
}
.pick__tick {
  flex: 0 0 auto; width: 20px; height: 20px; border-radius: 6px;
  border: 2px solid var(--border-strong, var(--border)); background: var(--surface);
  position: relative;
}
.pick__label { flex: 1 1 auto; }
.pick__note { flex: 0 0 auto; font-size: .78rem; color: var(--muted); }

.pick[aria-pressed="true"] {
  background: var(--accent-soft); border-color: var(--accent); color: var(--accent);
}
.pick[aria-pressed="true"] .pick__tick { background: var(--accent); border-color: var(--accent); }
/* The tick itself, drawn rather than an image so it follows the text colour. */
.pick[aria-pressed="true"] .pick__tick::after {
  content: ''; position: absolute; left: 5px; top: 1px;
  width: 4px; height: 9px; border: solid #fff;
  border-width: 0 2px 2px 0; transform: rotate(42deg);
}
.pick[disabled] { opacity: .55; cursor: default; }
.pick--done { opacity: .7; }
.pick--done .pick__label { text-decoration: line-through; }
.pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* One full-width button per row, for a choice of how to send. */
.btn-row--stack { flex-direction: column; align-items: stretch; }

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

.modal-backdrop {
  position: fixed; inset: 0; z-index: 80; background: rgba(10, 11, 15, .5);
  display: flex; align-items: flex-end; justify-content: center;
  animation: fade .15s ease;
}
@keyframes fade { from { opacity: 0; } }
.modal {
  background: var(--surface); width: 100%; max-width: 560px;
  border-radius: var(--radius) var(--radius) 0 0; padding: 20px 18px;
  padding-bottom: calc(20px + var(--safe-bottom));
  max-height: 88dvh; overflow-y: auto; animation: slideup .22s ease;
}
@keyframes slideup { from { transform: translateY(24px); } }
.modal__head { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.modal__head h2 { flex: 1; }

@media (min-width: 640px) {
  .modal-backdrop { align-items: center; }
  .modal { border-radius: var(--radius); }
  .tabbar { position: sticky; border-radius: 0; }
}

/* Shown when the server is running a newer build than this page loaded with. */
.update-bar {
  position: fixed; left: 12px; right: 12px; z-index: 95;
  bottom: calc(var(--tabbar) + var(--safe-bottom) + 12px);
  max-width: 560px; margin: 0 auto;
  display: flex; align-items: center; gap: 12px;
  background: var(--accent); color: var(--accent-text);
  padding: 10px 12px 10px 16px; border-radius: var(--radius-sm);
  box-shadow: var(--shadow); font-size: .88rem; font-weight: 550;
}
.update-bar span { flex: 1; }
.update-bar .btn { background: var(--accent-text); color: var(--accent); }

/* Sits above the tab bar for as long as somebody is penciled in but unasked.
   Pencilling in and asking are deliberately two steps, so that a month of
   dates can go out in one message each - which only works if the second step
   is impossible to miss from anywhere on the roster screen. */
.send-bar {
  position: fixed; left: 12px; right: 12px; z-index: 92;
  bottom: calc(var(--tabbar) + var(--safe-bottom) + 12px);
  max-width: 560px; margin: 0 auto;
  display: flex; align-items: center; gap: 12px;
  background: var(--attn); color: var(--attn-text);
  padding: 9px 10px 9px 15px; border-radius: var(--radius-sm);
  box-shadow: var(--shadow); animation: rise .2s ease;
}
.send-bar__text { flex: 1; min-width: 0; line-height: 1.25; }
.send-bar__text strong { display: block; font-size: .9rem; font-weight: 650; }
.send-bar__text span { font-size: .78rem; opacity: .85; }
.send-bar .btn {
  background: var(--attn-text); color: var(--attn);
  border-color: var(--attn-text); flex: none; font-weight: 600;
}
/* Nothing should come to rest underneath the bar. */
.has-send-bar .main { padding-bottom: calc(var(--tabbar) + var(--safe-bottom) + 92px); }
.has-send-bar .toasts,
.has-send-bar .update-bar { bottom: calc(var(--tabbar) + var(--safe-bottom) + 76px); }

/* Picking someone's level. Each option explains itself, because "presiding
   leader" and "administrator" mean nothing until you say what they can do. */
.level {
  display: flex; gap: 12px; align-items: flex-start; width: 100%; text-align: left;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 12px 14px;
  font: inherit; color: var(--text); cursor: pointer;
}
.level + .level { margin-top: 8px; }
.level:hover:not([disabled]) { border-color: var(--accent); }
.level--on { border-color: var(--accent); background: var(--accent-soft); }
.level[disabled] { opacity: .5; cursor: not-allowed; }
.level__mark {
  flex: none; width: 20px; height: 20px; margin-top: 1px; border-radius: 50%;
  border: 1.5px solid var(--border); background: var(--surface);
  display: grid; place-items: center; font-size: .72rem; color: var(--accent);
}
.level--on .level__mark { border-color: var(--accent); }
.level__text strong { display: block; font-size: .93rem; font-weight: 650; }
.level__text small { display: block; margin-top: 2px; font-size: .8rem; color: var(--text-muted); }

/* Choosing what one person can be rostered for. Grouped by roster, then team,
   because a position name alone is ambiguous across rosters. */
.role-group + .role-group { margin-top: 18px; }
.role-group__title {
  margin: 0 0 8px; font-size: .74rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--accent);
}
.role-team + .role-team { margin-top: 10px; }
.role-team > small { display: block; margin-bottom: 5px; font-size: .78rem; }

/* A label that only a screen reader needs, where the placeholder already says
   what the field is for sighted readers. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.muted { color: var(--text-muted); }
.small { font-size: .84rem; }
.stack > * + * { margin-top: 10px; }
.row { display: flex; align-items: center; gap: 9px; }
.row--between { justify-content: space-between; }
.spacer { flex: 1; }
.hide { display: none !important; }

/* ----------------------------------------------------- printing the roster */

/* The chooser, on screen. */
.pr-group { border: 1px solid var(--border); border-radius: var(--radius-sm); margin-top: 10px; }
.pr-head-row {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px;
  background: var(--surface-2); border-bottom: 1px solid var(--border);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0; cursor: pointer;
}
.pr-count { margin-left: auto; font-size: .78rem; color: var(--muted); }
.pr-row { display: flex; align-items: center; gap: 10px; padding: 7px 12px; cursor: pointer; }
.pr-row input, .pr-head-row input, .pr-opt input { width: 17px; height: 17px; flex: 0 0 auto; }
.pr-team { padding: 4px 0 8px; }
.pr-team__name {
  display: block; padding: 6px 12px 2px; font-size: .72rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.pr-opt { display: flex; align-items: center; gap: 10px; margin-top: 14px; cursor: pointer; }

/* The sheet itself is built off-screen and only revealed by the print CSS. */
#print-root { display: none; }

@media print {
  /* Hide the app, show the sheet. Everything the app draws outside the page
     flow -- sticky bars, the tab bar, any open dialog -- goes with it. */
  body > *:not(#print-root) { display: none !important; }
  #print-root { display: block !important; }

  /* A roster is wider than it is tall, and a term of Sabbaths needs the room. */
  @page { size: landscape; margin: 10mm; }

  html, body {
    background: #fff !important; color: #000 !important;
    margin: 0; padding: 0; font-size: 10pt;
  }

  .pr-head h1 { margin: 0 0 2px; font-size: 16pt; }
  .pr-head p { margin: 0 0 10px; font-size: 9pt; color: #444; }

  .pr-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
  .pr-table th, .pr-table td {
    border: 1px solid #999; padding: 4px 6px; vertical-align: top;
    font-size: 9pt; line-height: 1.3; text-align: left;
    /* Long names wrap rather than widening the column and pushing a date off
       the page, which is the whole reason for printing it in the first place. */
    overflow-wrap: anywhere;
  }
  .pr-table thead th { background: #eee; font-size: 9pt; }
  /* Repeat the dates at the top of every page of a long roster. */
  .pr-table thead { display: table-header-group; }
  .pr-table tr { break-inside: avoid; }

  .pr-role { width: 120px; font-weight: 600; background: #f7f7f7; }
  .pr-band th { background: #e8e8e8; text-transform: uppercase; letter-spacing: .05em;
    font-size: 8pt; }
  .pr-na { background: repeating-linear-gradient(135deg, #fff, #fff 4px, #f2f2f2 4px, #f2f2f2 8px); }
  .pr-gap { font-style: italic; color: #666; }
  .pr-dash { color: #999; }
  .pr-slots { font-weight: 400; color: #666; }
  .pr-art { max-width: 100%; max-height: 22mm; object-fit: contain; }
}

/* ------------------------------------------- picking your own dates */

/* The date block on a signup card, reusing the roster's date-stack idea so a
   Sabbath is read the same way wherever it appears. */
.signup-day { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.signup-day__date {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: 52px; padding: 6px 8px; border-radius: var(--radius-sm);
  background: var(--accent-soft); color: var(--accent); line-height: 1.1;
}
.signup-day__date b { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; }
.signup-day__date em { font-style: normal; font-size: 1.25rem; font-weight: 700; }
.signup-day__date i { font-style: normal; font-size: .68rem; text-transform: uppercase; }
.signup-day__what { flex: 1 1 auto; }
