/* =========================================================
   MyMon v1 — stylesheet
   1  Tokens
   2  Base & layout
   3  Header & footer
   4  Buttons, forms, chips
   5  Landing page
   6  Dashboard
   7  Charts
   8  Dialog, toast, empty states
   9  Responsive
   ========================================================= */

/* ---------- 1. Tokens ---------- */
:root {
  /* Tells the browser which way round the page is, so its own furniture —
     the date picker, the dropdowns, the scrollbars — comes out to match
     instead of staying stubbornly white in front of a dark page. */
  color-scheme: light;

  /* brand — taken from the MyMon logo.
     Two greens with two jobs, and the jobs are the point: --brand-700 is the
     one you read words in, --brand-600 is the one you put white words on.
     Mixing them up is how the footer line and the "spent less" figure ended
     up at 4.06:1, and how the dark theme put white on pale green.
     --brand-600 also moved seven steps darker (from #3d8a45) so white on it
     clears 4.5:1 — 4.86 now, against 4.27 before. */
  --brand-700: #2f6b35;
  --brand-600: #38803e;
  --brand-hover: #2d6833;   /* primary button, pressed or hovered */

  /* The band at the foot of the landing page is a deep green slab carrying
     white text, in both themes. It used to be mixed from --brand-600 and
     --brand-700, which works only while --brand-700 is the dark one — in the
     dark theme that token is pale, so the slab faded to pale green under its
     own white text, and the white button inside it was lettered in pale green
     on white. Its own tokens, so neither can happen again. */
  --band-from: #38803e;
  --band-to: #2f6b35;
  --band-ink: #2f6b35;   /* the lettering on the white button inside it */
  --brand-500: #5faa55;
  --brand-300: #a8d6a0;
  --brand-100: #e7f3e5;
  --brand-050: #f3f9f2;

  /* ink
     The three quiet steps were measured against the surfaces they actually
     sit on rather than chosen by eye. At their old values --ink-500 came to
     4.02:1 on the sunken surface and --ink-400 to 2.73:1, both below the
     floor for text somebody is meant to read, and --ink-300 was 1.94:1 while
     colouring a row's edit and delete icons. Secondary text that is too pale
     does not read as calm, it reads as unfinished. On white / page / sunken:
       --ink-700  8.98 / 8.54 / 8.32   headings and strong body
       --ink-500  5.65 / 5.38 / 5.24   secondary text
       --ink-400  4.92 / 4.68 / 4.56   the quietest text still meant to be read
       --ink-300  3.48 / 3.31 / 3.23   icons and separators, never prose */
  --ink-900: #10201a;
  --ink-700: #3c4d45;
  --ink-500: #5a6b62;
  --ink-400: #667469;
  --ink-300: #7e8d84;

  /* surfaces */
  --surface: #ffffff;
  --surface-sunken: #f4f7f4;
  --surface-page: #f7faf7;
  --line: #e4ebe5;
  --line-strong: #d3ded5;

  /* The landing page's first screen. Its own tokens, because the top of that
     gradient is not any surface the rest of the app uses, and each theme wants
     a different amount of light up there. A light value left to stand in the
     dark theme gave a bright band with a visible edge where it stopped; the
     cure is not to take the light away but to spread it. --hero-fade says how
     far down the gradient runs: short in the light theme, where the two ends
     are near neighbours, and all the way down in the dark one, where the eye
     would otherwise find the place it ends. */
  --hero-top: #eef7ec;
  --hero-fade: 72%;
  --hero-glow-warm: rgba(95, 170, 85, .24);
  --hero-glow-cool: rgba(126, 196, 232, .22);

  /* Text that sits on a filled --brand-700 shape. White on the light theme's
     deep green; nearly black on the dark theme's pale green, which white
     would be unreadable against. */
  --on-brand-700: #ffffff;

  /* The header sits over whatever is scrolling under it, so it is the page's
     own colour with some of that showing through. */
  --surface-glass: rgba(255, 255, 255, .82);

  /* Things that are meant to stand against the page rather than sit on it:
     the toast, the chart tooltip. They are dark on a light page and light on
     a dark one, which is why they cannot just borrow --ink-900. */
  --inverse: #10201a;
  --on-inverse: #ffffff;

  /* What dims the page behind a dialog. */
  --scrim: rgba(16, 32, 20, .42);

  /* "Who put in what": your own bar against everybody else's. These cannot be
     one pair of greens reused in both themes — on a white page the darker
     green is the one that stands out, and on a dark page it is the one that
     disappears. So the emphasis flips, and only the token knows that. */
  --bar-mine: var(--brand-600);
  --bar-other: var(--brand-300);

  /* category series — validated categorical palette, fixed order */
  --cat-food: #2a78d6;
  --cat-bills: #eb6834;
  --cat-transport: #1baf7a;
  --cat-entertainment: #eda100;
  --cat-hobby: #e87ba4;
  --cat-other: #4a3aa7;
  --cat-shopping: #e34948;

  --danger: #c23b3a;
  --danger-050: #fdecec;   /* the tint behind a delete button being hovered */
  --danger-200: #f3cfcf;

  /* shape */
  --r-xs: 8px;
  --r-sm: 12px;
  --r-md: 18px;
  --r-lg: 26px;
  --r-pill: 999px;

  /* elevation */
  --shadow-xs: 0 1px 2px rgba(16, 32, 20, .06);
  --shadow-sm: 0 1px 2px rgba(16, 32, 20, .05), 0 2px 8px rgba(16, 32, 20, .05);
  --shadow-md: 0 2px 6px rgba(16, 32, 20, .05), 0 14px 34px rgba(16, 32, 20, .07);
  --shadow-lg: 0 8px 20px rgba(16, 32, 20, .08), 0 34px 70px rgba(16, 32, 20, .13);

  --ring: 0 0 0 3px rgba(95, 170, 85, .32);

  /* rhythm */
  --gutter: 24px;
  --page: 1140px;
}

/* ---------- 1b. The dark theme ----------

   Not an inversion of the block above. A dark page needs its own greens (the
   one you read text in has to be light, the one you put white text on has to
   stay dark), its own shadows, and — the part that is easy to get wrong — its
   own category colours.

   Those seven were picked for a white page and checked there. Against #161d19
   four of them fall outside the lightness band a dark surface needs, and the
   indigo lands at 2:1 against the background, which is barely a mark at all.
   So a second set was chosen and run through the same validator with the dark
   surface. It comes out at: lightness band pass, chroma pass, colour-blind
   separation 10.7 on the worst adjacent pair (the target is 8), normal-vision
   separation 18.7 (the floor is 15), and every colour over 3:1 against the
   background. That last one the light set cannot manage — it leans on the
   written labels beside each mark instead.

   The rules are written out twice on purpose. The first follows the system
   setting; the second is the button in the header overruling it. Neither can
   be expressed in terms of the other in plain CSS, so they are kept next to
   each other where a change to one is obvious if it misses the other. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --brand-700: #9ad98f;   /* green you read words in */
    --brand-600: #38803e;   /* green you put white words on — white is 4.86:1 */
    /* Hover darkens here too. Going lighter is the usual move on a dark page,
       but every green pale enough to look lifted drops white under 4:1 — the
       old hover handed the button --brand-700 and produced white on pale
       green at 1.65:1, which is not a hover state, it is a disappearance. */
    --brand-hover: #2f6b35;

    /* Deeper than the light theme's, so a slab of it does not glare on a
       dark page. Still deep enough to carry white. */
    --band-from: #2f6b35;
    --band-to: #1f4a26;
    --band-ink: #2f6b35;
    --brand-500: #6fbb64;
    --brand-300: #7fc077;
    --brand-100: #1f2c23;   /* a tint, so it goes dark rather than pale */
    --brand-050: #18211b;

    --ink-900: #e9efea;
    --ink-700: #bdc9c2;
    --ink-500: #90a097;
    --ink-400: #7a8a81;
    --ink-300: #5f6e66;

    --surface: #161d19;
    --surface-sunken: #1b2420;
    --surface-page: #101613;
    --line: #273029;
    --line-strong: #35413a;
    --surface-glass: rgba(16, 22, 19, .82);

    /* Light at the top here too — it is the first thing anybody sees and it
       is worth having. It was the *edge* that was wrong, not the glow, so
       the ramp now runs the full height instead of stopping two thirds
       down, and the glows stay soft enough not to grey the page out. */
    --hero-top: #22302a;
    --hero-fade: 100%;
    --hero-glow-warm: rgba(120, 200, 110, .20);
    --hero-glow-cool: rgba(110, 170, 215, .15);

    --on-brand-700: #0e1611;

    --inverse: #28322c;
    --on-inverse: #e9efea;
    --scrim: rgba(0, 0, 0, .62);

    --cat-food: #4f93e0;
    --cat-bills: #df6634;
    --cat-transport: #11ac88;
    --cat-entertainment: #c08a12;
    --cat-hobby: #d4648f;
    --cat-other: #7a68d8;
    --cat-shopping: #e34948;

    --danger: #f08b87;
    --danger-050: #2b1c1c;
    --danger-200: #4a2d2d;

    /* A shadow on a dark page is not a paler grey, it is more black. */
    --shadow-xs: 0 1px 2px rgba(0, 0, 0, .40);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .36), 0 2px 8px rgba(0, 0, 0, .30);
    --shadow-md: 0 2px 6px rgba(0, 0, 0, .36), 0 14px 34px rgba(0, 0, 0, .42);
    --shadow-lg: 0 8px 20px rgba(0, 0, 0, .46), 0 34px 70px rgba(0, 0, 0, .50);

    --bar-mine: #9ad98f;
    --bar-other: #4f7a58;

    --ring: 0 0 0 3px rgba(143, 208, 134, .30);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --brand-700: #9ad98f;
  --brand-600: #38803e;
  --brand-hover: #2f6b35;
  --band-from: #2f6b35;
  --band-to: #1f4a26;
  --band-ink: #2f6b35;
  --brand-500: #6fbb64;
  --brand-300: #7fc077;
  --brand-100: #1f2c23;
  --brand-050: #18211b;

  --ink-900: #e9efea;
  --ink-700: #bdc9c2;
  --ink-500: #90a097;
  --ink-400: #7a8a81;
  --ink-300: #5f6e66;

  --surface: #161d19;
  --surface-sunken: #1b2420;
  --surface-page: #101613;
  --line: #273029;
  --line-strong: #35413a;
  --surface-glass: rgba(16, 22, 19, .82);

  /* Light at the top here too — it is the first thing anybody sees and it
     is worth having. It was the *edge* that was wrong, not the glow, so
     the ramp now runs the full height instead of stopping two thirds
     down, and the glows stay soft enough not to grey the page out. */
  --hero-top: #22302a;
  --hero-fade: 100%;
  --hero-glow-warm: rgba(120, 200, 110, .20);
  --hero-glow-cool: rgba(110, 170, 215, .15);

  --on-brand-700: #0e1611;

  --inverse: #28322c;
  --on-inverse: #e9efea;
  --scrim: rgba(0, 0, 0, .62);

  --cat-food: #4f93e0;
  --cat-bills: #df6634;
  --cat-transport: #11ac88;
  --cat-entertainment: #c08a12;
  --cat-hobby: #d4648f;
  --cat-other: #7a68d8;
  --cat-shopping: #e34948;

  --danger: #f08b87;
  --danger-050: #2b1c1c;
  --danger-200: #4a2d2d;

  --shadow-xs: 0 1px 2px rgba(0, 0, 0, .40);
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .36), 0 2px 8px rgba(0, 0, 0, .30);
  --shadow-md: 0 2px 6px rgba(0, 0, 0, .36), 0 14px 34px rgba(0, 0, 0, .42);
  --shadow-lg: 0 8px 20px rgba(0, 0, 0, .46), 0 34px 70px rgba(0, 0, 0, .50);

  --bar-mine: #9ad98f;
  --bar-other: #4f7a58;

  --ring: 0 0 0 3px rgba(143, 208, 134, .30);
}

/* ---------- 2. Base & layout ---------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  font-size: 16px;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: Nunito, "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, sans-serif;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink-700);
  background-color: var(--surface-page);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  color: var(--ink-900);
  line-height: 1.2;
  letter-spacing: -0.015em;
  margin: 0 0 .5rem;
  font-weight: 800;
}

p { margin: 0 0 1rem; }
p:last-child { margin-bottom: 0; }

/* Every page's own title, set at the same weight as the landing page's — the
   inner pages were wearing the browser's default heading size next to a hero
   built at four rem, and looked like a different site. */
main h1 {
  font-size: clamp(2.1rem, 4.5vw, 2.9rem);
  font-weight: 900;
  letter-spacing: -0.035em;
  margin-bottom: .75rem;
}

main h1 + .hero__lede,
main h1 + p { color: var(--ink-500); max-width: 56ch; }

a { color: var(--brand-700); }

img { max-width: 100%; height: auto; }

:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--r-xs);
}

.container {
  width: 100%;
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.container--narrow { max-width: 760px; }

.center { text-align: center; }

.muted { color: var(--ink-500); }

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

.hidden { display: none !important; }

/* The landing page has a signed-in face and a signed-out one. Knowing which to
   show needs a round trip, so the page guesses first (see the script in
   index.html's head) and this rule paints that guess. It outranks .hidden on
   purpose, and lives only for the instant before landing.js removes the
   attribute and hands the decision back to .hidden. */
html[data-session="in"] [data-when="signed-out"],
html[data-session="out"] [data-when="signed-in"] { display: none !important; }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.75rem;
}

.card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
}

.card__title {
  font-size: 1.15rem;
  font-weight: 800;
  letter-spacing: -0.015em;
  margin: 0;
}

.card__hint {
  font-size: .85rem;
  color: var(--ink-400);
  white-space: nowrap;
}

/* An "i" that holds the sentence somebody needs once and never again.
   Settings used to explain every switch in three lines. All of it was true and
   worth saying — but a page of nine choices then read like a manual, and the
   explanation is wanted on the first visit, not the fiftieth. So each one keeps
   a short line that says what the switch is, and the reasoning moves in here.

   A press, not a hover: a hover tooltip does not exist on a phone, and a phone
   is where this page is mostly read. The words stay in the document either way
   — findable, translatable, there for a screen reader — and `hidden` is the
   only thing that comes off. */
.info {
  flex: none;
  margin-right: auto;            /* beside its title, not out at the card's edge */
  width: 1.5rem; height: 1.5rem; /* a 24px target drawn as an 18px circle */
  padding: 0;
  border: 0;
  background: none;
  display: inline-grid;
  place-items: center;
  vertical-align: -.25em;        /* when it follows a name inside a paragraph */
  font: inherit;
  color: var(--ink-500);
  cursor: pointer;
}

.info::before {
  content: 'i';
  width: 1.15rem; height: 1.15rem;
  display: grid;
  place-items: center;
  /* The ring takes the letter's colour rather than a line token: at
     --line-strong it measured 1.38:1 against the card, which is a boundary
     nobody can see, and the hover and open states then came for free. */
  border: 1px solid currentColor;
  border-radius: 50%;
  font-size: .72rem;
  font-weight: 800;
  line-height: 1;
}

.info:hover { color: var(--ink-900); }

/* Open is a filled dot rather than a green letter. The green is a colour you
   put white words on — as a letter on the dark theme's card it measured
   3.53:1, which is worse than the plain grey it replaces, so the open state
   was harder to read than the shut one. Filled, the white letter sits at
   4.86:1 on both themes, and it matches how a chosen chip is drawn. */
.info[aria-expanded="true"]::before {
  background: var(--brand-600);
  border-color: var(--brand-600);
  color: #fff;
}
.info:focus-visible { outline: none; border-radius: 50%; box-shadow: var(--ring); }

.info-note {
  margin: .55rem 0 0;
  padding: .6rem .75rem;
  background: var(--surface-sunken);
  border-radius: var(--r-sm);
  font-size: .88rem;
  line-height: 1.55;
  color: var(--ink-500);
}
.info-note[hidden] { display: none; }

/* A small row of mutually exclusive buttons — used above the chart to say
   which currency it is about, in a month that holds several. */
.seg-row {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
  margin: 0 0 1.25rem;
}

.seg {
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink-500);
  font: inherit;
  font-size: .8rem;
  font-weight: 800;
  letter-spacing: .02em;
  padding: .3rem .7rem;
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.seg:hover { background: var(--surface-sunken); color: var(--ink-700); }

/* aria-pressed is what a screen reader reads, so the styling hangs off the
   same attribute rather than a class that could drift away from it. */
.seg[aria-pressed="true"] {
  background: var(--brand-600);
  border-color: var(--brand-600);
  color: #fff;
}

/* ---------- 3. Header & footer ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--surface-glass);
  backdrop-filter: saturate(160%) blur(12px);
  -webkit-backdrop-filter: saturate(160%) blur(12px);
  border-bottom: 1px solid var(--line);
}

/* There are three things in the header now, and "space-between" would have
   stranded the nav in the middle of the bar on its own. The brand holds the
   left; everything else is pushed right as one group (see .site-nav). */
.site-header .container {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: 68px;
}

/* The three round buttons at the end of the header: the bell, settings and
   the theme. Grouped, and set apart from the nav, so the bar reads as "where
   you can go" and then "things you can do" rather than as four equal items. */
.header-tools {
  position: relative;      /* the notification panel hangs off this, not off the bell */
  display: flex;
  align-items: center;
  gap: .1rem;
  flex: none;
}

/* Settings lost its word and became a cog. The word plus a bell did not fit
   the header on a 375px phone, and between the two the cog is the one that
   says the same thing without being read. Both the <a> and the <button> wear
   this, so they have to be told not to look like a link. */
.hdr-btn {
  width: 36px; height: 36px;
  flex: none;
  display: grid;
  place-items: center;
  border: 0;
  background: none;
  border-radius: 50%;
  color: var(--ink-500);
  text-decoration: none;
  cursor: pointer;
  transition: background-color .15s ease, color .15s ease;
}
.hdr-btn:hover { background: var(--surface-sunken); color: var(--ink-900); }
.hdr-btn:focus-visible { outline: none; box-shadow: var(--ring); }
.hdr-btn svg { width: 19px; height: 19px; }

/* The cog marks the page you are on the same way the words beside it do. */
.hdr-btn[aria-current="page"] { background: var(--brand-100); color: var(--brand-700); }

.theme-btn { position: relative; }

/* It shows where it takes you: a moon while the page is light, a sun while it
   is dark. The moon is the default, so the button is never an empty circle in
   the instant before the script has read which way round things are. */
.theme-btn__sun { display: none; }
.theme-btn[data-theme-state="dark"] .theme-btn__moon { display: none; }
.theme-btn[data-theme-state="dark"] .theme-btn__sun { display: block; }

/* ---------- 5c. The bell ----------
   The panel hangs off the button, not off the page. On a phone it still drops
   from the corner you pressed — it only grows to the width of the screen —
   because a sheet rising from the bottom would have no visible relationship to
   the thing that opened it. */
.bell { position: relative; display: flex; }

/* The same shape the Groups tab already uses for its count, on purpose: one
   language for "there are this many waiting for you". */
.bell__count {
  position: absolute;
  top: 1px;
  right: 0;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  display: grid;
  place-items: center;
  border-radius: var(--r-pill);
  background: var(--brand-600);
  color: #fff;
  font-size: .68rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  /* A ring in the header's own colour, so the badge reads as sitting on top
     of the bell rather than welded to it. */
  box-shadow: 0 0 0 2px var(--surface-glass);
}

.bell__panel {
  position: absolute;
  top: calc(100% + .55rem);
  right: 0;
  z-index: 60;
  width: min(23rem, calc(100vw - 2 * var(--gutter)));
  max-height: min(26rem, 70vh);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
}

.bell__head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .8rem 1rem .7rem;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.bell__title { margin: 0; font-size: .95rem; font-weight: 800; color: var(--ink-900); }

.bell__list { list-style: none; margin: 0; padding: .35rem; display: grid; gap: .1rem; }

/* One row is one button: the whole thing is the target, because a notification
   has exactly one thing it wants you to do — go and look at it. */
.note-row {
  width: 100%;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: .7rem;
  padding: .7rem .75rem;
  border: 0;
  background: none;
  border-radius: var(--r-sm);
  text-align: left;
  font: inherit;
  cursor: pointer;
  transition: background-color .15s ease;
}
.note-row:hover { background: var(--surface-sunken); }
.note-row:focus-visible { outline: none; box-shadow: var(--ring); }

.note-row__text { min-width: 0; font-size: .9rem; line-height: 1.45; color: var(--ink-700); }
.note-row__text b { color: var(--ink-900); font-weight: 800; }
.note-row__when { display: block; margin-top: .15rem; font-size: .78rem; color: var(--ink-400); }

/* Unread is a dot rather than a background wash: the row is already a hover
   target, and two kinds of highlight on one row fight each other.
   It lives in a column of its own. Hung off the bold words instead, it
   appeared once per bold word — and a sentence has two, the person and the
   group, so every unread row grew two dots. */
.note-row__dot {
  width: 7px; height: 7px;
  margin-top: .42rem;
  border-radius: 50%;
  background: var(--brand-600);
}

.bell__empty { padding: 2rem 1rem; text-align: center; color: var(--ink-500); font-size: .9rem; }

.brand { display: inline-flex; align-items: center; }
/* The wordmark is type, not a picture. It is set in the same Nunito as the
   rest of the site, so it is sharp at any size and on any screen, it costs no
   bytes, and it cannot be squeezed out of shape — the PNG it replaces was
   200KB to be shown at 38px, and on a crowded phone header the flex row
   squashed it to nothing. flex: none is what keeps that from happening. */
.logo {
  display: inline-flex;
  align-items: center;
  flex: none;
  height: 36px;
  padding: 0 .95rem;
  border-radius: var(--r-pill);
  background: var(--brand-600);
  color: #fff;
  font-weight: 900;
  font-size: 1.05rem;
  letter-spacing: -.005em;
  white-space: nowrap;
  transition: background-color .15s ease;
}

.brand { text-decoration: none; }
.brand:hover .logo { background: var(--brand-700); }

/* The footer says the name once more without competing with it. */
.logo--quiet {
  height: 30px;
  padding: 0 .75rem;
  font-size: .9rem;
  background: none;
  color: var(--brand-700);
  box-shadow: inset 0 0 0 2px var(--brand-300);
}

.site-nav { display: flex; align-items: center; gap: .35rem; margin-left: auto; }

.site-nav a {
  display: inline-block;
  padding: .45rem .8rem;
  border-radius: var(--r-pill);
  color: var(--ink-700);
  font-weight: 700;
  font-size: .95rem;
  text-decoration: none;
  transition: background-color .15s ease, color .15s ease;
}

.site-nav a:hover { background: var(--brand-100); color: var(--brand-700); }
.site-nav a[aria-current="page"] { background: var(--brand-100); color: var(--brand-700); }

.site-footer {
  margin-top: 4rem;
  padding: 2.5rem 0 3rem;
  border-top: 1px solid var(--line);
  font-size: .88rem;
  color: var(--ink-400);
}

.site-footer .container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .75rem 2rem;
}

.site-footer a { color: var(--ink-500); text-decoration: none; }
.site-footer a:hover { color: var(--brand-700); }

/* The line the hero opens with, said once more on the way out — quietly. */
.site-footer__brand {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-right: auto;
}


.site-footer__brand p { margin: 0; color: var(--ink-400); }
.site-footer__brand b { color: var(--brand-700); font-weight: 900; letter-spacing: .06em; }

.site-footer__meta { margin: 0; font-variant-numeric: tabular-nums; }

/* Home and About live here now rather than in the header. */
.site-footer__links { display: flex; align-items: center; gap: 1.1rem; }
.site-footer__links a { font-weight: 700; }
.site-footer__links a[aria-current="page"] { color: var(--brand-700); }

/* ---------- 4. Buttons, forms, chips ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: .7rem 1.35rem;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font: inherit;
  font-weight: 800;
  font-size: .95rem;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: transform .12s ease, box-shadow .15s ease, background-color .15s ease, border-color .15s ease;
}

.btn:active { transform: translateY(1px); }
.btn svg { width: 17px; height: 17px; }

.btn--primary {
  background: var(--brand-600);
  color: #fff;
  /* A hint of lift, not a glow. The wide green halo was fine on a page with
     one button on it and heavy on Settings, which stacks three. */
  box-shadow: 0 1px 2px rgba(16, 32, 20, .14), 0 5px 12px rgba(61, 138, 69, .15);
}
.btn--primary:hover { background: var(--brand-hover); }

.btn--ghost {
  background: var(--surface);
  color: var(--ink-700);
  border-color: var(--line-strong);
}
.btn--ghost:hover { background: var(--surface-sunken); color: var(--ink-900); }

.btn--quiet {
  background: transparent;
  color: var(--ink-500);
  padding: .45rem .8rem;
  font-weight: 700;
  font-size: .9rem;
}
.btn--quiet:hover { background: var(--surface-sunken); color: var(--ink-900); }

.btn--lg { padding: .95rem 1.9rem; font-size: 1.05rem; }
.btn--block { width: 100%; }

/* Buttons carry their own shadow, which would otherwise win over the global
   focus ring and leave keyboard users with nothing to see. */
.btn:focus-visible,
.icon-btn:focus-visible { box-shadow: var(--ring); }

.icon-btn {
  width: 38px; height: 38px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink-700);
  cursor: pointer;
  font: inherit;
  transition: background-color .15s ease, color .15s ease, opacity .15s ease;
}
.icon-btn:hover:not(:disabled) { background: var(--surface-sunken); color: var(--ink-900); }
.icon-btn:disabled { opacity: .35; cursor: not-allowed; }
.icon-btn svg { width: 18px; height: 18px; }

.field { margin-bottom: 1.15rem; }

.field > label,
.field > .field__label {
  display: block;
  font-weight: 800;
  font-size: .85rem;
  color: var(--ink-700);
  margin-bottom: .45rem;
}

.field__optional { font-weight: 600; color: var(--ink-400); }

.field__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .75rem;
}

.field__count {
  font-size: .8rem;
  font-weight: 700;
  color: var(--ink-400);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Turns red on the last stretch, so the cap is not a surprise. */
.field__count[data-state="full"] { color: var(--danger); }
.field__count[data-state="near"] { color: var(--ink-700); }

.input,
.textarea {
  width: 100%;
  padding: .7rem .9rem;
  font: inherit;
  color: var(--ink-900);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  transition: border-color .15s ease, box-shadow .15s ease;
}

.input:focus,
.textarea:focus {
  outline: none;
  border-color: var(--brand-500);
  box-shadow: var(--ring);
}

.textarea { resize: vertical; min-height: 64px; }

.input-money {
  display: flex;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  padding-left: .9rem;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.input-money:focus-within {
  border-color: var(--brand-500);
  box-shadow: var(--ring);
}
.input-money > span {
  font-size: 1.3rem;
  font-weight: 800;
  color: var(--ink-400);
  line-height: 1;
}
.input-money input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  padding: .55rem .9rem;
  font: inherit;
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}
/* The green ring belongs to the whole box, not to the field inside it —
   without this the global focus ring draws a second line next to the $. */
.input-money input:focus,
.input-money input:focus-visible { outline: none; box-shadow: none; }
.input-money input::-webkit-outer-spin-button,
.input-money input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.input-money input[type=number] { -moz-appearance: textfield; }

.field--invalid .input,
.field--invalid .textarea,
.field--invalid .input-money { border-color: var(--danger); }

.field__error {
  display: none;
  margin-top: .4rem;
  font-size: .85rem;
  font-weight: 700;
  color: var(--danger);
}
.field--invalid .field__error { display: block; }

/* category picker */
.cat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
  gap: .5rem;
  border: 0;
  padding: 0;
  margin: 0;
}

/* Seven into three columns leaves one on its own looking like a mistake.
   The last option is "Other", so giving it the full row is not a patch over
   the arithmetic — it is the catch-all, drawn as the catch-all. */
.cat-grid > *:last-child { grid-column: 1 / -1; }

.cat-option { position: relative; display: block; }
.cat-option input {
  position: absolute;
  inset: 0;
  opacity: 0;
  width: 100%; height: 100%;
  margin: 0;
  cursor: pointer;
}
.cat-option > span {
  display: flex;
  align-items: center;
  gap: .45rem;
  padding: .55rem .7rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  font-size: .88rem;
  font-weight: 700;
  color: var(--ink-700);
  transition: border-color .15s ease, background-color .15s ease, color .15s ease;
}
.cat-option > span::before {
  content: "";
  width: 10px; height: 10px;
  flex: none;
  border-radius: 50%;
  background: var(--dot, var(--ink-300));
}
.cat-option input:hover + span { background: var(--surface-sunken); }
.cat-option input:focus-visible + span { box-shadow: var(--ring); border-color: var(--brand-500); }
.cat-option input:checked + span {
  border-color: var(--dot, var(--brand-500));
  background: color-mix(in srgb, var(--dot, var(--brand-500)) 10%, var(--surface));
  color: var(--ink-900);
  box-shadow: inset 0 0 0 1px var(--dot, var(--brand-500));
}

/* ---------- 5. Landing page ---------- */
.hero {
  position: relative;
  overflow: hidden;
  padding: clamp(3.5rem, 9vw, 6rem) 0 clamp(3rem, 7vw, 5rem);
  background:
    radial-gradient(900px 420px at 12% -10%, var(--hero-glow-warm), transparent 62%),
    radial-gradient(760px 420px at 92% 0%, var(--hero-glow-cool), transparent 60%),
    linear-gradient(180deg, var(--hero-top) 0%, var(--surface-page) var(--hero-fade));
}

.hero__grid {
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .3rem .85rem;
  margin-bottom: 1.25rem;
  border-radius: var(--r-pill);
  background: var(--surface-glass);
  border: 1px solid var(--brand-300);
  color: var(--brand-700);
  font-size: .78rem;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
}

.hero h1 {
  font-size: clamp(2.5rem, 6vw, 4rem);
  font-weight: 900;
  letter-spacing: -0.035em;
  margin: 0 0 1rem;
}

.hero h1 em { font-style: normal; color: var(--brand-700); }

/* The project's own line. It is a joke, and a joke delivered in 16px grey is
   not a joke — so it gets the size, the weight and the rule down its side that
   say "this was written on purpose". */
.signature {
  margin: 0 0 1.6rem;
  padding-left: 1.1rem;
  border-left: 3px solid var(--brand-500);
  font-size: clamp(1.15rem, 2.3vw, 1.5rem);
  font-weight: 800;
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--ink-900);
}

.signature em { font-style: normal; color: var(--brand-700); }

/* "AGAIN." lands on its own line, like a stamp. */
.signature b {
  display: block;
  margin-top: .35rem;
  font-size: .62em;
  font-weight: 900;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--brand-700);
}

.hero__lede {
  font-size: clamp(1.02rem, 1.8vw, 1.12rem);
  color: var(--ink-500);
  max-width: 46ch;
  margin-bottom: 2rem;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .75rem;
  margin-bottom: 1rem;
}

.hero__note { font-size: .85rem; color: var(--ink-400); }

/* preview card inside the hero */
.preview {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  padding: 1.6rem;
  transform: rotate(-1.2deg);
}

.preview__label {
  font-size: .78rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-400);
  margin: 0;
}

.preview__value {
  font-size: clamp(2.2rem, 5vw, 2.9rem);
  font-weight: 900;
  letter-spacing: -0.035em;
  color: var(--ink-900);
  line-height: 1.05;
  margin: .2rem 0 1.25rem;
  font-variant-numeric: tabular-nums;
}

.preview__rows { display: grid; gap: .8rem; margin-top: 1.25rem; }

.preview__row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: .7rem;
  font-size: .9rem;
  font-weight: 700;
  color: var(--ink-700);
}

.preview__row i {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--dot);
}

.preview__row b { font-variant-numeric: tabular-nums; color: var(--ink-900); }

.section { padding: clamp(3.5rem, 8vw, 6rem) 0; }

/* Left, not centred. A centred heading over left-aligned columns was the one
   thing making this page look like it came out of a template. */
.section__head { max-width: 46ch; margin: 0 0 3rem; }
.section__head h2 {
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  letter-spacing: -0.03em;
}
.section__head p { color: var(--ink-500); font-size: 1.05rem; margin-top: .5rem; }

/* Three across, or one under the other — never two and a widow. auto-fit was
   picking two columns across a wide middle band and leaving the third reason
   alone on the second row. */
.reasons {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.5rem, 4vw, 3rem);
}
@media (max-width: 860px) {
  .reasons { grid-template-columns: 1fr; gap: 1.75rem; }
}

/* No box. A coloured rule and a numeral carry it, which reads as deliberate
   where three identical white cards read as filler. */
.reason { padding-top: 1.25rem; border-top: 3px solid var(--rule, var(--brand-500)); }

.reason__num {
  display: block;
  font-size: .8rem;
  font-weight: 900;
  letter-spacing: .18em;
  color: var(--rule, var(--brand-600));
  margin-bottom: .9rem;
}

.reason h3 { font-size: 1.25rem; margin-bottom: .4rem; letter-spacing: -0.015em; }
.reason p { color: var(--ink-500); font-size: .97rem; }

.problem {
  background: var(--surface);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.problem__layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: clamp(2rem, 6vw, 4.5rem);
}

.problem__text h2 {
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  letter-spacing: -0.03em;
  margin-bottom: 1.25rem;
}
.problem__text p { font-size: 1.05rem; color: var(--ink-700); }
.problem__text p:last-child { font-style: italic; color: var(--ink-400); }

.problem__image { display: flex; justify-content: center; }

.weeks { width: 100%; max-width: 440px; height: auto; }

/* The rows fade back as they go, so the eye starts at the top and the block
   reads as depth rather than as five things shouting at once. */
.weeks__row:nth-child(2) { opacity: .94; }
.weeks__row:nth-child(3) { opacity: .86; }
.weeks__row:nth-child(4) { opacity: .78; }
.weeks__row:nth-child(5) { opacity: .7; }

/* ---------- install band ---------- */
.install-band {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin: clamp(2rem, 5vw, 3rem) 0 0;
  padding: 1.5rem 1.75rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}

.install-band__icon img {
  display: block;
  width: 56px; height: 56px;
  border-radius: 14px;
  border: 1px solid var(--line);
}

.install-band__text { flex: 1; min-width: 0; }
.install-band__text h2 { font-size: 1.15rem; margin: 0 0 .2rem; }
.install-band__text p { font-size: .95rem; color: var(--ink-500); margin: 0; }

.steps {
  list-style: none;
  counter-reset: step;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1rem;
}

.steps li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: .85rem;
  font-size: .95rem;
  color: var(--ink-700);
}

.steps__icon {
  width: 38px; height: 38px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  background: var(--brand-100);
  color: var(--brand-700);
}
.steps__icon svg { width: 19px; height: 19px; }

.steps strong { color: var(--ink-900); }

@media (max-width: 680px) {
  .install-band {
    flex-wrap: wrap;
    padding: 1.25rem;
    gap: 1rem;
  }
  .install-band .btn { width: 100%; }
}

.cta-band {
  position: relative;
  overflow: hidden;
  margin: clamp(3rem, 7vw, 5rem) auto;
  max-width: calc(var(--page) - 2 * var(--gutter));
  border-radius: var(--r-lg);
  padding: clamp(3rem, 7vw, 4.5rem) var(--gutter);
  text-align: center;
  color: #fff;
  background:
    radial-gradient(600px 300px at 20% 0%, rgba(255, 255, 255, .18), transparent 60%),
    linear-gradient(135deg, var(--band-from), var(--band-to));
  box-shadow: var(--shadow-md);
}

/* The chart's colours, laid across the top edge — the app's signature, used
   once, where the page hands you over to it. */
.cta-band::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 5px;
  background: linear-gradient(90deg,
    var(--cat-food) 0 22%, var(--cat-bills) 22% 42%,
    var(--cat-transport) 42% 58%, var(--cat-entertainment) 58% 72%,
    var(--cat-hobby) 72% 84%, var(--cat-other) 84% 93%,
    var(--cat-shopping) 93% 100%);
}

.cta-band h2 {
  color: #fff;
  font-size: clamp(2rem, 4.2vw, 2.8rem);
  letter-spacing: -0.03em;
}
.cta-band p { color: rgba(255, 255, 255, .84); max-width: 46ch; margin: .6rem auto 2rem; }

.cta-band .btn--primary {
  background: #fff;
  color: var(--band-ink);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
}
.cta-band .btn--primary:hover { background: var(--brand-050); }

/* ---------- settings ---------- */
.input-at {
  display: flex;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  padding-left: .9rem;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.input-at:focus-within {
  border-color: var(--brand-500);
  box-shadow: var(--ring);
}
.input-at > span {
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--ink-400);
  line-height: 1;
}
.input-at input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  padding: .7rem .5rem;
  font: inherit;
  font-weight: 700;
  color: var(--ink-900);
}
/* Same reason as the amount box: one ring for the whole field, not two. */
.input-at input:focus,
.input-at input:focus-visible { outline: none; box-shadow: none; }

/* A hint that is reporting an outcome rather than explaining a field. The
   two classes are styled together because the state is the same idea in both
   places, and the friends box wore .field__hint while only .card__hint was
   being coloured — so a refusal came out the same grey as the instructions
   above it, which is the one message that has to be impossible to miss. */
.card__hint[data-state="good"],
.field__hint[data-state="good"] { color: var(--brand-700); font-weight: 700; }
.card__hint[data-state="bad"],
.field__hint[data-state="bad"] { color: var(--danger); font-weight: 700; }

.settings-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

.settings-actions {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin-top: 1.25rem;
}

.settings-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: .7rem 0;
  border-bottom: 1px solid var(--line);
}
.settings-row:last-of-type { border-bottom: 0; }

.settings-row__label { font-weight: 700; color: var(--ink-500); font-size: .95rem; }

.settings-row__value {
  font-weight: 800;
  color: var(--ink-900);
  text-align: right;
  overflow-wrap: anywhere;
}

/* Settings is three tabs of cards now rather than five cards in a column.
   The gap between them belongs here, not as an inline style on each card. */
.settings-stack > * + * { margin-top: 1.25rem; }

/* The tab row needs air under the heading it follows. */
.tabs--settings { margin-top: 1.75rem; }

/* ---------- a switch ------------------------------------------------------

   One setting you turn on or off. There is no Save button on this kind of
   row — the switch *is* the answer, so pressing it is what saves it. Which
   is also why the explanation sits under the name rather than after a
   button: with nothing to press afterwards, there is nothing for it to be
   stranded behind. */
.switch-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: 1rem;
  padding: 1.05rem 0;
  border-bottom: 1px solid var(--line);
}
.switch-row:last-of-type { border-bottom: 0; padding-bottom: .25rem; }
.switch-row:first-of-type { padding-top: .35rem; }

.switch-row__name {
  margin: 0;
  font-weight: 800;
  font-size: 1rem;
  color: var(--ink-900);
}

.switch-row__why {
  margin: .3rem 0 0;
  font-size: .9rem;
  line-height: 1.5;
  color: var(--ink-500);
}

/* The real checkbox is still there, merely not drawn — that is what keeps
   the keyboard, the label and a screen reader all working. */
.switch {
  position: relative;
  display: inline-block;
  flex: none;
  margin-top: .15rem;
}

.switch input {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.switch__track {
  position: relative;
  display: block;
  width: 46px; height: 28px;
  border-radius: var(--r-pill);
  /* 3.48:1 on white, 3.35:1 on the dark card — a control, not prose, so
     three is the bar it has to clear. */
  background: var(--ink-300);
  transition: background .18s ease;
}

.switch__track::after {
  content: '';
  position: absolute;
  top: 3px; left: 3px;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .28);
  transition: transform .18s ease;
}

.switch input:checked + .switch__track { background: var(--brand-600); }
.switch input:checked + .switch__track::after { transform: translateX(18px); }
.switch input:focus-visible + .switch__track { box-shadow: var(--ring); }
.switch input:disabled { cursor: default; }
.switch input:disabled + .switch__track { opacity: .5; }

/* Nothing should slide while the page is still deciding what is on. */
.booting .switch__track,
.booting .switch__track::after { transition: none; }

/* The chip turns into the way into this page. */

@media (max-width: 680px) {
  .settings-pair { grid-template-columns: 1fr; gap: 0; }
}

/* A label and its value side by side need room for both. An email does not
   get it on a phone: the label wraps to "Google / account" and the address
   breaks mid-word with its tail stranded against the right edge. Narrower
   than this the pair stacks, and the value reads left to right like
   everything above it. */
@media (max-width: 520px) {
  .settings-row {
    flex-direction: column;
    align-items: stretch;
    gap: .15rem;
  }
  .settings-row__value { text-align: left; }
}

/* prose pages (about) */
.prose h2 { font-size: 1.4rem; margin-bottom: .75rem; }
.prose ul { padding-left: 1.2rem; color: var(--ink-700); margin: 0; }
.prose li { margin-bottom: .4rem; }
.prose li:last-child { margin-bottom: 0; }

/* The list of what changed, on the About page. Numbered in the markup because
   it is an ordered thing, but the numbers are dropped: the version is already
   the label, and two numbers per line is one too many. The version column is a
   fixed width with tabular figures so the sentences all start on one line down
   the page rather than stepping in and out. */
.changelog {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .7rem;
}
.changelog li {
  display: grid;
  grid-template-columns: 9.5rem minmax(0, 1fr);
  gap: .2rem 1rem;
  padding-bottom: .7rem;
  border-bottom: 1px solid var(--line);
}
.changelog li:last-child { padding-bottom: 0; border-bottom: 0; }
.changelog b {
  color: var(--ink-900);
  font-weight: 800;
  font-size: .9rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.changelog span { color: var(--ink-700); font-size: .95rem; line-height: 1.55; }
.changelog span b { font-size: inherit; }

@media (max-width: 680px) {
  /* The version becomes a heading above its line rather than a column that
     would leave the sentence four words wide. */
  .changelog li { grid-template-columns: 1fr; gap: .15rem; }
}
.prose .card + .card { margin-top: 1.25rem; }
.prose-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }

/* ---------- 6. Dashboard ---------- */
.dash { padding: clamp(2rem, 5vw, 3rem) 0 1rem; }

.dash__greeting {
  font-size: .95rem;
  font-weight: 800;
  color: var(--ink-500);
  margin-bottom: 1.25rem;
}

.summary {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.5rem;
  margin-bottom: 2rem;
}

.summary__label {
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--ink-500);
  margin: 0;
}

.summary__value {
  font-size: clamp(3rem, 8vw, 4.5rem);
  font-weight: 900;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
  margin: .3rem 0 .6rem;
}

/* A month can hold more than one currency, and they are never added together.
   Each gets its own line; the largest leads, the rest follow a size down —
   smaller because they are smaller, not because they matter less. */
.summary__amount { display: block; }
.summary__amount--more {
  font-size: .45em;
  color: var(--ink-500);
  margin-top: .25rem;
}

.summary__delta {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  font-size: .95rem;
  font-weight: 800;
  color: var(--ink-500);
  margin: 0;
  min-height: 1.5rem;
}
.summary__delta[data-dir="up"] { color: var(--danger); }
.summary__delta[data-dir="down"] { color: var(--brand-700); }
.summary__delta small { font-weight: 700; color: var(--ink-400); }

.summary__actions { display: flex; align-items: center; gap: .75rem; }

.month-nav {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  padding: .3rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  box-shadow: var(--shadow-xs);
}

.month-nav__label {
  min-width: 12ch;
  text-align: center;
  font-weight: 800;
  font-size: .92rem;
  color: var(--ink-900);
  padding: 0 .4rem;
  white-space: nowrap;
}

.dash__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}

.stat-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 1rem 1.15rem;
  box-shadow: var(--shadow-xs);
}

.stat__label {
  font-size: .76rem;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-400);
}

.stat__value {
  font-size: 1.45rem;
  font-weight: 900;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  margin: 0;
}

.stat__note {
  font-size: .82rem;
  font-weight: 700;
  color: var(--ink-400);
  margin: 0;
}

.stat p { margin: 0; }

/* month by month */

/* The dashboard runs in three bands — the tiles, this chart, then the pair of
   cards — and they all leave the same gap. Without this the chart sat flush
   against the two below it, as if it were part of them. Hidden it leaves no
   gap at all, since .hidden takes the box out of the flow. */
.trend { margin-bottom: 1.5rem; }

/* The bars share one height, set here once: the average line is placed as a
   fraction of it, and a percentage would otherwise measure itself against the
   plot plus the labels underneath and land too low. */
.trend__plot {
  position: relative;
  --plot: 150px;
}

.trend__bars {
  position: relative;
  display: flex;
  align-items: stretch;
  gap: 2px;
}

/* Something for the bars to stand on. Exactly at their base, so it reads as a
   baseline rather than as one more gridline. */
.trend__bars::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  top: var(--plot);
  border-top: 1px solid var(--line);
  pointer-events: none;
}

/* The button is the whole column so the thing you have to hit is generous;
   the padding is what keeps the mark inside it thin. */
.trend__bar {
  flex: 1;
  min-width: 0;
  display: grid;
  grid-template-rows: var(--plot) auto;
  gap: .5rem;
  padding: 0 4px;
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
  border-radius: var(--r-xs);
}
.trend__bar:focus-visible { outline: none; box-shadow: var(--ring); }

.trend__col { position: relative; }

/* Anchored to the baseline and rounded only at the end that carries the
   value. A month with nothing in it keeps a sliver, so the gap is visible as
   a gap rather than as a missing bar. */
.trend__fill {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: min(100%, 34px);
  transform: translateX(-50%);
  height: max(3px, calc(var(--h) * 1%));
  background: var(--brand-500);
  border-radius: 4px 4px 0 0;
  transition: background-color .15s ease;
}
.trend__bar:hover .trend__fill { background: var(--brand-600); }

/* The month being shown above. Marked twice — the darkest green in the scale,
   and a filled label — because the step just below it sits too close to the
   others for anyone to tell apart, colour-blind or not. */
.trend__bar[aria-current] .trend__fill { background: var(--brand-700); }
.trend__bar[aria-current] .trend__tick {
  background: var(--brand-700);
  color: var(--on-brand-700);
}

.trend__tick {
  justify-self: center;
  max-width: 100%;
  padding: .15rem .4rem;
  border-radius: var(--r-pill);
  font-size: .78rem;
  font-weight: 800;
  color: var(--ink-400);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: clip;
}

/* What a usual month costs. Without it a tall bar only reads as tall; with it
   the same bar reads as more than usual, which is the question being asked.
   Averaged over the months that have something in them. */
.trend__avg {
  position: absolute;
  left: 0; right: 0;
  top: calc(var(--at) * var(--plot));
  border-top: 1px dashed var(--ink-300);
  pointer-events: none;
}

.trend__avg-label {
  position: absolute;
  right: 0;
  top: -.62rem;
  padding: 0 .3rem;
  background: var(--surface);
  font-size: .7rem;
  font-weight: 800;
  color: var(--ink-400);
  font-variant-numeric: tabular-nums;
}

/* searching the expenses */

/* Built like .input-money: the frame is the wrapper, so the magnifier and the
   clear button sit inside it and the whole thing lights up on focus. */
.search {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: 0 .75rem;
  margin-bottom: .85rem;
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
}
/* Everywhere else focus is a solid border plus the soft --ring outside it, and
   on a small radius the eye reads the two as one thickened edge. This is the
   only field shaped like a pill, and at that radius they separate into two
   visible rings — a dark one and a pale one with daylight between them. So
   here the halo is dropped and the ring is simply the border continued in the
   same colour: 1px of border and 1px of shadow, one unbroken 2px line. */
.search:focus-within {
  background: var(--surface);
  border-color: var(--brand-600);
  box-shadow: 0 0 0 1px var(--brand-600);
}

.search__icon {
  width: 17px; height: 17px;
  flex: none;
  color: var(--ink-300);
}
.search:focus-within .search__icon { color: var(--brand-600); }

.search__field {
  flex: 1;
  min-width: 0;
  padding: .55rem 0;
  font: inherit;
  font-size: .95rem;
  color: var(--ink-900);
  background: none;
  border: 0;
}
.search__field:focus { outline: none; }
.search__field::placeholder { color: var(--ink-400); }

/* Safari puts its own cross in a type="search" field; ours is the one that
   also tells the page the search is over, so the browser's is hidden. */
.search__field::-webkit-search-cancel-button,
.search__field::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }

.search__clear {
  width: 26px; height: 26px;
  flex: none;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--ink-400);
  cursor: pointer;
  transition: color .15s ease, background-color .15s ease;
}
.search__clear svg { width: 15px; height: 15px; }
.search__clear:hover { background: var(--line); color: var(--ink-900); }
.search__clear:focus-visible { outline: none; box-shadow: var(--ring); }

/* What the matches come to. Several currencies stay several numbers — a
   search that turns up lei and dollars has two answers, not one. */
.search__sum {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .4rem;
  margin: 0 0 .85rem;
  font-variant-numeric: tabular-nums;
}
.search__sum b { font-size: 1.15rem; font-weight: 800; color: var(--ink-900); }
.search__sum-label {
  font-size: .76rem;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-400);
}
.search__sum-and { color: var(--ink-400); font-weight: 800; }

/* transaction list */

/* The box the list is kept in. Without it a long month made the card as tall
   as the month was, and everything under it — the footer most of all — walked
   off the bottom of the screen. The height is capped instead, and the list
   scrolls inside; a short month simply makes a short box, because it is a
   max-height and not a height. */
.tx-scroll {
  max-height: min(32rem, 70vh);
  overflow-y: auto;
  padding: 0 .9rem;                 /* also absorbs the rows' -.5rem margins,
                                       so nothing can overflow sideways */
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);

  /* Reaching the end of the list should stop there rather than carry on
     scrolling the page underneath it. */
  overscroll-behavior-y: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}

.tx-scroll::-webkit-scrollbar { width: 10px; }
.tx-scroll::-webkit-scrollbar-track { background: transparent; }
.tx-scroll::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: var(--r-pill);
  border: 3px solid var(--surface);  /* keeps the thumb narrow and off the edge */
}
.tx-scroll::-webkit-scrollbar-thumb:hover { background: var(--ink-300); }

/* The top and bottom breathing room sits here, not on the box, so that a day
   heading can stick to the very top edge while scrolling. */
.tx-stack { padding: .9rem 0; }

.tx-day {
  font-size: .76rem;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-400);
  margin: 1.5rem -.5rem .1rem;

  /* Scrolling a list of dates is only useful if you can still see which date
     you are looking at, so the heading parks at the top edge until the next
     one pushes it off. It needs the card's own background to do that, or the
     rows would read straight through it. */
  position: sticky;
  top: 0;
  z-index: 1;
  padding: .25rem .5rem;
  background: var(--surface);
}
.tx-group:first-child .tx-day { margin-top: 0; }

.tx-list { list-style: none; margin: 0; padding: 0; }

.tx {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: .9rem;
  padding: .7rem .5rem;
  margin: 0 -.5rem;
  border-radius: var(--r-sm);
  transition: background-color .15s ease;
}
.tx + .tx { border-top: 1px solid var(--line); }
.tx:hover { background: var(--surface-sunken); }

.tx__icon {
  width: 38px; height: 38px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-size: 1rem;
  background: color-mix(in srgb, var(--dot) 14%, var(--surface));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--dot) 26%, var(--surface));
}

.tx__body { min-width: 0; display: block; }

/* The note already trimmed itself; the category name did not, so on a narrow
   phone "Entertainment" ran straight under the amount beside it. */
.tx__cat {
  display: block;
  font-weight: 800;
  color: var(--ink-900);
  font-size: .95rem;
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tx__note {
  display: block;
  font-size: .85rem;
  color: var(--ink-500);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tx__amount {
  font-weight: 800;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.tx__tools {
  display: flex;
  gap: .15rem;
}

.tx__tool {
  width: 30px; height: 30px;
  display: grid;
  place-items: center;
  border: 0;
  background: none;
  border-radius: 50%;
  color: var(--ink-300);
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s ease, color .15s ease, background-color .15s ease;
}
/* The tools stay out of the way until the row is pointed at, but a keyboard
   user has no pointer — reaching one with Tab has to reveal it. */
.tx:hover .tx__tool,
.tx__tool:focus-visible { opacity: 1; }
.tx__tool:hover { background: var(--brand-050); color: var(--brand-700); }
.tx__tool--danger:hover { background: var(--danger-050); color: var(--danger); }
.tx__tool svg { width: 15px; height: 15px; }

/* Nothing hovers on a touch screen, so there they are simply always visible. */
@media (hover: none) {
  .tx__tool { opacity: 1; }
}

/* ---------- 6b. Groups ---------- */

/* The two halves of the dashboard. An underline rather than a pill row: these
   are two pages of one thing, not two settings of one control — the segmented
   .seg-row look is used elsewhere for exactly that, and reusing it here would
   say the wrong thing. */
.tabs {
  display: flex;
  gap: .15rem;
  margin: 0 0 1.6rem;
  border-bottom: 1px solid var(--line);
}

.tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .55rem .85rem .7rem;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 800;
  font-size: .98rem;
  color: var(--ink-500);
  border-radius: var(--r-xs) var(--r-xs) 0 0;
  cursor: pointer;
  transition: color .15s ease;
}
.tab:hover { color: var(--ink-900); }
.tab.is-on { color: var(--brand-700); }

/* The marker sits on the shared line, so the on tab reads as joined to the
   panel below it rather than merely coloured. */
.tab::after {
  content: '';
  position: absolute;
  left: .5rem; right: .5rem; bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: transparent;
}
.tab.is-on::after { background: var(--brand-600); }
.tab:focus-visible { outline: none; box-shadow: var(--ring); }

.tab__badge {
  min-width: 19px;
  height: 19px;
  padding: 0 5px;
  display: inline-grid;
  place-items: center;
  border-radius: var(--r-pill);
  background: var(--brand-600);
  color: #fff;
  font-size: .72rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

.groups-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
}
.groups-head__title { font-size: 1.3rem; margin: 0; }
.groups-head__note {
  margin: .3rem 0 0;
  max-width: 46ch;
  font-size: .9rem;
  color: var(--ink-500);
}

.muted-note { color: var(--ink-500); font-size: .92rem; }

/* --- an invitation waiting on an answer --- */
.invite {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
  box-shadow: inset 0 0 0 2px var(--brand-300), var(--shadow-sm);
}
.invite__title { font-size: 1.05rem; margin: 0; }
.invite__note { margin: .25rem 0 0; font-size: .88rem; color: var(--ink-500); }
.invite__tools { display: flex; gap: .5rem; flex: none; }

/* --- the list of groups --- */
.group-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .75rem; }

.group-card {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: .2rem 1rem;
  padding: 1.1rem 1.25rem;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-xs);
  font: inherit;
  cursor: pointer;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.group-card:hover {
  border-color: var(--brand-300);
  box-shadow: var(--shadow-sm);
  transform: translateY(-1px);
}
.group-card:focus-visible { outline: none; box-shadow: var(--ring); }

.group-card__name {
  grid-column: 1;
  font-weight: 800;
  font-size: 1.05rem;
  color: var(--ink-900);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.group-card__meta { grid-column: 1; font-size: .85rem; color: var(--ink-500); }
.group-card__sum {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  font-weight: 800;
  font-size: 1.05rem;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.group-card__and { color: var(--ink-400); font-weight: 800; margin: 0 .3rem; }

.back-link {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  margin-bottom: 1rem;
  padding: .35rem .6rem .35rem .35rem;
  border: 0;
  background: none;
  border-radius: var(--r-pill);
  font: inherit;
  font-weight: 700;
  font-size: .9rem;
  color: var(--ink-500);
  cursor: pointer;
}
.back-link:hover { background: var(--surface-sunken); color: var(--ink-900); }
.back-link:focus-visible { outline: none; box-shadow: var(--ring); }
.back-link svg { width: 16px; height: 16px; }

/* --- one group's heading --- */
.group-head { margin-bottom: 1.5rem; }
.group-head__row {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  flex-wrap: wrap;
}

/* The name takes whatever is left; min-width: 0 is what lets a long one wrap
   instead of forcing the row wider than the card. */
.group-head__name { font-size: 1.3rem; margin: 0; flex: 1 1 auto; min-width: 0; }

/* margin-left rather than justify-content: space-between on the row. When the
   name is long enough to take the whole line, space-between drops the tools to
   the next line and then aligns them *left*, which is where "Leave" was ending
   up. An auto margin keeps them at the end on their own line too. */
.group-head__tools { display: flex; gap: .4rem; flex: none; margin-left: auto; }
.group-head__sum {
  margin: .6rem 0 0;
  font-size: 1.9rem;
  font-weight: 800;
  letter-spacing: -.02em;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}
.group-head__note { margin: .15rem 0 0; font-size: .88rem; color: var(--ink-500); }

/* --- who is in it --- */
.people {
  list-style: none;
  margin: 1.1rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
}

.person {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .25rem .4rem .25rem .25rem;
  background: var(--surface-sunken);
  border-radius: var(--r-pill);
  font-size: .85rem;
  font-weight: 700;
  color: var(--ink-700);
}
/* Nobody yet — a dashed edge says "not settled" without needing a second
   colour that would have to mean something on its own. */
.person--waiting {
  background: none;
  box-shadow: inset 0 0 0 1px var(--line-strong);
  color: var(--ink-500);
}
.person .badge { width: 21px; height: 21px; font-size: .66rem; }
.person--waiting .badge { background: var(--ink-500); }
.person__name { padding-right: .3rem; }
.person__tag {
  padding: .1rem .45rem;
  border-radius: var(--r-pill);
  background: var(--surface);
  font-size: .72rem;
  font-weight: 800;
  color: var(--ink-400);
  text-transform: lowercase;
}
.person--waiting .person__tag { background: var(--surface-sunken); }

/* A person you can press, sitting directly under a list of people you cannot.
   Hover alone could not carry that difference — there is no hover on a phone,
   and a thing that looks exactly like a label and acts like a button is a
   trap. So it takes the app's own colour for "this does something", and wears
   a + for what it does. Green among grey, and legible without moving a mouse.

   36px tall rather than the 30px a plain chip gets, because this one is a
   target for a thumb. */
.person--pick {
  min-height: 36px;
  border: 0;
  font: inherit;
  font-weight: 700;
  font-size: .85rem;
  cursor: pointer;
  padding-right: .5rem;
  background: var(--brand-050);
  color: var(--brand-700);
  transition: background-color .15s ease;
}
.person--pick .badge { background: var(--brand-600); }
.person--pick:hover { background: var(--brand-100); }
.person--pick:focus-visible { outline: none; box-shadow: var(--ring); }

/* The + after the name. Drawn rather than typed, so it matches the one on
   every other "add" in the app instead of being a plus-shaped character. */
.person__add {
  width: 15px; height: 15px;
  flex: none;
  opacity: .75;
}

/* The row of them, above the box you would otherwise have to type into. */
.invite-picks { margin-top: 1.1rem; }
.invite-picks .people { margin-top: .5rem; }

/* ---------------------------------------------------------------------------
   Narrowing a group's ledger: who paid, and what for.
   --------------------------------------------------------------------------- */

.filters {
  margin-top: 1.1rem;
  padding-bottom: .9rem;
  border-bottom: 1px solid var(--line);
}
.filters__row {
  display: flex;
  align-items: baseline;
  gap: .4rem;
}
.filters__row + .filters__row { margin-top: .55rem; }

/* The chips wrap inside their own box rather than in the row. Wrapping in the
   row puts the second line back under the label, where it reads as a third
   category of its own; here it lines up under the first chip, which is where
   the eye is already looking. */
.filters__chips {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  flex: 1 1 0;
  min-width: 0;
}

/* Fixed width so the two rows' chips start at the same place. They read as
   two takes on one list, and a ragged left edge makes them read as two
   unrelated controls. Below that width the label sits on its own line and
   the chips go full width, which is the honest thing on a narrow phone. */
.filters__label {
  flex: none;
  width: 5.5rem;
  /* Lifts the label to sit against the first row of chips rather than against
     the whole stack's middle once they wrap. */
  padding-top: .5rem;
  font-size: .78rem;
  font-weight: 800;
  letter-spacing: .02em;
  color: var(--ink-500);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  /* 32px tall. Smaller reads as a label; this has to read as a thing to
     press, and a finger needs somewhere to land. */
  min-height: 32px;
  padding: .25rem .7rem;
  /* Filled, not outlined. A hairline in --line comes to 1.21:1 against the
     card — the words inside were perfectly legible, but the thing around them
     did not read as something to press. A fill says "chip" the way the people
     chips elsewhere already do, and leaves the green one free to mean chosen. */
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  font: inherit;
  font-size: .85rem;
  font-weight: 700;
  color: var(--ink-700);
  cursor: pointer;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.chip:hover { background: var(--brand-100); color: var(--brand-700); }
.chip:focus-visible { outline: none; box-shadow: var(--ring); }

/* The chosen one. Filled rather than merely outlined, because at this size a
   border alone is not a difference anybody notices at a glance — and never
   colour alone: aria-pressed carries the same answer to a screen reader. */
.chip--on {
  background: var(--brand-600);
  border-color: var(--brand-600);
  color: #fff;
}
.chip--on:hover { background: var(--brand-700); border-color: var(--brand-700); }

.chip__icon { font-size: .95rem; line-height: 1; }

/* The figure, while something is filtered. */
.filters__sum {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .55rem;
  margin: .9rem 0 0;
}
.filters__sum .num { font-size: 1.35rem; font-weight: 800; color: var(--ink-900); }
.filters__count { font-size: .9rem; color: var(--ink-500); }
.filters__sum .btn { margin-left: auto; }

/* A button that is a word in a sentence. */
.link-button {
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  font-weight: 700;
  color: var(--brand-700);
  text-decoration: underline;
  cursor: pointer;
}
.link-button:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 4px; }

/* On a phone the label takes a line of its own and the chips get the full
   width underneath. The row has to be allowed to wrap for that: with a
   full-width label in a row that cannot, the chips are handed what is left of
   the line — nothing — and run off the side of the page instead of wrapping. */
@media (max-width: 520px) {
  .filters__row { flex-wrap: wrap; }
  .filters__label { width: 100%; padding-top: 0; padding-bottom: .15rem; }
}

.person__drop {
  width: 20px; height: 20px;
  display: grid;
  place-items: center;
  border: 0;
  background: none;
  border-radius: 50%;
  color: var(--ink-300);
  cursor: pointer;
}
.person__drop:hover { background: var(--danger-050); color: var(--danger); }
.person__drop:focus-visible { outline: none; box-shadow: var(--ring); }
.person__drop svg { width: 11px; height: 11px; }

.invite-row { margin-top: 1.2rem; }
.invite-row__label {
  display: block;
  margin-bottom: .4rem;
  font-size: .82rem;
  font-weight: 800;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--ink-500);
}
.invite-row__fields { display: flex; gap: .5rem; }
.invite-row__fields .input { flex: 1; min-width: 0; }
.invite-row__fields .btn { flex: none; }

.field__hint {
  margin: .4rem 0 0;
  font-size: .82rem;
  line-height: 1.5;
  color: var(--ink-500);
}

/* --- who put in what ---
   One bar per person, thin, in one hue. Not a categorical palette: these are
   amounts of the same thing, so the only difference that needs showing is
   which one is yours — and that is said with the name as well as the bar. */
.shares { list-style: none; margin: 0; padding: 0; display: grid; gap: .7rem; }

.share {
  display: grid;
  grid-template-columns: minmax(70px, auto) minmax(0, 1fr) auto;
  align-items: center;
  gap: .75rem;
}
.share__who {
  font-size: .9rem;
  font-weight: 700;
  color: var(--ink-700);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.share__track {
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  overflow: hidden;
}
.share__fill {
  display: block;
  width: var(--w);
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--bar-other);
}
.share__fill--me { background: var(--bar-mine); }
.share__sum {
  font-size: .9rem;
  font-weight: 800;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* --- an entry in a group --- */

/* Who wrote it, beside the category. The row is the same one the personal
   list uses; this is the one thing a shared ledger has to say that a private
   one does not. */
.tx__who {
  margin-left: .5rem;
  font-size: .78rem;
  font-weight: 700;
  color: var(--ink-400);
}

/* In a shared ledger the rows do not all carry the same buttons: your own have
   edit and delete on them and somebody else's do not. Left to itself the grid
   gives the tools exactly as much room as they need, which lands every row's
   amount at a different place down the column. Reserving the full width of
   three buttons, filled from the right, puts the figures back on one line —
   and a column of money that does not line up is barely a column. */
.tx--group .tx__tools {
  min-width: 96px;
  justify-content: flex-end;
}

/* A tool that is carrying information — a comment count, or the mark that
   this expense already has a copy in a group — cannot wait for a hover. */
.tx__tool.is-on { opacity: 1; color: var(--brand-600); }
.tx__tool-count {
  position: absolute;
  top: 1px; right: 0;
  min-width: 14px;
  padding: 0 3px;
  border-radius: var(--r-pill);
  background: var(--brand-600);
  color: #fff;
  font-size: .62rem;
  font-weight: 800;
  line-height: 14px;
}
.tx__tool { position: relative; }

/* --- comments --- */
.thread {
  grid-column: 1 / -1;
  margin: .6rem 0 .2rem;
  padding: .85rem 1rem;
  background: var(--surface-sunken);
  border-radius: var(--r-sm);
}
.thread__list { list-style: none; margin: 0 0 .7rem; padding: 0; display: grid; gap: .5rem; }
.thread__empty { margin: 0 0 .7rem; font-size: .85rem; color: var(--ink-400); }

/* A person's mark inside MyMon: their initial, in the shape and the green the
   logo uses. One colour for everybody on purpose — a colour per person reads
   as meaning something, the only thing it could mean here is already written
   beside it in words, and it would add hues to a page whose palette was
   checked for colour-blind separation as a fixed set. */
.badge {
  width: 26px; height: 26px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  /* Deliberately not the brand green. These circles say who somebody is, and
     green in this app says "press me"; when every face in a group wears it,
     the one button that meant it stops standing out. Ink flips with the
     theme on its own, so the initial stays readable either way. */
  background: var(--ink-700);
  color: var(--surface);
  font-size: .76rem;
  font-weight: 900;
  line-height: 1;
}

/* A comment is a small card: who said it, over what they said. */
.note {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: .1rem .55rem;
  font-size: .88rem;
}
.note .badge { grid-row: 1 / span 2; align-self: start; }

.note__who {
  grid-column: 2;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .3rem;
  min-width: 0;
}
.note__name {
  font-weight: 800;
  font-size: .86rem;
  color: var(--ink-900);
}
.note__handle,
.note__tag {
  font-style: normal;
  font-size: .75rem;
  font-weight: 700;
  color: var(--ink-400);
}
.note__tag {
  padding: .05rem .4rem;
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink-400);
}

.note__body {
  grid-column: 2 / -1;
  color: var(--ink-700);
  overflow-wrap: anywhere;
}
.note__drop {
  grid-column: 3;
  grid-row: 1;
  width: 18px; height: 18px;
  display: grid;
  place-items: center;
  align-self: center;
  border: 0;
  background: none;
  border-radius: 50%;
  color: var(--ink-300);
  cursor: pointer;
}
.note__drop:hover { background: var(--danger-050); color: var(--danger); }
.note__drop:focus-visible { outline: none; box-shadow: var(--ring); }
.note__drop svg { width: 10px; height: 10px; }

.thread__write { display: flex; gap: .5rem; }
.thread__write .input { flex: 1; min-width: 0; background: var(--surface); }
.thread__write .btn { flex: none; }

.btn--sm { padding: .45rem .9rem; font-size: .85rem; }

/* --- picking a group to copy into --- */
.pick { display: grid; gap: .5rem; }

.pick__option { display: block; cursor: pointer; }
.pick__option input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}
.pick__option > span {
  display: block;
  padding: .7rem .9rem;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  transition: border-color .15s ease, background-color .15s ease;
}
.pick__option > span b { display: block; font-weight: 800; color: var(--ink-900); }
.pick__option > span i {
  display: block;
  margin-top: .1rem;
  font-style: normal;
  font-size: .82rem;
  color: var(--ink-500);
}
.pick__option:hover > span { border-color: var(--brand-300); }
.pick__option input:checked + span {
  border-color: var(--brand-600);
  background: var(--brand-050);
}
.pick__option input:focus-visible + span { box-shadow: var(--ring); }

/* Already in that group. Left readable rather than hidden, so the answer to
   "why can I not pick it" is on the screen instead of being guessed at. */
.pick__option--done { cursor: default; }
.pick__option--done > span { background: var(--surface-sunken); opacity: .65; }
.pick__option--done:hover > span { border-color: var(--line-strong); }

/* ---------- 6c. The first screen after a first sign-in ---------- */

.welcome {
  padding: 3.5rem 0 4.5rem;
  display: grid;
  place-items: start center;
}

/* Narrow on purpose. Three fields and a button is not a page to spread across
   a desktop, and the line length keeps the explanation readable. */
.welcome__card {
  width: 100%;
  max-width: 520px;
  /* The grid above centres .container, which is already as wide as the page
     allows; the card inside it has to centre itself. */
  margin-inline: auto;
}

.welcome__title {
  font-size: 1.75rem;
  letter-spacing: -.02em;
  margin: 0 0 .6rem;
}

.welcome__lead {
  margin: 0 0 1.75rem;
  color: var(--ink-500);
  font-size: .95rem;
  line-height: 1.6;
}

/* Two names side by side, one under the other once that stops fitting. */
.welcome__names {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 1rem;
}

.welcome__status { white-space: normal; margin-top: .4rem; }

.welcome__card .btn--block { margin-top: .5rem; }

.welcome__foot {
  margin: 1.4rem 0 0;
  font-size: .85rem;
  line-height: 1.55;
  color: var(--ink-400);
}

/* ---------- 7. Charts ---------- */
.share-bar {
  display: flex;
  gap: 2px;                 /* 2px surface gap between segments */
  height: 14px;
  margin-bottom: 1.5rem;
  border-radius: var(--r-pill);
  overflow: hidden;
  background: var(--surface-sunken);
}

.share-bar__seg {
  background: var(--dot);
  min-width: 3px;
  border: 0;
  padding: 0;
  cursor: default;
  transition: opacity .15s ease;
}
.share-bar:hover .share-bar__seg { opacity: .4; }
.share-bar .share-bar__seg:hover { opacity: 1; }

.breakdown { list-style: none; margin: 0; padding: 0; display: grid; gap: .85rem; }

.breakdown__row { display: grid; gap: .35rem; }

.breakdown__head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: .55rem;
  font-size: .92rem;
}

.breakdown__dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--dot);
  flex: none;
}

.breakdown__name { font-weight: 800; color: var(--ink-900); }
.breakdown__amount { font-weight: 800; color: var(--ink-900); font-variant-numeric: tabular-nums; }
/* One decimal is two characters more than a whole number, so this steps down
   a size to buy the room back. Tabular figures plus a fixed width line the
   decimal points up down the column. */
.breakdown__pct {
  min-width: 5.5ch;
  text-align: right;
  color: var(--ink-400);
  font-size: .85rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.breakdown__track {
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  overflow: hidden;
}

.breakdown__fill {
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--dot);
  transition: width .5s cubic-bezier(.22, .61, .36, 1);
}

.viz-tip {
  position: fixed;
  z-index: 90;
  top: 0; left: 0;
  pointer-events: none;
  padding: .4rem .65rem;
  border-radius: var(--r-xs);
  background: var(--inverse);
  color: var(--on-inverse);
  font-size: .82rem;
  font-weight: 700;
  white-space: nowrap;
  box-shadow: var(--shadow-md);
  opacity: 0;
  transition: opacity .12s ease;
}
.viz-tip[data-show="1"] { opacity: 1; }

/* ---------- 8. Dialog, toast, empty states ---------- */
dialog.modal {
  width: min(520px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  background: var(--surface);
  color: var(--ink-700);
}

dialog.modal::backdrop {
  background: var(--scrim);
  backdrop-filter: blur(3px);
}

dialog.modal[open] { animation: pop .18s cubic-bezier(.22, .61, .36, 1); }

@keyframes pop {
  from { opacity: 0; transform: translateY(10px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

.modal__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.35rem 1.6rem .5rem;
}

.modal__head h2 { font-size: 1.25rem; margin: 0; }
.modal__head p { font-size: .9rem; color: var(--ink-500); margin: .25rem 0 0; }
.modal__body { padding: 1rem 1.6rem 1.5rem; }
.modal__foot {
  display: flex;
  justify-content: flex-end;
  gap: .6rem;
  padding: 1.1rem 1.6rem;
  border-top: 1px solid var(--line);
  background: var(--brand-050);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}

.modal__close {
  width: 34px; height: 34px;
  flex: none;
  display: grid;
  place-items: center;
  border: 0;
  background: none;
  border-radius: 50%;
  color: var(--ink-400);
  cursor: pointer;
}
.modal__close:hover { background: var(--surface-sunken); color: var(--ink-900); }
.modal__close svg { width: 16px; height: 16px; }

.empty {
  text-align: center;
  padding: 2.25rem 1rem;
  color: var(--ink-500);
}
.empty__art {
  width: 56px; height: 56px;
  margin: 0 auto 1rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--brand-100);
  color: var(--brand-700);
}
.empty__art svg { width: 26px; height: 26px; }
.empty h3 { font-size: 1.05rem; margin-bottom: .25rem; }
.empty p { font-size: .92rem; margin-bottom: 1rem; }

.toast-stack {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 100;
  display: grid;
  gap: .5rem;
  width: min(430px, calc(100vw - 32px));
}

.toast {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .8rem 1rem .8rem 1.1rem;
  border-radius: var(--r-md);
  background: var(--inverse);
  color: var(--on-inverse);
  font-weight: 700;
  font-size: .92rem;
  box-shadow: var(--shadow-lg);
  animation: rise .2s cubic-bezier(.22, .61, .36, 1);
}

.toast button {
  border: 0;
  background: none;
  color: var(--brand-300);
  font: inherit;
  font-weight: 800;
  cursor: pointer;
  padding: .1rem .3rem;
  border-radius: var(--r-xs);
  flex: none;
}
.toast button:hover { color: var(--on-inverse); text-decoration: underline; }

@keyframes rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* ---------- 9. Responsive ---------- */
@media (max-width: 960px) {
  .hero__grid { grid-template-columns: 1fr; }
  .preview { transform: none; max-width: 480px; }
  .dash__grid { grid-template-columns: 1fr; }
  .problem__layout { grid-template-columns: 1fr; }
  .problem__image { order: -1; max-width: 360px; margin: 0 auto; }
  .prose-grid { grid-template-columns: 1fr; }
}

@media (max-width: 680px) {
  :root { --gutter: 16px; }
  .site-header .container { min-height: 58px; gap: .4rem; }
  .logo { height: 32px; padding: 0 .75rem; font-size: .95rem; }
  .site-nav { gap: .1rem; }
  .site-nav a { padding: .35rem .5rem; font-size: .85rem; }
  .btn--quiet { padding: .35rem .5rem; font-size: .82rem; }
  .card { padding: 1.25rem; }
  /* Two columns put the third card on a row of its own at twice the width of
     its siblings, which looks like a layout that gave up rather than one that
     decided. One column makes all three the same — and then each card lays
     itself out sideways, label left and number right, so the width it just
     gained is actually used instead of standing empty beside a short word. */
  .stat-row { grid-template-columns: 1fr; gap: .65rem; }
  .stat {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    column-gap: 1rem;
    padding: .85rem 1.15rem;
  }
  .stat__label { grid-column: 1; grid-row: 1; }
  .stat__note  { grid-column: 1; grid-row: 2; }
  .stat__value { grid-column: 2; grid-row: 1 / span 2; }
  .stat__value { font-size: 1.25rem; }
  .summary { align-items: flex-start; }
  .summary__actions { width: 100%; justify-content: space-between; }
  .month-nav__label { min-width: 0; font-size: .85rem; }
  .icon-btn { width: 34px; height: 34px; }
  .modal__foot { padding: 1rem; }
  .toast-stack { bottom: 16px; }

  /* On a phone the row runs out of width: an icon, a category name, an amount
     carrying a three-letter currency, and two buttons do not fit on one line,
     and the name was the part that lost. So the amount drops underneath the
     name instead, which gives the name the whole width and costs nothing —
     the row was already two lines tall wherever there is a note. */
  .tx {
    grid-template-columns: auto minmax(0, 1fr) auto;
    column-gap: .7rem;
    row-gap: .1rem;
  }
  .tx__icon { grid-row: 1 / span 2; }
  .tx__body { grid-column: 2; grid-row: 1; }
  .tx__amount { grid-column: 2; grid-row: 2; font-size: 1.02rem; }
  .tx__cat { font-size: .9rem; }
  .tx__tools { grid-column: 3; grid-row: 1 / span 2; }

  /* Six bars instead of twelve — the script does that part. These only make
     the labels fit the narrower bars. */
  .trend__plot { --plot: 128px; }
  .trend__bar { padding: 0 2px; }
  .trend__tick { font-size: .72rem; padding: .15rem .3rem; }

  /* Taller rows and a shorter screen: at the desktop height the box would be
     nearly everything you can see, leaving almost nothing to grab to scroll
     the page itself. For the same reason the scroll is allowed to carry on
     into the page once the list ends, instead of stopping at its edge. */
  .tx-scroll {
    max-height: min(28rem, 55vh);
    overscroll-behavior-y: auto;
  }

  /* Groups: every row that was a heading beside its buttons becomes the
     heading above them. Nothing here shrinks text to fit — it stacks. */
  .tab { padding: .5rem .6rem .65rem; font-size: .92rem; }


  /* The invitation stacks, but its two buttons keep their own width and sit
     at the end of the row. Half the card each made "Join" the largest green
     thing on the screen — larger than "New group", which is the same kind of
     action — and weight is how a person tells which button matters. */
  .invite { flex-direction: column; align-items: stretch; }
  .invite__tools { justify-content: flex-end; }

  /* The heading goes above its button, but the button keeps its own width.
     Stretched across the screen it became the loudest thing on the page —
     louder than the groups themselves, which are the point of it — and it
     stopped matching the identical action one tab over. */
  .groups-head { flex-direction: column; align-items: flex-start; }

  /* A grid rather than two flexible buttons: flex sizes them from their own
     padding, and a quiet button and a solid one do not carry the same, so the
     pair came out visibly lopsided. Two equal columns cannot. */
  /* They stay beside the name on a phone too. Stacked under it they had
     nothing to sit against, and the owner's two buttons were being split into
     half-width cells — which left a member's single "Leave" filling the left
     half and reading as floating in the middle of the card. */
  .group-head__tools { gap: .5rem; }

  .group-card { padding: .95rem 1rem; }
  .group-card__sum { font-size: .95rem; }
  .group-head__sum { font-size: 1.55rem; }

  /* The name and the amount keep their own lines; only the bar gives way. */
  .share { grid-template-columns: minmax(0, 1fr) auto; row-gap: .25rem; }
  .share__track { grid-column: 1 / -1; grid-row: 2; }

  .welcome { padding: 2rem 0 3rem; }
  .welcome__title { font-size: 1.45rem; }
  .welcome__names { grid-template-columns: 1fr; gap: 0; }

  .thread { padding: .75rem .8rem; }
  .thread__write { flex-wrap: wrap; }
  .thread__write .input { flex: 1 1 100%; }
  .thread__write .btn { width: 100%; justify-content: center; }
}

/* ---------- 10. Signing in and waiting ---------- */

/* The dashboard stays blank until the account is known. */
body.booting .site-header,
body.booting main,
body.booting .site-footer { visibility: hidden; }

.booting-veil { display: none; }

body.booting .booting-veil {
  display: grid;
  position: fixed;
  inset: 0;
  z-index: 200;
  place-items: center;
  background: var(--surface-page);
}

.spinner {
  width: 28px; height: 28px;
  border-radius: 50%;
  border: 3px solid var(--brand-100);
  border-top-color: var(--brand-600);
  animation: spin .8s linear infinite;
}

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

.btn--google {
  background: var(--surface);
  color: var(--ink-900);
  border-color: var(--line-strong);
  box-shadow: var(--shadow-sm);
}
.btn--google:hover { background: var(--surface-sunken); }

.btn .g-logo { width: 18px; height: 18px; }

.btn:disabled { opacity: .6; cursor: not-allowed; }
.btn[data-busy="1"] { cursor: progress; }

.notice {
  margin-top: 1rem;
  padding: .7rem .9rem;
  border-radius: var(--r-sm);
  font-size: .9rem;
  font-weight: 700;
  max-width: 46ch;
}

.notice--problem {
  background: var(--danger-050);
  border: 1px solid var(--danger-200);
  color: var(--danger);
}

.notice--ok {
  background: var(--brand-100);
  border: 1px solid var(--brand-300);
  color: var(--brand-700);
}

/* Google hands us a profile picture; it replaces the initials. */

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