/* Banknote UI: silver and chrome on charcoal (steel on pale grey by day),
   Archivo throughout. Display type runs wide and light, like an engraved
   denomination; one guilloche rosette (static/guilloche.svg) sits behind
   whatever the page is about. Light and dark share every rule; only the
   tokens move. */

/* Archivo variable (SIL OFL 1.1, from @fontsource-variable/archivo v5.3.0;
   license in static/fonts/). One file carries weight 100-900 and width
   62-125. */
@font-face {
  font-family: "Archivo";
  src: url("fonts/archivo-variable.woff2") format("woff2-variations"),
    url("fonts/archivo-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: light;
  --ground: #E9ECEF;
  --surface: #F3F5F7;
  --ink: #121417;                         /* 15.6:1 on ground */
  --ink-2: #5B6068;                       /* 5.3:1 */
  --rule: rgba(18, 20, 23, 0.14);         /* 1px hairlines (decorative) */
  --accent: #4E5864;                      /* steel, 6.1:1 */
  --on-accent: #F3F5F7;                   /* 10:1 at the metal's lightest */
  /* Primary buttons are brushed titanium: a flat-reading fill whose only
     metal is a soft diagonal sheen (no hard horizon, no border). Graphite
     by day, silver by night. */
  --chrome: linear-gradient(115deg, #1B1E22 0%, #2C3137 38%, #3B4148 50%, #2A2E34 62%, #16191C 100%);
  --lift: 0 10px 24px -14px rgba(18, 20, 23, 0.55);
  --rosette: 0.2;                         /* guilloche strength */
  --shadow: 0 1px 0 rgba(255, 255, 255, 0.6) inset, 0 24px 60px -30px rgba(18, 20, 23, 0.3);
  --backdrop: rgba(18, 20, 23, 0.6);
  --qr-ground: #F2F4F6;                   /* QR quiet zone stays light in both themes */
  --font: "Archivo", "Helvetica Neue", Helvetica, sans-serif;
  --wide: 125%;                           /* display width */
  --gutter: 64px;
  --top: 36px;
  --bottom: 56px;
  --nav-h: 44px;
}

/* Dark: follows the system until the visitor flips the toggle, which pins
   <html data-theme> (set before first paint in base.html). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --ground: #0F1114;                    /* charcoal */
    --surface: #171A1E;
    --ink: #ECEEF1;                       /* 16.3:1 */
    --ink-2: #959BA4;                     /* 6.8:1 */
    --rule: rgba(236, 238, 241, 0.12);
    --accent: #BCC3CC;                    /* silver, 10.6:1: between ink and ink-2 */
    --on-accent: #0F1114;                 /* 10.6:1 at the metal's darkest */
    --chrome: linear-gradient(115deg, #C4CAD2 0%, #E9ECEF 38%, #F7F8FA 50%, #E3E7EB 62%, #C4CAD2 100%);
    --lift: 0 10px 28px -14px rgba(0, 0, 0, 0.9);
    --rosette: 0.16;
    --shadow: 0 1px 0 rgba(236, 238, 241, 0.04) inset, 0 30px 70px -30px rgba(0, 0, 0, 0.7);
    --backdrop: rgba(5, 6, 7, 0.72);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --ground: #0F1114;
  --surface: #171A1E;
  --ink: #ECEEF1;
  --ink-2: #959BA4;
  --rule: rgba(236, 238, 241, 0.12);
  --accent: #BCC3CC;
  --on-accent: #0F1114;
  --chrome: linear-gradient(115deg, #C4CAD2 0%, #E9ECEF 38%, #F7F8FA 50%, #E3E7EB 62%, #C4CAD2 100%);
  --lift: 0 10px 28px -14px rgba(0, 0, 0, 0.9);
  --rosette: 0.16;
  --shadow: 0 1px 0 rgba(236, 238, 241, 0.04) inset, 0 30px 70px -30px rgba(0, 0, 0, 0.7);
  --backdrop: rgba(5, 6, 7, 0.72);
}

* { box-sizing: border-box; }

/* Author display rules (e.g. button's inline-flex) otherwise beat the UA
   [hidden] rule and un-hide things app.js hid. */
[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font);
  font-weight: 400;
  font-size: 17px;
  line-height: 1.5;
  font-variant-numeric: tabular-nums lining-nums;
  -webkit-font-smoothing: antialiased;
  transition: background-color 300ms ease, color 300ms ease;
}
::selection { background: var(--accent); color: var(--on-accent); }

h1, h2, h3 { font-family: var(--font); font-size: inherit; font-weight: inherit; margin: 0; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
a:hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 6px; text-decoration-color: var(--accent); }
/* Links inside running copy stay visibly links. */
p a, li a, dd a { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; text-decoration-color: var(--accent); }
p a:hover, li a:hover, dd a:hover { color: var(--accent); }

.muted { color: var(--ink-2); }
.fine { font-size: 15px; color: var(--ink-2); }

/* The wordmark and display headings: wide, light, slightly open. */
.brand {
  font-weight: 600;
  font-stretch: var(--wide);
  letter-spacing: 0.04em;
  flex-grow: 1;
}

/* ---- Frame ----
   One <nav class="rail"> is three things: a 196px rail beside the page from
   900px up; hidden behind the slim header's Menu button when the visitor
   hides it (html[data-rail="hidden"], stored, restored before first paint);
   and the full-screen menu on phones (html[data-menu-open]). On desktop the
   rail slides by its left margin and the page, a flex sibling, narrows from
   the left as it goes: live layout, horizontal only, right edge fixed. */

body { display: flex; align-items: flex-start; }
.page { flex: 1; min-width: 0; overflow-x: clip; }

.rail {
  display: none;
  flex: none;
  flex-direction: column;
  width: 196px;
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow-y: auto;
  padding: var(--top) 16px var(--bottom) 32px;
  border-right: 1px solid var(--rule);
  font-size: 16px;
}
.rail-head { display: flex; align-items: center; gap: 8px; min-height: 44px; }
.rail a { display: flex; align-items: center; min-height: 40px; color: var(--ink-2); }
.rail a:hover { color: var(--ink); text-decoration: none; }
.rail .brand { color: var(--ink); }
.rail-links { margin-top: 56px; display: flex; flex-direction: column; }
/* The page you are on: ink, with a short silver rule just before it. */
.rail-links a[aria-current="page"] { color: var(--ink); position: relative; }
.rail-links a[aria-current="page"]::before {
  content: "";
  position: absolute;
  left: -20px;
  width: 12px;
  height: 1px;
  background: var(--accent);
}
.rail-foot { margin-top: auto; padding-top: 40px; display: flex; flex-direction: column; align-items: flex-start; }
.rail [data-menu-close] { display: none; }

/* Text buttons in the frame (Menu, Close, Hide menu, Sign in/out, theme). */
.rail button, .topbar button {
  height: auto;
  min-height: 44px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--ink-2);
  font-size: inherit;
  font-weight: 400;
  font-stretch: 100%;
  letter-spacing: 0;
  gap: 10px;
  box-shadow: none;
}
.rail button:hover, .topbar button:hover { background: transparent; color: var(--ink); }
.topbar [data-menu-toggle] { color: var(--ink); }
.theme-toggle svg { flex: none; transition: transform 400ms cubic-bezier(0.2, 0.8, 0.2, 1); }
:root[data-theme-now="dark"] .theme-toggle svg { transform: rotate(180deg); }
/* Hide, Menu and Close are the same 44px square with its icon centred 22px
   in from the square's edge, hung 12px into the gutter so the icon sits on
   the content edge: toggling moves neither the pointer's target nor the
   brand. On desktop that is the left edge (Hide and Menu share the panel
   icon); on phones the right (Menu's two lines turn into Close's X). */
.corner { margin-left: -12px; gap: 0 !important; justify-content: flex-start; }
.corner-icon { display: grid; place-items: center; width: 44px; height: 44px; flex: none; }
.corner-icon svg { grid-area: 1 / 1; }
.topbar .icon-menu { display: none; }
.icon-close path { transform-box: view-box; transform-origin: 50% 50%; }
.icon-close path:first-child { transform: translateY(0) rotate(45deg); }
.icon-close path:last-child { transform: translateY(0) rotate(-45deg); }
.rail-hide { color: var(--ink-2) !important; }
.rail-hide:hover { color: var(--ink) !important; }

/* The slim header: phones, and desktop with the rail hidden. */
.topbar, main {
  width: 100%;
  max-width: calc(1312px + var(--page-left, var(--gutter)) + var(--gutter));
  margin: 0 auto;
  padding-left: var(--page-left, var(--gutter));
  padding-right: var(--gutter);
}
.topbar {
  padding-top: var(--top);
  display: flex;
  align-items: center;
  gap: 24px;
  font-size: 16px;
  min-height: calc(var(--top) + var(--nav-h));
}
.topbar a { display: inline-flex; align-items: center; min-height: var(--nav-h); }
[data-menu-toggle] { justify-content: flex-start; }

/* The poster fills the first screen. */
main { padding-bottom: 96px; }
.poster {
  min-height: calc(100svh - var(--top) - var(--nav-h));
  padding-bottom: var(--bottom);
  display: flex;
  flex-direction: column;
}

@media (min-width: 900px) {
  /* The same vertical layout either way: the slim header floats over the
     page's top margin instead of taking a row, so nothing moves up or down
     when the rail comes or goes. */
  :root { --nav-h: 0px; --page-left: 64px; --top-max: 96px; --slide: 220ms; }
  .rail {
    display: flex;
    margin-left: 0;
    transition: margin-left var(--slide) linear;
  }
  html[data-rail="hidden"] .rail {
    margin-left: -196px;
    visibility: hidden;   /* out of tab order once it is off screen */
    transition: margin-left var(--slide) linear, visibility 0s linear var(--slide);
  }
  /* Open: next to the rail. Hidden: centred in the window past the page's
     max width. Both are lengths, so the margin slides between them. */
  main {
    padding-top: var(--top);
    margin-left: 0;
    transition: margin-left var(--slide) linear;
  }
  html[data-rail="hidden"] main {
    margin-left: max(0px, calc((100% - 1312px - var(--page-left) - var(--gutter)) / 2));
  }
  /* The header's first row mirrors the rail's top row, and appears once the
     rail has gone (it is hidden at once when the rail returns). */
  .topbar {
    --nav-h: 44px;
    position: absolute;
    top: 0;
    left: 0;
    width: auto;
    max-width: none;
    margin: 0;
    padding-left: 32px;
    gap: 8px;
    opacity: 0;
    visibility: hidden;
  }
  html[data-rail="hidden"] .topbar {
    opacity: 1;
    visibility: visible;
    transition: opacity 140ms linear var(--slide);
  }
  .topbar .corner-label { display: none; }
}

/* Phones: Menu opens the rail as a full-screen sheet. */
@media (max-width: 899px) {
  html[data-menu-open] body { overflow: hidden; }
  html[data-menu-open] .rail {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 50;
    width: auto;
    height: auto;
    padding: var(--top) var(--gutter) var(--bottom);
    border: 0;
    background: var(--ground);
    font-size: 15px;
  }
  html[data-menu-open] .rail [data-menu-close] { display: inline-flex; }
  /* Brand on the left; Menu, then Close over it, on the right. */
  .topbar [data-menu-toggle], html[data-menu-open] .rail [data-menu-close] {
    order: 2;
    margin: 0 -12px 0 auto;
    flex-direction: row-reverse;
  }
  .topbar .icon-panel { display: none; }
  .topbar .icon-menu { display: block; }
  html[data-menu-open] .rail-links { margin-top: 36px; border-top: 1px solid var(--accent); }
  html[data-menu-open] .rail-links a {
    min-height: 76px;
    border-bottom: 1px solid var(--rule);
    font-size: 36px;
    font-weight: 250;
    font-stretch: var(--wide);
    letter-spacing: -0.01em;
  }
  html[data-menu-open] .rail-links a[aria-current="page"] { font-weight: 500; }
  html[data-menu-open] .rail-links a[aria-current="page"]::before { display: none; }
  html[data-menu-open] .rail-hide { display: none; }
  html[data-menu-open] .rail-foot button { font-size: 16px; }
}

/* ---- Phone menu motion (app.js withMotion; html[data-motion] only while
   one runs: sheet-open, sheet-close). The desktop rail is not here: it
   slides in live layout (see Frame). ---- */
:root {
  --drawer: 440ms;
  --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);   /* fast start, long settle */
}
.rail { view-transition-name: rail; }
html[data-motion]::view-transition-group(*) {
  animation-duration: var(--drawer);
  animation-timing-function: var(--ease-drawer);
}
html[data-motion]::view-transition-old(root),
html[data-motion]::view-transition-new(root) { animation-duration: 200ms; }

/* Phones: the sheet unrolls from the top edge and rolls back up, quicker
   on the way out. */
html[data-motion="sheet-open"]::view-transition-new(rail) { animation: 560ms cubic-bezier(0.16, 1, 0.3, 1) both curtain-down; }
html[data-motion="sheet-close"]::view-transition-old(rail) { animation: 280ms cubic-bezier(0.7, 0, 0.84, 0) both curtain-up; }
@keyframes curtain-down { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0); } }
@keyframes curtain-up { from { clip-path: inset(0); } to { clip-path: inset(0 0 100% 0); } }

/* On open only, the sheet's contents follow the curtain down, one after
   another. */
html[data-motion="sheet-open"] :is(.rail-links, .rail-foot) > * {
  animation: 440ms var(--ease-drawer) both item-in-up;
  animation-delay: calc(80ms + var(--i, 0) * 35ms);
}
.rail-links > :nth-child(2) { --i: 1; }
.rail-links > :nth-child(3) { --i: 2; }
.rail-foot > :nth-child(1) { --i: 3; }
.rail-foot > :nth-child(2) { --i: 4; }
.rail-foot > :nth-child(n+3) { --i: 5; }
@keyframes item-in-up { from { opacity: 0; translate: 0 24px; } }

/* The Menu lines turn into the Close X in the same spot. */
html[data-motion="sheet-open"] .icon-close path { animation: 460ms var(--ease-drawer) both; }
html[data-motion="sheet-open"] .icon-close path:first-child { animation-name: x-down; }
html[data-motion="sheet-open"] .icon-close path:last-child { animation-name: x-up; }
@keyframes x-down { from { transform: translateY(-5px) rotate(0deg); } }
@keyframes x-up { from { transform: translateY(5px) rotate(0deg); } }

/* ---- Buttons: primary (brushed metal), .ghost (hairline outline). On
   hover the sheen glides across, as light does over metal. Medium weight,
   slightly open: a heavy label on a grey slab reads as a system button. ---- */
button {
  font-family: var(--font);
  font-size: 16px;
  font-weight: 500;
  font-stretch: 112%;
  letter-spacing: 0.04em;
  height: 52px;
  padding: 0 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: var(--on-accent);
  background: var(--chrome) 100% 0 / 220% 100%;
  border: 1px solid transparent;
  border-radius: 2px;
  box-shadow: var(--lift);
  cursor: pointer;
  white-space: nowrap;
  transition: background-position 700ms var(--ease-drawer), background-color 150ms ease, color 150ms ease, border-color 150ms ease;
}
button:hover { background-position: 0 0; }
.ghost { background: transparent; color: var(--ink); border-color: var(--rule); padding: 0 24px; box-shadow: none; }
.ghost:hover { background: transparent; border-color: var(--accent); color: var(--accent); }
button:disabled, button:disabled:hover {
  box-shadow: none;
  background: transparent;
  color: var(--ink-2);
  border: 1px dashed var(--ink-2);
  cursor: not-allowed;
}

input {
  font-family: var(--font);
  font-size: 17px;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--ink-2);
  border-radius: 0;
  min-height: 48px;
  padding: 0 2px;
  width: 100%;
  transition: border-color 150ms ease;
}
input:hover { border-bottom-color: var(--ink); }
input:focus { border-bottom-color: var(--accent); }
input::placeholder { color: var(--ink-2); }
/* Amounts are typed, not nudged: no spinner arrows. */
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
input::-webkit-inner-spin-button, input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
label { color: var(--ink-2); font-size: 15px; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
input:focus-visible { outline: none; }

/* ---- The rosette: a guilloche behind the one thing a page is about,
   painted in the accent through a mask so it follows the theme. It draws in
   once on load. ---- */
.engraved { position: relative; isolation: isolate; }
.engraved::before {
  content: "";
  position: absolute;
  z-index: -1;
  width: var(--rosette-size, 760px);
  aspect-ratio: 1;
  left: var(--rosette-x, 50%);
  top: var(--rosette-y, 50%);
  translate: -50% -50%;
  background: var(--accent);
  -webkit-mask: url("guilloche.svg") center / contain no-repeat;
  mask: url("guilloche.svg") center / contain no-repeat;
  opacity: var(--rosette);
  pointer-events: none;
  animation: rosette-in 1600ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
@keyframes rosette-in { from { opacity: 0; rotate: -12deg; scale: 0.96; } }

/* ---- Home ---- */
.signin-block { margin-top: var(--top-max, 120px); display: flex; flex-direction: column; gap: 28px; align-items: flex-start; }
.signin-title {
  font-size: 64px;
  font-weight: 250;
  font-stretch: var(--wide);
  letter-spacing: -0.02em;
  line-height: 1.05;
  max-width: 18ch;
  overflow-wrap: anywhere;
}
/* Signed out: a certificate in the middle of the first screen (auto margins
   centre it in the .poster flex column): double silver rule, rosette behind
   the wordmark. */
.signin-block.signin-card {
  margin: auto;
  align-items: center;
  text-align: center;
  gap: 24px;
  padding: 72px 56px 64px;
  width: min(600px, 100%);
  border: 1px solid color-mix(in srgb, var(--accent) 70%, transparent);
  outline: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  outline-offset: -8px;
  background: color-mix(in srgb, var(--surface) 70%, transparent);
  box-shadow: var(--shadow);
  --rosette-size: 860px;
}
.signin-card .signin-title { font-size: clamp(40px, 7vw, 64px); max-width: none; letter-spacing: 0.01em; font-weight: 300; }
.signin-card button { margin-top: 8px; min-width: 240px; }
.lede { font-size: 19px; line-height: 1.5; max-width: 36ch; }

/* Signed in: the greeting, then the balance as the page's figure. */
.welcome { --rosette-size: 820px; --rosette-x: 78%; --rosette-y: 62%; }
/* The greeting stays on one line at every width: it shrinks with the viewport, and only a very long username is clipped with an ellipsis. */
.welcome .signin-title { font-size: clamp(14px, 3.6vw, 48px); white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.home-balance { display: flex; flex-direction: column; gap: 4px; margin-top: 32px; padding-top: 28px; border-top: 1px solid var(--rule); min-width: min(420px, 100%); }
.home-balance .balance { font-size: 56px; }
.home-balance a { color: var(--ink-2); }
.home-balance a:hover { color: var(--accent); }

.username-form { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; width: min(440px, 100%); }
.username-form input { font-size: 26px; font-weight: 400; font-stretch: 112%; }
.username-form button { margin-top: 12px; }
/* The one thing to get right here, so it reads as an instruction. */
.username-form .note { font-weight: 600; padding-left: 14px; border-left: 2px solid var(--accent); margin-top: 4px; }

/* ---- Figures: an amount set wide and light, its unit small beside it. ---- */
.balance {
  font-size: clamp(56px, 8vw, 112px);
  font-weight: 250;
  font-stretch: var(--wide);
  letter-spacing: -0.02em;
  line-height: 1;
  white-space: nowrap;
}
.unit { font-size: 0.24em; font-weight: 500; letter-spacing: 0.12em; color: var(--accent); margin-left: 0.3em; vertical-align: 0.2em; }

/* ---- Tables: 1px ink rule on top, hairlines between rows. Always inside
   a .table-wrap so wide rows scroll, not the page. ---- */
.table-wrap { overflow-x: auto; }
.grid {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
  border-top: 1px solid var(--ink);
}
.grid th, .grid td {
  text-align: left;
  padding: 14px 24px 14px 0;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}
.grid th:last-child, .grid td:last-child { padding-right: 0; }
.grid th { color: var(--ink-2); font-weight: 400; font-size: 14px; padding-top: 12px; padding-bottom: 12px; }
.grid .num { text-align: right; }
.grid .when, .grid .id { color: var(--ink-2); }
.grid .credit { color: var(--accent); }
.grid button { height: 36px; padding: 0 16px; font-size: 14px; }
/* A payout D47 didn't confirm. */
.flag { color: var(--accent); font-weight: 600; }
/* A table with no rows yet: say so instead of a bare header. */
.table-wrap:has(tbody:empty)::after {
  content: "Nothing here yet.";
  display: block;
  padding: 16px 0;
  color: var(--ink-2);
  font-size: 15px;
}

/* ---- Wallet ---- */
.balance-hero { --rosette-size: 900px; --rosette-x: 82%; --rosette-y: 46%; padding-bottom: 24px; }
.balance-hero .balance { margin-bottom: 20px; }
/* Deposit and withdraw side by side: the two ways money moves. */
.moves { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(360px, 100%), 1fr)); gap: 24px; }
.move {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 36px 36px 32px;
  box-shadow: var(--shadow);
}
.panel .move h2 { font-size: 26px; margin-bottom: 12px; }
.move > p.muted { font-size: 15px; line-height: 1.55; margin-bottom: 28px; min-height: 3.1em; }
.amount-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 16px; margin-bottom: 12px; }
.amount-form label { flex-basis: 100%; }
.amount-form input { flex: 1 1 160px; width: auto; font-size: 26px; font-weight: 400; font-stretch: 112%; }
.deposit-pending { margin: 20px 0 16px; }
/* Same light quiet zone as the login QR, so it scans in both themes. */
.deposit-pending img { background: var(--qr-ground); padding: 10px; display: block; margin-bottom: 12px; border-radius: 2px; }

/* ---- Login dialog ---- */
dialog {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-top: 1px solid var(--accent);
  border-radius: 2px;
  padding: 40px 40px 12px;
  box-shadow: 0 40px 100px -20px rgba(0, 0, 0, 0.55);
  text-align: center;
  /* Fixed width, not max-width: the D47 and owner panels differ in natural
     width, and a shrink-to-fit dialog would jump when they swap. */
  width: min(460px, calc(100vw - 32px));
}
/* Three parts, centred: what to do, the code to scan (with its live
   status), and a footer row for the two ways out. */
dialog h2 { font-size: 28px; font-weight: 300; font-stretch: var(--wide); letter-spacing: 0; line-height: 1.15; margin-bottom: 12px; }
dialog p { margin-bottom: 14px; }
[data-d47-panel] > p.muted { font-size: 15px; line-height: 1.55; max-width: 36ch; margin: 0 auto 28px; text-wrap: balance; }
.scan { display: flex; flex-direction: column; align-items: center; gap: 14px; margin-bottom: 24px; }
.scan .status { margin: 0; font-size: 14px; color: var(--ink-2); }
.app-link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  border: 1px solid var(--rule);
  border-radius: 2px;
  font-weight: 500;
  transition: border-color 150ms ease, color 150ms ease;
}
.app-link:hover { text-decoration: none; border-color: var(--accent); color: var(--accent); }
.dialog-foot {
  display: flex;
  justify-content: space-between;
  margin-top: 28px;
  padding-top: 4px;
  border-top: 1px solid var(--rule);
}
/* Footer actions are quiet text: Owner sign-in is for one person, and
   Cancel only closes. */
.dialog-foot button {
  height: auto;
  min-height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
  font-size: 15px;
  font-weight: 400;
  font-stretch: 100%;
  letter-spacing: 0;
  color: var(--ink-2);
}
.dialog-foot button:hover { background: transparent; color: var(--accent); }
.dialog-foot [data-close] { margin-left: auto; color: var(--ink); }
dialog::backdrop { background: var(--backdrop); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
/* qrcode's SVG has no background of its own: give it a light quiet zone
   so it stays scannable in both themes. */
dialog img[data-qr] { background: var(--qr-ground); padding: 10px; display: block; border-radius: 2px; }
/* No QR yet (login unavailable): no empty light box / broken-image icon. */
dialog img[data-qr]:not([src]) { display: none; }
dialog input { margin-bottom: 12px; font-size: 26px; letter-spacing: 0.3em; text-align: center; }
[data-owner-form] button { width: 100%; margin-top: 4px; }

/* ---- Secondary pages (wallet, owner) ---- */
.panel { margin-top: 72px; }
.panel > h1 {
  font-size: 22px;
  font-weight: 500;
  font-stretch: var(--wide);
  letter-spacing: 0.04em;
  color: var(--ink-2);
  margin-bottom: 28px;
}
.panel h2 { font-size: 30px; font-weight: 300; font-stretch: var(--wide); letter-spacing: -0.01em; line-height: 1.15; margin: 0 0 20px; }
/* :where keeps this at element weight, so classed paragraphs (.balance)
   set their own size. */
:where(.panel) > p { max-width: 64ch; font-size: 17px; line-height: 1.55; margin-bottom: 20px; }
.panel > .table-wrap + h2 { margin-top: 64px; }
/* The owner's standing: one sentence set as a figure. */
.standing { font-size: 30px; font-weight: 300; font-stretch: var(--wide); line-height: 1.3; max-width: 30ch; margin-bottom: 0; }

@media (max-width: 600px) {
  :root { --gutter: 16px; --top: 20px; --bottom: 24px; }
  .topbar { gap: 18px; font-size: 15px; }
  body { font-size: 16px; }
  .signin-block { margin-top: 40px; }
  .signin-title { font-size: 34px; }
  .signin-block.signin-card { padding: 48px 24px 40px; --rosette-size: 460px; }
  .signin-card button { min-width: 0; width: 100%; }
  .lede { font-size: 16px; max-width: none; }
  button { height: 50px; font-size: 15px; padding: 0 22px; }
  .ghost { padding: 0 18px; }
  .panel { margin-top: 40px; }
  .panel h2 { font-size: 24px; }
  .balance { font-size: 48px; }
  .home-balance .balance { font-size: 40px; }
  .balance-hero { --rosette-size: 520px; --rosette-x: 90%; }
  .move { padding: 28px 20px 24px; }
  .standing { font-size: 22px; }
  .amount-form input, .amount-form button { flex-basis: 100%; }
}

/* CSS-only: JS motion checks prefers-reduced-motion itself (app.js
   withMotion). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* Between pages: the rail holds still (named in Frame), the rest
   cross-fades. Browsers without cross-document View Transitions navigate
   as before. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}
html:not([data-motion])::view-transition-old(root),
html:not([data-motion])::view-transition-new(root) { animation-duration: 180ms; }
