/* vzMoney styles (docs/PLAN.md §9). Tokens first, both themes designed (not
   inverted); every screen uses tokens only. No inline styles anywhere (the
   CSP forbids them): per-element values such as a bar's fill are CSS custom
   properties set through the CSSOM. */

/* The UI font, self-hosted (deps/ASSETS.md): Inter 4.1, variable weight,
   with tabular figures for amounts (.num). The system font shows until it
   has loaded. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/fonts/InterVariable.woff2") format("woff2");
}

:root {
  color-scheme: light;
  --bg: #f6f4ef;
  --surface: #ffffff;
  --surface-2: #efece5;
  --ink: #18201d;
  --muted: #59625f;
  --line: #e5e1d8;
  --accent: #0e7c66;
  --accent-soft: #dcf0e9;
  --accent-ink: #ffffff;
  --warn: #8f5400;
  --warn-soft: #fbefd6;
  --warn-bar: #e3a33a;
  --bad: #b42318;
  --bad-ink: #ffffff;
  --bad-soft: #fce5e1;
  --bad-line: #ebc3bd;
  --bar-track: #ece8e0;
  --row-on: #f3f8f6;
  --shadow: 0 6px 16px rgba(14, 124, 102, 0.28);
  --scrim: rgba(24, 32, 29, 0.45);

  --font: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-xl: 24px;
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --spring: cubic-bezier(0.2, 0.9, 0.3, 1.3);
  --fast: 160ms;
  --med: 300ms;
  --slow: 500ms;
  --spin: 700ms;
  --press: 80ms;
  --sheet: 380ms;
  --burst: 750ms;
  --roll: 250ms;
  --pulse: 1200ms;
  /* Text on a strong colour (brand tiles), and the switch's knob: the same
     in both themes. */
  --on-color: #ffffff;
  --knob: #ffffff;
  --knob-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  --raised: 0 1px 2px rgba(0, 0, 0, 0.08);
  /* Brand colours for account and payee tiles (§9.2), and a palette for
     names without one. */
  --brand-shopeepay: #c2410c;
  --brand-telkomsel: #c8102e;
  /* Logos (deps/ASSETS.md): the light tile they sit on, and each one-colour
     mark's brand colour (with its tile's, when the mark is light). */
  --logo-tile: #ffffff;
  --brand-visa: #1a1f71;
  --brand-mastercard: #eb001b;
  --brand-gojek: #ffffff;
  --brand-gojek-tile: #00aa13;
  --brand-grab: #ffffff;
  --brand-grab-tile: #00b14f;
  --brand-shopee: #ee4d2d;
  --brand-netflix: #e50914;
  --brand-spotify: #000000;
  --brand-spotify-tile: #1ed760;
  --brand-paypal: #002991;
  --brand-wise: #163300;
  --brand-wise-tile: #9fe870;
  --brand-youtube: #ff0000;
  --brand-youtubemusic: #ff0000;
  --brand-google: #4285f4;
  --brand-googleplay: #414141;
  --brand-googlecloud: #4285f4;
  --brand-apple: #000000;
  --brand-applemusic: #fa243c;
  --brand-icloud: #3693f3;
  --brand-starbucks: #006241;
  --brand-mcdonalds: #ffc72c;
  --brand-mcdonalds-tile: #da291c;
  --brand-kfc: #f40027;
  --brand-burgerking: #d62300;
  --brand-whatsapp: #ffffff;
  --brand-whatsapp-tile: #25d366;
  --brand-zoom: #0b5cff;
  --brand-claude: #d97757;
  --brand-blibli: #0072ff;
  --brand-bukalapak: #e31e52;
  --brand-ikea: #0058a3;
  --brand-uniqlo: #ff0000;
  --brand-zara: #000000;
  --brand-hm: #e50010;
  --brand-airasia: #ff0000;
  --brand-lionair: #ed3237;
  --brand-bookingcom: #003a9a;
  --brand-shell: #ffd500;
  --brand-shell-tile: #dd1d21;
  --brand-steam: #000000;
  --brand-uber: #000000;
  --brand-airbnb: #ff5a5f;
  --brand-dropbox: #0061ff;
  --brand-hetzner: #d50c2d;
  --brand-wasabi: #ffffff;
  --brand-wasabi-tile: #01cd3e;
  --brand-cloudflare: #f38020;
  --tile-0: #0e7c66;
  --tile-1: #2f6fd6;
  --tile-2: #8b5cf6;
  --tile-3: #b05a00;
  --tile-4: #c2417a;
  --tile-5: #4b5563;
  --tile-6: #7a5a2e;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #0f1513;
    --surface: #171f1c;
    --surface-2: #212b27;
    --ink: #e8efeb;
    --muted: #9daaa5;
    --line: #2a3531;
    --accent: #45c9a9;
    --accent-soft: #163a31;
    --accent-ink: #06251e;
    --warn: #f3b85e;
    --warn-soft: #3a2c14;
    --bad: #ff8b7b;
    --bad-ink: #2a0d09;
    --bad-soft: #3e1d19;
    --bad-line: #5a2a24;
    --bar-track: #27322e;
    --row-on: #1c2a25;
    --shadow: 0 6px 16px rgba(0, 0, 0, 0.45);
    --scrim: rgba(0, 0, 0, 0.6);
    --logo-tile: #e3e8e5;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0f1513;
  --surface: #171f1c;
  --surface-2: #212b27;
  --ink: #e8efeb;
  --muted: #9daaa5;
  --line: #2a3531;
  --accent: #45c9a9;
  --accent-soft: #163a31;
  --accent-ink: #06251e;
  --warn: #f3b85e;
  --warn-soft: #3a2c14;
  --bad: #ff8b7b;
  --bad-ink: #2a0d09;
  --bad-soft: #3e1d19;
  --bad-line: #5a2a24;
  --bar-track: #27322e;
  --row-on: #1c2a25;
  --shadow: 0 6px 16px rgba(0, 0, 0, 0.45);
  --scrim: rgba(0, 0, 0, 0.6);
  --logo-tile: #e3e8e5;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}
html,
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}
html.modal-open body {
  overflow: hidden;
}
button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}
button {
  cursor: pointer;
}
button:disabled {
  cursor: default;
}
a {
  color: inherit;
}
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
[tabindex="-1"]:focus {
  outline: none;
}
.num {
  font-variant-numeric: tabular-nums;
}
.muted {
  color: var(--muted);
}
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
/* "Hide amounts" (Settings): numbers blur until tapped. */
.hide-amounts .money {
  filter: blur(7px);
}
.hide-amounts .money:hover,
.hide-amounts .money:focus {
  filter: none;
}
.hide-amounts .cat .assign input:not(:focus),
.hide-amounts .sheet input[inputmode="numeric"]:not(:focus) {
  color: transparent;
  text-shadow: 0 0 8px var(--ink);
}

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

.shell {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}
.layout {
  flex: 1;
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  display: flex;
  gap: 16px;
  padding: 0 16px;
}
.sidebar {
  display: none;
}
.content {
  flex: 1;
  min-width: 0;
  padding-bottom: calc(96px + env(safe-area-inset-bottom));
}
.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(10px);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: calc(8px + env(safe-area-inset-top)) 0 8px 0;
}
.topbar h1 {
  margin: 0;
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.01em;
}
.topbar .spacer {
  flex: 1;
}
.month-nav {
  display: flex;
  align-items: center;
  gap: 2px;
}
.month-nav .label {
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
  min-width: 128px;
  text-align: center;
}

.bottomnav {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  align-items: center;
  padding: 8px 8px calc(12px + env(safe-area-inset-bottom)) 8px;
  background: var(--surface);
  border-top: 1px solid var(--line);
}
.bottomnav a {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-height: 48px;
  justify-content: center;
  text-decoration: none;
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
}
.bottomnav a[aria-current="page"] {
  color: var(--accent);
  font-weight: 700;
}
.bottomnav .add {
  justify-self: center;
  width: 56px;
  height: 56px;
  border: none;
  border-radius: 20px;
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--shadow);
  transition: transform var(--fast) var(--ease);
}
.bottomnav .add:active {
  transform: scale(0.94);
}

/* Tablets: a centred column, and the tab bar's items kept together. */
@media (min-width: 640px) and (max-width: 1023px) {
  .content > .page {
    max-width: 720px;
    margin: 0 auto;
  }
  .bottomnav {
    padding-inline: max(8px, calc((100% - 560px) / 2));
  }
}

@media (min-width: 1024px) {
  .layout {
    max-width: 1240px;
  }
  .bottomnav {
    display: none;
  }
  .content {
    padding-bottom: 32px;
  }
  .content > .page {
    max-width: 920px;
    margin: 0 auto;
  }
  .sidebar {
    display: flex;
    flex-direction: column;
    gap: 12px;
    width: 248px;
    flex: none;
    padding: 16px 0;
    position: sticky;
    top: 0;
    align-self: flex-start;
    max-height: 100dvh;
    overflow-y: auto;
  }
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 18px;
  font-weight: 800;
  letter-spacing: -0.02em;
  padding: 4px 8px 8px 8px;
}
.brand img {
  width: 32px;
  height: 32px;
}
.sidenav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sidenav a {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 12px;
  border-radius: 12px;
  text-decoration: none;
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
}
.sidenav a[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 800;
}
.sidebar .accts {
  padding: 6px 0;
}
.sidebar .accts h2 {
  margin: 4px 14px 6px;
}
.sidebar a.setting {
  text-decoration: none;
  color: var(--ink);
  border-top: none;
  border-radius: 12px;
  margin: 0 6px;
  padding: 8px;
  font-size: 14px;
}
.sidebar a.setting:hover,
.sidebar a.setting[aria-current="page"] {
  background: var(--surface-2);
}
.sidebar a.setting .label {
  font-size: 14px;
}
.sidebar a.setting .money {
  font-size: 13px;
  font-weight: 700;
}
.sidenav .new {
  justify-content: center;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 800;
  margin-bottom: 8px;
}

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

.iconbtn {
  width: 44px;
  height: 44px;
  border: none;
  border-radius: 12px;
  background: transparent;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.iconbtn:hover {
  background: var(--surface-2);
}
.seg {
  display: flex;
  padding: 3px;
  border-radius: 999px;
  background: var(--surface-2);
}
.seg button {
  min-width: 44px;
  height: 32px;
  padding: 0 12px;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
}
.seg button[aria-pressed="true"],
.seg button[aria-checked="true"],
.seg button[aria-selected="true"] {
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--raised);
}
/* Touch: small controls answer a finger-sized area around them. */
@media (pointer: coarse) {
  .seg button,
  .switch,
  .iconbtn.small,
  .clear-toggle {
    position: relative;
  }
  .seg button::before,
  .switch::before {
    content: "";
    position: absolute;
    inset: -6px 0;
  }
  .iconbtn.small::before,
  .clear-toggle::before {
    content: "";
    position: absolute;
    inset: -7px;
  }
}
.seg.wide {
  border-radius: 14px;
  padding: 4px;
}
.seg.wide button {
  flex: 1;
  height: 40px;
  border-radius: 10px;
  font-size: 14px;
}
.btn {
  height: 44px;
  padding: 0 16px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  font-size: 14px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-decoration: none;
  transition: transform var(--fast) var(--ease);
}
.btn:disabled:not(.primary) {
  opacity: 0.5;
}
.btn:active:not(:disabled) {
  transform: scale(0.97);
}
.btn.small {
  height: 40px;
  padding: 0 14px;
  font-size: 13px;
}
.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.btn.primary:disabled {
  background: var(--surface-2);
  border-color: var(--surface-2);
  color: var(--muted);
}
.btn.danger {
  background: transparent;
  border-color: var(--bad-line);
  color: var(--bad);
}
.btn[aria-busy="true"]::after {
  content: "";
  width: 16px;
  height: 16px;
  flex: none;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin var(--spin) linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
.btn.block {
  width: 100%;
  height: 52px;
  font-size: 16px;
  border-radius: 16px;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--line);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  color: var(--ink);
}
.chip.warn {
  background: var(--warn-soft);
  border-color: transparent;
  color: var(--warn);
  font-weight: 700;
}
.switch {
  width: 52px;
  height: 32px;
  flex: none;
  border: none;
  padding: 3px;
  border-radius: 999px;
  background: var(--line);
  display: flex;
}
.switch::after {
  content: "";
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--knob);
  box-shadow: var(--knob-shadow);
  transition: transform var(--med) var(--ease);
}
.switch[aria-checked="true"] {
  background: var(--accent);
}
.switch[aria-checked="true"]::after {
  transform: translateX(20px);
}
.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.field label {
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
}
.field input,
.field select {
  height: 48px;
  padding: 0 14px;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--surface);
  font-size: 16px;
}
.field input:focus,
.field select:focus {
  outline: 2px solid var(--accent);
  outline-offset: 0;
  border-color: var(--accent);
}

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

.card {
  border-radius: var(--r-lg);
  background: var(--surface);
  border: 1px solid var(--line);
  overflow: hidden;
}
.section-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 4px;
  margin: 0;
  padding: 4px 4px 8px 4px;
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}
/* A group's title carries its edit button, quiet until needed. */
section > .section-title > span {
  flex: 1;
  min-width: 0;
}
.iconbtn.small {
  width: 36px;
  height: 36px;
  margin: -8px 0;
}
/* A folded section's title is its toggle. */
button.section-title.fold {
  width: 100%;
  border: none;
  background: none;
  text-align: left;
  justify-content: flex-start;
  gap: 8px;
  cursor: pointer;
}
.field .hint {
  font-size: 12px;
  color: var(--muted);
}
.setting.flat {
  padding: 4px 0;
  border-top: none;
}
.hero .hero-fix {
  align-self: flex-start;
  margin-top: 8px;
  border-color: var(--bad-line);
  color: var(--bad);
}
.acct-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 0 4px;
}
.acct-head .actions {
  margin-left: auto;
}
.acct-head .hint {
  font-size: 13px;
  color: var(--muted);
}
/* The starter set: chips to leave categories out. */
.starter {
  padding: 18px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.starter h2 {
  margin: 0;
  font-size: 18px;
}
.starter .lead {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.5;
}
.starter .pickrow button {
  padding: 0 12px;
}
.starter .pickrow button[aria-pressed="false"] {
  color: var(--muted);
  text-decoration: line-through;
}
.stack {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.page {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-top: 4px;
  animation: page-in var(--med) var(--ease);
}
@keyframes page-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
}

/* --------------------------------------------------------------- budget */

.hero {
  border-radius: var(--r-xl);
  padding: 20px;
  display: flex;
  gap: 16px;
  align-items: center;
  background: var(--accent);
  color: var(--accent-ink);
  position: relative;
}
.hero.zero {
  background: var(--accent-soft);
  color: var(--accent);
}
.hero.negative {
  background: var(--bad-soft);
  color: var(--bad);
}
.hero .text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.hero .label,
.hero .hint {
  font-size: 13px;
  opacity: 0.88;
}
.hero .label {
  font-weight: 600;
}
.hero .amount {
  font-size: 34px;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.1;
  display: inline-block;
}
/* A changed amount rolls in (feel.js rollIn); nothing in between is drawn. */
.roll {
  animation: roll-in var(--roll) var(--ease);
}
@keyframes roll-in {
  from {
    opacity: 0;
    transform: translateY(45%);
  }
}
.hero .art {
  flex: none;
}
.hero .done {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--accent-ink);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  animation: pop var(--slow) var(--spring);
}
@keyframes pop {
  0% {
    transform: scale(0.6);
    opacity: 0;
  }
  60% {
    transform: scale(1.08);
    opacity: 1;
  }
  100% {
    transform: scale(1);
  }
}
/* Confetti bursts from the hero's centre (feel.js Confetti). Each piece's
   path is a preset picked by its position: no styles are set from script. */
.confetti {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 6;
}
.confetti i {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 12px;
  border-radius: 2px;
  background: var(--c, var(--accent));
  animation: confetti var(--burst) var(--ease) forwards;
}
.confetti i:nth-child(5n + 1) {
  --c: var(--tile-0);
}
.confetti i:nth-child(5n + 2) {
  --c: var(--accent);
}
.confetti i:nth-child(5n + 3) {
  --c: var(--warn-bar);
}
.confetti i:nth-child(5n + 4) {
  --c: var(--tile-1);
}
.confetti i:nth-child(5n + 5) {
  --c: var(--tile-4);
}
.confetti i:nth-child(7n + 1) {
  --dx: -170px;
  --dy: -210px;
  --rot: -300deg;
}
.confetti i:nth-child(7n + 2) {
  --dx: -110px;
  --dy: -290px;
  --rot: 220deg;
}
.confetti i:nth-child(7n + 3) {
  --dx: -40px;
  --dy: -250px;
  --rot: -160deg;
}
.confetti i:nth-child(7n + 4) {
  --dx: 30px;
  --dy: -310px;
  --rot: 340deg;
}
.confetti i:nth-child(7n + 5) {
  --dx: 95px;
  --dy: -230px;
  --rot: -240deg;
}
.confetti i:nth-child(7n + 6) {
  --dx: 150px;
  --dy: -280px;
  --rot: 180deg;
}
.confetti i:nth-child(7n + 7) {
  --dx: 180px;
  --dy: -170px;
  --rot: -90deg;
}
.confetti i:nth-child(odd) {
  --dy-scale: 0.7;
}
@keyframes confetti {
  to {
    transform: translate(var(--dx, 0), calc(var(--dy, -200px) * var(--dy-scale, 1))) rotate(var(--rot, 0));
    opacity: 0;
  }
}

.cat {
  border-top: 1px solid var(--line);
}
.cat:first-child {
  border-top: none;
}
.cat > button {
  width: 100%;
  border: none;
  background: transparent;
  text-align: left;
  padding: 14px 14px 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.cat > button:hover {
  background: var(--row-on);
}
.cat .top {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
}
.tile {
  width: 36px;
  height: 36px;
  flex: none;
  padding: 0;
  border-radius: 12px;
  background: var(--surface-2);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: -0.02em;
}
.tile.brand {
  background: var(--tile, var(--tile-0));
  color: var(--on-color);
}
/* Real logos (deps/ASSETS.md): a light tile holding the brand's own
   drawing (an image), or its one-colour mark masked in the brand colour.
   High-contrast modes keep them as drawn. */
.tile.logo {
  background: var(--logo-tile);
  border: 1px solid var(--line);
}
.tile.logo img {
  display: block;
  width: 78%;
  height: 78%;
  object-fit: contain;
}
/* A picture of one's own fills its tile. */
.tile.logo.own {
  overflow: hidden;
}
.tile.logo.own img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.bar.space {
  margin-top: 6px;
}
/* In a sheet, a setting row without the list's padding and line. */
.setting.picture-row {
  padding: 0;
  border-top: none;
}
.tile.logo .mark {
  display: block;
  width: 62%;
  height: 62%;
  background: var(--mark);
  -webkit-mask: var(--logo) center / contain no-repeat;
  mask: var(--logo) center / contain no-repeat;
}
@media (forced-colors: active) {
  .tile.logo,
  .tile.logo .mark {
    forced-color-adjust: none;
  }
}
.mark.l-visa {
  --logo: url("/logos/visa.svg");
  --mark: var(--brand-visa);
}
.mark.l-mastercard {
  --logo: url("/logos/mastercard.svg");
  --mark: var(--brand-mastercard);
}
.mark.l-gojek {
  --logo: url("/logos/gojek.svg");
  --mark: var(--brand-gojek);
}
.tile.logo.on-gojek {
  background: var(--brand-gojek-tile);
}
.mark.l-grab {
  --logo: url("/logos/grab.svg");
  --mark: var(--brand-grab);
}
.tile.logo.on-grab {
  background: var(--brand-grab-tile);
}
.mark.l-shopee {
  --logo: url("/logos/shopee.svg");
  --mark: var(--brand-shopee);
}
.mark.l-netflix {
  --logo: url("/logos/netflix.svg");
  --mark: var(--brand-netflix);
}
.mark.l-spotify {
  --logo: url("/logos/spotify.svg");
  --mark: var(--brand-spotify);
}
.tile.logo.on-spotify {
  background: var(--brand-spotify-tile);
}
.mark.l-paypal {
  --logo: url("/logos/paypal.svg");
  --mark: var(--brand-paypal);
}
.mark.l-wise {
  --logo: url("/logos/wise.svg");
  --mark: var(--brand-wise);
}
.tile.logo.on-wise {
  background: var(--brand-wise-tile);
}
.mark.l-youtube {
  --logo: url("/logos/youtube.svg");
  --mark: var(--brand-youtube);
}
.mark.l-youtubemusic {
  --logo: url("/logos/youtubemusic.svg");
  --mark: var(--brand-youtubemusic);
}
.mark.l-google {
  --logo: url("/logos/google.svg");
  --mark: var(--brand-google);
}
.mark.l-googleplay {
  --logo: url("/logos/googleplay.svg");
  --mark: var(--brand-googleplay);
}
.mark.l-googlecloud {
  --logo: url("/logos/googlecloud.svg");
  --mark: var(--brand-googlecloud);
}
.mark.l-apple {
  --logo: url("/logos/apple.svg");
  --mark: var(--brand-apple);
}
.mark.l-applemusic {
  --logo: url("/logos/applemusic.svg");
  --mark: var(--brand-applemusic);
}
.mark.l-icloud {
  --logo: url("/logos/icloud.svg");
  --mark: var(--brand-icloud);
}
.mark.l-starbucks {
  --logo: url("/logos/starbucks.svg");
  --mark: var(--brand-starbucks);
}
.mark.l-mcdonalds {
  --logo: url("/logos/mcdonalds.svg");
  --mark: var(--brand-mcdonalds);
}
.tile.logo.on-mcdonalds {
  background: var(--brand-mcdonalds-tile);
}
.mark.l-kfc {
  --logo: url("/logos/kfc.svg");
  --mark: var(--brand-kfc);
}
.mark.l-burgerking {
  --logo: url("/logos/burgerking.svg");
  --mark: var(--brand-burgerking);
}
.mark.l-whatsapp {
  --logo: url("/logos/whatsapp.svg");
  --mark: var(--brand-whatsapp);
}
.tile.logo.on-whatsapp {
  background: var(--brand-whatsapp-tile);
}
.mark.l-zoom {
  --logo: url("/logos/zoom.svg");
  --mark: var(--brand-zoom);
}
.mark.l-claude {
  --logo: url("/logos/claude.svg");
  --mark: var(--brand-claude);
}
.mark.l-blibli {
  --logo: url("/logos/blibli.svg");
  --mark: var(--brand-blibli);
}
.mark.l-bukalapak {
  --logo: url("/logos/bukalapak.svg");
  --mark: var(--brand-bukalapak);
}
.mark.l-ikea {
  --logo: url("/logos/ikea.svg");
  --mark: var(--brand-ikea);
}
.mark.l-uniqlo {
  --logo: url("/logos/uniqlo.svg");
  --mark: var(--brand-uniqlo);
}
.mark.l-zara {
  --logo: url("/logos/zara.svg");
  --mark: var(--brand-zara);
}
.mark.l-hm {
  --logo: url("/logos/hm.svg");
  --mark: var(--brand-hm);
}
.mark.l-airasia {
  --logo: url("/logos/airasia.svg");
  --mark: var(--brand-airasia);
}
.mark.l-lionair {
  --logo: url("/logos/lionair.svg");
  --mark: var(--brand-lionair);
}
.mark.l-bookingcom {
  --logo: url("/logos/bookingcom.svg");
  --mark: var(--brand-bookingcom);
}
.mark.l-shell {
  --logo: url("/logos/shell.svg");
  --mark: var(--brand-shell);
}
.tile.logo.on-shell {
  background: var(--brand-shell-tile);
}
.mark.l-steam {
  --logo: url("/logos/steam.svg");
  --mark: var(--brand-steam);
}
.mark.l-uber {
  --logo: url("/logos/uber.svg");
  --mark: var(--brand-uber);
}
.mark.l-airbnb {
  --logo: url("/logos/airbnb.svg");
  --mark: var(--brand-airbnb);
}
.mark.l-dropbox {
  --logo: url("/logos/dropbox.svg");
  --mark: var(--brand-dropbox);
}
.mark.l-hetzner {
  --logo: url("/logos/hetzner.svg");
  --mark: var(--brand-hetzner);
}
.mark.l-wasabi {
  --logo: url("/logos/wasabi.svg");
  --mark: var(--brand-wasabi);
}
.tile.logo.on-wasabi {
  background: var(--brand-wasabi-tile);
}
.mark.l-cloudflare {
  --logo: url("/logos/cloudflare.svg");
  --mark: var(--brand-cloudflare);
}
.tile.b-shopeepay {
  --tile: var(--brand-shopeepay);
}
.tile.b-telkomsel {
  --tile: var(--brand-telkomsel);
}
.tile.p-0 {
  --tile: var(--tile-0);
}
.tile.p-1 {
  --tile: var(--tile-1);
}
.tile.p-2 {
  --tile: var(--tile-2);
}
.tile.p-3 {
  --tile: var(--tile-3);
}
.tile.p-4 {
  --tile: var(--tile-4);
}
.tile.p-5 {
  --tile: var(--tile-5);
}
.tile.p-6 {
  --tile: var(--tile-6);
}

.tile.small {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  font-size: 9px;
}
.cat .names {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.cat .name {
  font-size: 15px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cat .note {
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
}
.pill {
  flex: none;
  padding: 6px 10px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 700;
  display: inline-block;
  background: var(--accent-soft);
  color: var(--accent);
}
.status-zero .pill {
  background: var(--surface-2);
  color: var(--muted);
}
.status-underfunded .pill,
.status-overspent_credit .pill {
  background: var(--warn-soft);
  color: var(--warn);
}
.status-overspent_cash .pill {
  background: var(--bad-soft);
  color: var(--bad);
}
.status-underfunded .note,
.status-overspent_credit .note {
  color: var(--warn);
}
.status-overspent_cash .note {
  color: var(--bad);
}
.bar {
  height: 6px;
  border-radius: 999px;
  background: var(--bar-track);
  overflow: hidden;
}
/* The fill is scaled, not resized (§9.3: transform only), in 5% steps
   from the server's figure; budget.js picks the class. */
.bar > span {
  display: block;
  height: 6px;
  border-radius: 999px;
  background: var(--accent);
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform var(--slow) var(--ease);
}
.bar > span.f-5 {
  transform: scaleX(0.05);
}
.bar > span.f-10 {
  transform: scaleX(0.1);
}
.bar > span.f-15 {
  transform: scaleX(0.15);
}
.bar > span.f-20 {
  transform: scaleX(0.2);
}
.bar > span.f-25 {
  transform: scaleX(0.25);
}
.bar > span.f-30 {
  transform: scaleX(0.3);
}
.bar > span.f-35 {
  transform: scaleX(0.35);
}
.bar > span.f-40 {
  transform: scaleX(0.4);
}
.bar > span.f-45 {
  transform: scaleX(0.45);
}
.bar > span.f-50 {
  transform: scaleX(0.5);
}
.bar > span.f-55 {
  transform: scaleX(0.55);
}
.bar > span.f-60 {
  transform: scaleX(0.6);
}
.bar > span.f-65 {
  transform: scaleX(0.65);
}
.bar > span.f-70 {
  transform: scaleX(0.7);
}
.bar > span.f-75 {
  transform: scaleX(0.75);
}
.bar > span.f-80 {
  transform: scaleX(0.8);
}
.bar > span.f-85 {
  transform: scaleX(0.85);
}
.bar > span.f-90 {
  transform: scaleX(0.9);
}
.bar > span.f-95 {
  transform: scaleX(0.95);
}
.bar > span.f-100 {
  transform: scaleX(1);
}

.status-underfunded .bar > span {
  background: var(--warn-bar);
}
.status-overspent_credit .bar > span {
  background: repeating-linear-gradient(-45deg, var(--warn-bar) 0 6px, color-mix(in srgb, var(--warn-bar) 60%, transparent) 6px 12px);
}
.status-overspent_cash .bar > span {
  background: var(--bad);
}
.cat .detail {
  padding: 0 14px 16px 62px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  animation: page-in var(--med) var(--ease);
}
.cat .figures {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  font-size: 12px;
  color: var(--muted);
}
.cat .figures div {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.cat .figures strong {
  font-size: 14px;
  color: var(--ink);
}
.cat .figures .money.neg {
  color: inherit;
}
.cat .assign {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.cat .assign input {
  flex: 1;
  min-width: 0;
  height: 40px;
  padding: 0 12px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  font-weight: 700;
}
.cat .assign input:focus {
  outline: none;
  border-color: var(--accent);
}
.cat .assign {
  flex-wrap: wrap;
}
.cat .assign .field-hint {
  order: 3;
  flex-basis: 100%;
}
.field input.invalid,
.field input.invalid:focus,
.cat .assign input.invalid,
.cat .assign input.invalid:focus {
  border-color: var(--bad);
}
.field-hint .error-text {
  font-size: 13px;
}
/* Coins waiting for a job (the hero's drawing while money is unassigned). */
.coins {
  position: relative;
  display: block;
  width: 64px;
  height: 64px;
}
.coins i {
  position: absolute;
  left: 14px;
  width: 36px;
  height: 12px;
  border-radius: 50%;
  background: currentColor;
}
.coins i:nth-child(1) {
  top: 42px;
  opacity: 0.18;
}
.coins i:nth-child(2) {
  top: 36px;
  opacity: 0.35;
}
.coins i:nth-child(3) {
  top: 29px;
  opacity: 0.55;
}
.coins i:nth-child(4) {
  top: 22px;
  opacity: 0.85;
}
.actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* ------------------------------------------------------------- accounts */

.acct-tabs {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 4px;
  scrollbar-width: none;
}
.acct-tabs button {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 10px 14px;
  border-radius: 16px;
  border: 1px solid var(--line);
  background: transparent;
  text-align: left;
  min-width: 120px;
}
.acct-tabs .btn {
  flex-direction: row;
  align-items: center;
  align-self: stretch;
  height: auto;
  color: var(--muted);
}
.acct-tabs button[aria-selected="true"] {
  background: var(--surface);
  border-color: var(--accent);
}
.acct-tabs .who {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 700;
  color: var(--muted);
}
.acct-tabs .bal {
  font-size: 15px;
  font-weight: 800;
}
.neg {
  color: var(--bad);
}
.pos {
  color: var(--accent);
}
.tx {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-top: 1px solid var(--line);
  width: 100%;
  border-left: none;
  border-right: none;
  border-bottom: none;
  background: transparent;
  text-align: left;
}
.tx:first-child {
  border-top: none;
}
.tx .names {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.tx .payee {
  font-size: 15px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tx .sub {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tx .amt {
  font-size: 15px;
  font-weight: 800;
  display: flex;
  align-items: center;
  gap: 6px;
}
/* Spending is the normal case: outflows in ink, inflows green. */
.tx .amt .money.neg {
  color: inherit;
}
button.tx,
.tx-main {
  cursor: pointer;
}
button.tx:hover,
.tx:has(> .tx-main:hover) {
  background: color-mix(in srgb, var(--surface-2) 60%, transparent);
}
/* A schedule's row is one button, padded like a transaction's. */
.tx.tx-main.sched {
  padding: 12px 14px;
}
/* Cleared or not (YNAB's "C"): a ring, a filled check, or a lock. */
.clear-toggle {
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: 50%;
  border: none;
  background: transparent;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.clear-toggle.cleared {
  background: var(--accent);
  color: var(--accent-ink);
}
.clear-toggle.reconciled {
  background: var(--surface-2);
  color: var(--muted);
}
/* Splitting: a line per category, and what is left to split. */
.split {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.split-line {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto;
  gap: 8px;
  align-items: start;
}
.split-line select,
.split-line input {
  height: 44px;
  min-width: 0;
  padding: 0 10px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--surface);
}
.split-line > select {
  grid-area: 1 / 1;
}
.split-line > input {
  grid-area: 1 / 2;
}
.split-line > button {
  grid-area: 1 / 3;
}
.split-line > .field-hint {
  grid-area: 2 / 1 / 3 / 4;
}
.split-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.split-status {
  font-size: 13px;
  font-weight: 700;
}
.split-status.off {
  color: var(--warn);
}
.split-status.ok {
  color: var(--accent);
}
.split-toggle {
  align-self: flex-start;
}
.notice.locked {
  align-items: center;
}
.notice.locked span {
  flex: 1;
}
.warn-text {
  color: var(--warn);
}
/* Receipts: thumbnails in a row, each with its place and a remove button. */
.receipts {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.receipt-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.receipt-row:empty {
  display: none;
}
.receipt {
  position: relative;
  flex: none;
  width: 96px;
  height: 96px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  overflow: hidden;
}
.receipt-photo,
.receipt-photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.receipt-file {
  height: 100%;
  padding: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-size: 12px;
  color: var(--muted);
  text-align: center;
  text-decoration: none;
  overflow-wrap: anywhere;
}
.receipt-file span {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.receipt-map,
.receipt-remove {
  position: absolute;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--scrim);
  color: var(--on-color);
}
.receipt-map {
  left: 4px;
  bottom: 4px;
}
.receipt-remove {
  right: 4px;
  top: 4px;
}
.receipt-remove.armed {
  background: var(--bad);
  color: var(--bad-ink);
}
/* A finger-sized target around the small round buttons. */
.receipt-map::after,
.receipt-remove::after {
  content: "";
  position: absolute;
  inset: -8px;
}
.receipt-map:focus-visible,
.receipt-remove:focus-visible,
.receipt-photo:focus-visible,
a.receipt-file:focus-visible {
  outline-offset: -2px;
}
.receipt.waiting {
  border-style: dashed;
}
.receipt.unsent {
  border-color: var(--warn);
}
button.receipt-file {
  width: 100%;
  border: none;
  background: none;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.receipt.unsent .receipt-file svg {
  color: var(--warn);
}
/* In place of Repeat while receipts wait: why it is not offered. */
.picker-note {
  align-self: center;
  margin: 0;
  padding: 0 4px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--muted);
}
label.btn {
  cursor: pointer;
}
/* The file input inside is hidden: its focus shows on the button. */
label.btn:has(:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
label.btn.busy {
  opacity: 0.6;
  pointer-events: none;
}
.has-receipt {
  color: var(--muted);
  display: inline-flex;
}
.notice.warn {
  background: var(--warn-soft);
  color: var(--warn);
}
.acct-head .names {
  flex: 1 1 160px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.acct-head .title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
}
.tx.fresh {
  animation: page-in var(--med) var(--ease);
}
.tx-main {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0;
  border: none;
  background: none;
  text-align: left;
}
.notice {
  margin: 0;
  display: flex;
  gap: 8px;
  align-items: flex-start;
  padding: 10px 12px;
  border-radius: 12px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: 13px;
  line-height: 1.45;
}
.review {
  background: var(--warn-soft);
  border-color: transparent;
}
.review .tx {
  border-color: color-mix(in srgb, var(--warn) 20%, transparent);
}
/* An account's kind (ui.js KindBadge): its own money green, borrowed
   money warm, a tracking account outlined. */
.kind-badge {
  flex: none;
  align-self: flex-start;
  padding: 0 6px;
  border-radius: 6px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
  white-space: nowrap;
}
.kind-badge.debt {
  background: var(--warn-soft);
  color: var(--warn);
}
.kind-badge.tracking {
  background: transparent;
  color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--line);
}
.acct-head .kind-badge,
.acct-tabs .kind-badge {
  align-self: center;
}
.tag {
  font-size: 11px;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 6px;
  background: var(--warn-soft);
  color: var(--warn);
}

/* Without a connection (PLAN §8): the bar at the top of every screen, the
   changes waiting in the register and on categories, the refused ones. */
.waiting-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  padding: 8px 12px;
  border-radius: 12px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: 13px;
  line-height: 1.4;
}
.waiting-bar.offline {
  background: var(--warn-soft);
  color: var(--warn);
}
.waiting-bar.refused {
  background: var(--bad-soft);
  color: var(--bad);
}
.waiting-bar .grow {
  flex: 1;
  min-width: 0;
}
.waiting-bar .btn {
  flex: none;
}
.tag.waiting {
  display: inline-flex;
  align-items: center;
  background: var(--surface-2);
  color: var(--muted);
}
.waiting-tx {
  padding: 10px 14px;
  opacity: 0.75;
}
.waiting-note {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--warn);
}
.refused-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-top: 1px solid var(--line);
}
.refused-row:first-child {
  border-top: none;
}
.refused-row .names {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.refused-row .hint {
  font-size: 12px;
  color: var(--bad);
}

/* -------------------------------------------------------- sheet (quick add) */

.scrim {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: var(--scrim);
  animation: fade var(--med) ease;
}
@keyframes fade {
  from {
    opacity: 0;
  }
}
.sheet {
  position: fixed;
  z-index: 31;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 96dvh;
  overflow-y: auto;
  background: var(--bg);
  border-radius: 24px 24px 0 0;
  padding: 8px 16px calc(16px + env(safe-area-inset-bottom)) 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  animation: sheet-up var(--sheet) var(--ease);
}
@keyframes sheet-up {
  from {
    transform: translateY(100%);
  }
}
.sheet .grab {
  width: 40px;
  height: 5px;
  border-radius: 999px;
  background: var(--line);
  margin: 4px auto 4px auto;
}
@media (min-width: 640px) {
  .sheet {
    left: 50%;
    right: auto;
    bottom: auto;
    top: 50%;
    width: 440px;
    transform: translate(-50%, -50%);
    border-radius: 24px;
    animation: dialog-in var(--med) var(--ease);
  }
  .sheet .grab {
    display: none;
  }
}
@keyframes dialog-in {
  from {
    opacity: 0;
    transform: translate(-50%, -46%);
  }
}
.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.amount-display {
  text-align: center;
  font-size: 44px;
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.15;
  padding: 4px 0;
  overflow-wrap: anywhere;
}
.amount-display.in {
  color: var(--accent);
}
.amount-display.long {
  font-size: 36px;
}
.amount-display.longer {
  font-size: 30px;
}

/* Quick add (PLAN §9.5): the amount, the details, then the keypad and
   Save. In one column the keypad stays at the bottom of the sheet while
   the details scroll above it (the details fade under it); side by side
   on a wide screen, the amount and keypad on the left. */
.add-amount,
.add-fields,
.add-pad,
.add-more {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}
.add-more:empty {
  display: none;
}
.add-pad {
  gap: 8px;
}
@media (max-width: 719px) and (min-height: 560px) {
  .add-pad {
    position: sticky;
    bottom: calc(-16px - env(safe-area-inset-bottom));
    z-index: 1;
    margin-bottom: calc(-16px - env(safe-area-inset-bottom));
    padding: 4px 0 calc(16px + env(safe-area-inset-bottom));
    background: var(--bg);
  }
  .add-pad::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    height: 16px;
    background: linear-gradient(to bottom, transparent, var(--bg));
    pointer-events: none;
  }
  .add-pad:not(:last-child) {
    margin-bottom: 0;
  }
}
@media (min-width: 720px) {
  .sheet:has(.add-pad) {
    width: min(840px, calc(100vw - 48px));
    padding: 16px 24px 24px;
  }
  .add:has(> .add-pad) {
    display: grid;
    grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
    grid-template-areas:
      "head head"
      "amount fields"
      "pad fields"
      "more fields";
    /* Taller details grow the last row, not the gaps between the left
       column's parts. */
    grid-template-rows: auto min-content min-content auto;
    column-gap: 32px;
    row-gap: 16px;
    align-items: start;
  }
  .add > .sheet-head {
    grid-area: head;
  }
  .add > .add-amount {
    grid-area: amount;
    justify-content: center;
  }
  .add > .add-fields {
    grid-area: fields;
  }
  .add > .add-pad {
    grid-area: pad;
  }
  .add > .add-more {
    grid-area: more;
  }
}
@media (min-width: 720px) and (max-width: 959px) {
  .add .amount-display.longer {
    font-size: 26px;
  }
}
@media (min-width: 960px) {
  .add:has(> .add-pad) {
    grid-template-columns: minmax(0, 340px) minmax(0, 1fr);
  }
}
/* Spending in another currency (PLAN §7.1): the switch above the amount,
   what it comes to below it. */
.currency-switch {
  align-self: center;
}
.fx-line {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
}
.fx-hint,
.fx-estimate {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--muted);
}
.fx-estimate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.fx-how {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0 8px;
}
.fx-estimate strong {
  font-size: 18px;
  color: var(--ink);
}
.fx-rate {
  width: 100%;
  max-width: 280px;
  text-align: left;
}
.fx-rate .fx-hint {
  font-size: 12px;
}
button.link {
  min-height: 32px;
  padding: 0 4px;
  border: none;
  background: none;
  color: var(--accent);
  font: inherit;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.fx-notice {
  align-items: center;
}
.fx-estimates {
  flex-direction: column;
  align-items: stretch;
}
.fx-estimates p {
  margin: 0;
}
.fx-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.fx-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  text-align: left;
}
.fx-row .names {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.fx-row .hint {
  font-size: 12px;
  color: var(--muted);
}
.fx-notice > span {
  flex: 1;
}
.rate-field {
  flex: none;
  width: 132px;
}
.rate-field input {
  width: 100%;
  height: 44px;
  padding: 0 12px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--surface);
  font-size: 16px;
  font-weight: 700;
  text-align: right;
}
.rate-field input:focus {
  outline: none;
  border-color: var(--accent);
}
.rate-field .field-hint:empty {
  display: none;
}
.keypad {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
.keypad button {
  height: 56px;
  border-radius: 16px;
  border: none;
  background: var(--surface);
  font-size: 22px;
  font-weight: 700;
  box-shadow: 0 1px 0 var(--line);
  transition: transform var(--press) ease;
}
.keypad button:active {
  transform: scale(0.95);
}
.pickrow {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.pickrow button {
  height: 40px;
  padding: 0 12px 0 6px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--surface);
  font-size: 14px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.pickrow button[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}
/* Two to a row; one left alone takes the whole row. */
.pickers {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.pickers > * {
  flex: 1 1 calc(50% - 4px);
  min-width: 0;
}
.pickers > .wide {
  flex-basis: 100%;
}
.picker {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 12px 0;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: var(--surface);
}
.picker label {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
}
/* The field reaches the box's bottom edge: the whole box answers a tap
   (the label above focuses it too). */
.picker select,
.picker input {
  border: none;
  background: transparent;
  font-size: 14px;
  font-weight: 700;
  padding: 2px 0 8px;
  width: 100%;
}
/* A phone zooms into a field under 16px. */
@media (pointer: coarse) {
  .picker select,
  .picker input {
    font-size: 16px;
  }
}
.picker select:focus,
.picker input:focus {
  outline: none;
}
.picker:focus-within {
  border-color: var(--accent);
}

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

.toasts {
  position: fixed;
  left: 16px;
  right: 16px;
  bottom: calc(96px + env(safe-area-inset-bottom));
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  width: 100%;
  max-width: 440px;
  padding: 12px 12px 12px 16px;
  border-radius: 16px;
  background: var(--ink);
  color: var(--bg);
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 14px;
  font-weight: 600;
  animation: toast-in var(--med) var(--ease);
}
.toast.error {
  background: var(--bad);
  color: var(--bad-ink);
}
.toast button {
  height: 36px;
  padding: 0 12px;
  border: none;
  border-radius: 10px;
  background: transparent;
  color: inherit;
  font-weight: 800;
  text-decoration: underline;
}
@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
}

/* ------------------------------------------------------------- settings */

.setting {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-top: 1px solid var(--line);
}
.setting:first-child {
  border-top: none;
}
.setting > .btn {
  flex: none;
}
.setting .names {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.setting .label {
  font-size: 15px;
  font-weight: 600;
}
.setting .hint {
  font-size: 12px;
  color: var(--muted);
}

/* -------------------------------------------------------------- sign in */

.signin {
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
}
.signin .card {
  width: 100%;
  max-width: 400px;
  padding: 28px 24px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  animation: page-in var(--slow) var(--ease);
}
.signin h1 {
  margin: 0;
  font-size: 26px;
  font-weight: 800;
  letter-spacing: -0.02em;
}
.signin .lead {
  margin: 0;
  color: var(--muted);
  line-height: 1.5;
}
.signin .or {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 12px;
  color: var(--muted);
}
.signin .or::before,
.signin .or::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--line);
}
.error-text {
  color: var(--bad);
  font-size: 14px;
  margin: 0;
}

/* ---------------------------------------------------------------- empty */

.empty {
  text-align: center;
  padding: 32px 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  color: var(--muted);
}
.empty strong {
  color: var(--ink);
  font-size: 17px;
}

.skeleton {
  border-radius: var(--r-lg);
  background: var(--surface-2);
  animation: pulse var(--pulse) ease-in-out infinite;
  height: 120px;
}
@keyframes pulse {
  50% {
    opacity: 0.55;
  }
}

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