/* SpendApp's site: ink and lime (D41), light and dark. */
:root {
  --ink: #121212;
  --lime: #c8f03c;
  --lime-pressed: #a3c925;
  --bg: #f2f2f7;
  --card: #ffffff;
  --text: #121212;
  --muted: #5c5c63;
  --line: #e3e3e8;
  --accent-text: #4a6800;
  --coral-text: #b3402a;
  --positive: #12806c;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #000000;
    --card: #1c1c1e;
    --text: #f5f5f7;
    --muted: #a1a1a8;
    --line: #2c2c2e;
    --accent-text: #c8f03c;
    --coral-text: #ff9a82;
    --positive: #3cd6b5;
  }
}

* { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  /* Space for a scrollbar is kept on both sides whether or not the page scrolls, so the centred logo and cards sit in
     exactly the same place on short screens ("Which one's you?") and long ones (your share). */
  scrollbar-gutter: stable both-edges;
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 17px/1.4 -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
}
/* On iPhone, follow the text size set in Settings › Display & Brightness › Text Size (and larger accessibility sizes). */
@supports (font: -apple-system-body) {
  body { font: -apple-system-body; line-height: 1.4; }
}
main { max-width: 480px; margin: 0 auto; padding: 16px 16px max(32px, env(safe-area-inset-bottom)); }
h1 { font-size: 1.65em; margin: 2px 0; line-height: 1.15; }
h2 { font-size: 1em; margin: 0 0 8px; }
a { color: var(--accent-text); }

.brand {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  font-weight: 700; font-size: 1.3em; letter-spacing: -0.2px; margin: 8px 4px 12px;
}
.brand img { width: 34px; height: 34px; border-radius: 22.5%; display: block; }
.card { background: var(--card); border-radius: 18px; padding: 16px; margin-bottom: 12px; }
.hero { background: var(--lime); color: var(--ink); }
.hero .muted { color: rgba(18, 18, 18, 0.72); }
.muted { color: var(--muted); margin: 0; }
.small { font-size: 0.88em; margin-top: 10px; }
.amount { font-size: 2.6em; font-weight: 700; margin: 2px 0 6px; font-variant-numeric: tabular-nums; }

.items, .people { list-style: none; margin: 8px 0 0; padding: 0; }
.items li, .people li {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 8px 0; border-top: 1px solid var(--line); font-variant-numeric: tabular-nums;
}
.state.paid { color: var(--positive); font-weight: 600; }
.state.says_paid, .state.owed, .state.not_yet { color: var(--coral-text); }

.row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 0; border-top: 1px solid var(--line); }
.row:first-of-type { border-top: 0; }
.label { display: block; font-size: 0.76em; color: var(--muted); }
.value { font-weight: 600; word-break: break-all; }

button { font: inherit; border: 0; cursor: pointer; }
.copy {
  flex: none; min-height: 44px; min-width: 6.2em; padding: 0 16px; border-radius: 22px;   /* same width as "Copied ✓" */
  background: var(--bg); color: var(--text); font-weight: 600;
}
.primary {
  width: 100%; min-height: 54px; border-radius: 16px;
  background: var(--lime); color: var(--ink); font-weight: 700; font-size: 1.06em;
}
.primary:active { background: var(--lime-pressed); }
.primary:disabled { opacity: 0.6; }
.names { display: grid; gap: 8px; }
.name { min-height: 50px; border-radius: 14px; background: var(--bg); color: var(--text); font-weight: 600; text-align: left; padding: 0 16px; }
.done { margin: 0; font-weight: 600; color: var(--positive); }

footer { color: var(--muted); font-size: 0.82em; padding: 8px 4px; }
footer p { margin: 0 0 8px; }

/* Keyboard and switch-control users see where they are. */
button:focus-visible, a:focus-visible { outline: 3px solid var(--accent-text); outline-offset: 2px; }
/* Long names and amounts wrap rather than squeeze at large text sizes. */
.items li, .people li, .row { flex-wrap: wrap; }
.amount { overflow-wrap: anywhere; }

/* D89: one card of numbered steps, folded details, a warm ending. */
.hero .amount { margin: 4px 0 2px; }
.hero-actions { margin-top: 12px; }
.hero .copy { background: rgba(18, 18, 18, 0.1); color: var(--ink); }
.steps { list-style: none; counter-reset: step; margin: 0; padding: 0; }
.steps > li { counter-increment: step; position: relative; padding: 4px 0 14px 40px; }
.steps > li + li { border-top: 1px solid var(--line); padding-top: 14px; }
.steps > li::before {
  content: counter(step); position: absolute; left: 0; top: 4px;
  width: 26px; height: 26px; border-radius: 13px; background: var(--lime); color: var(--ink);
  font-weight: 700; font-size: 0.85em; display: flex; align-items: center; justify-content: center;
}
.steps > li + li::before { top: 14px; }
.step-title { display: block; font-weight: 600; margin-bottom: 4px; }
.steps .row { border-top: 0; padding: 4px 0; }
.row.quiet .value { font-weight: 500; }
.center { text-align: center; }
.fold { padding: 0; }
.fold > summary { padding: 16px; font-weight: 600; cursor: pointer; list-style: none; display: flex; justify-content: space-between; }
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::after { content: "›"; color: var(--muted); transition: transform 0.15s; }
.fold[open] > summary::after { transform: rotate(90deg); }
.fold > ul { margin: 0; padding: 0 16px 8px; }
.state.owed, .state.not_yet { color: var(--muted); }
.state.says_paid { color: var(--text); }
.thanks { text-align: center; padding: 28px 16px; }
.thanks .big { font-size: 1.6em; font-weight: 700; margin: 0 0 6px; }
@media (prefers-reduced-motion: reduce) { .fold > summary::after { transition: none; } }

/* Motion (D90): each movement explains what just happened; iOS-like easing; nothing with Reduce Motion. */
:root {
  --ease-out: cubic-bezier(0.32, 0.72, 0, 1);      /* iOS sheets and navigation */
  --spring: cubic-bezier(0.34, 1.4, 0.64, 1);      /* a gentle overshoot, for the one celebratory moment */
}
* { -webkit-tap-highlight-color: transparent; }
button { transition: transform 0.2s var(--ease-out), background-color 0.25s var(--ease-out), color 0.25s var(--ease-out); }
button:active:not(:disabled) { transform: scale(0.96); }
.copy.copied { background: color-mix(in srgb, var(--positive) 16%, var(--card)); color: var(--positive); }

.enter { animation: rise 0.45s var(--ease-out) both; animation-delay: calc(var(--i, 0) * 70ms); }
@keyframes rise { from { opacity: 0; transform: translateY(12px); } }

.fold[open] > ul { animation: reveal 0.3s var(--ease-out); }
@keyframes reveal { from { opacity: 0; transform: translateY(-6px); } }
.fold > summary::after { transition: transform 0.25s var(--ease-out); }

/* The share card keeps its place while the screen changes around it (morph); the title stays put on every change. */
.hero { view-transition-name: share; }
.brand { view-transition-name: brand; }

/* Push and pop, as iOS navigation: the new screen slides in from the right while the old one drifts a third of
   the way left and dims; back reverses it. The whole screen moves together, so the share card isn't morphed. */
html[data-vt="push"] :is(.hero, .flow, .not-yet, .thanks, .fold, main > footer),
html[data-vt="pop"] :is(.hero, .flow, .not-yet, .thanks, .fold, main > footer) { view-transition-name: none; }
html[data-vt="push"]::view-transition-old(root) { animation: 0.42s var(--ease-out) both drift-left; }
html[data-vt="push"]::view-transition-new(root) { animation: 0.42s var(--ease-out) both slide-from-right; }
html[data-vt="pop"]::view-transition-old(root) { animation: 0.42s var(--ease-out) both slide-to-right; z-index: 1; }
html[data-vt="pop"]::view-transition-new(root) { animation: 0.42s var(--ease-out) both drift-from-left; }
@keyframes drift-left { to { transform: translateX(-30%); opacity: 0.4; } }
@keyframes slide-from-right { from { transform: translateX(100%); } }
@keyframes slide-to-right { to { transform: translateX(100%); } }
@keyframes drift-from-left { from { transform: translateX(-30%); opacity: 0.4; } }
::view-transition-group(*) { animation-duration: 0.4s; animation-timing-function: var(--ease-out); }
/* Morph (D95): every block is its own group, so blocks glide to their new place and size instead of the whole page
   cross-fading (which ghosted two layouts over each other). Inside a group the old content isn't stretched: it keeps
   its size, pinned to the top and clipped by the card, and fades out before the new content fades in. */
.flow, .not-yet, .thanks { view-transition-name: steps; }
.fold.items-fold { view-transition-name: items; }
.fold.group-fold { view-transition-name: group; }
main > footer { view-transition-name: foot; }
/* When the guided steps come back ("I still need to pay"), the folds wait to arrive after the path, so they don't
   glide anywhere: they fade where they are, as part of the page. */
.flow.guided ~ .fold, .flow.guided ~ footer { view-transition-name: none; }
html:is([data-vt="morph"], [data-vt="swap"])::view-transition-old(*),
html:is([data-vt="morph"], [data-vt="swap"])::view-transition-new(*) {
  height: 100%; object-fit: none; object-position: top; overflow: hidden; mix-blend-mode: normal;
}
/* A block that only moves (or loses its last row, like the share card's Copy amount) keeps its old picture solid
   underneath while the new one fades in on top: no dip in brightness. The steps change completely, so the old ones
   fade out first and the new content fades in after, never two layouts at once. */
html[data-vt="morph"]::view-transition-old(*) { animation: none; }
html[data-vt="morph"]::view-transition-new(*) { animation: 0.2s var(--ease-out) both fade-in; }
/* Whatever has nowhere to go (it's gone from the new screen, or now waits to arrive later) fades out where it is. */
html[data-vt="morph"]::view-transition-old(root),
html[data-vt="morph"]::view-transition-old(*):only-child { animation: 0.15s ease-in both fade-out; }
/* Swap (preview only, to and from "Which one's you?"): every block fades out, then the new screen fades in. */
html[data-vt="swap"]::view-transition-old(*) { animation: 0.15s ease-in both fade-out; }
html[data-vt="swap"]::view-transition-new(*) { animation: 0.25s var(--ease-out) 0.14s both fade-in; }
html[data-vt="swap"]::view-transition-old(brand), html[data-vt="swap"]::view-transition-old(preview-bar) { animation: none; }
html[data-vt="swap"]::view-transition-new(brand), html[data-vt="swap"]::view-transition-new(preview-bar) {
  animation: 0.2s var(--ease-out) both fade-in;   /* the same picture: held, so the title never blinks */
}
html[data-vt="morph"]::view-transition-old(steps) { animation: 0.15s ease-in both fade-out; }
html[data-vt="morph"]::view-transition-new(steps) { animation: 0.25s var(--ease-out) 0.1s both fade-in; }
@keyframes fade-out { to { opacity: 0; } }
@keyframes fade-in { from { opacity: 0; } }

.tick { width: 52px; height: 52px; display: block; margin: 0 auto 10px; }
.tick-circle { fill: none; stroke: var(--positive); stroke-width: 3; stroke-dasharray: 151; stroke-dashoffset: 151;
  transform: rotate(-90deg); transform-origin: center;   /* draws from the top, like the step outlines */
  animation: draw 0.5s var(--ease-out) 0.15s forwards; }
.tick-mark { fill: none; stroke: var(--positive); stroke-width: 4; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 36; stroke-dashoffset: 36; animation: draw 0.3s var(--ease-out) 0.5s forwards; }
@keyframes draw { to { stroke-dashoffset: 0; } }
/* The words arrive as the tick lands, together, so there's no gap waiting above a line that's already there. */
.thanks .big { animation: pop 0.45s var(--spring) 0.5s both; }
.thanks .big + .muted { animation: arrive 0.4s var(--ease-out) 0.58s both; }
@keyframes pop { from { opacity: 0; transform: scale(0.92); } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .tick-circle, .tick-mark { stroke-dashoffset: 0; }
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

/* Preview toolbar: only on the local server (bill.js), never on the real site. */
.preview-bar {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: max(12px, env(safe-area-inset-bottom));
  display: flex; gap: 4px; padding: 6px; border-radius: 18px; background: rgba(18, 18, 18, 0.88);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); z-index: 10; max-width: calc(100% - 24px); overflow-x: auto;
}
.preview-bar button {
  min-height: 36px; padding: 0 10px; border-radius: 12px; background: transparent; color: #f5f5f7;
  font-size: 0.8em; font-weight: 600; white-space: nowrap;
}
.preview-bar button.on { background: var(--lime); color: var(--ink); }
body:has(.preview-bar) main { padding-bottom: 96px; }
.preview-bar { view-transition-name: preview-bar; }   /* stays put while screens slide */

/* D91: the steps as a guided path. Each step is its own card with a numbered badge; a line joins the share card to
   step 1, 2 and 3. Guided: the line grows into each card and carries on around its outline, one stroke. */
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.flow { margin-bottom: 12px; }
.flow .step { position: relative; display: flex; gap: 12px; margin: 0; }
.flow .step { transition: box-shadow 0.35s var(--ease-out); }
.flow .step.next { box-shadow: 0 0 0 4px color-mix(in srgb, var(--lime) 22%, transparent); }
.flow .step h2 { margin: 3px 0 6px; }
.step-body { flex: 1; min-width: 0; }
.step-body .row { border-top: 0; padding: 4px 0; }
.badge {
  flex: none; width: 28px; height: 28px; border-radius: 14px; background: var(--lime); color: var(--ink);
  font-weight: 700; font-size: 0.9em; display: flex; align-items: center; justify-content: center;
}

/* The border, as an outline that can be drawn: lime for the step to do next, grey otherwise. */
.outline { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.outline path { fill: none; stroke: var(--line); stroke-width: 3; stroke-linecap: round; transition: stroke 0.35s var(--ease-out); }
.step.next .outline path { stroke: var(--lime); }

/* The line: thick, rounded, lime, down the middle, meeting each card at its top centre. */
:root { --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); }
.connector { height: 26px; display: flex; justify-content: center; }
.connector i { display: block; width: 4px; height: 100%; border-radius: 2px; background: var(--lime); }
.hero:has(+ .flow) { margin-bottom: 0; }   /* the line joins the share card straight to step 1 */

/* Guided arrival: line, then outline, then content, in one movement. */
/* The line speeds into each card and the outline slows as its halves meet; the next line leaves before they finish. */
.guided .connector i { transform-origin: top; animation: grow 0.3s cubic-bezier(0.45, 0, 0.75, 0.6) both; animation-delay: var(--d); }
.guided .step { animation: fill 0.5s var(--ease-in-out) both; animation-delay: var(--d); }
.guided .step .outline path { stroke-dasharray: 1; stroke-dashoffset: 1; animation: trace 0.5s cubic-bezier(0.3, 0.6, 0.4, 1) forwards; animation-delay: var(--d); }
.guided .step > :not(.outline) { animation: arrive 0.4s var(--ease-in-out) both; animation-delay: calc(var(--d) + 0.2s); }
@keyframes grow { from { transform: scaleY(0); } }
@keyframes trace { to { stroke-dashoffset: 0; } }
@keyframes fill { from { background-color: transparent; box-shadow: none; } }
@keyframes arrive { from { opacity: 0; transform: translateY(8px); } }
.flow.guided ~ .fold { animation: arrive 0.42s var(--ease-out) both; animation-delay: var(--after-flow, 0s); }

/* The page's content sits in the middle of the screen when it's shorter than the screen (the thank-you, "Which one's
   you?"), with SpendApp at the top and the small print at the bottom; longer pages scroll as usual. Every card is
   laid out from the start (just invisible while arriving), so nothing shifts as the steps draw in. */
main { min-height: 100svh; display: flex; flex-direction: column; }
main > .brand { margin-bottom: auto; }
main > footer { margin-top: auto; padding-top: 12px; }
main > .brand + * { margin-top: 0; }

/* Readability: names you can't miss, labels that aren't tiny, step titles a touch larger; all follow Text Size. */
.name {
  min-height: 56px; font-size: 1.1em; display: flex; align-items: center; justify-content: space-between;
}
.name::after { content: "›"; color: var(--muted); font-size: 1.2em; font-weight: 400; }
.label { font-size: 0.85em; }
.flow .step h2 { font-size: 1.08em; }
.hero h1 { font-size: 1.9em; }
/* Short screens: less empty space between the logo and the content. */
main { justify-content: flex-start; }
main > .brand { margin-bottom: 20px; }   /* content sits just under the logo, the same on every screen */
main > footer { margin-top: auto; }
/* In dark mode the icon's dark tile would vanish into the background: a faint edge, as iOS gives dark icons. */
@media (prefers-color-scheme: dark) {
  .brand img { box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.14); }
}

/* "Which one's you?" for one hand (D92): the question stays up top to read, the names sit at the bottom in thumb
   reach, and each name is a big, well-spaced target with its initial so nobody taps someone else's. */
main > .choose { margin-top: auto; }
main:has(> .choose) > footer { margin-top: 12px; }
.choose { padding: 14px; }
.choose .names { gap: 14px; }
.choose .name { min-height: 64px; font-size: 1.2em; gap: 14px; padding: 0 18px 0 12px; border-radius: 18px; }
.choose .name-text { flex: 1; }
.initial {
  flex: none; width: 40px; height: 40px; border-radius: 20px; display: flex; align-items: center; justify-content: center;
  background: var(--lime); color: var(--ink); font-weight: 700; font-size: 0.9em;
}

/* One rhythm inside every card: even gaps between title, details and buttons, no stray margins. */
.hero > * { margin: 0; }
.hero > * + * { margin-top: 6px; }
.hero .amount { margin-top: 2px; }
.hero .hero-actions { margin-top: 14px; }
.step-body { display: flex; flex-direction: column; gap: 8px; }
.step-body > * { margin: 0; }
.flow .step h2 { margin: 2px 0 0; }
.step-body .row { padding: 0; }
.step-body .small { margin: 0; }

/* "Not yet" (D93): a heads-up, not an error: amber, never red. Its choices sit low, in thumb reach. */
.not-yet { display: flex; flex-direction: column; flex: 1; margin-top: 12px; }
.notice { display: flex; flex-direction: column; gap: 10px; border: 1.5px solid color-mix(in srgb, #f5a524 55%, transparent);
  background: color-mix(in srgb, #f5a524 10%, var(--card)); }
.notice > * { margin: 0; }
.notice-head { display: flex; align-items: center; gap: 10px; }
.notice-head h2 { margin: 0; font-size: 1.08em; }
.notice-icon { flex: none; width: 32px; height: 32px; border-radius: 16px; display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, #f5a524 25%, transparent); font-size: 0.95em; }
.actions { margin-top: auto; padding-top: 16px; display: flex; flex-direction: column; gap: 12px; }
.secondary {
  width: 100%; min-height: 54px; border-radius: 16px; background: var(--card); color: var(--text);
  font-weight: 600; font-size: 1.02em; border: 1.5px solid var(--line);
}
main:has(.not-yet) > footer { margin-top: 12px; }
main:has(.not-yet) .hero { margin-bottom: 0; }
.not-yet { margin-bottom: 16px; }

/* Review fixes (D95). The details and small print have one fade-in (after the path is drawn), never two. */
.flow.guided ~ footer { animation: arrive 0.42s var(--ease-out) both; animation-delay: var(--after-flow, 0s); }
.hero .copy { min-width: 8.6em; }   /* "Copy amount" and "Copied ✓" the same width */
