/*
 * BitTabby -- Chalk & Pastel
 *
 * UI-04: every color, space, and type step is a token. Nothing below the
 * :root block hardcodes a color value, so alternate palettes stay cheap to add.
 * UI-02: mobile-first. The only media query raises the layout for wider
 * screens; the base styles are the phone layout.
 */

:root {
  /* Palette -- Chalk & Pastel */
  --chalk:        #f7f5f0;
  --chalk-sunk:   #efece5;
  --slate:        #2f3337;
  --slate-soft:   #5f676e;
  --line:         #ddd8cf;
  --surface:      #fffdf9;

  --mint:         #7fb99a;
  --mint-deep:    #3f7a5c;
  --rose:         #d98b8b;
  --rose-deep:    #a94f4f;
  --sky:          #8fb3cc;
  --sky-deep:     #3d6c8f;
  --sun:          #e8c07d;
  --sun-deep:     #8a6216;

  /* Semantic */
  --bg:           var(--chalk);
  --fg:           var(--slate);
  --fg-muted:     var(--slate-soft);
  --card-bg:      var(--surface);
  --border:       var(--line);
  --accent:       var(--sky-deep);
  --owed:         var(--rose-deep);
  --caution:      var(--sun-deep);
  --credit:       var(--mint-deep);
  --settled:      var(--slate-soft);

  /* Space scale */
  --s1: 0.25rem;
  --s2: 0.5rem;
  --s3: 0.75rem;
  --s4: 1rem;
  --s5: 1.5rem;
  --s6: 2rem;

  /* Type scale */
  --t-sm:   0.8125rem;
  --t-base: 1rem;
  --t-lg:   1.25rem;
  --t-xl:   1.75rem;

  --radius: 10px;
  --shadow: 0 1px 2px rgb(47 51 55 / 0.06), 0 4px 12px rgb(47 51 55 / 0.04);
  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-num: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --chalk:      #1c1f22;
    --chalk-sunk: #16181b;
    --slate:      #e8e5df;
    --slate-soft: #a3a9af;
    --line:       #32373c;
    --surface:    #23272b;
    --mint-deep:  #86c6a4;
    --rose-deep:  #e09a9a;
    --sun-deep:   #e0c07a;
    --sky-deep:   #9cc2dd;
    --shadow:     0 1px 2px rgb(0 0 0 / 0.3), 0 4px 12px rgb(0 0 0 / 0.2);
  }
}

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-size: var(--t-base);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

/* ---------- Layout ---------- */

.wrap {
  max-width: 46rem;
  margin: 0 auto;
  padding: var(--s4) var(--s4) var(--s6);
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  background: var(--card-bg);
  border-bottom: 1px solid var(--border);
}

.topbar nav { display: flex; align-items: center; gap: var(--s3); }

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-weight: 650;
  letter-spacing: -0.01em;
  color: var(--fg);
  text-decoration: none;
}

/* The logo art is nearly square; fix its box so the wordmark never shifts as
   the page loads. */
.brand .logo { display: block; width: 28px; height: 28px; }

.who { color: var(--fg-muted); font-size: var(--t-sm); }

.foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s3);
  padding: var(--s4);
  text-align: center;
  color: var(--fg-muted);
  font-size: var(--t-sm);
}

/* The version sits quietly beside the tagline; the full build string is in its
   title attribute for anyone who hovers. */
.foot .version { font-variant-numeric: tabular-nums; opacity: 0.8; }

.rowbetween {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
}

/* ---------- Cards ---------- */

.card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: var(--s4);
  margin-bottom: var(--s4);
}

.card.narrow { max-width: 26rem; margin-inline: auto; }
/* A form with a schedule, a fee policy, and a kind-scoped section needs more
   than the 26rem that suits a login box. */
.card.form { max-width: 34rem; margin-inline: auto; }
.card.empty { text-align: center; }

.cards { list-style: none; margin: 0; padding: 0; }
.cards li { margin-bottom: var(--s3); }

a.taba {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: border-color 120ms ease, transform 120ms ease;
}

a.taba:hover, a.taba:focus-visible {
  border-color: var(--accent);
  transform: translateY(-1px);
}

h1 { font-size: var(--t-xl); margin: 0 0 var(--s2); letter-spacing: -0.02em; }
h2 { font-size: var(--t-lg); margin: 0 0 var(--s3); letter-spacing: -0.01em; }

.lede { color: var(--fg-muted); }
.muted { color: var(--fg-muted); margin: var(--s1) 0; }
.small { font-size: var(--t-sm); }
.nowrap { white-space: nowrap; }
.right { text-align: right; }

/* ---------- Amounts ---------- */

.amount {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}

.amount.big { font-size: var(--t-xl); }
.amount.owed { color: var(--owed); }
.amount.credit { color: var(--credit); }
.amount.settled { color: var(--settled); }

.balanceblock { display: flex; flex-direction: column; align-items: flex-end; }

/* ---------- Forms ---------- */

.stack { display: flex; flex-direction: column; gap: var(--s4); }
.stack label { display: flex; flex-direction: column; gap: var(--s1); font-weight: 550; }
.stack small { font-weight: 400; color: var(--fg-muted); font-size: var(--t-sm); }

input[type="text"], input[type="email"], input[type="password"],
input[type="number"], input[type="date"], input[type="search"], input[type="file"],
textarea {
  font: inherit;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: var(--s3);
  /* 16px minimum prevents iOS Safari from zooming on focus. */
  font-size: max(1rem, var(--t-base));
  width: 100%;
}

/* A message template is the one place a person types more than a line, so the
   box matches the inputs above it and resizes only downward. */
textarea { resize: vertical; min-height: 5rem; }

input:focus-visible, textarea:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

fieldset {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--s4);
  margin: 0;
}

legend { font-weight: 600; padding: 0 var(--s2); }

.itemrow { display: grid; grid-template-columns: 1fr 8rem; gap: var(--s2); }

.inlineform { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
.inlineform input[type="text"] { flex: 1 1 8rem; width: auto; }
.inline { display: inline; }

button, .button {
  font: inherit;
  font-weight: 600;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--chalk-sunk);
  color: var(--fg);
  padding: var(--s3) var(--s4);
  cursor: pointer;
  /* Comfortable thumb target on a phone. */
  min-height: 44px;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

button.primary, .button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--surface);
}

button.link {
  background: none;
  border: none;
  color: var(--fg-muted);
  padding: var(--s1) var(--s2);
  min-height: auto;
  text-decoration: underline;
}

/* ---------- Tables ---------- */

table { width: 100%; border-collapse: collapse; }
th { text-align: left; font-size: var(--t-sm); color: var(--fg-muted); font-weight: 600; }
th, td { padding: var(--s2) var(--s1); border-bottom: 1px solid var(--border); }
tfoot td { font-weight: 650; border-bottom: none; }

.memo { margin-left: var(--s2); color: var(--fg-muted); }

.pill {
  display: inline-block;
  font-size: var(--t-sm);
  padding: 0 var(--s2);
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--chalk-sunk);
  text-transform: capitalize;
}

.pill-payment { color: var(--credit); }
.pill-charge, .pill-fee { color: var(--owed); }

/* An adjustment is labelled by what it did rather than by its kind, since the
   direction is what someone scanning history is looking for. A credit reduces
   what is owed and reads like a payment; a debit increases it. */
.pill-credit { color: var(--credit); }
.pill-debit { color: var(--owed); }

/* ---------- Banners ---------- */

.banner {
  padding: var(--s3) var(--s4);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  margin-bottom: var(--s4);
}

.banner-error { border-color: var(--rose); color: var(--owed); background: var(--card-bg); }
.banner-ok { border-color: var(--mint); color: var(--credit); background: var(--card-bg); }

/* The loan true-up. A payment that will not clear the loan in time warns; one
   that finishes early is only information, so it borrows the accent rather
   than the owed colour -- being ahead is not a problem to solve. */
.banner-warn { border-color: var(--sun); background: var(--card-bg); }
.banner-info { border-color: var(--sky); background: var(--card-bg); }

/* ---------- Avatars ---------- */

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
  /* An avatar is a picture of a person, and a person's picture should not be
     the loudest thing on a page of numbers. */
  border: 1px solid var(--border);
}

.avatarfallback {
  font-weight: 600;
  /* The initials scale with the box, so one class serves the 26px header and
     the 72px profile header without a second rule per size. */
  font-size: 0.42em;
  line-height: 1;
  color: var(--surface);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  user-select: none;
}

/* Tints derived from the account id, so two people rarely collide. */
.tone-0 { background: var(--sky-deep); }
.tone-1 { background: var(--mint-deep); }
.tone-2 { background: var(--rose-deep); }
.tone-3 { background: var(--slate-soft); }
.tone-4 { background: #7a6a9c; }

/* The header link is the whole name-plus-picture, so the target is easy to hit
   on a phone rather than being a word of text. */
a.who {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  color: inherit;
  text-decoration: none;
  border-radius: 999px;
  padding: 2px var(--s2) 2px 2px;
}

a.who:hover { background: var(--chalk-sunk); }

/* A name preceded by its avatar, used in the participants and admin lists. */
.person {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
}

.profilehead {
  display: flex;
  align-items: center;
  gap: var(--s4);
}

.profilehead > div {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

/* ---------- Kind-scoped form fields ---------- */

/* The create form shows only the fields that belong to the chosen tab kind.
   :has() on the checked radio does the whole job with no JavaScript, which the
   content-security policy would forbid inline. Where :has() is unsupported the
   rules never match and every field stays visible -- the behaviour before this
   existed, which is a fair place to land rather than a broken form. */
.kindform:has(#kind-services:checked) .payoff-only,
.kindform:has(#kind-payoff:checked) .services-only {
  display: none;
}

/* The kind-scoped groups are plain sections, not boxes. A bordered fieldset
   inside a bordered card inside a max-width column stacks three sets of padding
   and squeezes the fields into a gutter, which is what made this form look
   cramped and misaligned. They keep the fieldset element for the grouping
   semantics and lose the decoration. */
.kindform > fieldset {
  border: none;
  padding: 0;
}

.kindform > fieldset > legend {
  padding: 0;
  font-size: var(--t-sm);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg-muted);
}

.kindchoice { gap: var(--s2); }

/* The kind choice itself: two stacked options, each a whole clickable row
   rather than a bare radio and a word. */
.kindchoice .kindoption {
  display: flex;
  /* .stack label sets column, which stacked the radio above its own text.
     Every property that rule sets has to be answered here, not just the ones
     that look wrong. */
  flex-direction: row;
  align-items: flex-start;
  font-weight: 400;
  gap: var(--s3);
  padding: var(--s3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
}

.kindchoice .kindoption:has(input:checked) {
  border-color: var(--accent);
  background: var(--chalk-sunk);
}

/* Keyboard focus has to remain visible: the radio is real and focusable, and
   the ring belongs on the row a sighted keyboard user is actually on. */
.kindchoice .kindoption:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.kindchoice .kindoption input {
  margin-top: 0.15rem;
  flex: 0 0 auto;
  width: auto;
}

.kindchoice .kindoption span {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

.kindchoice .kindoption small {
  color: var(--fg-muted);
}

/* ---------- Interval control ---------- */

/* "Repeat every [ 3 ] weeks" reads as one phrase, so the number and its unit
   sit on a line together rather than stacking like the other fields. */
.inline-field {
  display: flex;
  align-items: center;
  gap: var(--s2);
}

.inline-field .narrow-number {
  width: 5rem;
  flex: 0 0 auto;
}

.inline-field .unit {
  color: var(--fg-muted);
  font-size: var(--t-sm);
}

/* ---------- Wider screens ---------- */

@media (min-width: 40rem) {
  .wrap { padding: var(--s5) var(--s5) var(--s6); }
  .card { padding: var(--s5); }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

/* ---------- Phase 2: the settle loop ---------- */

/* A card carries a colored edge so balance direction reads before the number
   does -- the at-a-glance requirement (UI-01). */
.tabcard {
  border-left: 4px solid var(--border);
  padding: 0;
  overflow: hidden;
}

.tabcard.owed    { border-left-color: var(--rose); }
.tabcard.credit  { border-left-color: var(--mint); }
.tabcard.settled { border-left-color: var(--border); }

.tabhero { border-left: 4px solid var(--border); }
.tabhero.owed   { border-left-color: var(--rose); }
.tabhero.credit { border-left-color: var(--mint); }

.cardhead {
  display: block;
  padding: var(--s4);
  color: inherit;
  text-decoration: none;
  transition: background 120ms ease;
}

.cardhead:hover, .cardhead:focus-visible { background: var(--chalk-sunk); }

.cardfoot {
  padding: 0 var(--s4) var(--s4);
  display: flex;
  gap: var(--s2);
}

/* The full-balance settle is the primary path and keeps the width; the
   different-amount control sits beside it as a quieter second option. */
.cardfoot .settle { flex: 1 1 auto; }
.cardfoot button.ghost.settle { flex: 0 1 auto; border-color: var(--border); }

/* The confirmation step. It replaces the card in place rather than navigating,
   so the answer to "did that work" arrives where the question was asked. */
.confirming {
  padding: var(--s4);
  border-left-color: var(--sky);
  animation: liftin 140ms ease-out;
}

@keyframes liftin {
  from { opacity: 0.4; transform: translateY(-2px); }
  to   { opacity: 1;   transform: none; }
}

.confirmform { display: flex; flex-direction: column; gap: var(--s3); margin-top: var(--s3); }
.confirmq { margin: 0; }
.confirmbtns { display: flex; gap: var(--s2); }
.confirmbtns button { flex: 1; }

.methodrow { display: flex; flex-direction: column; gap: var(--s1); font-size: var(--t-sm); color: var(--fg-muted); }

button.ghost { background: transparent; }

select {
  font: inherit;
  font-size: max(1rem, var(--t-base));
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: var(--s3);
  min-height: 44px;
}

.inlineform select { flex: 0 1 auto; }

/* Freshly swapped content gets a brief settled-state wash, so a successful
   payment is visible without reading the number (UI-03). */
.tabcard.settled .cardhead { animation: settled 900ms ease-out; }

@keyframes settled {
  0%   { background: color-mix(in srgb, var(--mint) 35%, transparent); }
  100% { background: transparent; }
}

/* ---------- People and history ---------- */

.people { list-style: none; margin: 0 0 var(--s3); padding: 0; }
.people li { display: flex; align-items: center; gap: var(--s2); padding: var(--s2) 0; border-bottom: 1px solid var(--border); }
.people li:last-child { border-bottom: none; }

.pill-provider { color: var(--sky-deep); }
.pill-payee    { color: var(--fg-muted); }
.pill-reversal { color: var(--fg-muted); }

/* A reversed entry stays visible but recedes: the history is the record, and
   erasing it would defeat the point of an append-only ledger. */
tr.reversed td { opacity: 0.55; text-decoration: line-through; }
tr.reversed td:last-child { text-decoration: none; }
tr.reversal td { color: var(--fg-muted); }

.checkrow { flex-direction: row !important; align-items: center; gap: var(--s2) !important; }
.checkrow input { width: auto; min-height: auto; }

.navlink { color: var(--fg-muted); text-decoration: none; font-size: var(--t-sm); }
.navlink:hover { color: var(--fg); text-decoration: underline; }

/* htmx swaps content into the page; keep the transition quiet for anyone who
   has asked the system to reduce motion. */
@media (prefers-reduced-motion: reduce) {
  .confirming, .tabcard.settled .cardhead { animation: none; }
}

/* ---------- Phase 3: recurrence ---------- */

/* A tab's own schedule, stated once under the balance so both parties read the
   same sentence about when the next charge lands (SCHED-05). */
.schedline { margin-top: var(--s3); }

/* Reading a tab is what bills it, so a balance can move on load. This says so,
   because a charge appearing without anyone posting one is otherwise
   startling (SCHED-03). */
.posted {
  margin-top: var(--s2);
  padding: var(--s2) var(--s3);
  border-radius: var(--radius);
  border: 1px solid var(--sun);
  background: var(--card-bg);
  color: var(--fg);
}

/* ---------- Period statements (CHG-04) ---------- */

.statements { list-style: none; margin: 0 0 var(--s3); padding: 0; }

.statement {
  padding: var(--s3) 0 var(--s3) var(--s3);
  border-bottom: 1px solid var(--border);
  border-left: 3px solid var(--border);
}

.statement:last-child { border-bottom: none; }

/* The same edge language the tab cards use, so a period reads the way a tab
   does without learning a second vocabulary. */
.statement.owed     { border-left-color: var(--rose); }
.statement.overdue  { border-left-color: var(--owed); }
.statement.settled  { border-left-color: var(--mint); }
.statement.reversal { border-left-color: var(--border); }

.statement.overdue .muted { color: var(--owed); }

/* An undone period recedes rather than disappearing, matching how a reversed
   entry is treated in the history. */
.statement.reversal { opacity: 0.6; }

/* The breakdown is supporting detail under the period's own heading, so it
   sits quieter than a table that stands on its own. */
.breakdown { margin-top: var(--s2); font-size: var(--t-sm); }
.breakdown td { border-bottom: none; padding: var(--s1) 0; color: var(--fg-muted); }

/* ---------- Item editing (CHG-02) ---------- */

/* Editing lives behind a disclosure so the common case, reading what a tab
   covers, is a table rather than a screen of input boxes. */
.editor { margin-top: var(--s3); }

.editor > summary {
  cursor: pointer;
  font-size: var(--t-sm);
  color: var(--accent);
  padding: var(--s2) 0;
}

.itemedit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--border);
}

.itemedit .inlineform { flex: 1 1 auto; }

/* A message about what was just typed, shown on the card itself. htmx declines
   to swap a non-2xx response, so the amount comes back with the reason rather
   than the card appearing to have done nothing. */
.confirmerr {
  margin: var(--s3) 0 0;
  padding: var(--s2) var(--s3);
  border-radius: var(--radius);
  border: 1px solid var(--rose);
  color: var(--owed);
  font-size: var(--t-sm);
}

/* In the editable mode the amount is the thing being answered, so it is typed
   at full size rather than at the label's size. */
.confirmform .methodrow input { font-size: var(--t-lg); font-family: var(--font-num); }

/* ---------- Tab kind, archival, and people (TAB-01, TAB-02, TAB-03) ---------- */

/* The kind sits directly under the tab's name, because how a tab is measured
   changes how every number below it should be read. */
.kindline { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; margin: var(--s2) 0; }

.pill-kind-services { color: var(--sky-deep); }
.pill-kind-payoff   { color: var(--mint-deep); }

/* Archiving is a quiet, reversible act, so its control does not look like a
   primary action or like a destructive one. */
.archiveform { margin-top: var(--s4); padding-top: var(--s3); border-top: 1px solid var(--border); }

/* An archived tab recedes on the dashboard rather than disappearing: it still
   holds a real balance and a real history. */
.tabcard.archived { opacity: 0.6; }

.people li .pushright { margin-left: auto; }


/* ---------- Phase 4: payoff progress and late fees ---------- */

/* Loan repayment progress (PAYOFF-01). A calm track with a filled bar whose
   width is a rounded percent class -- no inline style, so the CSP stays strict. */
.progress {
  height: 8px;
  border-radius: 999px;
  background: var(--chalk-sunk);
  overflow: hidden;
  margin: var(--s2) 0;
}
.progressfill {
  height: 100%;
  border-radius: 999px;
  background: var(--mint);
  transition: width 200ms ease-out;
}
.progressfill.behind { background: var(--rose); }
.progressfill.ahead  { background: var(--mint-deep); }
.progressfill.settled { background: var(--mint-deep); }

.progressfill.w-0 { width: 0%; }
.progressfill.w-5 { width: 5%; }
.progressfill.w-10 { width: 10%; }
.progressfill.w-15 { width: 15%; }
.progressfill.w-20 { width: 20%; }
.progressfill.w-25 { width: 25%; }
.progressfill.w-30 { width: 30%; }
.progressfill.w-35 { width: 35%; }
.progressfill.w-40 { width: 40%; }
.progressfill.w-45 { width: 45%; }
.progressfill.w-50 { width: 50%; }
.progressfill.w-55 { width: 55%; }
.progressfill.w-60 { width: 60%; }
.progressfill.w-65 { width: 65%; }
.progressfill.w-70 { width: 70%; }
.progressfill.w-75 { width: 75%; }
.progressfill.w-80 { width: 80%; }
.progressfill.w-85 { width: 85%; }
.progressfill.w-90 { width: 90%; }
.progressfill.w-95 { width: 95%; }
.progressfill.w-100 { width: 100%; }

.payoffhead { align-items: flex-end; margin-bottom: var(--s2); }
.payoffhead .amount.big { display: block; }

/* Status reads in the palette's own semantics: behind is the owed colour,
   ahead and settled the credit colour (PAYOFF-02). */
.statusline.behind, .pill.status.behind { color: var(--owed); }
.statusline.ahead, .pill.status.ahead,
.statusline.settled, .pill.status.settled { color: var(--credit); }
.statusline.ontrack, .pill.status.ontrack { color: var(--fg-muted); }
.statusline { margin-top: var(--s1); font-weight: 600; }

/* A settled loan recedes on the dashboard, like an archived tab: it is done,
   but its history stays (PAYOFF-03). */
.tabcard.settledtab { opacity: 0.6; }

/* The upcoming payment or charge notice -- the in-app half of the reminder,
   shown ahead of the due date. */
.upcomingcard {
  border-left: 3px solid var(--sky);
}
.upcomingcard h2 { margin-top: 0; }

/* Fee configuration lays two related inputs side by side on wider screens. */
.feerow { display: flex; gap: var(--s3); flex-wrap: wrap; }
.feerow label { flex: 1 1 8rem; }

/* A late fee reads in the owed colour, like a charge. */
.pill-fee { color: var(--owed); }

/* Waiving a fee takes a short reason inline with the control (FEE-07). */
.waiveform { display: flex; gap: var(--s1); align-items: center; justify-content: flex-end; }
.waiveform input { width: 8rem; min-height: auto; padding: var(--s1) var(--s2); font-size: var(--t-sm); }

/* Interest reads in the owed colour, like a charge, but is labeled distinctly. */
.pill-interest { color: var(--owed); }

/* ---------- Setup / Operational collapsible groups ---------- */

/* A group gathers related sections under one collapsible header, so the setup
   forms can stay folded away during day-to-day use. */
.group { margin-bottom: var(--s4); }
.group > .grouphead {
  cursor: pointer;
  list-style: none;
  font-weight: 650;
  font-size: var(--t-lg);
  letter-spacing: -0.01em;
  padding: var(--s3) var(--s4);
  background: var(--chalk-sunk);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  gap: var(--s2);
}
.group > .grouphead::-webkit-details-marker { display: none; }
.group > .grouphead::before {
  content: "\25B8"; /* right-pointing triangle */
  color: var(--fg-muted);
  transition: transform 120ms ease;
}
.group[open] > .grouphead::before { transform: rotate(90deg); }
.group[open] > .grouphead { border-radius: var(--radius) var(--radius) 0 0; }

/* The body indents slightly and drops the top border so it reads as one unit
   with the header. */
.groupbody {
  border: 1px solid var(--border);
  border-top: none;
  border-radius: 0 var(--radius) var(--radius);
  padding: var(--s4) var(--s4) var(--s1);
  background: var(--bg);
}
.groupbody .card:last-child { margin-bottom: var(--s3); }

/* ---------- A card that folds ---------- */

/* One card whose own contents collapse, as against .group, which folds several
   cards under a shared header. The projected payment schedule uses it: on a
   long loan it is hundreds of rows of reference material, so it stays shut
   until it is asked for, and its summary carries the figures worth seeing
   without opening it. */
.fold > .foldhead {
  cursor: pointer;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s2);
  font-size: var(--t-lg);
  font-weight: 650;
  letter-spacing: -0.01em;
}
.fold > .foldhead::-webkit-details-marker { display: none; }
.fold > .foldhead::before {
  content: "\25B8"; /* right-pointing triangle */
  color: var(--fg-muted);
  font-size: var(--t-sm);
  transition: transform 120ms ease;
}
.fold[open] > .foldhead { margin-bottom: var(--s3); }
.fold[open] > .foldhead::before { transform: rotate(90deg); }

/* The projected schedule is a long numeric table -- a thirty-year loan is 360
   rows -- so it scrolls inside its own card rather than stretching the page,
   and its head stays put while it does. */
.foldscroll { max-height: 26rem; overflow: auto; }
.forecast { font-variant-numeric: tabular-nums; }
.forecast thead th {
  position: sticky;
  top: 0;
  background: var(--card-bg);
  box-shadow: inset 0 -1px 0 var(--border);
}
.forecast tfoot td { font-weight: 650; border-bottom: none; }

/* The running balance beside each history entry is supporting context for the
   amount it follows, so it sits a shade quieter than the amount itself. */
.amount.running { font-weight: 500; }

/* ---------- Field size counters ---------- */

/* How much of a bounded field is used, kept current by counter.js and rendered
   by the server so it is right with scripting off too. It reads as supporting
   detail until it matters, then it does not. */
.counter { font-variant-numeric: tabular-nums; white-space: nowrap; }
.counter.warn { color: var(--caution); font-weight: 600; }
.counter.over { color: var(--owed); font-weight: 700; }

/* ---------- Instance notification settings ---------- */

/* A list of states rather than of things, so it drops the bullets and lets each
   row read as "pill, what it is, why". */
.statuslist { list-style: none; margin: var(--s3) 0 0; padding: 0; }
.statuslist li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s2);
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--border);
}
.statuslist li:last-child { border-bottom: none; }

/* Ready is the good state and says so; off is neutral, not alarming. An
   unconfigured channel is a deployment that has not got there yet, and colouring
   it like a fault would teach an operator to ignore the colour. */
.pill-ready { color: var(--credit); }
.pill-notready { color: var(--fg-muted); }

/* The offline fallback page (UI-05). Served by the service worker when a
   navigation cannot reach the network; it uses this same stylesheet from cache,
   so it must lean on classes that are already here plus this small block. */
.offline {
  max-width: 30rem;
  margin: var(--s6) auto;
  text-align: center;
}
.offline-logo { opacity: 0.85; margin-bottom: var(--s3); }
.offline h1 { margin: 0 0 var(--s3); }
.offline p { color: var(--fg-muted); margin: 0 0 var(--s4); }
