:root {
  color-scheme: dark;
  --bg: #100e17;
  --panel: #1a1724;
  --panel-2: #221e2e;
  --line: #302a40;
  --ink: #f2eefb;
  --muted: #9b93ad;
  --accent: #b78cff;
  --now-a: #ff4d6d; --now-b: #ff8a5b;
  --hour-a: #ffb020; --hour-b: #ffd66b;
  --today-a: #35c8b0; --today-b: #4aa8ff;
  --days-a: #4a7bff; --days-b: #8a7dff;
  --radius: 18px;
}

* { box-sizing: border-box; }

/* Button and .btn set an explicit display, which beats the browser's own
   [hidden] rule — so anything hidden from script would still be on screen. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: radial-gradient(120% 90% at 50% 0%, #1c1730 0%, var(--bg) 60%) fixed;
  color: var(--ink);
  font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.shell {
  position: relative;
  max-width: 560px;
  margin: 0 auto;
  padding: 24px 18px calc(96px + env(safe-area-inset-bottom));
}

/* Page titles in the logo's colours: pale aqua into deep blue, as in the
   glowing "oya1" on the icon. A little padding keeps descenders (the g in
   "Give", the y in "ya") inside the painted area on every browser. */
h1 {
  font-size: 1.7rem; font-weight: 800; margin: 0 0 2px; letter-spacing: -0.02em;
  line-height: 1.2; padding: 0 .04em .08em 0;
  width: fit-content; max-width: 100%;
  background: linear-gradient(100deg, #b8fff4 0%, #6fe6f2 30%, #3aa0e6 65%, #2a62c9 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
/* A receipt's big "Spent" is a message, not a page title. */
.receipt h1 { margin-inline: auto; }
h2 { font-size: 1.05rem; margin: 28px 0 12px; color: var(--muted); font-weight: 600;
     text-transform: uppercase; letter-spacing: 0.08em; }
.sub { color: var(--muted); margin: 0 0 22px; }

/* ---------- IOU cards ---------- */

.iou {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 16px 14px;
  margin-bottom: 12px;
  overflow: hidden;
}
.iou::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 4px;
  background: linear-gradient(var(--a), var(--b));
}
.tier-now   { --a: var(--now-a);   --b: var(--now-b); }
.tier-hour  { --a: var(--hour-a);  --b: var(--hour-b); }
.tier-today { --a: var(--today-a); --b: var(--today-b); }
.tier-days  { --a: var(--days-a);  --b: var(--days-b); }

.iou-top { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.qty { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--muted); }
.tier-chip {
  /* inline-flex with line-height 1 so the uppercase text sits in the middle of
     the pill. On the default line-height it rides high, because the box is
     taller than the capitals it contains. */
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .72rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  line-height: 1; padding: 6px 10px 5px; border-radius: 999px; color: #14101c;
  background: linear-gradient(100deg, var(--a), var(--b));
}
.item { margin: 0; font-size: 1.18rem; letter-spacing: -0.01em; }
.note { margin: 6px 0 0; color: var(--muted); font-size: .92rem; }
.iou-bottom {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line);
}
.who { color: var(--muted); font-size: .88rem; }

/* ---------- buttons & forms ---------- */

button, .btn {
  font: inherit; font-weight: 600; cursor: pointer;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--ink);
  padding: 11px 16px; border-radius: 12px; text-decoration: none; display: inline-block;
}
button:active, .btn:active { transform: translateY(1px); }
.btn-primary, button.primary {
  background: linear-gradient(100deg, var(--accent), #7ad0ff);
  color: #16101f; border-color: transparent;
}
.btn-small { padding: 8px 13px; font-size: .9rem; }
.btn-danger { color: #ff8fa3; }
.btn-block { display: block; width: 100%; text-align: center; }

label { display: block; font-weight: 600; margin: 18px 0 7px; }
/* Every text-ish input, by name. Miss one off and the browser styles it
   itself: sharp corners, its own font, its own smaller box. */
input[type=text], input[type=email], input[type=password], input[type=number],
input[type=tel], input[type=url], input[type=search], select, textarea {
  width: 100%; font: inherit; color: var(--ink);
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; padding: 12px 13px;
  -webkit-appearance: none; appearance: none;
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
textarea { resize: vertical; min-height: 64px; }

.tier-picker { display: grid; gap: 9px; }
.tier-option { position: relative; margin: 0; }
.tier-option input[type=radio] { position: absolute; opacity: 0; pointer-events: none; }
.tier-option span {
  display: flex; flex-direction: column; gap: 2px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 14px; padding: 12px 14px; background: var(--panel);
}
.tier-option b { font-size: 1rem; }
.tier-option small { color: var(--muted); }
.tier-option input[type=radio]:checked + span {
  border-color: var(--a, var(--accent));
  box-shadow: inset 0 0 0 1px var(--a, var(--accent));
}
.tier-option input[type=radio]:focus-visible + span { outline: 2px solid var(--accent); }

.row { display: flex; gap: 10px; align-items: flex-end; }
.row > * { flex: 1; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.chip {
  font-size: .88rem; padding: 7px 12px; border-radius: 999px; cursor: pointer;
  background: var(--panel); border: 1px solid var(--line); color: var(--ink);
}

/* ---------- misc ---------- */

.alert { border-radius: 12px; padding: 12px 14px; margin: 16px 0; font-size: .94rem; }
.alert-error { background: #3a1622; border: 1px solid #7d2440; color: #ffc2ce; }
.alert-ok { background: #142c26; border: 1px solid #276d5c; color: #a9f0dd; }

.empty { text-align: center; color: var(--muted); padding: 44px 20px; }
.empty .big { font-size: 2.4rem; display: block; margin-bottom: 10px; opacity: .7; }

.panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
         padding: 16px; margin-bottom: 12px; }
.token { word-break: break-all; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
         font-size: .82rem; background: #0c0a12; border: 1px solid var(--line);
         border-radius: 10px; padding: 12px; margin-top: 8px; }

.feed-item { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line); }
.feed-icon { width: 34px; height: 34px; flex: none; border-radius: 50%; display: grid;
             place-items: center; background: var(--panel-2); font-size: .95rem; }
.feed-body { flex: 1; min-width: 0; }
.feed-body .when {
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px;
  margin-top: 7px; color: var(--muted); font-size: .82rem;
}

.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 10;
  display: flex; justify-content: space-around;
  background: rgba(20,17,28,.92); backdrop-filter: blur(14px);
  border-top: 1px solid var(--line);
  padding: 8px 4px calc(8px + env(safe-area-inset-bottom));
}
.tabbar a {
  flex: 1; text-align: center; text-decoration: none; color: var(--muted);
  font-size: .68rem; font-weight: 600; padding: 4px 0;
}
.tabbar a span { display: block; font-size: 1.15rem; margin-bottom: 2px; }
.tabbar a.on { color: var(--ink); }

.receipt { text-align: center; padding: 30px 0; }
.receipt .tick {
  width: 78px; height: 78px; margin: 0 auto 18px; border-radius: 50%;
  display: grid; place-items: center; font-size: 2.2rem; color: #12101a;
  background: linear-gradient(120deg, var(--accent), #7ad0ff);
  animation: pop .32s cubic-bezier(.2,1.4,.4,1);
}
@keyframes pop { from { transform: scale(.5); opacity: 0; } }
.receipt .amount { font-size: 1.7rem; font-weight: 700; margin: 0 0 4px; }

.card-face {
  background: linear-gradient(140deg, #2b2340, #3a2a55 60%, #201a2e);
  border: 1px solid var(--line); border-radius: 16px; padding: 18px;
  aspect-ratio: 1.586; display: flex; flex-direction: column; justify-content: space-between;
}
.card-face .brand { font-weight: 700; letter-spacing: .18em; font-size: .74rem; color: var(--muted); }
.card-face .name { font-size: 1.15rem; font-weight: 600; }
.card-face.revoked { opacity: .45; }

.centered { min-height: 70vh; display: flex; flex-direction: column; justify-content: center; }

/* ---------- expiry ---------- */

.expiry {
  display: inline-flex; align-items: center;
  font-size: .72rem; font-weight: 600; letter-spacing: .03em; line-height: 1;
  padding: 6px 10px 5px; border-radius: 999px; color: var(--muted);
  border: 1px solid var(--line); white-space: nowrap;
}
.expiry.urgent { color: #ffd08a; border-color: #6b4a1a; background: #2a1e0c; }
.expiry.gone { color: var(--muted); text-decoration: line-through; }

.iou.is-lapsed { opacity: .5; }
.iou.is-lapsed::before { background: var(--line); }

.iou-top { flex-wrap: wrap; }

/* The "never" option is the valuable one, so it should look it. */
.tier-option.expiry-never input[type=radio]:checked + span {
  border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent);
}
.tier-option input[type=date] {
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 9px; padding: 8px 10px; color: var(--ink); font: inherit;
  width: auto; align-self: flex-start;
}

/* ---------- refunds ---------- */

.refund-flag {
  margin-top: 10px; font-size: .86rem; color: #ffc2ce;
  background: #35141f; border: 1px solid #6d2137; border-radius: 10px; padding: 8px 11px;
}
.refund-ask { border-color: #6d2137; background: #24121a; }
.drawer { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.drawer label { margin-top: 0; font-size: .88rem; }

/* ---------- logo ---------- */

/* Parked in the corner at roughly the height of a heading plus its subtitle,
   so it reads as a mark rather than a banner. */
.logo { position: absolute; top: 20px; right: 18px; line-height: 0; }
.logo img { height: 56px; width: 56px; display: block; border-radius: 14px; }
/* Only on the page that still shows it (Settings), and only direct children:
   a .sub inside a panel is nowhere near the logo. */
.logo ~ h1, .logo ~ .sub:first-of-type { margin-right: 72px; }

/* ---------- reminders ---------- */

.nudge { text-align: left; }
.nudge-item { margin: 0 0 2px; font-weight: 600; font-size: 1.05rem; }
.nudge .sub { margin: 0 0 12px; font-size: .88rem; }
.nudge .note { margin: 0; }
.nudge select { padding: 9px 11px; }

/* ---------- passkeys ---------- */

/* A divider that reads as a choice rather than a fallback. */
.or {
  display: flex; align-items: center; gap: 12px;
  color: var(--muted); font-size: .85rem; margin: 22px 0 0;
}
.or::before, .or::after {
  content: ""; flex: 1; height: 1px; background: var(--line);
}

/* ---------- tab bar badges ---------- */

/* Six tabs is one more than the bar was drawn for, so the labels shrink. The
   column keeps the icon above the label while letting the icon's box shrink to
   fit, which is what the badge anchors to. */
.tabbar a { font-size: .62rem; display: flex; flex-direction: column; align-items: center; }
.tabbar a span { position: relative; display: inline-block; margin-bottom: 2px; }

.dot {
  position: absolute; top: -5px; left: 100%;
  transform: translateX(-38%);
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 999px; background: #ff3b5c; color: #fff;
  font-size: .62rem; font-weight: 700; line-height: 17px; text-align: center;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px rgba(20, 17, 28, .92);
}

/* ---------- grouping what you owe by person ---------- */

.who-group { margin-bottom: 18px; }
.who-group summary {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  padding: 11px 14px; border: 1px solid var(--line); border-radius: 14px;
  background: var(--panel-2); list-style: none;
}
/* Safari draws its own triangle unless both of these are dealt with. */
.who-group summary::-webkit-details-marker { display: none; }
.who-group summary::marker { content: ""; }
.who-group summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.who-name { font-weight: 600; flex: 1; }
.who-count {
  min-width: 22px; padding: 2px 8px; border-radius: 999px;
  background: var(--bg); border: 1px solid var(--line);
  color: var(--muted); font-size: .78rem; font-weight: 700; text-align: center;
  font-variant-numeric: tabular-nums;
}
.who-caret { color: var(--muted); transition: transform .18s ease; }
.who-group[open] .who-caret { transform: rotate(180deg); }

.who-body { padding-top: 12px; }

/* ---------- the change-your-mind window ---------- */

.grace {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 8px;
  margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--line);
}
.grace-clock { color: var(--muted); font-size: .82rem; }
.grace-actions { display: flex; gap: 8px; }
.grace-actions .btn-small { line-height: 1.2; }

/* ---------- the tour ---------- */

.intro-lede { background: linear-gradient(150deg, #241d3a, var(--panel)); }

.intro-tabs { list-style: none; margin: 0; padding: 0; display: grid; gap: 11px; }
.intro-tabs li { display: flex; align-items: baseline; gap: 12px; }
.intro-tabs li > span {
  flex: none; width: 30px; text-align: center; font-size: 1.1rem; color: var(--accent);
}

.intro-tiers { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0; }

/* ---------- the device setup sheet ---------- */

.sheet {
  position: fixed; inset: 0; z-index: 40;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(8, 6, 14, .72); backdrop-filter: blur(3px);
  padding: 16px;
}
.sheet-card {
  width: 100%; max-width: 460px;
  max-height: 88vh; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px 18px calc(22px + env(safe-area-inset-bottom));
  animation: rise .24s cubic-bezier(.2, .9, .3, 1);
}
@keyframes rise { from { transform: translateY(14px); opacity: 0; } }

/* h2 elsewhere is a small uppercase section label; here it is a real title. */
.sheet-title {
  margin: 0 0 4px; font-size: 1.25rem; font-weight: 700;
  text-transform: none; letter-spacing: -0.01em; color: var(--ink);
}

.step { padding: 14px 0; border-top: 1px solid var(--line); }
.step-head { display: flex; align-items: center; gap: 10px; }
.step-dot {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; font-size: .78rem; font-weight: 700;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--muted);
}
.step-state { margin-left: auto; color: var(--muted); font-size: .8rem; white-space: nowrap; }
.step-how { margin: 8px 0 10px; color: var(--muted); font-size: .88rem; }
.step-how:empty { display: none; }

.step.is-done .step-dot {
  background: linear-gradient(120deg, var(--accent), #7ad0ff);
  border-color: transparent; color: #16101f; font-size: 0;
}
.step.is-done .step-dot::after { content: "\2713"; font-size: .8rem; }
.step.is-done .step-state { color: #8fe3c4; }

/* ---------- the beta agreement gate ---------- */

/* The agreement is a page, not a dismissible sheet, but it should feel like
   one — so it borrows the sheet's card and drops the bottom-anchoring, which
   at six clauses long would push the title off the top of the screen. */
.sheet-static { align-items: flex-start; padding-block: 20px; overflow-y: auto; }
.sheet-static .sheet-card { max-height: none; margin: auto 0; }

.agree {
  display: flex; gap: 12px; align-items: flex-start;
  font-weight: 400; margin: 0;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 14px; padding: 14px;
}
.agree input[type=checkbox] {
  flex: none; width: 22px; height: 22px; margin: 1px 0 0;
  accent-color: var(--accent);
}
.agree span { font-size: .93rem; line-height: 1.45; }

.code-input {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.15rem; letter-spacing: .12em; text-transform: uppercase;
}

/* ---------- report a problem ---------- */

/* Above the tab bar rather than in it: the tab bar is for places you go, and
   this is not a place. Small enough to forget, in the same corner every time
   so that it is findable the moment something goes wrong. */
.report-fab {
  position: fixed; z-index: 20;
  /* Bottom edge level with the scan button's, across the screen from it. */
  right: 12px; bottom: calc(76px + env(safe-area-inset-bottom));
  transition: opacity .2s ease;
  display: flex; align-items: center; gap: 7px;
  padding: 8px 13px; border-radius: 999px;
  font-size: .78rem; font-weight: 600;
  background: rgba(34, 30, 46, .72); backdrop-filter: blur(10px);
  border: 1px solid var(--line); color: var(--muted);
  box-shadow: 0 6px 18px -8px rgba(0, 0, 0, .8);
}
/* Both floating buttons fade back while the page scrolls, so whatever is
   passing under them can be read. */
body.is-scrolling .scan-fab, body.is-scrolling .report-fab { opacity: .35; }
.report-fab span {
  flex: none; width: 17px; height: 17px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent); color: #16101f;
  font-size: .7rem; font-weight: 800; line-height: 1;
}
.report-fab:active { transform: translateY(1px); }

.report-thumbs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.report-thumbs:empty { display: none; }
.report-thumb { position: relative; width: 72px; height: 72px; }
.report-thumb img {
  width: 100%; height: 100%; object-fit: cover;
  border-radius: 10px; border: 1px solid var(--line);
}
.report-thumb button {
  position: absolute; top: -6px; right: -6px;
  width: 22px; height: 22px; padding: 0; border-radius: 50%;
  display: grid; place-items: center; line-height: 1;
  font-size: .8rem; background: var(--panel-2); border: 1px solid var(--line);
}

input[type=file] {
  width: 100%; font: inherit; color: var(--muted); font-size: .88rem;
  background: var(--panel); border: 1px dashed var(--line);
  border-radius: 12px; padding: 12px 13px;
}
input[type=file]::file-selector-button {
  font: inherit; font-weight: 600; cursor: pointer; margin-right: 10px;
  background: var(--panel-2); color: var(--ink);
  border: 1px solid var(--line); border-radius: 9px; padding: 6px 11px;
}

/* ---------- admin ---------- */

.admin-nav { display: flex; gap: 8px; margin: 0 0 18px; }
.admin-nav a {
  flex: 1; text-align: center; text-decoration: none;
  padding: 10px 8px; border-radius: 12px;
  background: var(--panel); border: 1px solid var(--line);
  color: var(--muted); font-weight: 600; font-size: .9rem;
}
.admin-nav a.on { color: var(--ink); border-color: var(--accent); }
/* The tab bar's badge hangs off an absolutely positioned corner; here there is
   no corner to hang from, so it sits in the line like a count. */
.admin-nav .dot {
  position: static; transform: none; display: inline-block;
  box-shadow: none; margin-left: 4px;
}

.panel-link { display: block; text-decoration: none; color: inherit; }
.panel-link:active { transform: translateY(1px); }
.panel-fresh { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }

.chip-on { border-color: var(--accent); color: var(--ink); }

.state-chip {
  font-size: .72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; padding: 4px 9px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted); white-space: nowrap;
}
.state-new    { color: #ffd08a; border-color: #6b4b18; background: #2b2113; }
.state-open   { color: #9fd0ff; border-color: #2b4d72; background: #14212e; }
.state-closed { color: var(--muted); }

.shot { display: block; margin-bottom: 12px; }
.shot img {
  width: 100%; height: auto; display: block;
  border-radius: var(--radius); border: 1px solid var(--line);
}

/* ---------- picking the timing ---------- */

/* The X in "sometime in the next X days" sits inline in the option's title. */
.days-count {
  display: inline-block; width: auto; min-width: 3.4em;
  padding: 2px 6px; margin: 0 2px; font: inherit; font-weight: 700;
}

/* At a card tap each item carries its own timing picker; only the chosen
   item's is shown, which needs no script because the radio sits just before. */
.tier-option .timing { display: none; margin-top: 10px; }
.tier-option input[type=radio]:checked + span .timing { display: grid; gap: 6px; }

/* ---------- called in: the countdown ---------- */

.due-list { margin-bottom: 22px; }
.due {
  --glow: #5b8cff;
  position: relative;
  background: var(--panel); border: 1px solid var(--glow); border-radius: var(--radius);
  padding: 14px 16px; margin-bottom: 12px;
  box-shadow: 0 0 10px -2px var(--glow);
  animation: due-breathe 3.2s ease-in-out infinite alternate;
}
.due-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.due-label {
  font-size: .74rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  color: var(--glow);
}
.due-clock {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-variant-numeric: tabular-nums; font-weight: 700; font-size: 1.15rem;
  color: var(--glow); white-space: nowrap;
}
.due-item { margin: 6px 0 2px; font-size: 1.12rem; font-weight: 700; }
.due-who { margin: 0; color: var(--muted); font-size: .86rem; }

/* Blue while the deadline is days away, teal on its day (the colours of
   their chips), yellow for the last hour of either, orange for a "within the
   hour", red once it is due. */
.due[data-phase="days"]  { --glow: #5b8cff; }
.due[data-phase="today"] { --glow: #35c8b0; }
.due[data-phase="final"] { --glow: #ffd84a; }
.due[data-phase="hour"]  { --glow: #ff9a1f; }
.due[data-phase="now"]   {
  --glow: #ff3b55;
  /* Two flashes a second: urgent, and still under the three-a-second line
     past which flashing can set off photosensitive seizures. */
  animation: due-flash .5s steps(1, end) infinite;
}
.due[data-phase="now"] .due-label { font-size: 1rem; }

@keyframes due-breathe {
  from { box-shadow: 0 0 4px -2px var(--glow); border-color: color-mix(in srgb, var(--glow) 35%, transparent); }
  to   { box-shadow: 0 0 22px 2px var(--glow); border-color: var(--glow); }
}
@keyframes due-flash {
  0%   { box-shadow: 0 0 26px 6px var(--glow); border-color: var(--glow);
         background: color-mix(in srgb, var(--glow) 22%, var(--panel)); }
  50%  { box-shadow: 0 0 4px 0 var(--glow); border-color: color-mix(in srgb, var(--glow) 40%, transparent);
         background: var(--panel); }
}

@media (prefers-reduced-motion: reduce) {
  .due, .due[data-phase="now"] { animation: none; box-shadow: 0 0 16px 0 var(--glow); }
}

/* ---------- asking to spend ---------- */

/* Waiting at the top of I owe until the phones meet. Pulses rather than
   strobes: it may sit there a while, and it is a call to act, not an alarm. */
.iou.request {
  border-color: var(--a);
  animation: request-pulse 1.1s ease-in-out infinite alternate;
}
@keyframes request-pulse {
  from { box-shadow: 0 0 4px -2px var(--a); }
  to   { box-shadow: 0 0 24px 3px var(--a); border-color: var(--b); }
}
.iou.is-requested:not(.request) { outline: 1px dashed var(--a); outline-offset: -1px; }
.tier-option.is-requested span { box-shadow: 0 0 18px -4px var(--a); }

.requested-chip {
  display: inline-block; margin-left: 6px; padding: 2px 8px 1px; border-radius: 999px;
  font-size: .66rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  background: var(--accent); color: #14101c; vertical-align: middle;
}
.requested-note {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--line);
}

/* ---------- the QR code ---------- */

/* Heading stays left like every other page, clear of the logo; the code and
   what happens to it sit centred under it. */
.qr-expiry, .qr-done { text-align: center; }
.qr-code {
  width: min(78vw, 320px); margin: 18px auto 10px; padding: 0;
  background: #fff; border-radius: 16px; overflow: hidden;
  /* Full brightness, whatever the theme: a scanner needs the contrast. */
  box-shadow: 0 0 0 6px #fff, 0 0 30px -4px var(--accent);
}
.qr-code svg { display: block; width: 100%; height: auto; }
.qr-screen.is-expired .qr-code { opacity: .15; }
.qr-actions { justify-content: center; margin-top: 18px; }
.qr-actions .btn { flex: 0 0 auto; }

/* ---------- scanning one ---------- */

.scanner {
  position: relative; width: 100%; aspect-ratio: 1; max-width: 420px; margin: 14px auto;
  border-radius: var(--radius); overflow: hidden; background: #000;
}
.scanner video { width: 100%; height: 100%; object-fit: cover; display: block; }
.scanner-frame {
  position: absolute; inset: 16%; border-radius: 18px;
  border: 3px solid rgba(255,255,255,.85);
  box-shadow: 0 0 0 999px rgba(0,0,0,.35);
}

@media (prefers-reduced-motion: reduce) {
  .iou.request { animation: none; box-shadow: 0 0 18px 0 var(--a); }
}

/* ---------- "Requires PIN" ---------- */

.pin-toggle { margin: 12px 0 0; }
.panel > .pin-toggle { padding-top: 12px; border-top: 1px dashed var(--line); }
.check {
  display: flex; align-items: center; gap: 10px; margin: 0;
  font-weight: 600; cursor: pointer;
}
.check input[type=checkbox] {
  width: 20px; height: 20px; margin: 0; flex: none; accent-color: var(--accent);
}
/* The master switch is off: the boxes keep their ticks but are not in force. */
.pin-toggle.is-disabled .check { opacity: .42; cursor: not-allowed; }

/* ---------- one-line pickers ---------- */

.pick { margin-top: 18px; }
.pick-box {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  background: var(--panel); border: 1px solid var(--line); border-radius: 16px;
  padding: 12px 14px 12px 18px; color: var(--ink); font: inherit; cursor: pointer;
}
/* The chosen tier's colour down the left edge, like an IOU card. */
.pick-box::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 4px;
  background: linear-gradient(var(--a, var(--line)), var(--b, var(--line)));
}
.pick-text { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.pick-title { font-size: .8rem; font-weight: 600; color: var(--muted); }
.pick-value { font-weight: 700; font-size: 1rem; }
.pick-plus {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center; font-size: 1.3rem; line-height: 1;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--accent);
}
.pick-done { margin-top: 14px; }

/* With scripting off the options sit inline under their title. */
html:not(.js) .pick-box { display: none; }
html:not(.js) .pick-card .sheet-title { font-size: 1rem; margin: 0 0 7px; }
html:not(.js) .pick-done { display: none; }

/* With it on, the options open as a dimmed full-screen choice. */
.js .pick-sheet { display: none; }
.js .pick-sheet.open {
  display: flex; align-items: center; justify-content: center;
  position: fixed; inset: 0; z-index: 45; padding: 16px;
  background: rgba(8, 6, 14, .74); backdrop-filter: blur(3px);
}
.js .pick-card {
  width: 100%; max-width: 480px; max-height: calc(100vh - 32px); overflow-y: auto;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 20px 16px; animation: rise .2s cubic-bezier(.2, .9, .3, 1);
}
.js .pick-card .sheet-title { margin-bottom: 12px; }
.pick-card .tier-picker { gap: 8px; }
.pick-card .tier-option span { padding: 10px 14px; }
body.sheet-open { overflow: hidden; }

.condition-deadline .pick { margin-top: 10px; }

/* ---------- conditions ---------- */

.iou.has-condition { border-style: dashed; }
.condition {
  margin-top: 10px; padding: 10px 12px; border-radius: 12px;
  background: var(--panel-2); border: 1px solid var(--line);
}
.condition-text { margin: 0; font-size: .94rem; }
.condition-text b {
  font-size: .68rem; letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent); margin-right: 6px;
}
.condition-done { color: #8fe3c4; }
/* The deadline clock, inline and smaller than a delivery's. */
.due.due-cond { margin: 8px 0 0; padding: 8px 10px; border-radius: 10px; }
.due.due-cond .due-label { font-size: .66rem; }
.due.due-cond .due-clock { font-size: .95rem; }

/* ---------- list heads: title, sort, view ---------- */

.list-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 22px 0 10px;
}
.list-head h2 { margin: 0; }
.viewbar { display: flex; align-items: center; gap: 6px; flex: none; }
.viewbar-sort { margin: 0; }
.viewbar select {
  width: auto; padding: 6px 28px 6px 10px; font-size: .78rem; font-weight: 600;
  border-radius: 10px; color: var(--muted);
  background: var(--panel) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%239b93ad'/%3E%3C/svg%3E") no-repeat right 10px center;
}
.viewbar-toggle {
  width: 34px; height: 32px; padding: 0; display: grid; place-items: center;
  border-radius: 10px; font-size: 1rem; line-height: 1; color: var(--muted);
}
.viewbar-toggle .view-icon-list { display: none; }
.viewbar-toggle[aria-pressed="true"] .view-icon-list { display: inline; }
.viewbar-toggle[aria-pressed="true"] .view-icon-grid { display: none; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}

/* ---------- the grid view ---------- */

/* As many columns as fit at about 160px each, and never fewer than two. */
.collection.is-grid {
  display: grid; gap: 10px; align-items: start;
  grid-template-columns: repeat(auto-fill, minmax(min(160px, calc(50% - 5px)), 1fr));
}
.collection.is-grid > * { margin: 0; min-width: 0; }
.collection.is-grid .iou { padding: 12px 12px 12px 15px; }
.collection.is-grid .iou-top { flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }
.collection.is-grid .tier-chip { font-size: .62rem; padding: 4px 7px 3px; }
.collection.is-grid .expiry { font-size: .74rem; flex-basis: 100%; }
.collection.is-grid .item { font-size: 1rem; line-height: 1.25; overflow-wrap: anywhere; }
/* Notes are the first thing to give up space: two lines, then an ellipsis. */
.collection.is-grid .note {
  font-size: .82rem; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.collection.is-grid .iou-bottom { flex-direction: column; align-items: stretch; gap: 8px; margin-top: 10px; }
.collection.is-grid .who { font-size: .76rem; }
.collection.is-grid .iou-bottom form, .collection.is-grid .iou-bottom .btn-small { width: 100%; }
.collection.is-grid .iou-bottom form .btn-small { width: 100%; }
.collection.is-grid .condition { padding: 8px; }
.collection.is-grid .condition-text { font-size: .82rem; }
.collection.is-grid .requested-note { flex-direction: column; align-items: stretch; }
.collection.is-grid .grace { flex-direction: column; align-items: stretch; }

.feed-retry { display: inline-block; margin-top: 8px; }

/* ---------- friends ---------- */

.n-owed { color: #5fe0a0; font-weight: 700; }
.n-owing { color: #ff6b81; font-weight: 700; }
.friend-name { font-weight: 600; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.friend-count { font-size: .84rem; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }
.friend-meta { margin: 0; font-size: .82rem; overflow-wrap: anywhere; }
.friend-who { min-width: 0; }

.collection.is-grid .panel { margin: 0; padding: 12px; }
.collection.is-grid .friend-head { flex-direction: column; align-items: stretch; gap: 8px; }
.collection.is-grid .friend-head form .btn-small { width: 100%; }
.collection.is-grid .friend-meta { font-size: .76rem; }

/* ---------- Settings: tiles and their panels ---------- */

.tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.tile {
  display: flex; flex-direction: column; gap: 4px; min-height: 104px;
  padding: 14px; border-radius: var(--radius); text-decoration: none; color: var(--ink);
  background: var(--panel); border: 1px solid var(--line);
}
.tile:hover, .tile:focus-visible { border-color: var(--accent); }
.tile-icon { font-size: 1.3rem; line-height: 1; margin-bottom: 6px; }
.tile b { font-size: 1rem; }
.tile small { color: var(--muted); font-size: .8rem; line-height: 1.3; }

.panel-sheet { display: none; }
.panel-sheet.open {
  display: flex; align-items: center; justify-content: center;
  position: fixed; inset: 0; z-index: 45; padding: 16px;
  background: rgba(8, 6, 14, .74); backdrop-filter: blur(3px);
}
.panel-card {
  width: 100%; max-width: 520px; max-height: calc(100vh - 32px); overflow-y: auto;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 20px 16px; animation: rise .2s cubic-bezier(.2, .9, .3, 1);
}
.panel-sub {
  margin: 22px 0 8px; font-size: .8rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}
.panel-card .sheet-title + .panel-sub, .panel-card .alert + .panel-sub { margin-top: 6px; }
.panel-row { align-items: center; padding: 10px 0; border-top: 1px solid var(--line); margin-top: 10px; }
.panel-done { margin-top: 20px; }
.panel-card .btn-block + .panel-sub { margin-top: 24px; }
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr)); gap: 10px; }
.card-grid .card-face { aspect-ratio: auto; min-height: 126px; gap: 10px; }

.intro-steps { margin: 0; padding-left: 18px; display: grid; gap: 6px; }
/* A long heading (Friends · Owed / Owing) keeps one line; the controls drop
   underneath it on a narrow phone instead of squeezing it into three. */
.list-head { flex-wrap: wrap; row-gap: 8px; }
.list-head h2 { flex: 1 1 140px; }
.list-head .viewbar { margin-left: auto; }

/* ---------- the "Right now" strip ---------- */

.strip-section { margin: 4px 0 6px; }
.strip-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.strip-head h2 { margin: 14px 0 10px; }
.strip-hint { font-size: .76rem; color: var(--muted); }
/* Sideways, one card and a peek of the next, snapping card by card. The
   negative margin lets cards run to the screen edge while lining up with the
   page's own gutter. */
/* A scrolling box clips everything outside it, the pulsing glow included, so
   the strip carries 28px of padding for the glow to live in and gives it back
   with negative margins, leaving the layout where it was. */
.strip {
  display: flex; gap: 12px; overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scroll-padding-inline: 18px;
  margin: -24px -18px -14px; padding: 28px 18px 32px; scrollbar-width: none;
}
.strip::-webkit-scrollbar { display: none; }
.strip > .strip-item {
  flex: 0 0 min(86%, 420px); scroll-snap-align: start; margin: 0;
  display: flex; flex-direction: column;
}
/* A lone card takes the full width; there is nothing to swipe to. */
.strip > .strip-item:only-child { flex-basis: 100%; }
.strip-item .iou-bottom { margin-top: auto; padding-top: 10px; }
.strip-due > .due { margin: 0; height: 100%; }
.strip-kind {
  display: block; margin-bottom: 8px;
  font-size: .66rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent);
}

/* ---------- filter ---------- */

/* Filled when a filter is on, so a shortened list never passes for the whole. */
.viewbar-filter.is-active {
  background: linear-gradient(120deg, var(--accent), #7ad0ff); color: #16101f; border-color: transparent;
}
.filter-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.filter-chip { margin: 0; position: relative; }
.filter-chip input { position: absolute; opacity: 0; pointer-events: none; }
.filter-chip span {
  display: inline-block; padding: 8px 13px; border-radius: 999px; cursor: pointer;
  font-size: .86rem; font-weight: 600; background: var(--panel-2); border: 1px solid var(--line);
}
.filter-chip input:checked + span {
  background: linear-gradient(120deg, var(--accent), #7ad0ff); color: #16101f; border-color: transparent;
}
.filter-chip input:focus-visible + span { outline: 2px solid var(--accent); }

/* ---------- scan button ---------- */

/* Bottom-left, opposite the report button, one thumb from the list. A QR
   glyph so it says what it does; see-through enough to read the card under
   it, and more so while the list is moving. */
.scan-fab {
  position: fixed; z-index: 20; left: 14px; bottom: calc(76px + env(safe-area-inset-bottom));
  width: 52px; height: 52px; border-radius: 16px;
  display: grid; place-items: center; text-decoration: none; color: #0f1a2a;
  background: linear-gradient(130deg, rgba(127, 240, 224, .82), rgba(53, 182, 201, .82) 45%, rgba(28, 116, 184, .82));
  backdrop-filter: blur(6px);
  box-shadow: 0 0 0 1.5px rgba(216, 169, 74, .8), 0 10px 24px -10px rgba(0, 0, 0, .8);
  transition: opacity .2s ease;
}
.scan-fab:active { transform: scale(.96); }
/* So the last card is never stuck under it. */
body:has(.scan-fab) .shell { padding-bottom: calc(150px + env(safe-area-inset-bottom)); }

/* ---------- friends: give from the card ---------- */

.friend-actions { display: flex; gap: 8px; flex: 0 0 auto; }
.collection.is-grid .friend-actions { flex-direction: column; }
.collection.is-grid .friend-actions .btn, .collection.is-grid .friend-actions form .btn-small { width: 100%; text-align: center; }

/* ---------- live updates ---------- */

.live-pill {
  position: fixed; z-index: 30; left: 50%; top: calc(12px + env(safe-area-inset-top));
  transform: translateX(-50%);
  padding: 9px 16px; border-radius: 999px; font-size: .84rem; font-weight: 700;
  background: linear-gradient(120deg, var(--accent), #7ad0ff); color: #16101f; border: 0;
  box-shadow: 0 8px 22px -8px rgba(0, 0, 0, .8);
  animation: rise .25s cubic-bezier(.2, .9, .3, 1);
}

/* ---------- the title once it scrolls away ---------- */

.mini-title {
  position: fixed; z-index: 15; inset: 0 0 auto 0;
  padding: calc(10px + env(safe-area-inset-top)) 18px 10px;
  background: rgba(20, 17, 28, .9); backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
  transform: translateY(-100%); transition: transform .2s ease; cursor: pointer;
}
.mini-title.show { transform: none; }
.mini-title span {
  display: block; max-width: 524px; margin: 0 auto;
  font-size: 1.05rem; font-weight: 800; letter-spacing: -0.01em;
  background: linear-gradient(100deg, #b8fff4, #6fe6f2 30%, #3aa0e6 65%, #2a62c9);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.viewbar select { max-width: 116px; text-overflow: ellipsis; }

/* ---------- NSFW ---------- */

.nsfw-check { margin-top: 18px; align-items: flex-start; }
.nsfw-check span { display: flex; flex-direction: column; gap: 2px; }
.nsfw-check small { font-weight: 400; color: var(--muted); font-size: .8rem; }

.nsfw-title { cursor: pointer; }
.nsfw-badge {
  display: inline-block; vertical-align: middle; margin-right: 6px; padding: 2px 6px 1px;
  border-radius: 6px; font-size: .6rem; font-weight: 800; letter-spacing: .08em;
  background: #ff3b5c; color: #fff;
}
/* Pixelated stand-ins for hidden letters: a checker of two greys at about a
   letter's width each, so the shape of the title survives but not its words. */
.px {
  display: inline-block; vertical-align: -0.12em;
  width: calc(var(--n) * 0.55em); height: 0.95em; border-radius: 2px;
  background:
    conic-gradient(#5a5270 25%, #3a3450 0 50%, #6c6386 0 75%, #443d5a 0) 0 0 / 0.32em 0.32em;
  image-rendering: pixelated;
}
.nsfw-title.is-revealed { cursor: auto; }
.reveal-card input { margin-top: 4px; }

/* ---------- family ---------- */

.badge {
  display: inline-block; padding: 2px 7px 1px; border-radius: 999px;
  font-size: .64rem; font-weight: 800; letter-spacing: .05em; vertical-align: middle;
}
.badge-minor { background: #3a2f12; color: #ffd66b; border: 1px solid #6b5420; }
.badge-child { background: #12303a; color: #7ff0e0; border: 1px solid #1f5a66; }
.parent-code { font-size: 1.4rem; letter-spacing: .2em; text-align: center; }
.family-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 6px 0 4px; }
.family-stats > div {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 12px 6px; border-radius: 14px; background: var(--panel); border: 1px solid var(--line);
}
.family-stats b { font-size: 1.4rem; }
.family-stats small { color: var(--muted); font-size: .76rem; }
.friend-pills { display: flex; flex-wrap: wrap; gap: 8px; }
.friend-pills .friend-count { margin-left: 6px; }

/* A clock in a narrow card puts its time under its label rather than off the edge. */
.due-top { flex-wrap: wrap; row-gap: 2px; }

/* ---------- long-form pages (privacy) ---------- */
.prose h3 { margin: 18px 0 6px; font-size: 1rem; }
.prose h3:first-child { margin-top: 0; }
.prose p, .prose li { color: var(--ink); line-height: 1.5; font-size: .94rem; }
.prose ul { padding-left: 18px; margin: 6px 0; display: grid; gap: 6px; }

/* Sign-in: the icon itself, above the name. */
.brand-mark { display: block; width: 84px; height: 84px; border-radius: 20px; margin: 6px 0 14px;
  box-shadow: 0 10px 30px -10px rgba(58, 160, 230, .6); }

/* ---------- offline ---------- */

.offline-bar { display: none; }
body.is-offline .offline-bar {
  display: block; position: fixed; z-index: 35; inset: 0 0 auto 0;
  padding: calc(8px + env(safe-area-inset-top)) 16px 8px; text-align: center;
  font-size: .8rem; font-weight: 600; color: #16101f; background: #ffd66b;
}
body.is-offline .shell { padding-top: 64px; }
/* Nothing that changes anything works offline; make that visible. */
body.is-offline form button, body.is-offline [data-ask], body.is-offline .scan-fab,
body.is-offline .btn-primary, body.is-offline [data-open-panel] {
  opacity: .4; pointer-events: none;
}

/* ---------- inside the app ---------- */

/* The native app has its own notifications (coming with Firebase); the
   browser's "enable on this device" is for the website only. */
.in-app #push-enable, .in-app #push-test { display: none; }

/* ---------- the drawn icon set (views/_icon.ejs) ---------- */

.icon { display: block; flex: none; }
.tabbar a span { display: inline-flex; justify-content: center; margin-bottom: 3px; }
.tabbar a span .icon { width: 22px; height: 22px; }
.tabbar a.on .icon { stroke-width: 2.4; }
.tile-icon { display: inline-flex; color: var(--accent); margin-bottom: 6px; }
.intro-tabs li > span { display: inline-flex; color: var(--accent); align-self: center; }
.scan-fab .icon { stroke-width: 2.2; }
