@import url("./fonts/fonts.css");

/* ============================================================
   Passbook - account ledger
   A teller's instrument: ruled rows, a running balance column,
   and a spine of ins and outs down the left of the history.
   ============================================================ */

:root {
  --bg: #eef1f0;
  --surface: #ffffff;
  --surface-2: #f6f8f8;
  --surface-3: #eaeeee;
  --ink: #0e1618;
  --ink-2: #46585e;
  --muted: #7a8b91;
  --rule: #dce3e3;
  --rule-strong: #c3cfd0;
  --brand: #123b49;
  --brand-hover: #1b5567;
  --on-brand: #ffffff;
  --rail-bg: #123b49;
  --rail-ink: #ffffff;
  --rail-edge: transparent;
  --in: #0b7fae;
  /* Direction, where it has to read at a glance: green arriving, red leaving.
     Distinct from --in/--out, which are this ledger's older blue/orange pair
     and are still what amounts and pills use. */
  --pos: #1c7a51;
  --neg: #b3261e;
  --neutral-dir: #9a6a1c;

  --out: #b8551a;
  --accent: #6a4c93;
  --in-wash: #e7f2f8;
  --out-wash: #fbeee5;
  --danger: #a32b1e;
  --focus: #0b7fae;
  --hm-0: #e5e9e9;
  --hm-1: #cfe4ef;
  --hm-2: #94c8e1;
  --hm-3: #4a9dc6;
  --hm-4: #0b6f97;
  --shadow-1: 0 1px 2px rgba(14, 22, 24, .06), 0 6px 20px -12px rgba(14, 22, 24, .35);
  --shadow-2: 0 24px 60px -20px rgba(14, 22, 24, .35);

  --display: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
  --sans: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --radius: 10px;
  --radius-sm: 6px;
  color-scheme: light;
}

:root[data-theme="dark"] {
  /* The same pair, lifted for a dark ground. */
  --pos: #4fb383;
  --neg: #e2705f;
  --neutral-dir: #d3a04a;
  --bg: #0e1417;
  --surface: #151b1e;
  --surface-2: #1b2327;
  --surface-3: #222c30;
  --ink: #e8eff0;
  --ink-2: #a8babf;
  --muted: #7a8f95;
  --rule: #263033;
  --rule-strong: #354347;
  --brand: #58a9c4;
  --brand-hover: #79c0d8;
  --on-brand: #08161b;
  --rail-bg: #0b1114;
  --rail-ink: #dbe6e8;
  --rail-edge: #263033;
  --in: #3aa0d2;
  --out: #d07220;
  --accent: #9b7dc9;
  --in-wash: #14313f;
  --out-wash: #35240f;
  --danger: #e2705f;
  --focus: #58a9c4;
  --hm-0: #20282c;
  --hm-1: #123c52;
  --hm-2: #175a7a;
  --hm-3: #2b84ac;
  --hm-4: #57add7;
  --shadow-1: 0 1px 2px rgba(0, 0, 0, .4), 0 6px 20px -12px rgba(0, 0, 0, .7);
  --shadow-2: 0 24px 60px -20px rgba(0, 0, 0, .8);
  color-scheme: dark;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

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

a { color: inherit; text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 3px;
}

.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

.eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ------------------------------- shell -------------------------------- */

.shell { display: flex; flex-direction: column; min-height: 100%; }

/* Everything you can reach from anywhere, across the top: the name, the two
   sections as tabs, and the state of the connection. */
.topbar {
  position: sticky;
  top: 0;
  display: flex;
  align-items: center;
  gap: 26px;
  padding: 0 26px;
  background: var(--rail-bg);
  color: var(--rail-ink);
  border-bottom: 1px solid var(--rail-edge);
  z-index: 20;
}

/* `flex: none` so the name does not shrink to make room for the sections - the
   nav scrolls instead, which is the one thing in the header that can. */
.brand { display: flex; align-items: baseline; gap: 8px; flex: none; }

.brand__mark {
  font-family: var(--display);
  font-weight: 700;
  font-size: 20px;
  letter-spacing: -.02em;
}

.brand__tag {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  opacity: .7;
}

/* The sections, across the top.
   
   `min-width: 0` so this can shrink below the width of its own items, and
   `overflow-x: auto` so what will not fit scrolls HERE rather than pushing the
   page sideways. Nine items plus the brand plus four controls on the right is
   more than a 1366px laptop has room for, and the alternatives are worse: the
   page gaining a horizontal scrollbar, or items being hidden at a width where
   somebody is still looking for them.
   
   Nothing is hidden and nothing shrinks: the labels keep their size, the icons
   keep theirs, and below 960px this whole row is replaced by the tab bar as it
   always has been. */
.nav {
  display: flex;
  gap: 4px;
  align-self: stretch;
  min-width: 0;
  overflow-x: auto;
  /* No visible bar across the middle of the header. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.nav::-webkit-scrollbar { display: none; }


/* Tabs, so the section you are in is underlined rather than merely lit. */
.nav__item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 18px 14px 16px;
  border-bottom: 2px solid transparent;
  font-size: 14px;
  font-weight: 500;
  color: color-mix(in srgb, var(--rail-ink) 72%, transparent);
  transition: color .12s ease, border-color .12s ease, background .12s ease;
}

.nav__item:hover { background: color-mix(in srgb, var(--rail-ink) 8%, transparent); color: var(--rail-ink); }
.nav__item[aria-current="page"] { color: var(--rail-ink); border-bottom-color: var(--rail-ink); }
.nav__item svg { width: 17px; height: 17px; flex: none; }

/* Syncing, Extension, the theme toggle and Lock Passbook, pinned right.
   `flex: none` for the same reason the brand has it: when the sections do not
   fit, the nav scrolls and these stay exactly where they are. */
.topbar__end {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 14px;
  flex: none;
}

.sync {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .04em;
  color: color-mix(in srgb, var(--rail-ink) 72%, transparent);
}

.sync__dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; flex: none;
}
.sync[data-state="live"] .sync__dot { background: #57d38f; box-shadow: 0 0 0 3px color-mix(in srgb, #57d38f 22%, transparent); }
.sync[data-state="polling"] .sync__dot { background: #e0b64a; }
.sync[data-state="offline"] .sync__dot { background: #e2705f; }

.main {
  flex: 1;
  min-width: 0;
  padding: 26px 30px 90px;
}

.page { max-width: 1360px; margin: 0 auto; }

.page__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}

.page__title {
  font-family: var(--display);
  font-size: clamp(24px, 3vw, 32px);
  font-weight: 700;
  letter-spacing: -.02em;
  margin: 4px 0 0;
  line-height: 1.1;
}

.page__sub { color: var(--ink-2); font-size: 14px; margin: 6px 0 0; }

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

.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background .12s ease, border-color .12s ease, transform .06s ease;
  white-space: nowrap;
}

.btn:hover { background: var(--surface-2); border-color: var(--muted); }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .5; cursor: not-allowed; }
.btn svg { width: 15px; height: 15px; }
/* An icon that gets swapped out lives in its own box, which must not add a
   text line's descender under it. */
.btn__icon { display: inline-flex; }

.btn--primary { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
.btn--primary:hover { background: var(--brand-hover); border-color: var(--brand-hover); }
.btn--ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn--ghost:hover { background: var(--surface-3); color: var(--ink); }
.btn--danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, var(--rule)); }
.btn--danger:hover { background: color-mix(in srgb, var(--danger) 10%, transparent); }
.btn--sm { padding: 5px 10px; font-size: 13px; }
.btn--in { background: var(--in); border-color: var(--in); color: #fff; }
.btn--out { background: var(--out); border-color: var(--out); color: #fff; }

.input, .select, textarea.input {
  width: 100%;
  padding: 8px 11px;
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  font-size: 14px;
}

.input::placeholder { color: var(--muted); }
.select { appearance: none; padding-right: 28px; cursor: pointer; }

.field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.field > label { font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }

/* Platform, as chips rather than a dropdown. One row, thumb-sized, and the
   chosen one is obvious without reading it - which is what makes this quicker
   on a phone than a select that opens a wheel. */
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  flex: 0 0 auto;
  min-width: 46px;
  padding: 7px 10px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink-2);
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .04em;
  cursor: pointer;
}
.chip:hover { border-color: var(--muted); color: var(--ink); }
.chip--on {
  border-color: var(--in);
  background: color-mix(in srgb, var(--in) 16%, var(--surface));
  color: var(--ink);
}
.chip:focus-visible { outline: 2px solid var(--in); outline-offset: 2px; }

/* An input with an action beside it. The input takes what is left, so the
   button never pushes it off a narrow screen. */
.field__row { display: flex; gap: 6px; align-items: stretch; }
.field__row > .input { flex: 1 1 auto; min-width: 0; }
.btn--icon {
  flex: 0 0 auto;
  min-width: 40px;
  padding: 0 10px;
  font-size: 15px;
  line-height: 1;
}

/* Tighter than the rest of the app's forms on purpose: this one is filled in
   on a phone, one-handed, many times a day. */
.createplayer { display: flex; flex-direction: column; gap: 10px; }
.createplayer .field { gap: 4px; }

/* The line under a field that explains it. It had no rule at all, so every
   hint in a dialog was set at body size and read as loudly as the field. */
.field__hint {
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--muted);
}
/* A name the platform will refuse. Said in the same place the hint about what
   it will be created as would have been, because it replaces that hint: there
   is no point telling somebody what a name will become when it will not. */
.field__hint--bad { color: var(--danger, #a32b1e); }

/* Where a suggested username came from. Quieter still - it is provenance, not
   instruction - with the detected name set apart so it can be checked against
   what was copied without reading the sentence around it. */
.field__hint--clip {
  display: block;
  margin-top: 2px;
}
.field__clipName {
  font-family: var(--mono);
  color: var(--ink);
  font-weight: 600;
}

.search {
  position: relative;
  flex: 1 1 240px;
  min-width: 0;
}
.field > .search { flex: none; width: 100%; }
.search .input { padding-left: 34px; }
.search svg {
  position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--muted); pointer-events: none;
}

.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: flex-end;
  padding: 14px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  margin-bottom: 14px;
  box-shadow: var(--shadow-1);
}

.toolbar__row { display: flex; gap: 10px; flex-wrap: wrap; width: 100%; align-items: flex-end; }
.toolbar .field { flex: 0 1 150px; }

.chips { display: flex; gap: 6px; flex-wrap: wrap; }

.chip {
  padding: 5px 10px;
  border: 1px solid var(--rule-strong);
  border-radius: 100px;
  background: var(--surface);
  font-size: 12.5px;
  cursor: pointer;
  color: var(--ink-2);
  white-space: nowrap;
}
.chip:hover { border-color: var(--muted); }
.chip[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--surface); }

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

.card {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
}

.card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--rule);
  flex-wrap: wrap;
}

.card__title { font-family: var(--display); font-size: 15px; font-weight: 700; letter-spacing: -.01em; margin: 0; }
.card__body { padding: 16px; }

/* ------------------------------ summary ------------------------------- */

.summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 18px;
}

.stat { background: var(--surface); padding: 14px 16px; min-width: 0; }
.stat__label { font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.stat__value {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -.02em;
  margin-top: 5px;
  overflow-wrap: anywhere;
}
.stat__note { font-size: 12px; color: var(--muted); margin-top: 2px; }

/* ------------------------------- avatars ------------------------------ */

.avatar {
  width: 34px; height: 34px;
  border-radius: 9px;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  color: #fff;
  flex: none;
  overflow: hidden;
  position: relative;
  letter-spacing: .02em;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 26px; height: 26px; border-radius: 7px; font-size: 10.5px; }
.avatar--lg { width: 54px; height: 54px; border-radius: 14px; font-size: 18px; }

/* A shortcut is longer than a pair of initials, so it is set tighter to keep
   it inside the same square. Three characters is the usual case. */
.avatar--three { font-size: 11px; letter-spacing: -.02em; }
.avatar--long { font-size: 9px; letter-spacing: -.03em; }
.avatar--sm.avatar--three { font-size: 9.5px; letter-spacing: -.03em; }
.avatar--sm.avatar--long { font-size: 8px; letter-spacing: -.04em; }
.avatar--lg.avatar--three { font-size: 17px; letter-spacing: -.02em; }
.avatar--lg.avatar--long { font-size: 13px; letter-spacing: -.03em; }

.avatar--recent::after {
  content: "";
  position: absolute;
  inset: auto -1px -1px auto;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: #2fa36a;
  border: 2px solid var(--surface);
}

/* -------------------------------- pills ------------------------------- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 7px;
  border-radius: 100px;
  border: 1px solid var(--rule-strong);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}
.pill--active { color: #1c7a51; border-color: color-mix(in srgb, #1c7a51 35%, var(--rule)); }
.pill--inactive { color: var(--muted); }
.pill--recent { color: var(--in); border-color: color-mix(in srgb, var(--in) 35%, var(--rule)); background: var(--in-wash); }
.pill--in { color: var(--in); border-color: color-mix(in srgb, var(--in) 40%, var(--rule)); }
.pill--out { color: var(--out); border-color: color-mix(in srgb, var(--out) 40%, var(--rule)); }
.pill--muted { color: var(--muted); border-style: dashed; }

.dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }

.metaline { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ------------------------------- ledger ------------------------------- */

.ledger { display: flex; flex-direction: column; }

.ledger__head {
  display: grid;
  gap: 12px;
  padding: 9px 16px;
  border-bottom: 1px solid var(--rule-strong);
  background: var(--surface-2);
  border-radius: var(--radius) var(--radius) 0 0;
}

.ledger__head button,
.ledger__head span {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  cursor: default;
  min-width: 0;
}
.ledger__head button { cursor: pointer; display: inline-flex; align-items: center; gap: 4px; }
.ledger__head button:hover { color: var(--ink); }
.ledger__head button[data-active="true"] { color: var(--ink); }
.num { text-align: right; justify-content: flex-end; }

.lrow {
  display: grid;
  gap: 12px;
  align-items: center;
  padding: 11px 16px;
  border-bottom: 1px solid var(--rule);
  background: var(--surface);
  text-align: left;
  width: 100%;
  border-left: 0; border-right: 0; border-top: 0;
  cursor: pointer;
  transition: background .1s ease;
}
.lrow:last-child { border-bottom: 0; }
.lrow:hover { background: var(--surface-2); }
.lrow:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

.cell { min-width: 0; }
.cell--num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }

.who { display: flex; align-items: center; gap: 10px; min-width: 0; }
.who__name { font-weight: 600; font-size: 14.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.who__sub { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.shortcut {
  font-family: var(--mono);
  font-size: 11px;
  padding: 2px 6px;
  border-radius: 4px;
  background: var(--surface-3);
  color: var(--ink-2);
  letter-spacing: .06em;
  text-transform: uppercase;
}

.balance { font-family: var(--mono); font-variant-numeric: tabular-nums; font-weight: 600; font-size: 15px; }
.amount { font-family: var(--mono); font-variant-numeric: tabular-nums; font-weight: 600; }
.amount--in { color: var(--in); }
.amount--out { color: var(--out); }

/* A remark is what the entry is about, so it gets room to be read rather than
   a single line to be cut off in - on a wide table as much as on a phone.
   Anything past two lines ends in the clamp's ellipsis and sits in full in the
   cell's own title. */
.remark {
  font-size: 13.5px;
  color: var(--ink-2);
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
/* The clamp belongs to each line rather than the cell, or a note under a
   remark would eat the two lines between them. */
.remark__text,
.remark__note {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
}
/* Put on the receipt by hand before it was filed: quieter than the remark,
   and labelled so the two are never read as one. */
.remark__note {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--muted);
  /* 'No remark' above it is set in italics; the note is not that. */
  font-style: normal;
}
.remark--empty { color: var(--muted); font-style: italic; }

/* The spine: a hairline down the history with a tick per entry, left for
   money out, right for money in. Scrolling it reads like a passbook margin. */
.spine {
  position: relative;
  width: 22px;
  height: 26px;
  flex: none;
}
.spine::before {
  content: "";
  position: absolute;
  left: 50%; top: -32px; bottom: -32px;
  width: 1px;
  background: var(--rule-strong);
}
.spine::after {
  content: "";
  position: absolute;
  top: 50%;
  height: 2px;
  width: 9px;
  border-radius: 1px;
  transform: translateY(-50%);
}
.spine--in::after { left: 50%; background: var(--in); }
.spine--out::after { right: 50%; background: var(--out); }
.spine--first::before { top: 50%; }
.spine--last::before { bottom: 50%; }

/* When the row reached the ledger, under the date the entry itself carries.
   Quieter than the date, because it is about the record rather than the money. */
/* Where an entry stands with verification. Only ever drawn for entries that
   need it; an account whose email arrives shows none of this. */
/* The verification queue: an account, then what it is waiting on. */
/* ---------------------------- CSV mapping import -------------------------- */

.csvsample {
  margin: 0 0 12px; padding: 9px 11px;
  background: var(--surface-2); border: 1px solid var(--rule); border-radius: var(--radius-sm);
  font-family: var(--mono); font-size: 11.5px; color: var(--ink-2);
  overflow-x: auto;
}
.csvsummary {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
  gap: 1px; background: var(--rule);
  border: 1px solid var(--rule); border-radius: var(--radius-sm);
  overflow: hidden; margin-bottom: 12px;
}
.csvstat { background: var(--surface); padding: 9px 11px; }
.csvstat__value { font-family: var(--mono); font-size: 17px; font-weight: 600; }
.csvstat__label {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}
.csvstat--new .csvstat__value { color: #1c7a51; }
.csvstat--conflict .csvstat__value { color: var(--danger); }
.csvstat--invalid .csvstat__value { color: var(--danger); }
.csvstat--unmatched .csvstat__value { color: var(--out); }

/* The table scrolls inside itself; the dialog does not grow sideways. */
.csvtable-wrap { max-height: 46vh; overflow: auto; border: 1px solid var(--rule); border-radius: var(--radius-sm); }
.csvtable { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.csvtable th, .csvtable td { padding: 7px 9px; text-align: left; border-bottom: 1px solid var(--rule); vertical-align: top; }
.csvtable th {
  position: sticky; top: 0; background: var(--surface-2);
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted); z-index: 1;
}
.csvtable tr:last-child td { border-bottom: 0; }
.csvtable td.mono { font-family: var(--mono); font-size: 11.5px; }

/* Each status readable at a glance, and not by colour alone - the word is
   there too, which is what the pill says. */
.csvpill { font-family: var(--mono); font-size: 9.5px; letter-spacing: .06em; white-space: nowrap; }
.csvpill--new { color: #1c7a51; border-color: color-mix(in srgb, #1c7a51 45%, var(--rule)); }
.csvpill--duplicate { color: var(--muted); }
.csvpill--conflict { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--rule)); }
.csvpill--unmatched { color: var(--out); border-color: color-mix(in srgb, var(--out) 45%, var(--rule)); }
.csvpill--invalid { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--rule)); }
.csvrow--conflict, .csvrow--invalid { background: color-mix(in srgb, var(--danger) 6%, transparent); }
.csvrow--unmatched { background: color-mix(in srgb, var(--out) 6%, transparent); }
.csvrow--new { background: color-mix(in srgb, #1c7a51 6%, transparent); }

.csvdone { margin: 8px 0 0; padding-left: 18px; color: var(--ink-2); font-size: 14px; }
.csvdone li { margin: 3px 0; }

.vgroup { border: 1px solid var(--rule); border-radius: var(--radius-sm); overflow: hidden; }
.vgroup__head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 13px; background: var(--surface-2);
  border-bottom: 1px solid var(--rule); color: inherit;
  flex-wrap: wrap;
}
/* The account and what it is worth; the action sits opposite it. */
.vgroup__who { display: flex; align-items: center; gap: 10px; min-width: 0; color: inherit; }
.vgroup__who b { color: var(--ink-2); }
.vrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 12px; align-items: center;
  padding: 11px 13px;
  border-bottom: 1px solid var(--rule);
}
.vrow:last-child { border-bottom: 0; }
.vrow__what { display: block; font-weight: 600; color: inherit; overflow-wrap: anywhere; }
.vrow__state { font-family: var(--mono); font-size: 11.5px; color: var(--muted); white-space: nowrap; }
.vrow__state--ready { color: var(--in); }
.vrow__state--verified { color: #1c7a51; }
.vrow__state--failed { color: var(--danger); }
.vrow__act { justify-self: end; }

/* Turning while a verification is in the air. */
.spinner {
  width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--ink) 25%, transparent);
  border-top-color: var(--ink);
  display: inline-block;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

@media (max-width: 620px) {
  .vrow { grid-template-columns: minmax(0, 1fr) auto; }
  .vrow__act { grid-column: 2; }
  .vrow__state { grid-column: 1 / -1; }
}

.pill--verify { font-family: var(--mono); }
.pill--verify-pending { color: var(--muted); }
.pill--verify-ready { color: var(--in); border-color: color-mix(in srgb, var(--in) 45%, var(--rule)); }
.pill--verify-verifying { color: var(--muted); }
.pill--verify-verified { color: #1c7a51; border-color: color-mix(in srgb, #1c7a51 45%, var(--rule)); }
.pill--verify-failed { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--rule)); }

.addedago {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--muted);
  margin-top: 2px;
}
.addedago:empty { display: none; }

.timecell { font-family: var(--mono); font-size: 12.5px; font-variant-numeric: tabular-nums; }
.timecell b { display: block; font-weight: 500; color: var(--ink); }
.timecell span { color: var(--muted); font-size: 11.5px; }

/* Account list columns */
.ledger--accounts .ledger__head,
.ledger--accounts .lrow {
  grid-template-columns: minmax(180px, 2fr) minmax(104px, .85fr) 60px minmax(238px, 1.5fr) 96px 90px;
}

/* History columns */
.ledger--history .ledger__head,
.ledger--history .lrow {
  grid-template-columns: 22px 118px minmax(150px, 1.4fr) 104px minmax(96px, .8fr) minmax(140px, 1.6fr) minmax(104px, .9fr) 78px 118px;
}

/* The same row on an account page, where there is no account column to show
   and nothing to copy: six columns instead of nine. */
.ledger--history.ledger--recent .lrow {
  grid-template-columns: 22px 128px 112px minmax(96px, .8fr) minmax(140px, 1.6fr) minmax(96px, .9fr);
}

/* ------------------------------- heatmap ------------------------------ */

.heatmap-wrap { overflow-x: auto; padding-bottom: 4px; }

.heatmap {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(7, 12px);
  gap: 3px;
  width: max-content;
}

.hm-cell {
  width: 12px; height: 12px;
  border-radius: 2px;
  background: var(--hm-0);
  border: 0;
  padding: 0;
  cursor: pointer;
}
.hm-cell[data-level="1"] { background: var(--hm-1); }
.hm-cell[data-level="2"] { background: var(--hm-2); }
.hm-cell[data-level="3"] { background: var(--hm-3); }
.hm-cell[data-level="4"] { background: var(--hm-4); }
.hm-cell[data-empty="true"] { visibility: hidden; cursor: default; }
.hm-cell:hover { outline: 2px solid var(--ink); outline-offset: 1px; }

.hm-months {
  display: grid;
  grid-auto-flow: column;
  gap: 3px;
  width: max-content;
  margin-bottom: 5px;
  font-family: var(--mono);
  font-size: 10px;
  color: var(--muted);
  height: 13px;
}

.hm-legend {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--muted);
  margin-top: 10px;
}
.hm-legend i { width: 12px; height: 12px; border-radius: 2px; display: inline-block; }

.tooltip {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  background: var(--ink);
  color: var(--bg);
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  font-size: 12px;
  line-height: 1.45;
  box-shadow: var(--shadow-2);
  max-width: 240px;
}
.tooltip b { font-family: var(--mono); font-weight: 600; }
.tooltip[hidden] { display: none; }

/* ----------------------------- period grid ---------------------------- */

.periods {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
}

.period { background: var(--surface); padding: 15px 16px; }
.period__label { font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.period__net {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -.02em;
  margin: 6px 0 10px;
}
.period__rows { display: flex; flex-direction: column; gap: 5px; font-size: 13px; }
.period__row { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; }
.period__row span { color: var(--ink-2); display: inline-flex; align-items: center; gap: 6px; }
.period__row b { font-family: var(--mono); font-variant-numeric: tabular-nums; font-weight: 600; }
.period__count { color: var(--muted); font-family: var(--mono); font-size: 11.5px; }
.tick { width: 8px; height: 2px; border-radius: 1px; display: inline-block; }
.tick--in { background: var(--in); }
.tick--out { background: var(--out); }

/* -------------------------------- hero -------------------------------- */

.hero {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  margin-bottom: 18px;
}

.hero__balance {
  font-family: var(--display);
  font-size: clamp(30px, 5vw, 44px);
  font-weight: 700;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.hero__spacer { flex: 1; }

/* ------------------------------- detail ------------------------------- */

.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1px;
  background: var(--rule);
  border-top: 1px solid var(--rule);
}
.detail-item { background: var(--surface); padding: 13px 16px; }
.detail-item dt { font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.detail-item dd { margin: 5px 0 0; font-size: 14.5px; overflow-wrap: anywhere; }
.detail-item dd.mono { font-size: 13px; }

.remark-block {
  padding: 14px 16px;
  background: var(--surface-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  font-size: 15px;
  line-height: 1.55;
}
.remark-block--original { border-style: dashed; background: transparent; }

.flow {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}
.flow__step { display: flex; flex-direction: column; gap: 3px; }
.flow__step span { font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.flow__step b { font-size: 19px; font-weight: 600; }
.flow__arrow { color: var(--muted); }

/* -------------------------------- chart ------------------------------- */

.chart { width: 100%; height: auto; display: block; touch-action: pan-y; }
.chart .grid-line { stroke: var(--rule); stroke-width: 1; }
.chart .axis-label { fill: var(--muted); font-family: var(--mono); font-size: 10px; }
.chart .series { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart .series-fill { fill: color-mix(in srgb, var(--accent) 12%, transparent); }
.chart .crosshair { stroke: var(--ink); stroke-width: 1; stroke-dasharray: 3 3; }
.chart .marker { fill: var(--accent); stroke: var(--surface); stroke-width: 2; }

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

.scrim {
  position: fixed;
  inset: 0;
  background: color-mix(in srgb, var(--ink) 45%, transparent);
  display: grid;
  place-items: center;
  padding: 18px;
  z-index: 50;
  backdrop-filter: blur(2px);
}

.modal {
  width: min(560px, 100%);
  max-height: 88vh;
  overflow: auto;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
}

.modal__head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 15px 18px; border-bottom: 1px solid var(--rule);
  position: sticky; top: 0; background: var(--surface); z-index: 1;
}
.modal__body { padding: 18px; display: flex; flex-direction: column; gap: 14px; }
.modal__foot {
  display: flex; justify-content: flex-end; gap: 9px;
  padding: 14px 18px; border-top: 1px solid var(--rule);
  position: sticky; bottom: 0; background: var(--surface);
}

.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 13px; }

.segmented { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.segmented button {
  padding: 9px 6px;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
}
.segmented button[aria-pressed="true"] { border-color: var(--ink); background: var(--ink); color: var(--surface); }
.segmented button[data-dir="in"][aria-pressed="true"] { background: var(--in); border-color: var(--in); color: #fff; }
.segmented button[data-dir="out"][aria-pressed="true"] { background: var(--out); border-color: var(--out); color: #fff; }

/* ---------------------------- install steps ---------------------------- */

/* What changed in the panel, and when. Folded away unless there is an update
   waiting, so the dialog stays about downloading it. */
.extlog { border: 1px solid var(--rule); border-radius: var(--radius-sm); background: var(--surface-2); }
.extlog > summary {
  padding: 8px 12px;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}
.extlog > summary:hover { color: var(--ink); }
.extlog[open] > summary { border-bottom: 1px solid var(--rule); }
.extlog__list { margin: 0; padding: 8px 12px 10px; list-style: none; max-height: 34vh; overflow: auto; }
.extlog__entry + .extlog__entry { margin-top: 10px; border-top: 1px solid var(--rule); padding-top: 10px; }
.extlog__when {
  font-family: var(--mono); font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 3px;
}
.extlog__notes { margin: 0; padding-left: 16px; font-size: 13.5px; color: var(--ink-2); }
.extlog__notes li { margin: 2px 0; }

.steps {
  margin: 0;
  padding-left: 20px;
  display: grid;
  gap: 7px;
  font-size: 13.5px;
  color: var(--ink-2);
}
.steps li::marker { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.steps b { color: var(--ink); font-weight: 600; }

/* --------------------------- history, by account ----------------------- */

/* A band that opens each account's run of entries in the grouped arrangement. */
.lgroup {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  gap: 10px;
  align-items: center;
  padding: 10px 14px;
  border-top: 1px solid var(--rule-strong);
  background: var(--surface-2);
  color: inherit;
  text-decoration: none;
  position: sticky;
  top: 0;
  z-index: 1;
}
.lgroup:first-child { border-top: 0; }
.lgroup:hover { background: var(--surface-3); }
.lgroup__name { font-weight: 600; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lgroup__count { font-family: var(--mono); font-size: 11px; color: var(--muted); white-space: nowrap; }
.lgroup__net { font-family: var(--mono); font-variant-numeric: tabular-nums; font-weight: 600; font-size: 14px; white-space: nowrap; }
.lgroup__net--in { color: var(--in); }
.lgroup__net--out { color: var(--out); }

.pill--dupe { color: var(--out); border-color: color-mix(in srgb, var(--out) 45%, var(--rule)); background: var(--out-wash); }

/* ------------------------------- receipt ------------------------------ */

/* The reader is a modal, so its parts sit inside .modal__body's column. */
/* .modal__body already pads; the reader only sets its own rhythm. */
.receipt { display: flex; flex-direction: column; gap: 14px; }
.receipt__foot { display: contents; }

.receipt__head { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: center; }
.receipt__thumb {
  width: 56px; height: 56px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  background: var(--surface-3);
  border: 1px solid var(--rule);
}
.receipt__name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.receipt__drop {
  display: grid; justify-items: center; gap: 4px;
  padding: 34px 18px;
  border: 2px dashed var(--rule-strong);
  border-radius: var(--radius);
  background: var(--surface-2);
  text-align: center;
  cursor: pointer;
}
.receipt__drop:hover { border-color: var(--in); }
.receipt__drop b { font-family: var(--display); font-size: 15px; }

/* Reading takes a couple of seconds; say so rather than freezing the dialog. */
.receipt__bar {
  height: 3px; border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--in), transparent);
  background-size: 40% 100%;
  background-repeat: no-repeat;
  animation: receipt-sweep 1.1s ease-in-out infinite;
}
@keyframes receipt-sweep { from { background-position: -40% 0; } to { background-position: 140% 0; } }

.receipt__hint { margin: 0; font-size: 12.5px; color: var(--muted); }
.receipt__hint--warn { color: var(--out); }
/* Recording anyway is a choice, not an accident: say so above the fields. */
.receipt__dupe {
  display: grid;
  gap: 3px;
  padding: 11px 13px;
  border-radius: var(--radius-sm);
  background: var(--out-wash);
  border: 1px solid color-mix(in srgb, var(--out) 40%, var(--rule));
  font-size: 12.5px;
  color: var(--ink-2);
}
.receipt__dupe b {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--out);
}

.receipt__facts { margin: 0; font-size: 11.5px; color: var(--muted); overflow-wrap: anywhere; }

.receipt__text summary {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted); cursor: pointer;
}
.receipt__text summary:hover { color: var(--ink); }
.receipt__text pre {
  margin: 8px 0 0;
  padding: 10px 12px;
  max-height: 240px;
  overflow: auto;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.55;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

#receipt-form { display: flex; flex-direction: column; gap: 14px; }

/* -------------------------------- drop veil ---------------------------- */

.veil {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  backdrop-filter: blur(2px);
}
.veil[hidden] { display: none; }
.veil__card {
  display: grid; justify-items: center; gap: 5px;
  padding: 26px 34px;
  border: 2px dashed var(--in);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-2);
  text-align: center;
  pointer-events: none;
}
.veil__card svg { width: 30px; height: 30px; color: var(--in); }
.veil__card b { font-family: var(--display); font-size: 16px; }
.veil__card span { font-family: var(--mono); font-size: 11px; color: var(--muted); }

/* -------------------------------- misc -------------------------------- */

.empty {
  padding: 46px 20px;
  text-align: center;
  color: var(--muted);
}
.empty h3 { font-family: var(--display); color: var(--ink); margin: 0 0 6px; font-size: 17px; }
.empty p { margin: 0 auto 14px; max-width: 380px; font-size: 14px; }

.skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.3s ease infinite;
  border-radius: var(--radius-sm);
  height: 44px;
}
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }

.toasts {
  position: fixed;
  right: 18px; bottom: 18px;
  display: flex; flex-direction: column; gap: 8px;
  z-index: 70;
  max-width: min(360px, calc(100vw - 36px));
}

.toast {
  background: var(--ink);
  color: var(--bg);
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-2);
  font-size: 13.5px;
  animation: rise .2s ease;
  display: flex;
  align-items: center;
  gap: 10px;
}
.toast--error { background: var(--danger); color: #fff; }
.toast__text { flex: 1; min-width: 0; }
/* Borrows the toast's own colour so it reads on the dark and the red alike. */
.toast__act {
  flex: none;
  padding: 3px 9px;
  border: 1px solid currentColor;
  border-radius: 5px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.toast__act:hover { background: rgba(255, 255, 255, .17); }
.toast__act:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

.pager { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 16px; flex-wrap: wrap; }
.pager__info { font-family: var(--mono); font-size: 12px; color: var(--muted); }

.tabbar { display: none; }

.row-actions { display: flex; gap: 6px; justify-content: flex-end; }

.quick {
  display: flex;
  gap: 8px;
  align-items: flex-end;
  flex-wrap: wrap;
}
.quick .field { flex: 1 1 130px; }

.breadcrumb {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--ink-2);
  margin-bottom: 2px;
  white-space: nowrap;
}
.breadcrumb:hover { color: var(--ink); }

.split { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 18px; align-items: start; }
.stack { display: flex; flex-direction: column; gap: 18px; }

/* ----------------------------- responsive ----------------------------- */

@media (max-width: 1180px) {
  .ledger--history .ledger__head,
  .ledger--history .lrow {
    grid-template-columns: 22px 112px minmax(130px, 1.3fr) 100px minmax(90px, .8fr) minmax(120px, 1.4fr) 92px;
  }
  .ledger--history .col-device { display: none; }
  .split { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 960px) {
  .topbar { padding: 0 14px; gap: 12px; }
  /* The sections move to the bar along the bottom, where a thumb can reach. */
  .topbar .nav { display: none; }
  .topbar__end { gap: 10px; }

  /* Room for the bar, plus whatever the phone reserves below it. The bar is
     one row - see below - so this is the height of one row and no guessing. */
  .main { padding: 16px 14px calc(74px + env(safe-area-inset-bottom, 0px)); }

  .tabbar {
    display: grid;
    /* One column per section - seven of them since the mail and the
       recommendation joined. minmax(0, 1fr) rather than 1fr: a plain 1fr
       floors at the content's width, so one long label would push the others
       out instead of being truncated. At 320px that is 45px a column, which
       truncates "Accounts" - the intent, but worth knowing. */
    grid-template-columns: repeat(7, minmax(0, 1fr));
    position: fixed;
    inset: auto 0 0 0;
    background: var(--surface);
    border-top: 1px solid var(--rule);
    z-index: 30;
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  .tabbar a {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    /* 44px of height before padding, which is the smallest a thumb should be
       asked to hit. */
    min-height: 44px;
    min-width: 0;
    padding: 9px 2px;
    font-family: var(--mono);
    font-size: 10px;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--muted);
    text-align: center;
  }
  /* The label is its own element so it can be cut short without taking the
     icon with it. Nothing here wraps: a second line is what broke this. */
  .tabbar a > span {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .tabbar a[aria-current="page"] { color: var(--accent, var(--ink)); }
  .tabbar svg { width: 20px; height: 20px; flex: none; }

  /* Below about 340px the letter-spacing is what stops five labels fitting. */
  @media (max-width: 340px) {
    .tabbar a { font-size: 9px; letter-spacing: .02em; padding: 8px 1px; }
    .tabbar svg { width: 18px; height: 18px; }
  }

  /* The verification queue as cards. Three columns - entry, status, action -
     do not fit a phone: the status is pushed off and the buttons are squeezed
     to nothing. Stacked, each row is a record with its action beneath it and
     nothing has to be scrolled sideways to reach. */
  .vrow {
    grid-template-columns: minmax(0, 1fr);
    gap: 7px;
    padding: 12px 13px;
  }
  .vrow__state { justify-self: start; }
  .vrow__act { justify-self: stretch; }
  /* A thumb-sized target, and both actions share the width rather than one of
     them being a sliver. */
  .vrow__act .metaline { display: flex; gap: 8px; }
  .vrow__act .btn { flex: 1 1 0; min-height: 40px; justify-content: center; }

  .vgroup__head { align-items: flex-start; }
  /* Verify All goes full width under the account rather than beside it, where
     a long account name would otherwise leave it a few pixels wide. */
  .vgroup__head > .btn { width: 100%; }
  .vgroup__who { flex: 1 1 100%; }

  /* The three chip rows scroll sideways within themselves rather than making
     the page do it. */
  .tabs--groups {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .tabs--groups::-webkit-scrollbar { display: none; }
  .tabs--groups .tabs__tab { flex: none; }

  /* Tables become cards: each row lays out as a stacked record. */
  .ledger__head { display: none; }

  .ledger--accounts .lrow {
    grid-template-columns: 1fr auto;
    gap: 8px 12px;
    padding: 13px 14px;
    align-items: start;
  }

  /* A history entry reads down the card rather than across a table: when and
     how much, then who and the balance it left behind, then the remark on a
     line wide enough to hold it. Auto-placement cannot be trusted with this -
     the cells are named into place. */
  .ledger--history .lrow {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "time   time   amount"
      "who    who    balance"
      "type   meta   copy"
      "remark remark remark";
    gap: 8px 10px;
    padding: 13px 14px;
    align-items: start;
  }
  .ledger--history .lrow .col-time { grid-area: time; }
  .ledger--history .lrow .who { grid-area: who; }
  .ledger--history .lrow .col-type { grid-area: type; align-self: center; }
  .ledger--history .lrow .col-amount { grid-area: amount; align-self: center; font-size: 15px; }
  .ledger--history .lrow .col-balance { grid-area: balance; align-self: center; }
  .ledger--history .lrow .remark { grid-area: remark; }
  .ledger--history .lrow .cell--copy { grid-area: copy; justify-self: end; align-self: center; }

  /* Room for the badges again: the card has a line to spare where the wide
     table did not. */
  .ledger--history .lrow .col-device { grid-area: meta; display: flex; align-self: center; }

  /* On an account page there is no account, no copy button and no badge line,
     so the same entry folds into three rows. */
  .ledger--history.ledger--recent .lrow {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "time   amount"
      "type   balance"
      "remark remark";
  }

  /* The grouping band above each account's run of entries. */
  .lgroup { padding: 9px 14px; gap: 8px; }

  .lrow .cell--num { text-align: right; }
  .lrow .col-full { grid-column: 1 / -1; }
  .lrow .col-hide-sm { display: none; }
  .spine { display: none; }
  .lrow .stack-meta { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
}

@media (max-width: 620px) {
  .toolbar .field { flex: 1 1 130px; }
  .hero { padding: 16px; }
  .segmented { grid-template-columns: repeat(2, 1fr); }

  /* The bar has to fit the narrowest phone, or the whole page scrolls
     sideways and every table under it comes out shifted. The wordier faces
     go and the icons carry them; each control still names itself to a
     screen reader through its own label. */
  .topbar { padding: 0 12px; gap: 10px; }
  .brand__tag { display: none; }
  .topbar__end { gap: 8px; }
  .topbar__end .btn { padding: 6px 9px; }
  #sync-label,
  #theme-label,
  .topbar__end .btn__face {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

@media print {
  .topbar, .tabbar, .toolbar, .row-actions, .pager { display: none !important; }
  .main { margin: 0; padding: 0; }
  .card, .lrow { box-shadow: none; }
}

/* heatmap frame: weekday gutter + month-labelled grid */
.hm-frame { display: flex; gap: 7px; align-items: flex-start; }
.hm-days {
  display: grid;
  grid-template-rows: repeat(7, 12px);
  gap: 3px;
  margin-top: 18px;
  font-family: var(--mono);
  font-size: 9.5px;
  color: var(--muted);
  line-height: 12px;
}
.hm-legend__scale { margin-left: auto; }

/* collapsible filter drawer (open by default on desktop) */
.filters { width: 100%; }
.filters > summary {
  cursor: pointer;
  list-style: none;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding: 8px 0 4px;
  display: flex;
  align-items: center;
  gap: 7px;
}
.filters > summary::-webkit-details-marker { display: none; }
.filters > summary::before { content: "+"; font-size: 14px; line-height: 1; }
.filters[open] > summary::before { content: "−"; }
.filters > .toolbar__row { margin-top: 10px; }
@media (min-width: 961px) {
  .filters > summary { display: none; }
  .filters > .toolbar__row:first-of-type { margin-top: 0; }
}

@media (max-width: 960px) {
  /* the running balance needs naming once the column header is gone */
  .col-balance::before {
    content: "Balance after";
    display: block;
    font-family: var(--mono);
    font-size: 9.5px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--muted);
  }
}

/* --------------------------------- copy -------------------------------- */

.btn--copy { gap: 6px; }
.btn--copy svg { width: 14px; height: 14px; }
.btn--copied {
  color: #1c7a51;
  border-color: color-mix(in srgb, #1c7a51 45%, var(--rule));
  background: color-mix(in srgb, #1c7a51 10%, var(--surface));
}
.detail-item dd .btn--copy { margin-left: 8px; vertical-align: middle; }

/* --------------------------- a list beside the page --------------------- */

/* The sidebar's width is one custom property, so the content beside it needs
   no arithmetic of its own - the grid does the reflowing. */
.withside {
  --side-w: 248px;
  display: grid;
  grid-template-columns: var(--side-w) auto minmax(0, 1fr);
  align-items: start;
  gap: 0;
}
.withside__main { min-width: 0; }
.withside--sizing { user-select: none; cursor: col-resize; }

.side {
  position: sticky;
  top: 78px;
  align-self: start;
  max-height: calc(100vh - 96px);
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
}

.side__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 11px 12px 8px;
}
.side__search { padding: 0 12px 10px; }
.side__search .input { width: 100%; }
.side__list { overflow-y: auto; border-top: 1px solid var(--rule); }

.side__item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 9px;
  align-items: center;
  width: 100%;
  padding: 8px 12px;
  border: 0;
  border-left: 3px solid transparent;
  background: none;
  text-align: left;
  cursor: pointer;
}
.side__item:hover { background: var(--surface-2); }
.side__item--on { background: var(--in-wash); border-left-color: var(--in); }
.side__text { display: grid; min-width: 0; }
.side__name { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side__sub { font-family: var(--mono); font-size: 10.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side__bal { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 12px; color: var(--ink-2); }
.side__all {
  display: grid; place-items: center;
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--surface-3); color: var(--ink-2);
  font-family: var(--mono); font-size: 13px;
}
.side__none { padding: 14px 12px; font-size: 12.5px; color: var(--muted); }

/* The edge you drag. Wider than it looks, so it is not a pixel hunt. */
.side__grip {
  width: 11px;
  align-self: stretch;
  cursor: col-resize;
  background: none;
  border: 0;
  position: relative;
}
.side__grip::after {
  content: "";
  position: absolute;
  inset: 18px 5px;
  border-radius: 2px;
  background: var(--rule);
  transition: background .12s ease;
}
.side__grip:hover::after, .side__grip:focus-visible::after { background: var(--in); }

@media (max-width: 960px) {
  /* Stacked, and nothing to drag: the account list sits above the table. */
  .withside { grid-template-columns: minmax(0, 1fr); }
  .side { position: static; max-height: 320px; margin-bottom: 14px; }
  .side__grip { display: none; }
}

/* --------------------------- where it came from ------------------------ */

.srcbox {
  display: grid;
  gap: 6px;
  padding: 11px 13px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.srcline { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 9px; align-items: center; }
.srcline__icon {
  display: grid; place-items: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--surface-3); color: var(--ink-2);
}
.srcline__icon svg { width: 14px; height: 14px; }
.srcline__text {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 620px) {
  /* Stacked, so a long address does not squeeze the button off the row. */
  .srcline { grid-template-columns: auto minmax(0, 1fr); }
  .srcline .btn { grid-column: 2; justify-self: start; }
}

/* ---------------------------- copied, and marked ----------------------- */

/* The row is no longer a link itself: the link inside it is stretched over the
   whole row, so clicking anywhere still opens the entry while a button laid on
   top stays a button. */
.lrow { position: relative; }
.lrow__link { display: block; color: inherit; text-decoration: none; }
.lrow__link::after { content: ""; position: absolute; inset: 0; }

.cell--copy { position: relative; z-index: 1; justify-self: start; }
.cell--copy .btn { padding: 4px 9px; }
/* An entry naming several people copies one line each, so the buttons stack
   rather than run off the row. */
.copyset { display: flex; flex-wrap: wrap; gap: 4px; }
.copyset .btn { max-width: 100%; }
.copyset .btn span { overflow: hidden; text-overflow: ellipsis; }

.lrow--copied { background: color-mix(in srgb, #1c7a51 12%, var(--surface)); box-shadow: inset 3px 0 0 #1c7a51; }

.avatarwrap { position: relative; display: inline-grid; }
.bookmark {
  position: absolute;
  right: -6px;
  bottom: -5px;
  width: 18px;
  height: 18px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 0 0 2px var(--surface);
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  z-index: 1;
}
.bookmark:hover { transform: scale(1.12); }

/* Something new to take away. */
.btn--flag { position: relative; border-color: var(--in); color: var(--in); }
.btn--flag::after {
  content: "";
  position: absolute;
  top: -3px;
  right: -3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--in);
  box-shadow: 0 0 0 2px var(--rail-bg);
}

/* A request is not a payment: warned in a colour you stop at. */
.receipt__dupe--request {
  background: color-mix(in srgb, var(--danger) 11%, transparent);
  border-color: color-mix(in srgb, var(--danger) 45%, var(--rule));
}
.receipt__dupe--request b { color: var(--danger); }

/* ------------------------------ full screen ---------------------------- */

/* The table lifted out of the page to fill the viewport - the page's own space,
   not the screen's. The browser keeps its address bar and its tabs, so the
   table can be read wide while still switching away and back. */
.card--full {
  position: fixed;
  inset: 0;
  /* Over the header and the phone's tab bar, under modals, tips and toasts, so
     anything opened from a row still lands on top of it. */
  z-index: 40;
  display: flex;
  flex-direction: column;
  margin: 0;
  border: 0;
  border-radius: 0;
  max-height: 100vh;
  max-height: 100dvh;
  background: var(--surface);
}

/* The entries take the height that is left; the control and the count keep
   their places at either end of it. */
.card--full .card__head,
.card--full .pager { flex: none; }
.card--full .ledger { flex: 1 1 auto; min-height: 0; overflow: auto; }
.card--full .pager { border-top: 1px solid var(--rule); }

/* Column names stay in sight through a long scroll. */
.card--full .ledger__head {
  position: sticky;
  top: 0;
  z-index: 1;
  border-radius: 0;
}

/* Nothing behind it has any business scrolling. */
body.has-fulltable { overflow: hidden; }

/* ------------------------------------------------------------------ *
 * Account overview: the two lists, and what the rotation adds
 *
 * All of it in the existing tokens, so both themes follow the page
 * rather than each being painted by hand.
 * ------------------------------------------------------------------ */

.tabs {
  /* Flex, and only as wide as its contents - not INLINE-flex, which let the
     tab row, the Receive/Send row and the prefix row all settle onto one
     line, so the recommendation controls read as part of the header above
     them. Each row is its own row. */
  display: flex;
  width: fit-content;
  gap: 2px;
  padding: 3px;
  margin: 14px 0 0;
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: var(--surface-2);
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tabs__tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 13px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.tabs__tab:hover { color: var(--ink); }
/* Selected reads as raised rather than coloured in: subtle, and it survives
   both themes without a bright accent shouting on the page. */
.tabs__tab[aria-selected="true"] {
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 2px rgb(0 0 0 / 12%);
}
.tabs__tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.tabs__count {
  min-width: 18px;
  padding: 1px 6px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--accent);
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.5;
  text-align: center;
}

.tabs__hint {
  margin: 9px 0 0;
  color: var(--muted);
  font-size: 12.5px;
}

/* The one account to use next, and the account that has nothing left. Both
   are said in the row's own pill style rather than a new one. */
.pill--rec {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--rule));
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.pill--limit {
  color: var(--out);
  border-color: color-mix(in srgb, var(--out) 35%, var(--rule));
}

/* What the rotation is actually reading, in place of the "last active" column.
   Recent sending, not the lifetime count - which stays on the row above and is
   deliberately not what decides any of this. */
.rot__usage { gap: 8px; flex-wrap: wrap; }
.rot__count {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink);
}
.rot__left { font-family: var(--mono); font-size: 12px; color: var(--muted); }
.rot__left[data-low="true"] { color: var(--out); }

.lrow--rot .who__name { font-weight: 600; }

/* The monthly allowance on an account's own page. */
.limit { display: grid; gap: 7px; }
.limit__figures { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.limit__spent { font-family: var(--mono); font-size: 18px; color: var(--ink); }
.limit__of { color: var(--muted); font-size: 13px; }
.limit__bar {
  height: 6px;
  border-radius: 999px;
  background: var(--surface-3);
  overflow: hidden;
}
.limit__fill {
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  transition: width .2s ease;
}
.limit__fill[data-full="true"] { background: var(--out); }
.limit__left { color: var(--muted); font-size: 12.5px; }

/* The sending-limit line in the entry modal, shown only for a send. */
.preview__limit { margin-top: 2px; }
.preview__limit[data-over="true"] { color: var(--out); }

@media (max-width: 560px) {
  /* The screenshot's layout: tabs on one line, no extra page padding and
     nothing that can scroll the page sideways. */
  .tabs { display: flex; width: 100%; }
  .tabs__tab { flex: 1 1 auto; justify-content: center; padding: 7px 10px; }
  .rot__usage { gap: 6px; }
}

/* A switch for a setting that is genuinely on or off. */
.switch { display: flex; align-items: flex-start; gap: 11px; cursor: pointer; }
.switch__input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch__track {
  flex: 0 0 auto;
  width: 38px; height: 22px;
  margin-top: 1px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--surface-3);
  transition: background .14s ease, border-color .14s ease;
}
.switch__knob {
  display: block;
  width: 16px; height: 16px;
  margin: 2px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 1px 2px rgb(0 0 0 / 22%);
  transition: transform .14s ease;
}
.switch__input:checked + .switch__track {
  background: var(--accent);
  border-color: var(--accent);
}
.switch__input:checked + .switch__track .switch__knob { transform: translateX(16px); }
.switch__input:focus-visible + .switch__track { outline: 2px solid var(--accent); outline-offset: 2px; }
.switch__text { display: grid; gap: 2px; min-width: 0; }
.switch__title { font-size: 13px; color: var(--ink); }
.switch__note { margin: 0; color: var(--muted); font-size: 12px; }

/* Receive / Send, inside Recommended. Lighter than the tabs above it, so the
   two levels read as a hierarchy rather than as two rows of equals. */
.tabs--sub {
  margin-top: 9px;
  background: transparent;
  border-color: transparent;
  padding: 0;
  gap: 4px;
}
.tabs--sub .tabs__tab {
  padding: 4px 11px;
  font-size: 12.5px;
  border: 1px solid var(--rule);
  background: var(--surface);
}
.tabs--sub .tabs__tab[aria-selected="true"] {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--rule));
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--ink);
  box-shadow: none;
}

/* The shortcut groups. There may be many, so this row - and only this row -
   scrolls sideways on a narrow screen; the cards below stay full width. */
.tabs--groups {
  margin-top: 8px;
  padding: 0;
  gap: 5px;
  background: transparent;
  border-color: transparent;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.tabs--groups::-webkit-scrollbar { display: none; }
.tabs--groups .tabs__tab {
  padding: 3px 9px;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .03em;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--surface);
  flex: 0 0 auto;
}
.tabs--groups .tabs__tab[aria-selected="true"] {
  border-color: color-mix(in srgb, var(--accent) 50%, var(--rule));
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--ink);
  box-shadow: none;
}
.tabs--groups .tabs__count {
  padding: 0 4px;
  background: transparent;
  color: var(--muted);
  font-size: 10.5px;
}

@media (max-width: 560px) {
  /* Only the filter row scrolls. The tabs above still share the width. */
  .tabs--groups { display: flex; }
  .tabs--groups .tabs__tab { flex: 0 0 auto; }
}

/* An account's shortcut beside its name - smaller than the name, never
   competing with it. */
.shortcut--sm {
  margin-left: 7px;
  padding: 1px 5px;
  font-size: 10px;
  vertical-align: middle;
}

/* Direction, as an arrow. Green arriving, red leaving, amber for money out by
   card - which is not a send and must not look like one. */
.dir { display: inline-flex; align-items: center; vertical-align: -3px; margin-right: 6px; }
.dir svg { width: 15px; height: 15px; }
.dir--in { color: var(--pos); }
.dir--out { color: var(--neg); }
.dir--card { color: var(--neutral-dir); }

/* Read aloud, never shown: direction must not be available only as a colour. */
.sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* What was loaded onto a player, under the remark it came from. Distinct from
   the remark and the note: those are what the entry says, this is what was
   done about it. */
.remark__load {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 4px;
}
.remark__load__mark {
  font-family: var(--mono);
  font-size: 10.5px;
  padding: 1px 6px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--pos) 12%, transparent);
  color: var(--pos);
  white-space: nowrap;
}
.remark__load--unsure .remark__load__mark {
  background: color-mix(in srgb, var(--neutral-dir) 14%, transparent);
  color: var(--neutral-dir);
}

/* Icon-only copy: no label, and no gap where one would have been. */
.btn--copy-icon { padding: 2px 5px; gap: 0; }
.btn--copy-icon span:empty { display: none; }

/* A username the ledger says to be wary of. Beside the name, never around it. */
.uname { display: inline-flex; align-items: center; gap: 5px; min-width: 0; }
.uflag { display: inline-flex; align-items: center; color: #d91212; flex: none; }
.uflag svg { width: 14px; height: 14px; }

/* A beta waiting to be looked at. Offered beside the download rather than in
   place of it: the button most people press keeps handing out what was
   approved, and this is the copy somebody tries first. */
.extbeta { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
           gap: 10px; padding: 10px 12px; border: 1px dashed var(--rule);
           border-radius: var(--radius-sm); background: var(--surface-2); }
.extbeta__what { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.extbeta__what b { font-size: 13px; }
.extbeta__what span { font-size: 11.5px; color: var(--muted); }
.extbeta__acts { display: flex; gap: 7px; flex: none; }

/* ------------------------------ Outlook inbox ----------------------------- *
 * Mail from every connected account in one list. The row is a button because
 * that is what it does - it opens a message - and a button is what a keyboard
 * and a screen reader already know how to work with.
 * -------------------------------------------------------------------------- */

.mailrow {
  display: grid;
  grid-template-columns: minmax(9rem, 14rem) minmax(0, 1fr) auto;
  grid-template-areas: "who subject meta" "who preview meta";
  gap: 2px 14px;
  align-items: baseline;
  width: 100%;
  padding: 10px 14px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.mailrow:hover { background: var(--surface-2, rgba(127, 127, 127, .09)); }
.mailrow:focus-visible { outline: 2px solid var(--accent, #0b57d0); outline-offset: -2px; }

.mailrow__who     { grid-area: who; font-weight: 500; }
.mailrow__subject { grid-area: subject; font-weight: 500; }
.mailrow__preview { grid-area: preview; font-size: .88em; }
.mailrow__meta    { grid-area: meta; display: flex; align-items: center; gap: 8px; white-space: nowrap; }

/* Unread is carried by weight and a marker rather than colour alone, so it
   survives a colourblind reader and a monochrome screen. */
.mailrow[data-unread="true"] .mailrow__who,
.mailrow[data-unread="true"] .mailrow__subject { font-weight: 700; }
.mailrow[data-unread="true"]::before {
  content: "";
  position: absolute;
  margin-left: -10px;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent, #0b57d0);
  align-self: center;
}
.mailrow { position: relative; }

.mailrow__who, .mailrow__subject, .mailrow__preview {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 720px) {
  .mailrow {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "who meta" "subject subject" "preview preview";
  }
}

.mailview { display: grid; gap: 12px; }
.mailview__head { display: grid; gap: 2px; }

/* The message itself, walled off: no scripts, no network, no reach into this
   page. See the CSP in views/inbox.js. */
.mailbody {
  width: 100%;
  min-height: 40vh;
  border: 1px solid var(--line, rgba(127, 127, 127, .25));
  border-radius: 10px;
  background: var(--surface, #fff);
}

/* --------------------------------- use next ------------------------------- *
 * Which account to use next, by how little each has carried in the last
 * rolling day. The row leads with the count, because the count is the whole
 * of the decision; the balance and the limit are context beside it.
 * -------------------------------------------------------------------------- */

.usenext__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  gap: 12px;
  align-items: center;
  padding: 12px 14px;
  border: 1px solid var(--rule);
  border-radius: 12px;
  background: var(--surface);
  color: inherit;
  text-decoration: none;
}
.usenext__row:hover { background: var(--surface-2); }
.usenext__row:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* The one to use. Marked by its border and its badge, not by a fill - it is
   first in an ordered list, not a different kind of thing. */
.usenext__row--top { border-color: color-mix(in srgb, var(--brand) 55%, var(--rule)); }

.usenext__who { display: grid; gap: 2px; min-width: 0; }
.usenext__name { font-weight: 600; font-size: 14.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.usenext__handle { font-size: 11.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.usenext__state { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-top: 2px; }

.usenext__limit { font-size: 11px; color: var(--muted); }
.usenext__limit--out { color: var(--out); font-weight: 600; }

.usenext__load { display: grid; gap: 2px; text-align: right; white-space: nowrap; }
.usenext__count {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  font-weight: 600;
}
.usenext__last, .usenext__why { font-size: 11px; color: var(--muted); }

.usenext__end { display: grid; gap: 5px; justify-items: end; }
.usenext__balance { font-size: 14px; font-variant-numeric: tabular-nums; }

.pill--rec {
  color: var(--brand);
  border-color: color-mix(in srgb, var(--brand) 45%, var(--rule));
  font-weight: 600;
}

.usenext__summary {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  align-items: baseline;
  padding: 12px 16px;
  cursor: pointer;
  font-weight: 600;
  font-size: 13px;
}

/* On a phone the row folds: who and load on the first line, balance and the
   button under them, rather than four columns fighting over 320px. */
@media (max-width: 720px) {
  .usenext__row {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "avatar who  load"
      "end    end  end";
    gap: 8px 10px;
    padding: 11px 12px;
  }
  .usenext__row > .avatar { grid-area: avatar; }
  .usenext__who  { grid-area: who; }
  .usenext__load { grid-area: load; }
  .usenext__end  {
    grid-area: end;
    grid-auto-flow: column;
    justify-items: center;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding-top: 8px;
    border-top: 1px solid var(--rule);
  }
}

/* What is left of an account's monthly sending limit, on the row that already
   carries its label. Four words on an existing line - the row does not grow
   for it, and the whole picture stays on the account page. */
.who__limit { color: var(--muted); }
.who__limit--out { color: var(--out); font-weight: 600; }

/* The monthly sending limit, previewed beside the balance while an entry is
   being written. Only for Sent: received and card out do not touch it, and a
   row saying so every time would be noise. */
.adjust__limit {
  margin: 2px 0 0;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}
.adjust__limit--short { color: var(--out); font-weight: 600; text-transform: none; letter-spacing: 0; }

/* Three entry types rather than four. Two across and the third full width on a
   narrow modal, so none of them is too small to hit; the wide layout keeps all
   three on one line. */
.typepick { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }

@media (max-width: 420px) {
  .typepick { grid-template-columns: 1fr 1fr; }
  .typepick > :last-child { grid-column: 1 / -1; }
}

/* What a newly opened player account came back with. Sits under the form
   rather than replacing it, so another can be opened straight after. */
.createplayer__result:empty { display: none; }
.createplayer__result {
  display: grid;
  gap: 6px;
  margin-top: 14px;
  padding: 12px;
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: var(--surface-2);
  font-size: 13px;
}
.createplayer__result p { margin: 0; }
.createplayer__result code {
  font-family: var(--mono);
  font-size: 12.5px;
  padding: 1px 5px;
  border-radius: 5px;
  background: var(--surface-3);
}

/* ---------------------- naming a player before acting ---------------------- *
 * Both clipboard actions - freeplay and a password reset - are irreversible
 * from here, and the clipboard is the least reliable input in the application.
 * So the player is named, large, and waits for a second press.
 * -------------------------------------------------------------------------- */

.confirmplayer { display: grid; gap: 12px; }
.confirmplayer__lead { margin: 0; color: var(--ink-2); }

.confirmplayer__who {
  display: grid;
  gap: 2px;
  padding: 12px 14px;
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: var(--surface-2);
}
.confirmplayer__name {
  font-family: var(--mono);
  font-size: 17px;
  font-weight: 700;
  word-break: break-all;
}
.confirmplayer__where { font-size: 12.5px; color: var(--muted); }

.confirmplayer__flag {
  margin: 0;
  padding: 9px 11px;
  border: 1px solid color-mix(in srgb, var(--out) 40%, var(--rule));
  border-radius: 9px;
  background: var(--out-wash, var(--surface-2));
  color: var(--out);
  font-size: 12.5px;
}

.confirmplayer__detail { display: grid; gap: 6px; font-size: 13px; }
.confirmplayer__detail p { margin: 0; }
.confirmplayer__secret {
  font-family: var(--mono);
  font-size: 15px;
  letter-spacing: .04em;
  padding: 4px 9px;
  border-radius: 7px;
  background: var(--surface-3);
}

/* ------------------------------- usernames -------------------------------- *
 * Every player across every platform. A table on a desktop; on a phone each
 * row folds into a card, because seven columns of money on a 360px screen is
 * a horizontal scrollbar nobody wants.
 * -------------------------------------------------------------------------- */

.unchips { margin-top: 10px; }

.unrow {
  display: grid;
  grid-template-columns: minmax(0, 2.2fr) repeat(6, minmax(72px, 1fr));
  gap: 10px;
  align-items: center;
  width: 100%;
  padding: 11px 16px;
  border: 0;
  border-bottom: 1px solid var(--rule);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.unrow:last-child { border-bottom: 0; }
.unrow:hover { background: var(--surface-2); }
.unrow:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

.unrow--head {
  cursor: default;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--surface-2);
}
.unrow--head:hover { background: var(--surface-2); }

.unrow__who { min-width: 0; }
.unrow__name { font-weight: 600; font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.unrow__meta { font-size: 11.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.unrow__num {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  text-align: right;
  white-space: nowrap;
}
/* The column name travels with the number on a phone, where the header row
   is gone. */
.unrow__label { display: none; }

@media (max-width: 860px) {
  .unrow--head { display: none; }
  .unrow {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px 12px;
    padding: 12px 14px;
  }
  .unrow__who { grid-column: 1 / -1; }
  .unrow__num { text-align: left; display: grid; gap: 1px; }
  .unrow__label {
    display: block;
    font-family: var(--mono);
    font-size: 9.5px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--muted);
  }
}

@media (max-width: 420px) {
  .unrow { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.undetail { display: grid; gap: 14px; }
.undetail__who { display: grid; gap: 2px; }
.undetail__who .mono { font-size: 16px; font-weight: 700; word-break: break-all; }
.undetail__block { display: grid; gap: 4px; }
.undetail__pair {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
  padding: 3px 0;
  border-bottom: 1px dashed var(--rule);
}
.undetail__pair:last-child { border-bottom: 0; }
.undetail__pair b { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* --------------------------- the reset-password popup ---------------------- *
 * Opens first, then looks at the clipboard - so nothing waits on a permission
 * prompt before anything appears. What the clipboard said sits at the top,
 * with whether it can actually be used.
 * -------------------------------------------------------------------------- */

.pwreset { display: grid; gap: 14px; }
.pwreset__status p { margin: 0 0 4px; }

.pwreset__clip {
  display: grid;
  gap: 3px;
  padding: 11px 13px;
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: var(--surface-2);
}
/* Supported reads as settled rather than as a warning: it is the good case. */
.pwreset__clip--ok {
  border-color: color-mix(in srgb, var(--in) 45%, var(--rule));
  background: var(--in-wash, var(--surface-2));
}

.pwreset__caption {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
.pwreset__name { font-size: 15px; font-weight: 700; word-break: break-all; }

/* Said in words, not only in colour. */
.pwreset__ok { font-size: 12.5px; font-weight: 600; color: var(--in); }
.pwreset__no { font-size: 12.5px; color: var(--out); }

.pwreset__pick { display: grid; gap: 12px; }

/* ------------------ credentials for a newly opened account ---------------- */

/* Shown once and not stored anywhere, so this block exists to get two strings
   out of the screen and into wherever they are going - on a phone as well as
   a desktop. Compact on purpose: it sits inside a floating window that must
   not grow to hold it. */
.cred {
  display: grid; gap: 7px;
  padding: 9px 10px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--surface-2);
}
.cred__head {
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
  font-size: 12px; font-weight: 600; color: var(--ink);
}
.cred__tick { color: var(--pos); font-weight: 700; }
.cred__plat {
  font-family: var(--mono); font-size: 9px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.cred__nick { font-size: 11px; color: var(--muted); margin-top: -3px; }
.cred__line { display: grid; gap: 2px; }
.cred__label {
  font-family: var(--mono); font-size: 9px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}
.cred__row { display: flex; align-items: stretch; gap: 6px; min-width: 0; }

/* The value is a button too: on a phone the thing somebody taps is the text,
   not a 20px icon beside it. */
.cred__value {
  flex: 1; min-width: 0;
  text-align: left;
  font-family: var(--mono); font-size: 13px; color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 6px 8px;
  cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  /* Letting it be selected as well means a long press still offers Copy. */
  user-select: all;
}
.cred__value:hover { border-color: var(--rule-strong); }

.cred__copy {
  flex: none;
  display: grid; place-items: center;
  width: 32px;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--surface);
  color: var(--ink-2);
  cursor: pointer;
  padding: 0;
}
.cred__copy:hover { border-color: var(--rule-strong); color: var(--ink); }
.cred__copy--done { color: var(--pos); border-color: var(--pos); }

.cred__icon { display: grid; place-items: center; }
.cred__icon svg { width: 15px; height: 15px; display: block; }

.cred__both {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  border: 1px solid var(--brand);
  background: var(--brand);
  color: var(--on-brand);
  border-radius: 7px;
  padding: 8px 12px;
  font-size: 12.5px; font-weight: 600;
  cursor: pointer;
}
.cred__both--done { background: var(--pos); border-color: var(--pos); }
.cred__both .cred__icon svg { width: 14px; height: 14px; }

.cred__note { font-size: 10.5px; color: var(--muted); line-height: 1.35; }

/* ------------------------------ signing in -------------------------------- */

/* One card, centred, and nothing else on the page: there is nothing else to do
   here, and a sign-in that looks like the app with the data missing reads as a
   loading failure. */
.signin { display: grid; place-items: center; min-height: 70vh; padding: 24px 16px; }
.signin__card { width: 100%; max-width: 360px; padding: 24px; }
.signin__title { margin: 0 0 4px; font-size: 22px; }
.signin__blurb { margin: 0 0 18px; color: var(--muted); font-size: 13px; }
.signin__form { display: grid; gap: 8px; }
.signin__label {
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
.signin__hint { margin: 2px 0 0; font-size: 11.5px; color: var(--muted); }
/* Refusals are shown, never dressed up. The server's wording is deliberately
   uninformative and repeating it plainly is the honest thing. */
.signin__said { min-height: 18px; font-size: 12.5px; color: var(--danger); line-height: 1.35; }
.signin__form .btn--go { margin-top: 6px; }
.signin__forgot {
  background: none; border: 0; padding: 4px 0; margin-top: 2px;
  color: var(--muted); font-size: 12px; text-align: left; cursor: pointer;
  text-decoration: underline;
}
.signin__forgot:hover { color: var(--ink); }

/* ----------------------------- admin activity ----------------------------- */

.activity { display: grid; gap: 12px; }
.activity__filters {
  display: grid; gap: 8px; padding: 12px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.activity__head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 12px;
}
.activity__count { font-size: 12px; color: var(--muted); }
.activity__pager { display: flex; gap: 6px; }
.activity__table { width: 100%; border-collapse: collapse; font-size: 13px; }
.activity__table th {
  text-align: left; padding: 8px 12px; font-size: 10.5px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted); border-bottom: 1px solid var(--rule);
}
.activity__table td { padding: 9px 12px; border-bottom: 1px solid var(--rule); vertical-align: top; }
.activity__row { cursor: pointer; }
.activity__row:hover { background: var(--surface-2); }
/* Colour is never the only signal - the action's own words say it too. */
.activity__row--bad .activity__what { color: var(--danger); }
.activity__row--warn .activity__what { color: var(--out); }
.activity__when { white-space: nowrap; color: var(--muted); }
.activity__what { font-weight: 600; }
.activity__src {
  display: block; font-size: 10px; letter-spacing: .05em; text-transform: uppercase; color: var(--muted);
}
.activity__who { display: block; }
/* Rows carried over from before sign-in existed. Set apart on purpose: an
   unattributed row must not read like one that names somebody. */
.activity__who--legacy { color: var(--muted); font-style: italic; }
.activity__note, .activity__target {
  display: block; font-size: 11px; color: var(--muted); font-family: var(--mono);
}
.activity__about { max-width: 420px; }
.activity__bit { display: inline-block; margin: 0 10px 2px 0; }
.activity__bit em {
  font-style: normal; font-size: 10px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); margin-right: 4px;
}
.activity__ip { font-family: var(--mono); font-size: 11px; color: var(--muted); white-space: nowrap; }
.activity__none { padding: 22px 12px; text-align: center; color: var(--muted); }
/* Started and never finished: the only thing on this page that asks for an
   action rather than a read, so it sits above everything. */
.activity__unfinished {
  display: grid; gap: 4px; padding: 12px 14px;
  border: 1px solid var(--out); border-radius: 8px;
  background: color-mix(in srgb, var(--out) 8%, var(--surface));
  font-size: 12.5px;
}
.activity__unfinished ul { margin: 4px 0 0; padding-left: 18px; }
.activity__detail { display: grid; gap: 12px; }
.activity__detailTable { width: 100%; border-collapse: collapse; font-size: 13px; }
.activity__detailTable th {
  text-align: left; padding: 6px 10px 6px 0; color: var(--muted); font-weight: 500;
  white-space: nowrap; vertical-align: top;
}
.activity__detailTable td { padding: 6px 0; overflow-wrap: anywhere; }
.activity__jsonLabel {
  font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--muted);
  margin-bottom: 4px;
}
.activity__json pre {
  margin: 0; padding: 10px; border-radius: 6px; background: var(--surface-2);
  font-family: var(--mono); font-size: 11.5px; overflow: auto; max-height: 300px;
  white-space: pre-wrap; overflow-wrap: anywhere;
}

/* ------------------------------- Admins ---------------------------------- */

.admins__said:not(:empty) {
  margin-bottom: 12px;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--bad-bg, #fdecea);
  color: var(--bad-fg, #8a1c13);
  font-size: 13px;
}

.admins__made {
  margin-bottom: 12px;
  padding: 14px 16px;
  border: 1px solid var(--line, #e3e6ea);
  border-left: 3px solid var(--go, #1f7a4d);
  border-radius: 8px;
  background: var(--card, #fff);
}

.admins__made h3 { margin: 0 0 6px; font-size: 15px; }
.admins__made p { margin: 0 0 10px; font-size: 13px; color: var(--muted, #667); }

.admins__link {
  display: block;
  margin-bottom: 10px;
  padding: 10px 12px;
  border-radius: 6px;
  background: var(--sunken, #f4f6f8);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  /* An address and a password on two lines, and a long link that has to wrap
     rather than push the card sideways. */
  white-space: pre-wrap;
  word-break: break-all;
}

.admins__table { width: 100%; border-collapse: collapse; font-size: 13px; }
.admins__table th,
.admins__table td { padding: 9px 10px; text-align: left; border-bottom: 1px solid var(--line, #eceff2); }
.admins__table th { font-weight: 600; color: var(--muted, #667); font-size: 12px; }
.admins__row--off { opacity: 0.55; }

.admins__who { display: inline-flex; align-items: center; gap: 8px; }
.admins__you { color: var(--muted, #667); }

.admins__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  flex: none;
}

.admins__doing { display: flex; gap: 6px; flex-wrap: wrap; }

.admins__note { margin: 0 0 12px; font-size: 13px; color: var(--muted, #667); }
.admins__fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px;
  margin-bottom: 12px;
}
.admins__label { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--muted, #667); }
.admins__pw { display: flex; gap: 6px; align-items: center; }
.admins__pw .input { flex: 1 1 auto; min-width: 0; }

@media (max-width: 720px) {
  /* Six columns do not fit a phone. The two that answer "who is this" stay. */
  .admins__table th:nth-child(2),
  .admins__table td:nth-child(2),
  .admins__table th:nth-child(5),
  .admins__table td:nth-child(5) { display: none; }
}

/* ------------------------------ Passbook Locked -------------------------- */

/* No chrome while locked. The header and tab bar are links to pages nothing
   can be read from, and two of them name pages to somebody who is not in yet.
   One attribute on the root element, set by app.js, so a view cannot leave
   half a header behind by forgetting. */
:root[data-bare] .topbar,
:root[data-bare] .tabbar { display: none !important; }
:root[data-bare] .main { padding: 0; }
:root[data-bare] .shell { display: block; }

.lock {
  min-height: 100vh;
  /* The dynamic unit where it exists: on a phone the toolbar comes and goes,
     and 100vh puts the button underneath it. */
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Insets, so a notch or a home bar does not sit on top of the card. */
  padding:
    calc(24px + env(safe-area-inset-top, 0px))
    calc(20px + env(safe-area-inset-right, 0px))
    calc(24px + env(safe-area-inset-bottom, 0px))
    calc(20px + env(safe-area-inset-left, 0px));
  background: var(--bg);
  color: var(--ink);
}

.lock__card {
  width: 100%;
  /* Mobile first: full width up to this, then it stops growing and centres. */
  max-width: 380px;
  padding: 28px 24px 24px;
  border: 1px solid var(--rule);
  border-radius: 14px;
  background: var(--surface);
  box-shadow: 0 1px 2px rgb(0 0 0 / 4%), 0 12px 32px rgb(0 0 0 / 6%);
  text-align: center;
}

.lock__brand {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}

.lock__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  margin: 18px auto 14px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: var(--surface-2);
  color: var(--brand);
}

:root[data-theme="dark"] .lock__icon { color: var(--rail-ink); }

.lock__title { margin: 0; font-size: 20px; letter-spacing: -0.01em; }
.lock__sub { margin: 6px 0 22px; font-size: 13.5px; color: var(--ink-2); }

.lock__form { display: block; text-align: left; }

.lock__label {
  display: block;
  margin-bottom: 5px;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.lock__input { width: 100%; box-sizing: border-box; }
/* 16px or larger, or iOS zooms the page the moment the field is focused and
   leaves the card off-centre. */
.lock__input { font-size: 16px; }
.lock__label + .lock__input { margin-bottom: 14px; }

.lock__pw { position: relative; margin-bottom: 14px; }
.lock__input--pw { padding-right: 68px; margin-bottom: 0; }

.lock__reveal {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  padding: 6px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ink-2);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
  /* Big enough to hit with a thumb without covering the text being typed. */
  min-width: 52px;
  min-height: 32px;
}

.lock__reveal:hover { background: var(--surface-3); color: var(--ink); }
.lock__reveal:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

.lock__said {
  min-height: 0;
  font-size: 13px;
  color: var(--neg);
}
/* Only takes space when it has something to say, so the card does not shift
   downward the first time it is wrong. */
.lock__said:not(:empty) { margin: 0 0 12px; }

.lock__go {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  width: 100%;
  min-height: 46px;
  font-size: 15px;
}

.lock__go[disabled] { opacity: 0.72; cursor: default; }

.lock__spinner {
  width: 14px;
  height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: lock-spin 0.7s linear infinite;
  flex: none;
}

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

/* Somebody who has been staring at a spinner does not need it spinning; the
   button text already says what is happening. */
@media (prefers-reduced-motion: reduce) {
  .lock__spinner { animation-duration: 0s; border-top-color: currentColor; opacity: 0.5; }
}

.lock__forgot {
  display: block;
  width: 100%;
  margin-top: 14px;
  padding: 8px;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 12.5px;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.lock__forgot:hover { color: var(--ink-2); }

@media (max-width: 420px) {
  /* Edge to edge on a small phone, and no shadow to imply a floating card
     when it fills the screen. */
  .lock__card {
    max-width: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
    padding: 8px 4px 0;
  }
  .lock { align-items: flex-start; padding-top: calc(40px + env(safe-area-inset-top, 0px)); }
}

/* Before the answer arrives.
   
   The shell is a static file and paints immediately; whether anybody is signed
   in takes a request to find out. Between those two moments a locked browser
   used to show the header, the side navigation and the tab bar - the whole
   skeleton of the app, and every page name in it.
   
   So nothing shows until it is known. Not `visibility`, which leaves the space
   behind: an empty page for a few milliseconds, then either the app or the
   lock screen. app.js removes the attribute once, whichever way it goes. */
:root[data-booting] .topbar,
:root[data-booting] .tabbar,
:root[data-booting] .main { display: none !important; }

/* The ground stays the right colour while it waits, so the wait does not flash
   white on a dark theme. */
:root[data-booting] body { background: var(--bg); }

/* When the monthly allowance comes back. Quieter than the figures above it -
   it is context for a decision, not the number being read. */
.limit__reset {
  margin-top: 3px;
  font-size: 11.5px;
  color: var(--muted);
}

/* Why a connected platform is not in the list above - a cashier permission
   somebody can have switched on, rather than a limit of the API. */
.resetpw__why { margin-top: 10px; }
.resetpw__why p { margin: 0 0 4px; font-size: 12px; line-height: 1.45; }
