/* ============================================================
   SEMANTIC TOKENS
   Components consume meaning, never a hex. Five themes, each with a
   light and a dark face; appearance (light/dark/system) is a
   separate axis from theme. Dark faces never use pure black.
   ============================================================ */
:root{
  /* ---- surfaces + ink ---- */
  --background:#F7F5F0; --surface:#FFFFFF; --surface-raised:#FCFBF8; --surface-muted:#F0EDE7;
  --text-primary:#19171D; --text-secondary:#5F5A65; --text-tertiary:#6E6876;
  --border:#D9D4DD; --border-strong:#BEB7C4;
  /* ---- brand ---- */
  --primary:#5B3FD6; --primary-hover:#4D33C2; --primary-active:#432CB0; --on-primary:#FFFFFF;
  --primary-ink:#432CB0;              /* accessible primary as TEXT on light */
  --accent:#D95E32; --accent-soft:#F7DDD3;
  --focus-ring:#7B63EA;
  /* ---- semantic money ---- */
  --income:#1E7A50; --commitment:#3B5BA5; --flexible:#8A4FBF; --saving:#12796A; --debt:#A8631B;
  --warning:#8F5A0A; --warning-soft:#F9EFDD; --warn-hot:#B4551F;
  --danger:#B3362B;  --danger-soft:#F9E9E7;
  --success:#1E7A50; --success-soft:#E7F3EC;
  --shadow:rgba(25,23,29,.09);
  --logo-orb:var(--accent);
  --illustration-one:var(--primary); --illustration-two:var(--accent); --illustration-three:var(--income);
  --gradient-pride:linear-gradient(100deg,#E14855,#E87924,#D7A900,#2B9A66,#3478D4,#7655D9,#B64C96);
  --cat-1:#6E5BA6; --cat-2:#3F7C8C; --cat-3:#C08A4E; --cat-4:#5A8F4E; --cat-5:#B0637A; --cat-rest:#8B8494;

  /* ---- type ---- */
  --display:'Schibsted Grotesk',system-ui,sans-serif;
  --body:'Schibsted Grotesk',system-ui,sans-serif;
  --data:'IBM Plex Mono',ui-monospace,monospace;
  --brand:'Unbounded',system-ui,sans-serif;

  /* ---- spacing (4px scale) ---- */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:24px;
  --s8:32px; --s10:40px; --s12:48px; --s16:64px; --s20:80px; --s24:96px;
  --gutter:clamp(20px,4vw,64px); --maxw:1120px;
  --r-sm:10px; --r:12px; --r-lg:18px; --r-xl:24px;
  --ease:cubic-bezier(.22,1,.36,1);
  --spring:cubic-bezier(.6,-.28,.32,1.4);
  /* ---- motion: four durations and three curves, named by intent ----
     There were thirty-odd ad-hoc durations in this file and no token for
     any of them. These are the vocabulary; a number written inline now
     needs a reason. --m-press is the touch-down, which must be quicker
     than thought; --m-release overshoots slightly so a button feels like
     it has mass rather than simply changing colour. */
  --m-press:90ms; --m-fast:160ms; --m-standard:240ms; --m-emphasis:380ms;
  --m-sheet:300ms; --m-page:280ms;
  --ease-release:cubic-bezier(.34,1.56,.64,1);
  --shadow-1:0 1px 2px var(--shadow),0 4px 16px color-mix(in srgb,var(--shadow) 70%,transparent);
  --shadow-2:0 2px 4px var(--shadow),0 14px 34px color-mix(in srgb,var(--shadow) 85%,transparent);
  --ring:0 0 0 3px color-mix(in srgb,var(--focus-ring) 35%,transparent);

  /* ---- legacy aliases: existing components keep working ---- */
  --paper:var(--background); --surface-2:var(--surface-muted); --surface-3:var(--border);
  --ink:var(--text-primary); --text:var(--text-primary); --muted:var(--text-secondary); --faint:var(--text-tertiary);
  --rule:var(--border); --rule-2:var(--border-strong);
  --accent-strong:var(--primary-ink); --accent-ink:var(--on-primary);
  --accent-tint:color-mix(in srgb,var(--primary) 9%,var(--surface));
  --good:var(--success); --good-lift:var(--success); --good-tint:var(--success-soft);
  --warn:var(--warning); --warn-lift:var(--warning); --warn-tint:var(--warning-soft);
  --bad:var(--danger); --bad-tint:var(--danger-soft);
  --navy:var(--text-primary); --navy-2:var(--surface-muted);
  --harbour:var(--primary-ink); --sky:var(--primary); --sky-2:var(--accent);
  --on-dark:var(--text-primary); --on-dark-soft:var(--text-secondary); --on-dark-faint:var(--text-tertiary);
  --line-dark:var(--border); --jade:var(--success); --jade-lift:var(--success);
  --amber:var(--primary-ink); --amber-lift:var(--primary); --red:var(--danger);
  --app-bg:var(--background); --app-surface:var(--surface); --app-surface-2:var(--surface-muted);
  --app-text:var(--text-primary); --app-text-soft:var(--text-secondary); --app-text-faint:var(--text-tertiary);
  --app-rule:var(--border); --app-rule-2:var(--border-strong);
  --app-accent:var(--primary); --app-accent-soft:var(--accent); --app-accent-ink:var(--on-primary);
  --app-good:var(--success); --app-warn:var(--warning); --app-bad:var(--danger);
  --app-radius:var(--r-lg); --app-radius-sm:var(--r);
  --app-shadow:var(--shadow-1); --app-shadow-lift:var(--shadow-2);
  --app-gap:24px;
}

/* ============ ATELIER (default) ============ */
html[data-theme="atelier"][data-mode="dark"]{
  --background:#151419; --surface:#1E1C23; --surface-raised:#27242D; --surface-muted:#232028;
  --text-primary:#F7F4FA; --text-secondary:#C5BFCB; --text-tertiary:#9E97A6;
  --border:#3B3742; --border-strong:#4E4956;
  --primary:#A996FF; --primary-hover:#B9AAFF; --primary-active:#8E78F5; --on-primary:#17131F;
  --primary-ink:#C1B4FF;
  --accent:#FF946E; --accent-soft:#43271F;
  --focus-ring:#C1B4FF;
  --income:#5FCB97; --commitment:#8FB0FF; --flexible:#C9A5FF; --saving:#6FD3C2; --debt:#E8B35F;
  --warning:#E6B96B; --warning-soft:#332A1B; --warn-hot:#F0A860;
  --danger:#F0968B; --danger-soft:#38221F;
  --success:#5FCB97; --success-soft:#1D2F27;
  --shadow:rgba(0,0,0,.5);
}
/* ============ COAST ============ */
html[data-theme="coast"]{
  --background:#F3F8F7; --surface:#FFFFFF; --surface-raised:#F9FCFB; --surface-muted:#E9F1F0;
  --text-primary:#142022; --text-secondary:#536467; --text-tertiary:#617275;
  --border:#C9D8D7; --border-strong:#A9BEBD;
  --primary:#006D77; --primary-hover:#005E67; --primary-active:#004F57; --on-primary:#FFFFFF;
  --primary-ink:#005A63;
  --accent:#C47A16; --accent-soft:#F7E7C9;
  --focus-ring:#138A95;
  --income:#0F7A5F; --commitment:#2A5F8F; --flexible:#8A4FBF; --saving:#0E7A6C; --debt:#9A6413;
  --shadow:rgba(20,32,34,.10);
}
html[data-theme="coast"][data-mode="dark"]{
  --background:#11191A; --surface:#182325; --surface-raised:#202D2F; --surface-muted:#1D292B;
  --text-primary:#F2F8F8; --text-secondary:#B8C9C9; --text-tertiary:#95A8A8;
  --border:#354749; --border-strong:#475C5E;
  --primary:#64D4D8; --primary-hover:#7FE0E3; --primary-active:#4CBFC3; --on-primary:#082123;
  --primary-ink:#8CE8EB;
  --accent:#F2B95E; --accent-soft:#3B301D;
  --focus-ring:#8CE8EB;
  --income:#5FCB97; --commitment:#7FB6E8; --flexible:#C9A5FF; --saving:#64D4D8; --debt:#E8B35F;
  --warning:#E6B96B; --warning-soft:#332A1B; --warn-hot:#F0A860; --danger:#F0968B; --danger-soft:#38221F;
  --success:#5FCB97; --success-soft:#16302A;
  --shadow:rgba(0,0,0,.5);
}
/* ============ EMBER ============ */
html[data-theme="ember"]{
  --background:#FBF5F2; --surface:#FFFFFF; --surface-raised:#FFF9F6; --surface-muted:#F5E9E4;
  --text-primary:#231719; --text-secondary:#6D5559; --text-tertiary:#87696E;
  --border:#E1D0D3; --border-strong:#C9B2B6;
  --primary:#A63D4A; --primary-hover:#91333F; --primary-active:#7C2B36; --on-primary:#FFFFFF;
  --primary-ink:#8E2F3B;
  --accent:#B95C16; --accent-soft:#F8DEC9;
  --focus-ring:#C85F6D;
  --income:#1E7A50; --commitment:#3B5BA5; --flexible:#9A4A7F; --saving:#12796A; --debt:#A8631B;
  --shadow:rgba(35,23,25,.10);
}
html[data-theme="ember"][data-mode="dark"]{
  --background:#191315; --surface:#241B1E; --surface-raised:#302326; --surface-muted:#2A2023;
  --text-primary:#FFF5F6; --text-secondary:#D8BEC2; --text-tertiary:#B39BA0;
  --border:#4B363B; --border-strong:#61474D;
  --primary:#FF9DA8; --primary-hover:#FFB2BB; --primary-active:#E88793; --on-primary:#2C1015;
  --primary-ink:#FFC1C8;
  --accent:#FFAD6D; --accent-soft:#46291A;
  --focus-ring:#FFC1C8;
  --income:#5FCB97; --commitment:#8FB0FF; --flexible:#E29ACB; --saving:#6FD3C2; --debt:#E8B35F;
  --warning:#E6B96B; --warning-soft:#332A1B; --warn-hot:#F0A860; --danger:#F0968B; --danger-soft:#3A2220;
  --success:#5FCB97; --success-soft:#1E2F27;
  --shadow:rgba(0,0,0,.5);
}
/* ============ SIGNAL ============ */
html[data-theme="signal"]{
  --background:#F5F7FC; --surface:#FFFFFF; --surface-raised:#FAFBFF; --surface-muted:#EAEEF7;
  --text-primary:#151A24; --text-secondary:#566073; --text-tertiary:#666F7F;
  --border:#CDD4E2; --border-strong:#AEB8CB;
  --primary:#2457D6; --primary-hover:#1D49BA; --primary-active:#193FA2; --on-primary:#FFFFFF;
  --primary-ink:#1D49BA;
  --accent:#087E75; --accent-soft:#D6F1EE;
  --focus-ring:#4A75E6;
  --income:#0B7A63; --commitment:#2457D6; --flexible:#7B3FD6; --saving:#087E75; --debt:#9A6413;
  --shadow:rgba(21,26,36,.10);
}
html[data-theme="signal"][data-mode="dark"]{
  --background:#11151E; --surface:#191F2B; --surface-raised:#222A38; --surface-muted:#1E2532;
  --text-primary:#F5F7FD; --text-secondary:#C1C8D8; --text-tertiary:#9BA5B9;
  --border:#354052; --border-strong:#485470;
  --primary:#85A7FF; --primary-hover:#9CB8FF; --primary-active:#6F92F0; --on-primary:#0B1838;
  --primary-ink:#A7BDFF;
  --accent:#5ED7CB; --accent-soft:#173D3A;
  --focus-ring:#A7BDFF;
  --income:#5FCB97; --commitment:#85A7FF; --flexible:#B99BFF; --saving:#5ED7CB; --debt:#E8B35F;
  --warning:#E6B96B; --warning-soft:#2E2717; --warn-hot:#F0A860; --danger:#F0968B; --danger-soft:#36211F;
  --success:#5FCB97; --success-soft:#17302A;
  --shadow:rgba(0,0,0,.55);
}
/* ============ PRIDE ============
   Neutral surfaces, high-contrast type, the gradient used only as an
   accent: outlines, thin edges, the orb, rings, focus halos. Never a
   rainbow behind running text, never a rainbow fill on every card. */
html[data-theme="pride"]{
  --background:#F8F7FA; --surface:#FFFFFF; --surface-raised:#FCFBFD; --surface-muted:#EFECF3;
  --text-primary:#19161E; --text-secondary:#615A68; --text-tertiary:#726A7A;
  --border:#D9D3DF; --border-strong:#BEB6C7;
  --primary:#7B2D8E; --primary-hover:#6A2679; --primary-active:#592065; --on-primary:#FFFFFF;
  --primary-ink:#6A2679;
  --accent:#B64C96; --accent-soft:#F7E3F1;
  --focus-ring:#825BE2;
  --income:#2B9A66; --commitment:#3478D4; --flexible:#7655D9; --saving:#0E7A6C; --debt:#E87924;
  --shadow:rgba(25,22,30,.10);
}
html[data-theme="pride"][data-mode="dark"]{
  --background:#141218; --surface:#1D1A22; --surface-raised:#27222D; --surface-muted:#231F29;
  --text-primary:#FAF6FC; --text-secondary:#CBC1D0; --text-tertiary:#A79DB0;
  --border:#403846; --border-strong:#554B5D;
  --primary:#B69AFF; --primary-hover:#C6AEFF; --primary-active:#9E7FF0; --on-primary:#1D1231;
  --primary-ink:#D0BEFF;
  --accent:#E77CC2; --accent-soft:#3A2434;
  --focus-ring:#D0BEFF;
  --income:#5FCB97; --commitment:#7FB6F0; --flexible:#B69AFF; --saving:#6FD3C2; --debt:#F2A15E;
  --warning:#E6B96B; --warning-soft:#332A1B; --warn-hot:#F0A860; --danger:#F0968B; --danger-soft:#38221F;
  --success:#5FCB97; --success-soft:#1D2F27;
  --shadow:rgba(0,0,0,.55);
}
/* ---- Pride: recognisably multicolour, deliberately placed ----
   The gradient lives on identity and selection, never on data. Bars and
   charts keep their semantic colours; a rainbow bar would erase what the
   colour is telling you. */
/* P1: primary buttons wear a gradient border. The old masked ::after was
   clipped to nothing by the button's own overflow:hidden — this
   double-background survives it because the border IS the background. */
html[data-theme="pride"] .solid,html[data-theme="pride"] .btn:not(.btn--ghost){
  border:2px solid transparent;
  background:linear-gradient(var(--primary),var(--primary)) padding-box,
             var(--gradient-pride) border-box}
html[data-theme="pride"] .solid:hover,html[data-theme="pride"] .btn:not(.btn--ghost):hover{
  background:linear-gradient(var(--primary-hover),var(--primary-hover)) padding-box,
             var(--gradient-pride) border-box}
/* P2: in Pride, each star of the constellation takes a flag colour */
html[data-theme="pride"] .sxOrb .s1{fill:#E14855}
html[data-theme="pride"] .sxOrb .s2{fill:#D7A900}
html[data-theme="pride"] .sxOrb .s3{fill:#2B9A66}
html[data-theme="pride"] .sxOrb .s4{fill:#3478D4}
/* P3: selected cards get a true gradient ring (border-image discards the
   corner radius; this keeps it) */
html[data-theme="pride"] .selCard[aria-pressed="true"],html[data-theme="pride"] .palCard[aria-pressed="true"]{
  border:2px solid transparent;
  background:linear-gradient(var(--pv-bg,var(--surface)),var(--pv-bg,var(--surface))) padding-box,
             var(--gradient-pride) border-box}
/* P4: focus carries the gradient as a soft outer halo behind the ring */
/* The halo used to reach 14px past the button (2px outline at 3px offset,
   then a 9px ring) into a 12px gap, so it collided with the field above and
   read as an overlap. It now stops inside 7px, which every gap in the
   product can hold. */
html[data-theme="pride"] :focus-visible:not(input):not(textarea):not(select){
  outline-color:var(--primary);outline-offset:2px;
  box-shadow:0 0 0 3px color-mix(in srgb,#E14855 24%,transparent),
             0 0 0 6px color-mix(in srgb,#3478D4 16%,transparent)}
/* P5: the active nav item is underlined in the flag */
html[data-theme="pride"] .route[aria-current="page"]::after{background:var(--gradient-pride);transform:scaleX(1)}
/* P6: the reading-progress hairline is the one full-width gradient, static */
html[data-theme="pride"] #prog{background:var(--gradient-pride)}

html{--app-gap:24px}

/* ============ BASE ============ */
*{box-sizing:border-box}
/* an author display rule must never resurrect something the script hid */
[hidden]{display:none!important}
html{color-scheme:light;background:var(--paper);-webkit-text-size-adjust:100%}
html[data-mode="dark"]{color-scheme:dark}
main,section,.page{background:transparent}
main{background:var(--paper)}
body{margin:0;background:var(--paper);color:var(--text);font-family:var(--body);font-size:16.5px;
  line-height:1.6;-webkit-font-smoothing:antialiased;overflow-x:clip}
body.lock{overflow:hidden}
svg{display:block}
button{font:inherit;cursor:pointer;border:0;background:none;color:inherit;padding:0}
input,textarea,select{font:inherit}
:focus-visible{outline:2px solid var(--focus-ring);outline-offset:3px;border-radius:2px}
.skip{position:absolute;left:-9999px;z-index:300;background:var(--ink);color:#fff;padding:10px 18px;font-size:14px;border-radius:0 0 var(--r) 0}
.skip:focus{left:0;top:0}
.inner{max-width:var(--maxw);margin:0 auto;padding-inline:var(--gutter);position:relative;z-index:1}
.hidden{display:none!important}
::selection{background:var(--accent-tint);color:var(--accent-strong)}

/* ============ TYPE ============ */
.eyebrow{font-family:var(--data);font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--accent-strong);margin:0 0 var(--s4);font-weight:500}
h1{font-family:var(--display);font-weight:700;font-size:clamp(38px,5.4vw,64px);letter-spacing:-.035em;line-height:1.02;margin:0;color:var(--text)}
h2{font-family:var(--display);font-weight:700;font-size:clamp(30px,3.4vw,44px);letter-spacing:-.028em;line-height:1.08;margin:0 0 var(--s4);color:var(--text)}
h3{font-family:var(--display);font-weight:600;font-size:19px;letter-spacing:-.012em;margin:0 0 var(--s2);color:var(--text)}
.lede{color:var(--muted);margin:0;max-width:58ch;font-size:clamp(16px,1.5vw,18px);line-height:1.65}
.num{font-family:var(--data);font-variant-numeric:tabular-nums lining-nums}

/* ============ BUTTONS ============
   Hierarchy: .solid (primary) · .outl (secondary) · .lnk (tertiary).
   .btn (app areas) mirrors .solid. All ≥48px, primaries ≥52px. */
.lnk{display:inline-flex;align-items:center;gap:9px;position:relative;font-family:var(--display);
  font-weight:600;font-size:16px;letter-spacing:-.01em;color:var(--accent-strong);padding:10px 2px;min-height:44px;
  /* The rule sits this far above the bottom of the PADDING box, and 6px is
     measured against the 10px of padding below it. Any rule that changes
     .lnk's padding or min-height has to set this too, or the underline walks
     into the words (.pCardHead .lnk) or onto the line below (.authLink). */
  --lnk-rule:6px}
.lnk .arw{width:15px;height:15px;margin-top:-1px;transition:transform .3s var(--ease)}
.lnk::after{content:"";position:absolute;left:0;right:0;bottom:var(--lnk-rule);height:1.5px;background:var(--accent);
  transform-origin:left;transform:scaleX(.3);transition:transform .35s var(--ease)}
.lnk:hover::after{transform:scaleX(1)}
/* The growing rule is a pointer affordance. Where there is no pointer it never
   grows, and a permanent 30% stub reads as a broken underline. */
@media(hover:none){.lnk::after{transform:none}}
.lnk:hover .arw{transform:translateX(4px)}
.lnk.on-dark{color:var(--accent-strong)}
.lnk.jade::after{background:var(--good)}
.lnk.sky::after{background:var(--primary)}
.lnk.sm{font-size:14.5px}
/* .lnk carries min-height:44px, but a small one inside a flex row can be
   compressed below it by its parent. Nothing may shrink a target. */
/* Nothing may shrink a target, and nothing may STRETCH one either: .lnk draws
   its rule across its own box, so a link stretched by a column-flex parent
   draws a rule that runs past the end of the words. */
.lnk,.lnk.sm{flex:0 0 auto;width:fit-content;max-width:100%}

.solid,.btn{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:10px;
  background:var(--primary);color:var(--on-primary);min-height:52px;padding:0 26px;border-radius:var(--r);
  font-family:var(--display);font-weight:600;font-size:16px;letter-spacing:-.005em;overflow:hidden;
  box-shadow:0 1px 2px var(--shadow),inset 0 1px 0 rgba(255,255,255,.12);
  transition:transform .14s var(--ease),box-shadow .14s var(--ease),background .2s}
/* hover darkens within the same hue — never a hue jump */
.solid:hover,.btn:hover{background:color-mix(in srgb,var(--primary) 86%,var(--text-primary))}
.solid:active,.btn:active{transform:translateY(1px) scale(.97);box-shadow:0 0 1px rgba(29,27,34,.2)}
.solid:focus-visible,.btn:focus-visible{outline:2px solid var(--focus-ring);outline-offset:3px}
/* one-shot shine sweep: runs once per hover entry, never loops */
/* the shine belongs to the marketing primary only (council: it leaked
   onto ghost buttons as an invisible white-on-white sweep) */
.solid::before{content:"";position:absolute;top:-20%;bottom:-20%;width:36%;left:-45%;
  background:linear-gradient(105deg,transparent,rgba(255,255,255,.22),transparent);
  transform:skewX(-18deg);pointer-events:none;opacity:0}
.solid:hover::before{animation:sweep-once .45s var(--ease) forwards}
@keyframes sweep-once{0%{left:-45%;opacity:1}100%{left:115%;opacity:1}}
.solid .arw,.btn .arw{width:15px;height:15px;transition:transform .3s var(--ease)}
.solid:hover .arw{transform:translateX(4px)}
.solid.inv{background:var(--primary);color:var(--on-primary)}
.solid.full,.outl.full,.btn--wide{width:100%}
.outl,.btn--ghost{display:inline-flex;align-items:center;justify-content:center;gap:10px;border:1.5px solid var(--rule-2);
  background:var(--surface);color:var(--text);min-height:48px;padding:0 22px;border-radius:var(--r);
  font-family:var(--display);font-weight:600;font-size:15.5px;
  transition:border-color .2s var(--ease),background .2s,transform .14s var(--ease)}
.outl:hover,.btn--ghost:hover{border-color:var(--accent);background:var(--accent-tint)}
.outl:active,.btn--ghost:active{transform:translateY(1px) scale(.985)}
.outl.on-dark{border-color:var(--rule-2);color:var(--text);background:var(--surface)}
.outl.on-dark:hover{border-color:var(--accent)}
.btn--ghost{color:var(--accent-strong)}
.is-busy{pointer-events:none;opacity:.7}
.spin{width:15px;height:15px;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;
  animation:sp .7s linear infinite;flex:none}
@keyframes sp{to{transform:rotate(360deg)}}

/* ============ HEADER + MENU ============ */
#prog{position:fixed;top:0;left:0;height:2px;width:0;z-index:80;background:var(--accent)}
header.top{position:sticky;top:0;z-index:70;background:color-mix(in srgb,var(--paper) 88%,transparent);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-bottom:1px solid var(--rule)}
header.top .inner{display:flex;align-items:center;justify-content:space-between;gap:var(--s4);height:68px}
.mark{font-family:var(--display);font-weight:700;letter-spacing:-.04em;font-size:22px;display:flex;align-items:center;gap:10px;color:var(--text)}

.routes{display:none;gap:var(--s6)}
@media(min-width:900px){.routes{display:flex}}
.route{color:var(--muted);font-size:15px;font-weight:500;position:relative;padding:6px 2px;transition:color .2s;min-height:44px;display:inline-flex;align-items:center}
.route::after{content:"";position:absolute;left:0;right:0;bottom:2px;height:2px;border-radius:2px;background:var(--accent);
  transform:scaleX(0);transform-origin:left;transition:transform .3s var(--ease)}
.route:hover{color:var(--text)}
.route:hover::after,.route[aria-current="page"]::after{transform:scaleX(1)}
.route[aria-current="page"]{color:var(--text);font-weight:600}
.hend{display:flex;align-items:center;gap:var(--s5)}

/* ============================================================
   THE MOBILE MENU

   Was a full-bleed black screen wiped in with a clip-path circle over 0.7s,
   six lines of 30px display type centred with 168px of dead space above,
   11px mono numerals, a second wordmark drawn over the very button that
   opened it, and the button itself painted UNDER the overlay by a stacking
   context, so nothing visible could close it. It is now what every other
   overlay in the product already is: a surface panel on a scrim, dropped
   from under the header, which stays in place with its button visible and
   turned into a close. Rows are the portal sheet's rows. Open is 300ms.
   ============================================================ */
.drop{position:relative;width:44px;height:44px;z-index:92;border-radius:12px;-webkit-tap-highlight-color:transparent;
  transition:transform var(--m-fast) var(--spring)}
.drop:active{transform:scale(.94)}
@media(min-width:900px){.drop{display:none}}
/* two bars, the nav icons' stroke weight; they turn into a cross to close */
.drop b{position:absolute;left:50%;top:50%;width:18px;height:2px;border-radius:2px;background:var(--text);
  transform:translate(-50%,-50%) translateY(-3.5px);transition:transform var(--m-standard) var(--ease)}
.drop b:nth-child(2){transform:translate(-50%,-50%) translateY(3.5px)}
.drop b:nth-child(3){display:none}
.drop.open b:nth-child(1){transform:translate(-50%,-50%) rotate(45deg)}
.drop.open b:nth-child(2){transform:translate(-50%,-50%) rotate(-45deg)}

/* above the cookie bar (120), below the portal sheet (130) */
/* starts under the header, so the header — and the button that is now a
   close — stays fully visible and tappable above it */
#menu{position:fixed;top:69px;left:0;right:0;bottom:0;z-index:125;visibility:hidden;pointer-events:none;transition:visibility 0s .22s}
#menu.open{visibility:visible;pointer-events:auto;transition:none}
.menuBg{position:absolute;inset:0;background:color-mix(in srgb,var(--text) 42%,transparent);
  opacity:0;transition:opacity var(--m-fast) var(--ease)}
html[data-mode="dark"] .menuBg{background:rgba(0,0,0,.58)}
#menu.open .menuBg{opacity:1}
/* the panel hangs from the header's bottom edge: 68px plus its hairline */
.menuIn{position:absolute;left:0;right:0;top:0;max-height:calc(100dvh - 69px);overflow-y:auto;
  background:var(--surface);color:var(--text);border-radius:0 0 var(--r-xl) var(--r-xl);
  padding:var(--s4) var(--s5) calc(var(--s6) + env(safe-area-inset-bottom,0px));
  box-shadow:0 18px 44px color-mix(in srgb,var(--text) 18%,transparent);
  transform:translateY(-12px);opacity:0;
  transition:transform .2s var(--ease),opacity .2s var(--ease)}
#menu.open .menuIn{transform:none;opacity:1;transition:transform var(--m-sheet) var(--ease),opacity var(--m-sheet) var(--ease)}
.menuList{list-style:none;margin:0;padding:0;display:grid;gap:4px}
.menuList li{margin:0}
.menuList a{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);min-height:56px;padding:0 16px;
  border-radius:var(--r);font-family:var(--display);font-weight:600;font-size:17px;letter-spacing:-.01em;
  color:var(--text);text-decoration:none;
  transform:translateY(6px);opacity:0;
  transition:transform var(--m-standard) var(--ease),opacity var(--m-standard) var(--ease),background var(--m-fast),color var(--m-fast)}
#menu.open .menuList a{transform:none;opacity:1;transition-delay:calc(60ms + var(--i,0) * 20ms)}
.menuList a svg{width:16px;height:16px;flex:none;stroke:var(--text-tertiary);fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.menuList a:hover,.menuList a:active{background:color-mix(in srgb,var(--primary) 10%,var(--surface-muted))}
.menuList a[aria-current="page"]{color:var(--primary-ink);background:color-mix(in srgb,var(--primary) 8%,var(--surface))}
.menuList a[aria-current="page"] svg{stroke:var(--primary-ink)}
.menuFoot{display:flex;flex-wrap:wrap;gap:0 2px;margin-top:var(--s3);padding-top:var(--s3);border-top:1px solid var(--rule)}
.menuFoot button{min-height:44px;min-width:44px;padding:0 11px;border-radius:var(--r-sm);font-family:var(--body);font-weight:600;
  font-size:13.5px;color:var(--text-secondary);transition:color var(--m-fast),background var(--m-fast)}
.menuFoot button:hover,.menuFoot button:active{color:var(--text);background:var(--surface-muted)}
#menu :focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}
html[data-motion="reduced"] #menu,html[data-motion="reduced"] #menu *,html[data-motion="reduced"] .drop b{transition-duration:.01ms!important;transition-delay:0ms!important}
@media(prefers-reduced-motion:reduce){#menu,#menu *,.drop b{transition-duration:.01ms!important;transition-delay:0ms!important}}

/* ============ PAGES / REVEALS ============ */
.page{display:none}.page.active{display:block;animation:pageIn .45s var(--ease)}
@keyframes pageIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.pad{padding-block:clamp(56px,8vw,104px);position:relative}
.rv{opacity:0;transform:translateY(16px);transition:opacity .7s var(--ease),transform .7s var(--ease);transition-delay:var(--d,0ms)}
.rv.in{opacity:1;transform:none}
@media(prefers-reduced-motion:reduce){
  .rv{opacity:1!important;transform:none!important}
  *,*::after,*::before{animation:none!important;transition-duration:.01ms!important}
}
html[data-motion="reduced"] .rv{opacity:1!important;transform:none!important}
html[data-motion="reduced"] *,html[data-motion="reduced"] *::before,html[data-motion="reduced"] *::after{animation:none!important;transition-duration:.01ms!important}
.word{display:inline-block;opacity:0;transform:translateY(30px);animation:word .8s var(--ease) forwards;animation-delay:var(--wd)}
@keyframes word{to{opacity:1;transform:none}}

/* ============ HERO ============ */
.hero{background:linear-gradient(170deg,var(--accent-tint) 0%,var(--paper) 46%);color:var(--text);position:relative;overflow:hidden}
.hero .inner{padding-block:clamp(56px,8.5vw,104px);z-index:2}
.hero h1{color:var(--text);max-width:17ch;text-wrap:balance}
h1,h2,h3{text-wrap:balance}p,li{text-wrap:pretty}
.hero .lede{color:var(--muted);margin-top:var(--s6)}
.hero .eyebrow{color:var(--accent-strong)}
/* A single-column grid defaults to auto-sized tracks, so the widest thing
   inside (the payslip printer visual) sized the column: on a 375px phone
   the hero's track measured 594px, pushing the heading, copy and BOTH
   buttons past the screen edge to be clipped. That is why the buttons did
   not fit and the hero looked out of its container. minmax(0,1fr) stops a
   child from sizing the track it lives in; min-width:0 lets the children
   actually shrink to it. */
.heroGrid{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s12);align-items:center}
.heroGrid>*{min-width:0}
@media(min-width:1000px){.heroGrid{grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);gap:var(--s16)}}
.hbtns{display:flex;gap:var(--s4) var(--s6);flex-wrap:wrap;align-items:center;margin-top:var(--s8)}
/* On a phone these wrapped to two lines at their own natural widths (245px
   and 198px) with mismatched heights (52 and 48), which read as two
   unrelated buttons rather than a primary and a secondary. Stack them and
   let them share a width and a height. */
@media(max-width:560px){
  .hbtns{flex-direction:column;align-items:stretch;gap:var(--s3)}
  .hbtns>*{width:100%;min-height:52px;justify-content:center}
}
.cross{position:absolute;right:6%;top:10%;width:min(230px,28vw);z-index:0;pointer-events:none;opacity:.5}
.cross .star{fill:var(--accent-soft);transform-origin:center;transform-box:fill-box;animation:tw 4.2s var(--ease) 1}
.cross .star:nth-child(2){animation-delay:.7s}.cross .star:nth-child(3){animation-delay:1.4s}.cross .star:nth-child(4){animation-delay:2.1s}
@keyframes tw{0%,100%{opacity:.4;transform:scale(.92)}50%{opacity:1;transform:scale(1.1)}}
.rotor{display:block;height:1.06em;overflow:hidden;position:relative;color:var(--accent)}
.rotor span{display:block;position:absolute;inset:0;opacity:0;transform:translateY(105%);transition:transform .6s var(--ease),opacity .6s var(--ease)}
.rotor span.on{opacity:1;transform:none}
.rotor span.out{opacity:0;transform:translateY(-105%)}
.dot{width:6px;height:6px;border-radius:50%;background:var(--good-lift);flex:none}

/* ============ PRINTER / RECEIPT ============ */
.printer{position:relative}
.tear{height:16px;position:relative;z-index:3;
  background:repeating-linear-gradient(90deg,var(--rule-2) 0 5px,transparent 5px 11px);
  background-size:11px 1px;background-repeat:repeat-x;background-position:0 50%;opacity:.6}
.chute{overflow:hidden;position:relative;z-index:2}
.printed{transform:translateY(-101%);transition:transform 1.1s var(--ease)}
.printer.run .printed{transform:none}
.printed .rline{opacity:0;transform:translateY(6px);transition:opacity .4s var(--ease),transform .4s var(--ease)}
.printer.run .printed .rline{opacity:1;transform:none;transition-delay:calc(650ms + var(--i,0)*120ms)}
.replay{margin-top:var(--s5);display:inline-flex;align-items:center;gap:10px;font-family:var(--data);font-size:11px;
  letter-spacing:.15em;text-transform:uppercase;color:var(--faint);position:relative;z-index:3;min-height:44px}
.replay:hover{color:var(--text)}
.replay i{display:block;width:22px;height:1px;background:currentColor;transition:width .3s var(--ease)}
.replay:hover i{width:34px}

.receipt{background:var(--surface);border:1px solid var(--rule);border-radius:var(--r-lg);overflow:hidden;box-shadow:var(--shadow-1)}
.receipt.dark{background:var(--surface);border-color:var(--rule);color:var(--text)}
.receipt.dark h3{color:var(--text)}
.rhead{display:flex;justify-content:space-between;align-items:center;gap:var(--s3);padding:var(--s4) var(--s6);
  border-bottom:1px solid var(--rule);background:var(--surface-2);font-family:var(--data);font-size:10.5px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--muted)}
.rbody{padding:var(--s6)}
.rfoot{border-top:1px solid var(--rule);background:var(--surface-2);padding:var(--s3) var(--s6);font-family:var(--data);font-size:10px;
  letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.tag{font-family:var(--data);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;padding:5px 10px;border-radius:999px;
  border:1px solid currentColor;color:var(--warn);white-space:nowrap}
.tag.clear{color:var(--good)}
.tag.locked{color:var(--faint)}
.calc{margin-top:var(--s5);border-top:1px solid var(--rule);padding-top:var(--s4);font-family:var(--data);font-size:13.5px;
  color:var(--text);font-variant-numeric:tabular-nums}
.row{display:flex;justify-content:space-between;gap:var(--s4);padding:8px 0}
.row span:first-child{color:var(--muted)}
.row.hi{border-top:1px solid var(--rule);margin-top:6px;padding-top:14px;font-weight:600;font-size:15px}
.row.hi span:last-child{color:var(--accent-strong)}

/* ============ STATS / STEPS / CARDS / FAQ ============ */
.stats{display:grid;margin-top:var(--s12);border-top:1px solid var(--rule)}
@media(min-width:760px){.stats{grid-template-columns:repeat(auto-fit,minmax(132px,1fr))}}
.stat{padding:var(--s8) 0;border-bottom:1px solid var(--rule)}
@media(min-width:760px){.stat{padding:var(--s8)}.stat+.stat{border-left:1px solid var(--rule)}.stat:first-child{padding-left:0}}
.stat .v{font-family:var(--data);font-weight:600;font-size:clamp(32px,4vw,46px);letter-spacing:-.03em;line-height:1;color:var(--text);font-variant-numeric:tabular-nums}
.stat .v.amber{color:var(--accent-strong)}.stat .v.jade{color:var(--good)}
.stat .k{font-family:var(--data);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);margin-top:14px}

.steps{margin-top:var(--s12);position:relative}
.steps .rail{position:absolute;left:13px;top:8px;bottom:8px;width:2px;background:var(--rule);border-radius:2px}
.steps .rail i{display:block;width:100%;height:0;background:var(--accent);transition:height 1.4s var(--ease)}
.steps.in .rail i{height:100%}
.step{display:grid;grid-template-columns:28px 1fr;gap:var(--s5);padding:var(--s5) 0;border-bottom:1px solid var(--rule)}
.step:last-child{border-bottom:0}
.step .bullet{width:27px;height:27px;border-radius:50%;border:1.5px solid var(--rule-2);background:var(--paper);
  display:grid;place-items:center;font-family:var(--data);font-size:10.5px;color:var(--faint);z-index:1}
.steps.in .step .bullet{border-color:var(--accent);color:var(--accent-strong)}
.step h3{margin:0 0 6px}.step p{margin:0;color:var(--muted);font-size:15.5px;max-width:60ch}

.cards{display:grid;margin-top:var(--s12);gap:var(--s4);align-items:stretch}
@media(min-width:640px){.cards{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(min-width:1100px){.cards{grid-template-columns:repeat(4,minmax(0,1fr))}}
.card{display:flex;flex-direction:column;min-width:0}
.card p{margin-top:auto}
.card{background:var(--surface);border:1px solid var(--rule);border-radius:var(--r-lg);padding:var(--s6);box-shadow:var(--shadow-1);position:relative;color:var(--text)}
.card .n{font-family:var(--data);font-size:11px;letter-spacing:.18em;color:var(--accent-strong)}
.card h3{margin-top:var(--s4)}.card p{margin:0;color:var(--muted);font-size:15px}
.card .sub{margin:0;color:var(--faint);font-size:14.5px}
.card--flush{padding:0;overflow:hidden}

.acc{margin-top:var(--s10);border-top:1px solid var(--rule)}
.acc details{border-bottom:1px solid var(--rule)}
.acc summary{list-style:none;cursor:pointer;padding:var(--s6) 0;display:flex;justify-content:space-between;align-items:center;
  gap:var(--s4);font-family:var(--display);font-weight:600;font-size:17.5px;letter-spacing:-.01em}
.acc summary::-webkit-details-marker{display:none}
.pm{width:22px;height:22px;flex:none;position:relative}
.pm::before,.pm::after{content:"";position:absolute;inset:50% 0 auto;height:1.6px;background:var(--text);transform:translateY(-50%);transition:.35s var(--ease)}
.pm::after{transform:translateY(-50%) rotate(90deg)}
.acc details[open] .pm::after{transform:translateY(-50%) rotate(0);opacity:0}
.acc details[open] .pm::before{background:var(--accent)}
.ans{padding:0 0 var(--s6);color:var(--muted);font-size:15.5px;max-width:74ch;animation:accIn .4s var(--ease)}
@keyframes accIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}

/* band: light tinted section (was dark navy) */
.band{background:var(--surface-2);color:var(--text);position:relative;overflow:hidden}
.fan+.band{border-top:1px solid var(--rule)}
.band h2,.band h3{color:var(--text)}.band .lede{color:var(--muted)}.band .eyebrow{color:var(--accent-strong)}

.scan{margin-top:var(--s12);border:1px solid var(--rule);border-radius:var(--r-lg);min-height:220px;position:relative;overflow:hidden;
  background:var(--surface);display:grid;place-items:center;text-align:center;padding:var(--s12) var(--s5)}
/* a real document scans: beam travels by transform, rows tint as it passes */
.scan{display:flex;gap:26px;align-items:center;flex-wrap:wrap;padding:26px}
.scanDoc{flex:none;overflow:visible}
.scanDoc .scRow{fill:var(--surface-muted);transition:fill .4s var(--ease)}
.scanDoc .scanBeam{fill:var(--accent);opacity:0}
.rv.in .scanDoc .scanBeam{animation:scanY 1.6s var(--ease) .3s 1 forwards}
@keyframes scanY{0%{opacity:0;transform:translateY(0)}8%{opacity:.75}92%{opacity:.75}100%{opacity:0;transform:translateY(156px)}}
.rv.in .scanDoc .scRow{fill:var(--accent-soft)}
.rv.in .scanDoc .scRow:nth-of-type(2){transition-delay:.45s}
.rv.in .scanDoc .scRow:nth-of-type(3){transition-delay:.65s}
.rv.in .scanDoc .scRow:nth-of-type(4){transition-delay:.85s}
.rv.in .scanDoc .scRow:nth-of-type(5){transition-delay:1.05s}
.rv.in .scanDoc .scRow:nth-of-type(6){transition-delay:1.25s}
.scanDoc .scTick{fill:none;stroke:var(--success);stroke-width:3;stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:34;stroke-dashoffset:34}
.rv.in .scanDoc .scTick{animation:ecTick .35s var(--ease) 1.9s forwards}
html[data-motion="reduced"] .scanDoc .scTick{stroke-dashoffset:0;animation:none}
@media(prefers-reduced-motion:reduce){.scanDoc .scTick{stroke-dashoffset:0}.scanDoc .scanBeam{animation:none}}
.scan .t{font-family:var(--display);font-weight:600;font-size:20px;color:var(--text);position:relative}
.scan .s{font-family:var(--data);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--faint);margin-top:12px;position:relative}

.prose{display:grid;gap:var(--s10);margin-top:var(--s12)}
@media(min-width:900px){.prose{grid-template-columns:1fr 1fr;gap:var(--s16)}}
.prose p{color:var(--muted);font-size:16.5px;margin:0 0 var(--s4)}
.prose p:last-child{margin-bottom:0}
.prose strong{color:var(--text);font-weight:600}
.band .prose p{color:var(--muted)}.band .prose strong{color:var(--text)}

/* ============ FORM FIELDS ============
   Real controls. Label sits above (column-reverse keeps the
   existing input+label DOM working). Every colour is explicit. */
.form{margin-top:var(--s10);display:grid;gap:var(--s4);max-width:640px}
/* A field must be able to get narrower than its content wants to be.
   Flex and grid children default to min-width:auto, and iOS Safari gives
   <input type="date"> a wide intrinsic size it will not go below, which is
   how a date input ends up hanging off the right of a phone screen even
   though its container is only 170px wide. */
.field{position:relative;display:flex;flex-direction:column-reverse;gap:6px;border:0;padding:0;min-width:0;max-width:100%}
.field input,.field select,.field textarea{min-width:0;max-width:100%;box-sizing:border-box}
input[type="date"],input[type="datetime-local"],input[type="month"],input[type="time"]{min-width:0;max-width:100%;box-sizing:border-box}
/* iOS gives these an intrinsic width it will not go below and centres the
   value, until appearance is off; with appearance off an empty one has no
   height. So: appearance off, explicit height, value aligned left, and a
   glyph on the right so it reads as the control it is, like the selects. */
.field input[type="date"],.field input[type="datetime-local"],.field input[type="month"],.field input[type="time"]{
  appearance:none;-webkit-appearance:none;display:flex;align-items:center;width:100%;min-height:52px;
  text-align:left;cursor:pointer;padding-right:44px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23514D59' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4'/%3E%3C/svg%3E");
  background-position:right 14px center;background-size:18px 18px;background-repeat:no-repeat}
.field input[type="date"]::-webkit-date-and-time-value,.field input[type="datetime-local"]::-webkit-date-and-time-value,
.field input[type="month"]::-webkit-date-and-time-value,.field input[type="time"]::-webkit-date-and-time-value{
  text-align:left;min-height:1.3em;margin:0}
.field input[type="date"]::-webkit-calendar-picker-indicator{opacity:0;position:absolute;right:0;top:0;width:52px;height:100%;cursor:pointer}
html[data-mode="dark"] .field input[type="date"],html[data-mode="dark"] .field input[type="time"]{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23b8b3c2' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4'/%3E%3C/svg%3E")}
@media(max-width:560px){
  /* inline widths on these fields are desktop measurements */
  .checkOpts .field[style*="width"],#budApp .field[style*="width"]{width:100%!important}
}
.field input,.field textarea,.field select{width:100%;background:var(--surface);border:1.5px solid var(--rule-2);
  border-radius:10px;color:var(--text);padding:14px 16px;font-size:16px;min-height:52px;outline:none;
  transition:border-color .18s var(--ease),box-shadow .18s var(--ease),background .18s}
.field textarea{resize:vertical;min-height:112px}
.field input::placeholder,.field textarea::placeholder{color:transparent}
.field input:hover,.field textarea:hover,.field select:hover{border-color:var(--faint)}
.field input:focus,.field textarea:focus,.field select:focus{border-color:var(--accent);box-shadow:var(--ring)}
.field input:disabled,.field textarea:disabled,.field select:disabled{background:var(--surface-2);color:var(--faint);border-color:var(--rule)}
.field label{position:static;color:var(--muted);font-size:13.5px;font-weight:600;letter-spacing:.01em;
  pointer-events:none;font-family:var(--body);text-transform:none}
.field input:focus~label,.field textarea:focus~label,.field select:focus~label{color:var(--accent-strong)}
.field input:focus+label,.field input:not(:placeholder-shown)+label,
.field textarea:focus+label,.field textarea:not(:placeholder-shown)+label,
.field select+label{position:static;font-size:13.5px;letter-spacing:.01em;text-transform:none;color:var(--muted);font-family:var(--body)}
.field input:focus+label,.field textarea:focus+label,.field select:focus+label{color:var(--accent-strong)}
.field::after{display:none}
.field select{appearance:none;-webkit-appearance:none;color:var(--text);padding-right:38px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23514D59' stroke-width='1.8'/%3E%3C/svg%3E");
  background-position:right 14px center;background-size:12px 8px;background-repeat:no-repeat}
.field select:has(option[value='']:checked){color:var(--faint)}
.field select option{background:var(--surface);color:var(--text)}
/* browser autofill: keep our surface + our ink, never yellow/white-on-white */
.field input:-webkit-autofill,.field input:-webkit-autofill:hover,.field input:-webkit-autofill:focus{
  -webkit-box-shadow:0 0 0 60px var(--surface) inset;box-shadow:0 0 0 60px var(--surface) inset;
  -webkit-text-fill-color:var(--text);caret-color:var(--text);border-color:var(--rule-2)}
.field.invalid input,.field.invalid select,.field.invalid textarea{border-color:var(--bad)}
.err{color:var(--bad);font-size:13.5px;font-weight:500;padding:6px 0 0}
input[type=number]{appearance:textfield;-moz-appearance:textfield}
input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}

/* ============ ABOUT / CONTACT / LEGAL ============ */
.values{display:grid;margin-top:var(--s12);gap:var(--s4)}
@media(min-width:780px){.values{grid-template-columns:repeat(2,1fr)}}
.value{background:var(--surface);border:1px solid var(--rule);border-radius:var(--r-lg);padding:var(--s6);position:relative;overflow:hidden}
.value h3{margin-top:var(--s4)}
.value p{margin:0;color:var(--muted);font-size:15px}
.tl{margin-top:var(--s12);position:relative;border-left:2px solid var(--rule);padding-left:var(--s8)}
.chans{display:grid;margin-top:var(--s12);gap:var(--s4)}
@media(min-width:780px){.chans{grid-template-columns:repeat(3,1fr)}}
.chan{background:var(--surface);border:1px solid var(--rule);border-radius:var(--r-lg);padding:var(--s6)}
.chan .k{font-family:var(--data);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--accent-strong)}
.chan h3{margin:14px 0 6px;color:var(--text)}
.chan p{margin:0;color:var(--muted);font-size:14.5px}
.legal{max-width:74ch;margin-top:var(--s10)}
.legal h3{margin-top:var(--s10)}
.legal p,.legal li{color:var(--muted);font-size:16px}
.legal ul{padding-left:20px}
.legal .upd{font-family:var(--data);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}

/* The categorical chart palette was declared once in :root and never
   overridden for dark, leaving navy/maroon at roughly 2:1 on a dark
   card while every other semantic colour was re-tuned per theme. */
html[data-mode="dark"]{
  --cat-1:#A79AD8;--cat-2:#7FB8C6;--cat-3:#E0B183;--cat-4:#89C4A2;--cat-5:#DE9BAE;
  --cat-rest:#8A8496;
}
/* the skip link inverted to white-on-white in dark */
html[data-mode="dark"] .skip{background:var(--surface-raised);color:var(--text-primary);
  border:1px solid var(--border)}

/* ---- two-stream contributions chart ---- */
.streamWrap{margin:2px 0 14px}
.stream{width:100%;height:170px;display:block;border-radius:var(--r);outline:none}
.stream:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 35%,transparent)}
.stLine{stroke-dasharray:1200;stroke-dashoffset:1200;animation:stDraw 1s var(--ease) forwards;
  animation-delay:calc(var(--i)*120ms)}
@keyframes stDraw{to{stroke-dashoffset:0}}
.stArea{opacity:0;animation:stFade .7s var(--ease) .3s forwards}
@keyframes stFade{to{opacity:1}}
.stScrub{stroke:var(--text-tertiary);stroke-width:1;stroke-dasharray:3 3}
.stTick{font-size:10px;fill:var(--text-tertiary);font-family:var(--data)}
.stRead{display:flex;flex-wrap:wrap;gap:6px 16px;align-items:center;
  font-size:13.5px;color:var(--text-secondary);min-height:22px}
.stRead b{color:var(--text-primary);font-family:var(--data)}
.stRead span{display:inline-flex;align-items:center;gap:7px}
.stRead i{width:9px;height:9px;border-radius:50%;flex:none}
html[data-motion="reduced"] .stLine{animation:none;stroke-dashoffset:0}
html[data-motion="reduced"] .stArea{animation:none;opacity:1}
@media(prefers-reduced-motion:reduce){.stLine{animation:none;stroke-dashoffset:0}
  .stArea{animation:none;opacity:1}}

/* ---- disclosure: long reference content, folded away until wanted ---- */
.disc{border:1px solid var(--rule-2);border-radius:var(--r);margin-top:10px;
  background:color-mix(in srgb,var(--surface) 6%,transparent);overflow:hidden}
.disc+.disc{margin-top:8px}
.disc summary{display:flex;align-items:center;justify-content:space-between;gap:12px;
  min-height:52px;padding:0 16px;cursor:pointer;list-style:none;
  font-size:15px;font-weight:600;color:var(--on-dark,var(--text-primary))}
.disc summary::-webkit-details-marker{display:none}
.disc summary:hover{background:color-mix(in srgb,var(--surface) 10%,transparent)}
.disc summary:focus-visible{outline:2px solid var(--primary);outline-offset:-2px}
.disc summary i{width:10px;height:10px;flex:none;border-right:2px solid currentColor;
  border-bottom:2px solid currentColor;transform:rotate(45deg) translate(-2px,-2px);
  opacity:.6;transition:transform .25s var(--ease)}
.disc[open] summary i{transform:rotate(-135deg) translate(-2px,-2px)}
.disc .discBody{padding:2px 16px 18px}
.dataList{display:grid;gap:12px;margin-top:4px}
.dataList>div{display:grid;gap:3px}
.dataList b{font-size:14px;font-weight:650;color:var(--on-dark,var(--text-primary))}
.dataList span{font-size:14px;line-height:1.5;color:var(--on-dark-faint,var(--text-secondary))}
/* the transaction variant sits inside a card, so it drops the outer box */
.discTx{border:0;border-radius:0;background:none;margin-top:0;border-top:1px solid var(--rule)}
.discTx:first-of-type{border-top:0}
.discTx summary{padding:0;min-height:48px;color:var(--text-primary);font-weight:600}
.discTx summary:hover{background:none;color:var(--primary-ink)}
.discTx summary small{font-weight:400;color:var(--text-tertiary);font-size:12.5px;margin-left:6px}
.discTx .discSum{display:flex;align-items:center;gap:10px;font-family:var(--data);
  font-weight:600;font-size:14px;color:var(--text-secondary)}
.discTx .discBody{padding:0 0 10px}
html[data-motion="reduced"] .disc summary i{transition:none}
@media(prefers-reduced-motion:reduce){.disc summary i{transition:none}}

/* ============ TOASTS ============ */
/* Above the sheet (z130), because an error raised inside a sheet was painted
   behind it and never seen. pointer-events stay off the layer so a toast
   cannot swallow a tap meant for the button underneath it; only the toast's
   own action button takes pointer events back. */
#toasts{position:fixed;left:50%;bottom:calc(26px + env(safe-area-inset-bottom,0px));transform:translateX(-50%);z-index:140;display:grid;gap:10px;width:min(420px,92vw);pointer-events:none}
#toasts .toast{pointer-events:none}
#toasts .toast button,#toasts .toast a{pointer-events:auto}
/* clear the fixed bottom nav so an Undo link never lands on a nav button */
/* clear the whole Add control, not a guessed number: bar height, the lift
   that raises the squircle, its ring, and a breath */
@media(max-width:1023px){body.in-portal #toasts{
  bottom:calc(var(--pnav-h) + var(--pnav-lift) + var(--pnav-ring) + 18px + env(safe-area-inset-bottom,0px))}}
.toast{background:var(--surface);color:var(--text);border:1px solid var(--rule-2);border-radius:var(--r);
  padding:14px 18px;display:flex;gap:12px;align-items:center;font-size:14.5px;box-shadow:var(--shadow-2);animation:toastIn .45s var(--spring)}
.toast i{width:8px;height:8px;border-radius:50%;background:var(--good-lift);flex:none}
.toast.warn i{background:var(--warn-lift)}
@keyframes toastIn{from{opacity:0;transform:translateY(20px) scale(.96)}to{opacity:1;transform:none}}

/* ============ CTA / FOOTER (footer stays the dark anchor) ============ */
.cta{position:relative;background:var(--accent-tint);color:var(--text);text-align:center;padding-block:clamp(56px,7vw,88px)}
.cta h2{color:var(--text)}.cta .lede{color:var(--muted);margin-inline:auto}
footer .inner{padding-block:var(--s16)}
.fcols{display:grid;gap:var(--s8);margin-top:var(--s10)}
@media(min-width:760px){.fcols{grid-template-columns:repeat(3,1fr)}}
.fcol h4{font-family:var(--data);font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--text-tertiary);margin:0 0 var(--s4);font-weight:400}
.fcol button{display:block;color:var(--text-secondary);font-size:15px;padding:9px 0;transition:color .2s;min-height:44px;text-align:left}
footer .lede{color:var(--text-secondary)}
.fbar{display:flex;flex-wrap:wrap;gap:var(--s5);justify-content:space-between;align-items:center;border-top:1px solid rgba(255,255,255,.12);
  margin-top:var(--s10);padding-top:var(--s6);font-family:var(--data);font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--text-tertiary)}
.fbar a{color:var(--text-secondary)}
.flag{display:inline-flex;align-items:center;gap:10px}
.flag svg{width:26px;height:16px;border:1px solid rgba(255,255,255,.2)}

/* ============ HOME SHOWPIECES (light now) ============ */
.fan{background:var(--surface);color:var(--text);overflow:hidden;position:relative;
  align-content:start;padding:88px 0 0;min-height:min(96vh,860px);display:grid;place-items:center}
.fan .inner{text-align:center;z-index:3;position:relative}
.fan .inner::before{content:"";position:absolute;left:50%;top:-6%;width:min(880px,120%);height:150%;
  transform:translateX(-50%);z-index:-1;pointer-events:none;
  background:radial-gradient(60% 60% at 50% 44%,color-mix(in srgb,var(--surface) 94%,transparent) 0%,color-mix(in srgb,var(--surface) 72%,transparent) 46%,transparent 78%)}
.fan .kicker{font-family:var(--data);font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;color:var(--faint)}
.fan .l1{font-family:var(--display);font-weight:600;font-size:clamp(20px,2.6vw,30px);letter-spacing:-.02em;color:var(--muted);margin:22px 0 0}
.fan .l1 .w{display:inline-block;overflow:hidden;vertical-align:bottom}
.fan .l1 .w>span{display:inline-block}
.fan .big{font-family:var(--display);font-weight:700;letter-spacing:-.045em;line-height:.92;
  font-size:clamp(48px,11vw,150px);margin:6px 0 0;color:var(--text);white-space:nowrap}
.fan .big .ltr{display:inline-block;will-change:transform}
.fan .big .lead{color:var(--primary)}
.fan .sub{color:var(--muted);max-width:46ch;margin:22px auto 0;font-size:clamp(14.5px,1.5vw,17px)}
.fanCards{position:absolute;inset:0;display:grid;place-items:center;align-items:end;pointer-events:none;z-index:1}
.fanRow{position:relative;width:min(1100px,96vw);height:clamp(220px,34vh,330px);margin-bottom:-4vh;pointer-events:none}
.slipcard{position:absolute;left:50%;top:50%;width:clamp(96px,12.4vw,164px);height:clamp(134px,17.6vw,228px);
  background:var(--surface);border:1px solid var(--rule);border-radius:8px;will-change:transform;pointer-events:none;user-select:none;
  box-shadow:0 24px 48px -28px color-mix(in srgb,var(--text) 45%,transparent);padding:14px 13px;overflow:hidden}
.slipcard .sh{display:flex;justify-content:space-between;font-family:var(--data);font-size:7.4px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint);border-bottom:1px dashed var(--rule-2);padding-bottom:8px}
.slipcard .sr{display:flex;justify-content:space-between;font-family:var(--data);font-size:8.6px;
  color:var(--muted);padding:6px 0;border-bottom:1px solid var(--rule);font-variant-numeric:tabular-nums}
.slipcard .sr b{color:var(--text);font-weight:500}
.slipcard .sf{position:absolute;left:13px;right:13px;bottom:12px;display:flex;justify-content:space-between;align-items:center;
  font-family:var(--data);font-size:7px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint)}
/* a miniature monthly pay summary: paydays, take-home, gross, PAYE, a
   deduction split. The coloured dots that used to sit here meant nothing. */
.slipcard .pd{color:var(--text)}
.slipcard .net{display:block;font-family:var(--data);font-size:clamp(12px,1.35vw,17px);font-weight:600;color:var(--text);
  letter-spacing:-.01em;margin:8px 0 0;font-variant-numeric:tabular-nums}
.slipcard .netL{display:block;font-family:var(--data);font-size:6.6px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--faint);margin-bottom:5px}
.slipcard .split{display:flex;gap:1.5px;height:3px;margin:7px 0 0;border-radius:2px;overflow:hidden}
.slipcard .split i{width:var(--w);background:var(--c)}
.slipcard .sf{justify-content:flex-start}
.slipcard .cue{color:var(--income);font-weight:600}
.slipcard.third{border-color:color-mix(in srgb,var(--income) 45%,var(--rule))}
/* The fan is the design at every size. A phone used to get a flat strip
   of cards ABOVE the heading with six-pixel type and a "scroll" hint that
   pointed at nothing: the desktop composition inverted and shrunk. It now
   gets the same thing desktop gets, heading first and twelve cards fanned
   beneath it dealing in and drifting on scroll, with the geometry scaled to
   the width rather than replaced. */
.fanHint{position:absolute;left:50%;bottom:14px;transform:translateX(-50%);z-index:3;
  font-family:var(--data);font-size:9.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--faint);
  display:flex;align-items:center;gap:10px}
.fanHint i{display:block;width:1px;height:20px;background:currentColor}
@keyframes hint{0%,100%{transform:scaleY(.35);opacity:.4}50%{transform:scaleY(1);opacity:1}}
@media(max-width:760px){
  /* room for the fan under the copy; the cards sit in the lower part of
     the section exactly as they do on a wide screen */
  /* The cards are pinned to the section's end. On a wide screen the section
     is a viewport tall so the fan sits low and drifts up as you scroll; on a
     phone that same rule left 350px of nothing between the total and the
     cards. The section is now as tall as its copy plus exactly the room the
     fan needs, so the cards sit right under the words. */
  .fan{min-height:auto;padding:76px 0 calc(clamp(200px,32vh,280px) + 28px)}
  .fanRow{height:clamp(200px,32vh,280px);margin-bottom:-2vh}
  /* the hint sat on top of the cards; under the total it points where the
     cards actually are */
  .fanHint{position:static;transform:none;margin:18px auto 0;justify-content:center}
  /* the same card as desktop at the bottom of its own size range, and the
     same type: the 84px card with 6px labels was unreadable */
  .slipcard{width:104px;height:146px;padding:11px 10px}
  .fan .big{font-size:clamp(38px,13vw,64px)}
}

/* ============ PANELS (iwrap) / CHIPS ============ */
.iwrap{margin-top:var(--s10);border:1px solid var(--rule);background:var(--surface);border-radius:var(--r-lg);box-shadow:var(--shadow-1);overflow:hidden}
.band .iwrap{border-color:var(--rule);background:var(--surface)}
.ihead{padding:var(--s4) var(--s6);border-bottom:1px solid var(--rule);background:var(--surface-2);font-family:var(--data);font-size:10px;
  letter-spacing:.18em;text-transform:uppercase;color:var(--muted);display:flex;justify-content:space-between;gap:var(--s3);flex-wrap:wrap;align-items:center}
.band .ihead{border-color:var(--rule);color:var(--muted)}
.ibody{padding:var(--s6)}
@media(min-width:768px){.ibody{padding:var(--s8)}}

.chips{display:flex;flex-wrap:wrap;gap:10px}
.chip{border:1.5px solid var(--rule-2);background:var(--surface);border-radius:999px;padding:11px 18px;
  font-family:var(--body);font-weight:600;font-size:13.5px;color:var(--muted);min-height:44px;
  transition:border-color .2s var(--ease),background .2s,color .2s,transform .14s}
.chip:hover{border-color:var(--accent);color:var(--accent-strong)}
.chip:active{transform:scale(.97)}
.chip[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.band .chip{border-color:var(--rule-2);color:var(--muted);background:var(--surface)}
.band .chip:hover{border-color:var(--accent);color:var(--accent-strong)}
.band .chip[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
@keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* ============ CHECK/INCOME ROWS ============ */
.checkOpts{display:flex;flex-wrap:wrap;gap:var(--s3);align-items:flex-end;margin:var(--s5) 0 var(--s1)}
.checkOpts .field{min-width:170px;flex:0 0 auto}
.periodList{display:grid;gap:var(--s4);margin-top:var(--s4)}
.periodCard{border:1px solid var(--rule);background:var(--surface);border-radius:var(--r-lg);padding:var(--s5);position:relative;box-shadow:var(--shadow-1)}
.periodCard .pcHead{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:var(--s3)}
.periodCard .pcNum{font-family:var(--data);font-size:11px;letter-spacing:.16em;color:var(--faint);text-transform:uppercase}
.periodCard .pcDel{font-size:13.5px;font-weight:600;color:var(--faint);min-height:44px;display:inline-flex;align-items:center}
.periodCard .pcDel:hover{color:var(--bad)}
.pcGrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:var(--s3) var(--s4)}
/* ---- add form + saved lines: type, press Add, see it land ---- */
.addBox{border:1px solid var(--rule);background:var(--surface-2,var(--surface));border-radius:var(--r-lg);padding:var(--s5);margin-top:var(--s4)}
.addBar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s3) var(--s4);margin-top:var(--s4)}
.addBar .btn{flex:0 0 auto}
.addMsg{margin:0;flex:1 1 200px;min-width:0;font-size:13.5px;line-height:1.45;color:var(--faint)}
.addMsg.bad{color:var(--bad);font-weight:600}
/* name over date on the left, amount over Remove on the right: two lines,
   never three, even on the narrowest phone */
.savedRow{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;
  column-gap:var(--s3);
  border:1px solid var(--rule);background:var(--surface);border-radius:var(--r-lg);
  padding:var(--s2) var(--s4);box-shadow:var(--shadow-1)}
.savedRow .srMain{grid-column:1;grid-row:1/3;min-width:0;display:flex;flex-direction:column;gap:1px}
.savedRow .srMain b{font-weight:600;overflow-wrap:anywhere}
.savedRow .srMeta{font-family:var(--data);font-size:12px;letter-spacing:.04em;color:var(--faint)}
.savedRow .srAmt{font-family:var(--data);font-weight:600;color:var(--good,var(--ink));white-space:nowrap;
  grid-column:2;grid-row:1;justify-self:end;align-self:end}
.savedRow .pcDel{font-size:13px;font-weight:600;color:var(--faint);min-height:40px;display:inline-flex;align-items:center;
  grid-column:2;grid-row:2;justify-self:end}
.savedRow .pcDel:hover{color:var(--bad)}
/* 16px is the floor for any field on iOS: below it Safari zooms the page on
   focus, which moves every fixed element and cannot be dismissed by hand. */
.pcGrid .field input{font-family:var(--data);font-size:16px}
.pcCheck{display:flex;align-items:center;gap:10px;margin-top:var(--s5);color:var(--muted);font-size:14.5px}
.pcCheck input{width:18px;height:18px;accent-color:var(--accent)}
.checkActions{display:flex;flex-wrap:wrap;gap:var(--s3) var(--s5);align-items:center;margin-top:var(--s5)}
.checkErrors{border:1.5px solid var(--warn-lift);background:var(--warn-tint);border-radius:var(--r);padding:var(--s4) var(--s5);margin-top:var(--s5);color:var(--text)}
.checkErrors ul{margin:8px 0 0;padding-left:18px}
.checkErrors li{margin:4px 0;color:var(--muted);font-size:14.5px}
/* This class carries almost every sentence that teaches the app. It was
   14px tertiary, which fails AA on our background. */
.checkNote{font-size:15px;color:var(--text-secondary);margin-top:var(--s3);max-width:60ch;line-height:1.5}

/* ============ STATE PAGES ============ */
.stateBox{max-width:520px;margin:0 auto;text-align:center;padding:var(--s16) 0 var(--s2)}
.stateBox .stCode{font-family:var(--data);font-size:12px;letter-spacing:.22em;color:var(--faint);text-transform:uppercase}
.stateBox h2{font-weight:700;letter-spacing:-.03em;font-size:clamp(28px,5vw,40px);margin:14px 0 10px;color:var(--text)}
.stateBox p{color:var(--muted);margin:0 0 var(--s5)}
.band .stateBox h2{color:var(--text)}
.band .stateBox p{color:var(--muted)}
.band .stateBox .stCode{color:var(--faint)}
.stateStars{width:120px;margin:0 auto var(--s2);opacity:.6}

/* ============ AUTH ============ */
.authWrap{max-width:460px;margin:0 auto}
.authTabs{display:flex;gap:var(--s2);background:var(--surface-2);border-radius:var(--r);padding:4px;margin-bottom:var(--s6)}
.authTabs button{flex:1;font-weight:600;font-size:14.5px;color:var(--muted);padding:12px;border-radius:9px;min-height:48px;transition:background .2s,color .2s}
.authTabs button::after{display:none}
.authTabs button[aria-selected="true"]{background:var(--surface);color:var(--text);box-shadow:var(--shadow-1)}
.authPanel{display:none}
.authPanel.on{display:block;animation:fadeUp .35s var(--ease)}
.authMeta{display:flex;justify-content:space-between;align-items:center;margin-top:var(--s4);font-size:14px}
.authMeta a,.authLink{color:var(--accent-strong);border-bottom:1px solid var(--rule-2);cursor:pointer;font-weight:500}
.authMeta a:hover,.authLink:hover{border-color:var(--accent)}
.authErr{border:1.5px solid var(--bad);background:var(--bad-tint);border-radius:var(--r);padding:12px 14px;margin-top:var(--s4);font-size:14px;color:var(--text);display:none}
.authErr.on{display:block}
.oauthRow{display:grid;gap:10px;margin-top:var(--s6)}
.oauthRow button{border:1.5px solid var(--rule-2);background:var(--surface);border-radius:var(--r);padding:13px 16px;color:var(--text);
  font-size:15px;font-weight:500;min-height:52px;display:flex;align-items:center;justify-content:center;gap:10px;transition:border-color .2s,background .2s}
.oauthRow button:hover{border-color:var(--accent);background:var(--accent-tint)}
.oauthRow svg{width:17px;height:17px}
.authOr{display:flex;align-items:center;gap:14px;margin-top:var(--s6);color:var(--faint);
  font-family:var(--data);font-size:10.5px;letter-spacing:.2em;text-transform:uppercase}
.authOr::before,.authOr::after{content:"";flex:1;height:1px;background:var(--rule)}
.hintBox{border:1.5px dashed var(--rule-2);border-radius:var(--r);padding:12px 14px;margin-top:var(--s5);font-size:13.5px;color:var(--muted)}
.hintBox .k{font-family:var(--data);color:var(--text);font-weight:600}
.authWrap h3{color:var(--text)!important}
/* Waiting on an email is the one place the product asks somebody to go
   and look somewhere else. These three pieces carry that moment: what
   the email looks like, how long the resend button stays shut, and the
   two ways out for a person who has been here before. */
.mailTips{list-style:none;margin:var(--s4) 0 0;padding:0;display:grid;gap:8px;
  font-size:14.5px;color:var(--text-secondary);line-height:1.5;max-width:52ch}
.mailTips li{position:relative;padding-left:20px}
.mailTips li::before{content:"";position:absolute;left:3px;top:.62em;width:6px;height:6px;
  border-radius:50%;background:var(--accent)}
.mailTips b{color:var(--text);font-weight:600}
.waitNote{margin:10px 0 0;font-size:13.5px;color:var(--muted);font-variant-numeric:tabular-nums}
.altBox{border:1.5px solid var(--rule);background:var(--surface-2);border-radius:var(--r);
  padding:14px 16px;margin-top:var(--s5)}
.altBox p{margin:0;font-size:14px;color:var(--text-secondary);line-height:1.55;max-width:56ch}
.altBox b{color:var(--text)}
.altRow{display:flex;flex-wrap:wrap;gap:12px 14px;margin-top:14px}
.altRow .solid,.altRow .outl{flex:1 1 auto;justify-content:center;min-width:150px}
/* An inline link inside a note keeps its 44px touch target, but the
   underline has to stay attached to the words. The padding that makes
   the target pushed the border a clear 10px below the text, which left a
   grey dash floating on its own under the paragraph. */
.checkNote .authLink{border-bottom:0;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px}

/* ============ ACCOUNT / DASHBOARD ============ */
.acct-top{display:flex;flex-wrap:wrap;gap:var(--s5);align-items:center;justify-content:space-between;padding:var(--s6) 0;
  border-block:1px solid var(--rule);margin-top:var(--s10)}
.avatar{width:52px;height:52px;border-radius:50%;display:grid;place-items:center;flex:none;
  border:1.5px solid var(--rule-2);background:var(--accent-tint)}
.avatar svg{width:21px;height:26px}
.avatar .pg{fill:none;stroke:var(--accent-strong);stroke-width:1.4;stroke-linejoin:round}
.avatar .pl{stroke:var(--accent-soft);stroke-width:1.3;stroke-linecap:round}
.avatar .pd{fill:var(--accent)}
.uname{font-family:var(--display);font-weight:700;font-size:20px;letter-spacing:-.02em;color:var(--text)}
.meta{font-family:var(--data);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);margin-top:5px}
.acct-grid{display:grid;gap:var(--s10);margin-top:var(--s10)}
@media(min-width:960px){.acct-grid{grid-template-columns:1.12fr .88fr}}
.ph{display:flex;justify-content:space-between;align-items:center;gap:var(--s3);padding-bottom:var(--s4);border-bottom:1px solid var(--rule);
  font-family:var(--data);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.line{display:grid;grid-template-columns:1fr auto;gap:var(--s3) var(--s4);align-items:center;padding:var(--s5) 0;border-bottom:1px solid var(--rule);animation:accIn .45s var(--ease)}
.rtitle{font-family:var(--display);font-weight:600;font-size:16px;color:var(--text)}
.amt-r{font-family:var(--data);font-size:15px;font-variant-numeric:tabular-nums;color:var(--accent-strong)}
.amt-r.none{color:var(--faint)}
.rtitle,.uname,.line .meta{overflow-wrap:anywhere}
.line>div:first-child{min-width:0}
.sw{display:flex;justify-content:space-between;gap:var(--s5);align-items:flex-start;padding:var(--s5) 0;border-bottom:1px solid var(--rule)}
.sw strong{display:block;font-family:var(--display);font-weight:600;font-size:15.5px;color:var(--text)}
.sw span{display:block;font-size:14px;color:var(--muted);margin-top:6px;max-width:44ch}
.toggle{width:52px;height:30px;border-radius:999px;flex:none;border:1.5px solid var(--rule-2);background:var(--surface-2);position:relative;transition:.3s var(--ease)}
.toggle::after{content:"";position:absolute;top:3px;left:3px;width:20px;height:20px;border-radius:50%;background:var(--faint);transition:.3s var(--ease)}
.toggle[aria-pressed="true"]{border-color:var(--good);background:var(--good-tint)}
.toggle[aria-pressed="true"]::after{left:25px;background:var(--good)}
.bar{height:6px;background:var(--surface-3);overflow:hidden;margin-top:var(--s6);border-radius:999px}
.bar i{display:block;height:100%;width:0;background:var(--accent);transition:width 1.2s var(--ease);border-radius:999px}
.danger{margin-top:var(--s10);padding:var(--s5);border:1.5px solid color-mix(in srgb,var(--bad) 35%,transparent);border-radius:var(--r-lg);background:var(--bad-tint)}
.danger strong{font-family:var(--display);color:var(--text);font-size:15.5px}
.danger p{margin:9px 0 var(--s4);color:var(--muted);font-size:14px;max-width:52ch}

/* app cards (dashboard/settings/pricing) */
.appwrap{max-width:760px;margin:0 auto;display:grid;gap:var(--app-gap,24px)}
.bignum{font-family:var(--data);font-weight:600;letter-spacing:-.02em;
  font-size:clamp(36px,9vw,54px);line-height:1;color:var(--text);font-variant-numeric:tabular-nums}
.bignum.good{color:var(--good)} .bignum.warn{color:var(--warn)}
.kpi{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.kpi .unit{font-family:var(--data);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.tile{display:grid;gap:6px;padding:var(--s4);border-radius:var(--r);background:var(--surface-2)}
.tile b{font-family:var(--data);font-size:20px;letter-spacing:-.01em;font-variant-numeric:tabular-nums;color:var(--text)}
.tile span{font-size:13px;color:var(--faint)}
.tilerow{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:var(--s3)}
.seg{display:inline-flex;background:var(--surface-2);border-radius:var(--r);padding:4px;gap:4px;flex-wrap:wrap}
.seg button{min-height:44px;padding:0 16px;border-radius:9px;font-size:14.5px;font-weight:500;color:var(--muted);background:transparent;transition:background .2s,color .2s}
.seg button[aria-pressed="true"]{background:var(--surface);color:var(--text);box-shadow:var(--shadow-1);font-weight:600}

/* ============ ONBOARDING JOURNEY ============ */
.journey{max-width:660px;margin:0 auto}
.jtop{display:flex;justify-content:space-between;align-items:center;gap:var(--s4);margin-bottom:var(--s5)}
.jtop .jstage{font-family:var(--data);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--faint)}
.jroute{display:flex;align-items:center;gap:0;margin:0 0 var(--s8);padding:var(--s2) 0;overflow-x:auto;scrollbar-width:none}
.jroute::-webkit-scrollbar{display:none}
.jnode{display:flex;flex-direction:column;align-items:center;gap:8px;min-width:64px;position:relative;flex:1}
.jnode .jdot{width:14px;height:14px;border-radius:50%;background:var(--surface);border:2.5px solid var(--rule-2);
  transition:all .35s var(--ease);position:relative;z-index:1}
.jnode .jlabel{font-size:11.5px;font-weight:600;color:var(--faint);white-space:nowrap;transition:color .3s}
.jnode.done .jdot{background:var(--accent);border-color:var(--accent)}
.jnode.done .jdot::after{content:"";position:absolute;left:3px;top:1px;width:4px;height:7px;
  border:1.8px solid #fff;border-top:0;border-left:0;transform:rotate(40deg)}
.jnode.done .jlabel{color:var(--muted)}
.jnode.active .jdot{background:var(--accent-tint);border-color:var(--accent);transform:scale(1.35);box-shadow:var(--ring)}
.jnode.active .jlabel{color:var(--accent-strong);font-weight:700}
.jline{height:2.5px;flex:1 1 20px;min-width:14px;background:var(--rule-2);border-radius:2px;position:relative;top:-11px;overflow:hidden}
.jline i{display:block;height:100%;width:0;background:var(--accent);transition:width .45s var(--ease)}
.jline.done i{width:100%}
.jpanel{background:var(--surface);border:1px solid var(--rule);border-radius:var(--r-xl);padding:var(--s6);box-shadow:var(--shadow-1)}
@media(min-width:768px){.jpanel{padding:var(--s8)}}
.jpanel h3{font-size:clamp(22px,3vw,27px);letter-spacing:-.02em;margin-bottom:6px}
.jpanel .jhelp{color:var(--muted);font-size:15.5px;margin:0 0 var(--s6)}
.jbody{animation:jIn .4s var(--ease)}
.jbody.back{animation:jInBack .4s var(--ease)}
@keyframes jIn{from{opacity:0;transform:translateX(22px)}to{opacity:1;transform:none}}
@keyframes jInBack{from{opacity:0;transform:translateX(-22px)}to{opacity:1;transform:none}}
.jacts{display:flex;gap:var(--s3);margin-top:var(--s8);align-items:stretch}
.jacts .solid{flex:1}
/* Back and Continue sit in one row: same height, same type size, or the
   row reads as two different controls */
.jacts .outl{min-height:52px;padding:0 26px;font-size:16px}
.selGrid{display:grid;gap:10px}
@media(min-width:560px){.selGrid.two{grid-template-columns:1fr 1fr}}
.selCard{position:relative;text-align:left;border:1.5px solid var(--rule-2);background:var(--surface);border-radius:var(--r);
  padding:15px 48px 15px 18px;min-height:56px;font-size:15.5px;font-weight:500;color:var(--text);width:100%;
  transition:border-color .18s var(--ease),background .18s,transform .14s,box-shadow .18s}
.selCard small{display:block;font-weight:400;color:var(--faint);font-size:13.5px;margin-top:3px}
@media(hover:hover){.selCard:hover{border-color:var(--accent);transform:translateY(-1px);box-shadow:var(--shadow-1)}}
.selCard:active{transform:scale(.985)}
.selCard::after{content:"";position:absolute;right:16px;top:50%;transform:translateY(-50%) scale(.5);
  width:22px;height:22px;border-radius:50%;border:2px solid var(--rule-2);opacity:.6;transition:all .2s var(--ease)}
.selCard[aria-pressed="true"]{border-color:var(--accent);background:var(--accent-tint);font-weight:600}
.selCard[aria-pressed="true"]::after{opacity:1;transform:translateY(-50%) scale(1);border-color:var(--accent);background:var(--accent);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='9'%3E%3Cpath d='M1 4.5l3 3L10 1' fill='none' stroke='white' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
  background-position:center;background-repeat:no-repeat}
.palGrid{display:grid;gap:var(--s3);grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
.palCard{position:relative;border:2px solid var(--rule-2);border-radius:var(--r);overflow:hidden;padding:0;text-align:left;
  transition:border-color .2s var(--ease),transform .14s,box-shadow .2s;background:var(--pv-bg,#FAF9F6)}
@media(hover:hover){.palCard:hover{transform:translateY(-2px);box-shadow:var(--shadow-2)}}
.palCard[aria-pressed="true"]{border-color:var(--pv-ac);box-shadow:0 0 0 3px color-mix(in srgb,var(--pv-ac) 25%,transparent)}
.palCard .pv{padding:14px 14px 12px}
.palCard .pvCard{background:var(--pv-surface,#fff);border:1px solid rgba(29,27,34,.12);border-radius:8px;padding:10px}
.palCard.pride .pvBtn{background:var(--gradient-pride)}
.palCard.pride .pvName::after{content:" \1F3F3\FE0F\200D\1F308";font-size:11px}
.palCard .pvRow{height:6px;border-radius:3px;background:var(--pv-ink,#232028);opacity:.85;width:70%}
.palCard .pvRow+.pvRow{margin-top:5px;width:46%;opacity:.4}
.palCard .pvBtn{margin-top:10px;height:18px;border-radius:6px;background:var(--pv-ac);width:60%}
.palCard .pvName{padding:10px 14px;font-size:13.5px;font-weight:600;color:var(--text);background:var(--surface);border-top:1px solid var(--rule);display:flex;justify-content:space-between;align-items:center}
.palCard[aria-pressed="true"] .pvName::after{content:"✓";color:var(--accent-strong);font-weight:700}

/* ============ BUDGET GROUPS + STICKY SUMMARY ============ */
.bgroup{border:1px solid var(--rule);background:var(--surface);border-radius:var(--r-lg);box-shadow:var(--shadow-1);overflow:hidden;margin-top:var(--s4)}
.bgroup>summary{list-style:none;cursor:pointer;display:flex;justify-content:space-between;align-items:center;gap:var(--s3);
  padding:var(--s4) var(--s5);background:var(--surface);min-height:60px}
.bgroup>summary::-webkit-details-marker{display:none}
.bgroup .gname{display:flex;align-items:center;gap:12px;font-weight:600;font-size:16px;color:var(--text)}
.bgroup .gname .gi{width:34px;height:34px;border-radius:10px;display:grid;place-items:center;background:var(--accent-tint);color:var(--accent-strong)}
.bgroup .gsum{font-family:var(--data);font-size:14.5px;color:var(--muted);font-variant-numeric:tabular-nums;display:flex;align-items:center;gap:10px}
.bgroup .gsum .pm{width:18px;height:18px}
.bgroup .gbody{padding:0 var(--s5) var(--s5);border-top:1px solid var(--rule)}
/* Was solid black (heavy, and it fought the page) and sat at z-index 40
   BENEATH the fixed bottom nav at z-index 60, so on a phone the save
   control was literally behind the navigation. Now it is a light,
   readable surface that clears the nav bar. */
/* ============================================================
   THE BUDGET ACTION BAR

   It was a white slab floating between the form and the navigation,
   with its own border and its own shadow, repeating "Safe to spend
   $600.00" a hundred pixels below the panel that already said it, and
   the central Add control cut across its bottom edge.

   Now it docks onto the navigation and shares its glass, so the two
   read as one pane. Three things make that work:

     The middle column is EMPTY, always, at the exact width of the Add
     control's bump. That is the gap the control rises through, and
     nothing may ever be laid out into it.

     It paints BELOW the navigation, so the Add control is always in
     front of it rather than fighting it for the same pixels.

     The figure appears only once the panel carrying the same figure
     has scrolled away. Two elements saying the same number in the same
     viewport is not redundancy, it is a reason to distrust both.
   ============================================================ */
.stickySum{position:sticky;z-index:70;margin-top:var(--s6);
  bottom:calc(14px + env(safe-area-inset-bottom,0px));
  background:color-mix(in srgb,var(--surface) 92%,transparent);
  backdrop-filter:blur(14px) saturate(1.2);
  -webkit-backdrop-filter:blur(14px) saturate(1.2);
  color:var(--text-primary);border:1px solid var(--border);
  border-radius:var(--r-lg);padding:var(--s4) var(--s5);
  display:flex;justify-content:space-between;align-items:center;gap:var(--s4);
  box-shadow:0 8px 28px color-mix(in srgb,var(--text) 14%,transparent)}
/* on a desktop there is no bottom navigation to dock to, and no Add
   control to leave room for */
@media(min-width:1024px){.stickySum .ssGap{display:none}}

@media(max-width:1023px){
  .stickySum{
    position:fixed;left:0;right:0;
    /* flush on top of the navigation: no gap, no inset, no floating */
    bottom:calc(var(--pnav-h) + env(safe-area-inset-bottom,0px));
    margin:0;border-radius:0;border:0;
    border-top:1px solid var(--border);
    /* one hairline between the two, and the same glass on both sides of
       it, so the eye reads a single pane with a divider rather than two
       objects that happen to touch */
    background:color-mix(in srgb,var(--surface) 82%,transparent);
    backdrop-filter:blur(22px) saturate(1.6);
    -webkit-backdrop-filter:blur(22px) saturate(1.6);
    box-shadow:0 -8px 24px color-mix(in srgb,var(--text) 7%,transparent);
    /* below the navigation, so the Add control paints over it */
    z-index:55;
    display:grid;
    grid-template-columns:minmax(0,1fr) var(--pnav-bump) minmax(0,1fr);
    align-items:center;gap:0;
    min-height:54px;padding:0 var(--s4) 0 var(--s5);
    transition:transform .28s var(--ease),opacity .22s var(--ease)}
  html[data-mode="dark"] .stickySum{
    background:color-mix(in srgb,var(--surface) 86%,transparent);
    box-shadow:0 -8px 24px rgba(0,0,0,.32)}
  /* transparency without blur is only a legibility problem */
  /* Safari shipped unprefixed backdrop-filter only in 18.0, so this test was
   TRUE on iOS 16 and 17 and stripped the glass from phones where the
   prefixed property works perfectly. Ask about both spellings. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
    .stickySum,html[data-mode="dark"] .stickySum{background:var(--surface)}}
  @media(prefers-reduced-transparency:reduce){
    .stickySum,html[data-mode="dark"] .stickySum{
      background:var(--surface);backdrop-filter:none;-webkit-backdrop-filter:none}}

  /* the form can always be scrolled clear of both bars, so the last
     field is never trapped behind them at the end of the page */
  body.in-portal #budApp{padding-bottom:calc(150px + env(safe-area-inset-bottom,0px))}

  /* It rides the keyboard with the navigation, so the two never separate
     while somebody types. It used to drop away the moment any field had
     focus, and the Budget page IS fields, so it was gone almost all the
     time: it appeared, and then it left. It stays. The form has 150px of
     clearance underneath it, so nothing is ever typed into beneath it. */
  .stickySum{transform:translateY(calc(var(--kb,0px) * -1))}

  /* Nothing to act on yet: no figure, no bar. It arrives when the page has
     something to say. Once it has arrived it does not leave again for the
     rest of the visit: budQuickSum latches it. */
  .stickySum[data-ready="0"]{transform:translateY(140%);opacity:0;pointer-events:none}

  .stickySum .ssGo{justify-self:end}
}

/* the gap the Add control rises through. It holds its width and never
   holds content. */
.stickySum .ssGap{width:var(--pnav-bump);flex:none;pointer-events:none}

/* The figure, shown only when the panel above is out of sight. It fades
   rather than appearing, because a number that pops into a toolbar reads
   as a change in the number itself. */
.stickySum .ssFig{display:flex;flex-direction:column;justify-content:center;min-width:0;
  gap:1px;opacity:0;transform:translateY(3px);pointer-events:none;
  transition:opacity .22s var(--ease),transform .22s var(--ease)}
.stickySum[data-fig="1"] .ssFig{opacity:1;transform:none}
html[data-motion="reduced"] .stickySum .ssFig{transition:none}
.stickySum .ssLabel{font-size:11.5px;letter-spacing:.01em;color:var(--text-tertiary,var(--text-secondary));
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2}
.stickySum .ssAmt{font-family:var(--data);font-weight:600;font-size:17px;
  font-variant-numeric:tabular-nums;line-height:1.15;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.stickySum .ssAmt.neg{color:var(--danger)}

/* A toolbar action is a tinted label, not a filled slab. It still meets
   the 44px target through its padding rather than through a box drawn
   around it. */
.stickySum .ssGo{appearance:none;background:none;border:0;padding:0 2px;margin:0;
  font:inherit;font-family:var(--display);font-weight:600;font-size:15.5px;
  letter-spacing:-.005em;color:var(--accent-strong);cursor:pointer;
  min-height:44px;display:inline-flex;align-items:center;white-space:nowrap;
  border-radius:8px;
  transition:opacity var(--m-fast) var(--ease-release),transform var(--m-press) var(--ease-release)}
.stickySum .ssGo:hover{opacity:.75}
.stickySum .ssGo:active{transform:scale(.96)}
.stickySum .ssGo:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
html[data-motion="reduced"] .stickySum .ssGo:active{transform:none;filter:brightness(.9)}
/* there is room for the long label until the figure arrives beside it */
@media(max-width:359px){.stickySum .ssGoLong{display:none}}

/* ============ PAYSLIP STATES + REVIEW ============ */
.psStage{display:grid;gap:var(--s3);padding:var(--s5) 0}
.psStep{display:flex;align-items:center;gap:12px;color:var(--faint);font-size:14.5px;transition:color .3s}
.psStep i{width:18px;height:18px;border-radius:50%;border:2px solid var(--rule-2);flex:none;display:grid;place-items:center;transition:all .3s}
.psStep.on{color:var(--text)}
.psStep.on i{border-color:var(--accent);background:var(--accent-tint)}
.psStep.done{color:var(--muted)}
.psStep.done i{border-color:var(--good);background:var(--good);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='7'%3E%3Cpath d='M1 3.5l2.2 2.2L8 1' fill='none' stroke='white' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
  background-position:center;background-repeat:no-repeat}
.confChip{font-size:11.5px;font-weight:600;padding:4px 10px;border-radius:999px;white-space:nowrap}
.confChip.ok{background:var(--good-tint);color:var(--good)}
.confChip.check{background:var(--warn-tint);color:var(--warn)}
.confChip.miss{background:var(--surface-3);color:var(--faint)}
.psField{display:grid;grid-template-columns:1fr auto;gap:var(--s2) var(--s4);align-items:center;padding:12px 0;border-bottom:1px solid var(--rule)}
.psField .k{font-size:14.5px;color:var(--muted)}
.psField .v{font-family:var(--data);font-size:15px;color:var(--text);font-variant-numeric:tabular-nums;display:flex;align-items:center;gap:10px;justify-content:flex-end}
/*16px floor: iOS zooms the page on focus below it*/
.psField input{font-size:16px;width:130px;background:var(--surface);border:1.5px solid var(--rule-2);border-radius:8px;color:var(--text);
  padding:8px 10px;font-family:var(--data);font-size:16px;text-align:right}
.psField input:focus{border-color:var(--accent);outline:none;box-shadow:var(--ring)}
.psGroupHead{font-family:var(--data);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--accent-strong);padding:var(--s5) 0 var(--s2)}
body.has-bottomnav main{padding-bottom:76px}
@media(min-width:820px){.flips{grid-template-columns:repeat(3,1fr)}}
.flip{perspective:1200px;height:210px}
.barRow{display:grid;grid-template-columns:minmax(64px,auto) 1fr 72px;align-items:center;gap:var(--s4);padding:4px 0;
  font-size:13px;font-weight:500;color:var(--muted)}
.barRow .track{height:8px;background:var(--surface-3);overflow:hidden;border-radius:999px}
.barRow .track i{display:block;height:100%;width:0;background:var(--accent);transition:width 1s var(--ease);border-radius:999px}
.barRow b{color:var(--text);font-weight:600;text-align:right;font-family:var(--data);font-variant-numeric:tabular-nums}
/* The sweep carries a trace of the brand accent rather than a lighter grey.
   It is under ten percent, so it reads as warmth rather than as colour, and
   a loading screen still looks like this product and not like any other. */
.skel{height:14px;border-radius:6px;
  background:linear-gradient(90deg,
    var(--surface-2),
    color-mix(in srgb,var(--primary) 7%,var(--surface-3)),
    var(--surface-2));
  background-size:200% 100%;animation:shimmer 1.4s linear infinite}
html[data-motion="reduced"] .skel{animation:none;background:var(--surface-2)}
@keyframes shimmer{to{background-position:-200% 0}}
.repBlock{border:1px solid var(--rule);border-radius:var(--r);padding:var(--s5);margin-top:var(--s4);background:var(--surface-2)}
.repBlock h4{font-family:var(--data);font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--faint);margin:0 0 10px}
.repBlock ul{margin:0;padding-left:18px}
.repBlock li{font-size:14px;color:var(--muted);margin:6px 0}
.cookieBar{position:fixed;left:0;right:0;bottom:0;z-index:120;background:var(--surface);color:var(--text);
  border-top:1px solid var(--rule-2);box-shadow:var(--shadow-2);transform:translateY(102%);transition:transform .5s var(--ease)}
.cookieBar.on{transform:translateY(0)}
.cookieBar .inner{display:flex;flex-wrap:wrap;gap:14px 26px;align-items:center;padding-block:var(--s4)}
.cookieBar p{margin:0;font-size:13.5px;color:var(--muted);flex:1 1 380px}
.cookieBar p a{color:var(--text);border-bottom:1px solid var(--rule-2)}
.cookieActs{display:flex;gap:10px;flex-wrap:wrap}
@media(max-width:560px){.cookieActs{display:grid;grid-template-columns:1fr 1fr;width:100%;gap:8px}.cookieActs .chip{justify-content:center}.cookieActs .chip:last-child{grid-column:1/-1}.cookieBar p{font-size:13px;margin-bottom:10px}}
.cookieActs .chip{cursor:pointer}
svg.arw{width:15px;height:15px;flex:none}
.blurRows{filter:blur(5px);pointer-events:none;user-select:none;opacity:.8}

/* coffee */
/* The saving tag used to be absolutely positioned under the cup, so
   revealing it dropped a nowrap pill straight onto the lede beneath at
   every width. It now has its own row, reserved whether shown or not. */
.coffee{position:relative;display:grid;justify-items:center;gap:var(--s3);grid-template-rows:auto auto;
  margin:var(--s6) 0 var(--s5)}
.coffee .cup{grid-row:1}
.coffee svg{overflow:visible}
.coffee .steam path{fill:none;stroke:var(--app-accent-soft,var(--accent-soft));stroke-width:3.6;stroke-linecap:round;
  transform-box:fill-box;transform-origin:50% 100%;opacity:0;animation:steamRise 3.6s var(--ease) infinite}
.coffee .steam .st1{--o:.72}
.coffee .steam .st2{--o:.85;animation-duration:4.4s;animation-delay:-1.2s}
.coffee .steam .st3{--o:.66;animation-duration:3.9s;animation-delay:-2.4s}
/* the steam leans as it climbs, because steam does */
@keyframes steamRise{0%{opacity:0;transform:translate(0,5px) scaleY(.8) rotate(0)}
 14%{opacity:var(--o)}
 55%{transform:translate(-2.5px,-6px) scaleY(1) rotate(-4deg)}
 86%{opacity:calc(var(--o) * .6)}
 100%{opacity:0;transform:translate(2.5px,-18px) scaleY(1.12) rotate(5deg)}}
/* the cup itself breathes, and leans into you when you point at it */
.coffee .cRise{transform-box:fill-box;transform-origin:50% 100%;animation:cupRise 5s ease-in-out infinite}
@keyframes cupRise{0%,100%{transform:translateY(0) rotate(0)}50%{transform:translateY(-5px) rotate(-1.4deg)}}
.coffee .cCup{fill:var(--app-accent)}
.coffee .cLid{fill:var(--app-accent-soft)}
.coffee .cBand{fill:var(--app-accent-ink);opacity:.35}
.coffee .cShine{fill:none;stroke:#fff;stroke-width:4;stroke-linecap:round;opacity:.22}
.coffee .cCoin text{font-size:13px}
.coffee .cSpark path{fill:var(--accent)}
.coffee:hover .cRise,.coffee.showSave .cRise{animation-play-state:paused;transform:translateY(-6px) rotate(-3deg);
  transition:transform .3s var(--spring,var(--ease))}
html[data-motion="reduced"] .coffee .steam path{animation:none;opacity:.45;transform:none}
html[data-motion="reduced"] .coffee .cRise{animation:none;transform:none}
@media(prefers-reduced-motion:reduce){.coffee .steam path{animation:none;opacity:.45;transform:none}
  .coffee .cRise{animation:none;transform:none}}

/* ============ PRINT ============ */
@media print{
  body{background:#fff}
  main{padding-top:0!important}
  .page{display:none!important}
  .page.printTarget{display:block!important}
  .receipt,.repBlock{border-color:#bbb!important;break-inside:avoid}
  a{text-decoration:none;color:#000}
}

/* header: keep one row on small screens */
header.top .inner{flex-wrap:nowrap}
#navCta{white-space:nowrap}
/* Both of these become the way back into the portal once signed in
   (#navCta -> "Open my Portal", #navAuth -> "Account"). Hiding both on a
   phone made the app unreachable at 390px, which is most of New Zealand.
   Keep one visible at every width. */
@media(max-width:680px){#navCta{display:none}}
#navAuth{min-height:44px}
.hend{flex:none}

/* ============ REMAINING PANEL (integrated, translucent, calm) ============ */
.remainPanel{margin-top:var(--s8);border:1.5px solid var(--rule-2);border-radius:var(--r-xl);
  background:color-mix(in srgb,var(--surface) 72%,transparent);backdrop-filter:blur(10px);
  -webkit-backdrop-filter:blur(10px);
  padding:var(--s6);display:grid;gap:var(--s5);box-shadow:var(--shadow-1)}
/* Safari shipped unprefixed backdrop-filter only in 18.0, so this test was
   TRUE on iOS 16 and 17 and stripped the glass from phones where the
   prefixed property works perfectly. Ask about both spellings. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){.remainPanel{background:var(--surface)}}
@media(min-width:900px){.remainPanel{grid-template-columns:1.1fr .9fr;align-items:center;padding:var(--s8)}}
.rpLabel{font-size:14px;font-weight:600;color:var(--muted)}
.rpAmt{font-family:var(--data);font-weight:600;font-size:clamp(30px,8vw,60px);line-height:1.05;color:var(--text);letter-spacing:-.02em;white-space:nowrap}
@media(max-width:360px){.rpAmt{font-size:clamp(24px,7.5vw,30px)}}
.rpSub{font-size:14.5px;color:var(--muted);margin-top:6px}
.rpSplit{display:grid;gap:8px}
.rpRow{display:flex;justify-content:space-between;gap:var(--s4);font-size:14px;color:var(--muted);
  padding:9px 12px;border-radius:10px;background:color-mix(in srgb,var(--surface-2) 80%,transparent)}
.rpRow b{font-family:var(--data);font-variant-numeric:tabular-nums;color:var(--text);font-weight:500}
.rpRow.hero b{color:var(--accent-strong);font-weight:600}

/* category progress */
.catRow{padding:var(--s3) 0;border-bottom:1px solid var(--rule)}
.catRow:last-child{border-bottom:0}
.catTop{display:flex;justify-content:space-between;gap:var(--s3);align-items:baseline;font-size:15px;font-weight:600;color:var(--text)}
.catTop .catAmt{font-family:var(--data);font-size:13.5px;font-weight:500;color:var(--muted);font-variant-numeric:tabular-nums}
.catBar{height:8px;background:var(--surface-3);border-radius:999px;overflow:hidden;margin-top:8px}
.catBar i{display:block;height:100%;border-radius:999px;background:var(--accent);transition:width .5s var(--ease)}
.catBar i.warn{background:var(--warn-lift)}
.catBar i.hot{background:var(--warn-hot,#B4551F)}
/* no planned amount: a quiet hatched track, so spending without a plan
   never masquerades as being "on plan" */
/* the hatch used --border, the same colour as the track, so this state
   was invisible: the bar read as empty either way */
.catBar i.noplan{background:repeating-linear-gradient(45deg,
  color-mix(in srgb,var(--text-tertiary) 55%,transparent) 0 5px,transparent 5px 10px)}
.catBar i.over{background:var(--bad)}
.catMeta{display:flex;gap:14px;font-size:12.5px;color:var(--faint);margin-top:6px;flex-wrap:wrap}

/* timeline */
.tlRow{display:grid;grid-template-columns:86px 1fr auto;gap:var(--s3);align-items:center;padding:11px 0;border-bottom:1px solid var(--rule);font-size:14.5px}
.tlRow:last-child{border-bottom:0}
.tlRow .tlDate{font-family:var(--data);font-size:12.5px;color:var(--faint);text-transform:uppercase;letter-spacing:.06em}
.tlRow .tlWhat{color:var(--text);display:flex;align-items:center;gap:10px;min-width:0}
.tlRow .tlWhat i{width:8px;height:8px;border-radius:50%;flex:none}
.tlRow .tlAmt{font-family:var(--data);font-variant-numeric:tabular-nums;color:var(--muted)}
.tlRow.pay .tlWhat i{background:var(--good)}
.tlRow.pay{font-weight:600}
.tlRow.bill .tlWhat i{background:var(--commitment)}
.tlRow.major .tlWhat i{background:var(--warn-lift)}
.tlRow.save .tlWhat i{background:var(--good-lift)}

/* transactions */
.txRow{display:grid;grid-template-columns:1fr auto auto;gap:var(--s3);align-items:center;padding:10px 0;border-bottom:1px solid var(--rule);font-size:14.5px;animation:accIn .35s var(--ease)}
.txRow:last-child{border-bottom:0}
.txRow .txWhat{color:var(--text);min-width:0;overflow-wrap:anywhere}
.txRow .txWhat small{display:block;color:var(--faint);font-size:12.5px}
.txRow .txAmt{font-family:var(--data);font-variant-numeric:tabular-nums;color:var(--text);font-weight:500}
.txRow .txDel{color:var(--faint);font-size:13px;min-height:44px;display:inline-flex;align-items:center}
.txRow .txDel:hover{color:var(--bad)}

/* template editor */
.tplRow{display:grid;grid-template-columns:24px 1fr 76px 92px;gap:var(--s3);align-items:center;padding:9px 0;border-bottom:1px solid var(--rule);font-size:14.5px}
.tplRow:last-child{border-bottom:0}
.tplRow .lockBtn{width:24px;height:24px;display:grid;place-items:center;color:var(--faint);border-radius:6px}
.tplRow .lockBtn[aria-pressed="true"]{color:var(--accent-strong);background:var(--accent-tint)}
.tplRow input{font-size:16px;width:70px;background:var(--surface);border:1.5px solid var(--rule-2);border-radius:8px;color:var(--text);padding:7px 8px;font-family:var(--data);font-size:16px;text-align:right}
.tplRow input:focus{border-color:var(--accent);outline:none;box-shadow:var(--ring)}
.tplRow .tplAmt{font-family:var(--data);font-size:13.5px;color:var(--muted);text-align:right;font-variant-numeric:tabular-nums}

/* ============ v13: TYPOGRAPHY + WORDMARK + TRUST ROW ============ */
/* the OWED wordmark: one distinctive treatment, used only where the
   logo appears; never for headings, buttons or values */
.wordmark{font-family:var(--brand);font-weight:700;font-size:19px;letter-spacing:.02em;
  color:var(--text);line-height:1;display:inline-block;transform:translateY(1px)}
.wordmark .wl:first-child{color:var(--accent)}
#menu .wordmark{color:#fff}
.mark{gap:9px}


/* refined coffee cup */
.coffee{position:relative}
.coffee .cup{transition:transform .16s var(--ease);cursor:pointer}
.coffee .cup:active{transform:scale(.96) translateY(1px)}
.coffee .saveTag{grid-row:2;position:static;transform:none;white-space:normal;text-wrap:balance;text-align:center;
  max-width:min(34ch,calc(100vw - 32px));background:var(--good-tint);color:var(--good);font-size:12.5px;font-weight:600;
  padding:6px 12px;border-radius:999px;opacity:0;transition:opacity .3s var(--ease);pointer-events:none}
.coffee:hover .saveTag,.coffee.showSave .saveTag{opacity:1;transform:none}

/* profile emoji picker */
.emojiGrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(52px,1fr));gap:8px;margin-top:var(--s4)}
.emojiBtn{font-size:24px;min-height:52px;border-radius:12px;border:2px solid transparent;background:var(--surface-2);
  display:grid;place-items:center;transition:border-color .15s,transform .12s}
.emojiBtn:hover{transform:translateY(-1px)}
.emojiBtn[aria-pressed="true"]{border-color:var(--accent);background:var(--accent-tint)}
.avatarEmoji{font-size:26px;line-height:1}

/* about: payslip detection animation */
.slipDemoAnim{position:relative}
.sda-doc{background:var(--surface);border:1px solid var(--rule);border-radius:var(--r-lg);padding:26px 24px;box-shadow:var(--shadow-1);display:grid;gap:14px}
.sda-line{height:12px;border-radius:6px;background:var(--surface-3);position:relative}
.sda-line.w50{width:50%}.sda-line.w55{width:55%}.sda-line.w60{width:60%}.sda-line.w65{width:65%}.sda-line.w70{width:70%}.sda-line.w80{width:80%}
.sda-line[data-k]::before{content:"";position:absolute;inset:-4px -6px;border:1.5px solid var(--accent);border-radius:8px;opacity:0}
.sda-line[data-k]::after{content:attr(data-k);position:absolute;left:calc(100% + 14px);top:50%;transform:translateY(-50%);
  font-size:12px;font-weight:600;color:var(--accent-strong);white-space:nowrap;opacity:0}
.in .sda-line[data-k]::before,.in .sda-line[data-k]::after{animation:sdaDetect .5s var(--ease) forwards}
.in .sda-line[data-k]:nth-child(2)::before,.in .sda-line[data-k]:nth-child(2)::after{animation-delay:.3s}
.in .sda-line[data-k]:nth-child(3)::before,.in .sda-line[data-k]:nth-child(3)::after{animation-delay:.7s}
.in .sda-line[data-k]:nth-child(4)::before,.in .sda-line[data-k]:nth-child(4)::after{animation-delay:1.1s}
.in .sda-line[data-k]:nth-child(5)::before,.in .sda-line[data-k]:nth-child(5)::after{animation-delay:1.5s}
.in .sda-line[data-k]:nth-child(6)::before,.in .sda-line[data-k]:nth-child(6)::after{animation-delay:1.9s}
@keyframes sdaDetect{to{opacity:1}}
@media(max-width:640px){.sda-line[data-k]::after{left:auto;right:8px;top:-18px;transform:none;font-size:10.5px}}

/* about: mini cycle demo */
.abTl{display:grid;gap:8px}
.abTl .abRow{display:grid;grid-template-columns:80px 1fr auto;gap:12px;font-size:14px;color:var(--muted);padding:8px 0;border-bottom:1px solid var(--rule)}
.abTl .abRow:last-child{border-bottom:0}
.abTl .abRow b{color:var(--text);font-family:var(--data);font-variant-numeric:tabular-nums}
.abBig{font-family:var(--data);font-weight:600;font-size:clamp(26px,4vw,34px);color:var(--text);margin:6px 0 2px}

/* ============================================================
   PORTAL SHELL — the authenticated application environment
   Desktop: labelled sidebar (232-264px) + top app bar + 12-col grid.
   Mobile: compact app bar + bottom navigation with a centre Add.
   The public marketing header and footer never appear here.
   ============================================================ */
body.in-portal .top,body.in-portal footer,body.in-portal .cookieBar{display:none!important}
body.in-portal main{background:var(--background)}
.portalShell{display:none}
body.in-portal .portalShell{display:block}
/* a remembered session: the boot script hides the home page before first
   paint so the app, not the brochure, is the first thing seen */
/* `has-session` is a pre-paint hint, not a state: it hides the marketing
   page for the instant between first paint and knowing whether the stored
   token is real. route() removes it as soon as it knows, either way. */
html.has-session #home,html.has-session .top,html.has-session footer{display:none!important}
html.has-session main{min-height:100dvh;background:var(--background)}
/* When the portal owns the screen, <main> must contribute no height at all.
   It sits BEFORE #portalShell in the document, so a main still holding
   min-height:100dvh pushed the entire dashboard one viewport down and left
   a blank first screen. Every .page inside it is already display:none here,
   so this is belt and braces: no child page can put height back. */
body.in-portal main{min-height:0!important;height:0!important;overflow:hidden!important}

/* ---- app-shell stability -------------------------------------------------
   Inside the signed-in app the geometry must hold still while browser chrome
   comes and goes. What is deliberately NOT done here: user-scalable=no and
   maximum-scale, which break pinch zoom for people who need it (and which
   iOS has ignored since iOS 10 anyway). Zoom stays available. What is
   controlled is the accidental behaviour: the double-tap zoom that fires
   when somebody taps a button twice, the rubber-band that drags the whole
   page when a list is already at its end, and the text inflation Safari
   applies on rotation. */
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
button,a,summary,label,input,select,textarea,[role="button"],[role="switch"],[role="tab"]{touch-action:manipulation}
body.in-portal{overscroll-behavior-y:contain}
.pBody,.sheetIn,.sheetList,#pBody{overscroll-behavior:contain}
/* the app shell owns exactly one viewport and never more: svh is the small
   viewport, so a collapsing URL bar cannot make it taller than the screen */
body.in-portal .pShell{min-height:100svh}
@supports not (min-height:100svh){body.in-portal .pShell{min-height:100vh}}
body.in-portal .page:not(#portal){display:none!important}

.pShell{min-height:100dvh;display:grid;grid-template-columns:1fr}
@media(min-width:1024px){.pShell{grid-template-columns:248px 1fr}}
@media(min-width:1440px){.pShell{grid-template-columns:264px 1fr}}

/* ---- sidebar ---- */
.pSide{display:none;border-right:1px solid var(--border);background:var(--surface);
  position:sticky;top:0;height:100dvh;padding:var(--s5) var(--s4);flex-direction:column;gap:var(--s2)}
@media(min-width:1024px){.pSide{display:flex}}
.pSide .pBrand{display:flex;align-items:center;gap:10px;padding:6px 8px var(--s5);cursor:pointer;color:inherit;text-align:left;width:100%}
.pBar .pTitle .pMobileMark{align-items:center;justify-content:center;min-width:34px;min-height:34px;cursor:pointer}
.pNav{display:grid;gap:2px}
.pNav a{display:flex;align-items:center;gap:12px;padding:11px 12px;border-radius:var(--r);
  color:var(--text-secondary);font-size:14.5px;font-weight:500;text-decoration:none;min-height:44px;
  transition:background .18s var(--ease),color .18s}
.pNav a svg{width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;flex:none}
.pNav a:hover{background:var(--surface-muted);color:var(--text-primary)}
.pNav a[aria-current="page"]{background:color-mix(in srgb,var(--primary) 12%,var(--surface));color:var(--primary-ink);font-weight:600;position:relative}
.pNav a[aria-current="page"] svg{stroke:var(--primary)}
.pSideDiv{height:1px;background:var(--border);margin:var(--s4) 4px}
.pSideFoot{margin-top:auto;border-top:1px solid var(--border);padding-top:var(--s4);display:flex;align-items:center;gap:10px}
.pSideFoot .pAv{width:36px;height:36px;border-radius:50%;background:var(--surface-muted);display:grid;place-items:center;font-size:18px;flex:none}
.pSideFoot .pWho{min-width:0}
.pSideFoot .pWho b{display:block;font-size:14px;color:var(--text-primary);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pSideFoot .pWho span{display:block;font-size:12px;color:var(--text-tertiary)}

/* ---- top app bar ---- */
.pMain{min-width:0;display:flex;flex-direction:column}
@media(min-width:1024px){.pBar{padding-inline:var(--s8)!important}}
body.in-portal #budPeriodH{font-size:clamp(24px,3vw,32px);letter-spacing:-.02em}
.acc summary{align-items:flex-start}.acc .pm{margin-top:1px}
[data-price]{font-family:var(--data);font-variant-numeric:tabular-nums}
.pBar{position:sticky;top:0;z-index:30;background:color-mix(in srgb,var(--background) 88%,transparent);
  backdrop-filter:blur(18px) saturate(1.5);-webkit-backdrop-filter:blur(18px) saturate(1.5);
  border-bottom:1px solid var(--border);
  display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
  padding:var(--s3) var(--s5);min-height:60px}
.pBar .pTitle{font-size:17px;font-weight:700;letter-spacing:-.015em;color:var(--text-primary);display:flex;align-items:center;gap:10px;min-width:0}
.pBar .pTitle .pMobileMark{display:flex}
@media(min-width:1024px){.pBar .pTitle .pMobileMark{display:none}}
.pBarActs{display:flex;align-items:center;gap:var(--s2)}
/* two primary tiers, by register: 52px marketing (.solid, sweep),
   44px portal (.pQuick, quiet) — deliberate, do not "fix" upward */
.pQuick{min-height:44px;padding:0 16px;border-radius:var(--r);background:var(--primary);color:var(--on-primary);
  font-weight:600;font-size:14.5px;display:inline-flex;align-items:center;gap:7px;position:relative}
.pQuick:hover{background:color-mix(in srgb,var(--primary) 86%,var(--text-primary))}
.pQuick svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;flex:none}
.pQuick:active{transform:translateY(1px) scale(.97)}

/* ---- physical press ------------------------------------------------------
   touch-down is immediate and slightly faster than the release, so the
   control gives under the finger and springs back when it is let go. One
   rule, so every button in the product answers the same way. */
.solid,.outl,.btn,.pQuick,.chip,.pAction,.sheetList button,.moreList button,
.selCard,.tCard,.dcMove,.pcDel,.txDel,.admBtn,.admChip,.admTab,.pAmount.heroTap{
  transition:transform var(--m-fast) var(--ease-release),
             box-shadow var(--m-fast) var(--ease),
             background var(--m-fast) var(--ease),
             border-color var(--m-fast) var(--ease),
             color var(--m-fast) var(--ease)}
.solid:active,.outl:active,.btn:active,.pQuick:active,.chip:active,.pAction:active,
.sheetList button:active,.moreList button:active,.dcMove:active,.pcDel:active,.txDel:active,
.admBtn:active,.admChip:active{
  transition-duration:var(--m-press)}
/* A little further than before. The give was measurable but not felt: on a
   phone, under a thumb that covers most of the control, two percent of a
   44px target is under a pixel of visible movement. Four is felt without
   looking like the button is running away. */
.chip:active,.pAction:active,.sheetList button:active,.moreList button:active,
.admChip:active{transform:scale(.955)}
.dcMove:active,.pcDel:active,.txDel:active{transform:scale(.92)}
/* the raised controls also settle, so the press reads as depth and not only
   as size: the shadow shortens as the surface goes down */
.solid:active,.pQuick:active{box-shadow:0 1px 2px rgba(29,27,34,.22)}
.selCard:active,.tCard:active{transform:scale(.978)}
/* a tap on a phone has no hover to precede it, so the feedback must be the
   press itself and it must not be delayed by the 300ms click delay */
.solid,.outl,.btn,.pQuick,.chip,.pAction,.admBtn,.admChip,.txDel,.pcDel,.dcMove{
  touch-action:manipulation;-webkit-tap-highlight-color:transparent}
html[data-motion="reduced"] .solid:active,html[data-motion="reduced"] .outl:active,
html[data-motion="reduced"] .btn:active,html[data-motion="reduced"] .pQuick:active,
html[data-motion="reduced"] .chip:active,html[data-motion="reduced"] .pAction:active,
html[data-motion="reduced"] .admBtn:active,html[data-motion="reduced"] .admChip:active,
html[data-motion="reduced"] .txDel:active,html[data-motion="reduced"] .pcDel:active,
html[data-motion="reduced"] .dcMove:active,html[data-motion="reduced"] .selCard:active,
html[data-motion="reduced"] .tCard:active{
  /* somebody who asked for less motion still needs to know the tap landed:
     the movement goes, the change of surface stays */
  transform:none;filter:brightness(.95)}

/* a save that worked says so for a moment, in the control that did it */
@keyframes okPulse{
  0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--success) 55%,transparent)}
  100%{box-shadow:0 0 0 13px transparent}}
.did-ok{animation:okPulse var(--m-emphasis) var(--ease)}
html[data-motion="reduced"] .did-ok{animation:none}
.pBody{padding:var(--s6) var(--s5) var(--s16);flex:1}
@media(min-width:1024px){.pBody{padding:var(--s8) var(--s8) var(--s16)}}
body.in-portal{padding-bottom:0}
@media(max-width:1023px){body.in-portal .pBody{padding-bottom:calc(var(--pnav-h) + var(--pnav-lift) + 20px + env(safe-area-inset-bottom,0px))}}

/* ---- 12-column dashboard grid, deliberately asymmetric ---- */
/* ---- dashboard layout: a wrapping row of cards, not a rigid 12-column grid
   The old system used fixed spans {4,5,7,8} in the core and {6,12} in the
   optional modules. Those do not compose: hero(8)+actions(4) fills row one,
   then catprog(7) opens row two with a five-column residue that ONLY
   recent(5) can fill - and recent is user-hideable. Hide it and the hole is
   permanent and unfillable by any module, in any order. A combinatorial
   sweep of every possible dashboard found 84% of them carrying at least one
   hole, the worst leaving 27% of the area empty.

   Flex-wrap cannot represent a hole: items on the last line GROW to consume
   whatever width is left. The span classes are kept as a size PREFERENCE
   (a bigger span asks for a wider card) so every portal page benefits
   without touching a single renderer. Column count now follows the space
   actually available, so no breakpoint is needed here at all. */
.pGrid{display:flex;flex-wrap:wrap;gap:var(--s6);align-items:stretch}
.pGrid>[class*="span"]{display:flex;min-width:0;flex:1 1 var(--mod-basis,320px)}
.pGrid>[class*="span"]>*{flex:1;min-width:0}
/* A grid cell holds one card and stretches it to the row height. When a page
   puts several cards in one cell it means a stack, never a row of slivers:
   without this, three cards in a span5 rendered 117px wide at 390px and 144px
   wide at 1280px, and the donut painted across its neighbour. Stated here so
   no page has to remember it. */
.pGrid>[class*="span"]:has(>*+*){flex-direction:column}
.span4{--mod-basis:280px}
.span5{--mod-basis:320px}
.span6{--mod-basis:340px}
.span7{--mod-basis:400px}
.span8{--mod-basis:480px}
.span12{--mod-basis:100%;flex-basis:100%}
/* cards fill the stretched slot, content stays pinned to the top */
.pGrid .pCard,.pGrid .pHero{display:flex;flex-direction:column}
.pGrid .pCard>*:last-child,.pGrid .pHero>*:last-child{margin-bottom:0}

/* ---- portal cards: three tiers only ---- */
.pCard{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);
  padding:var(--s5);box-shadow:var(--shadow-1)}
@media(min-width:768px){.pCard{padding:var(--s6)}}
.pCard h3{font-size:15px;font-weight:600;color:var(--text-secondary);margin:0 0 var(--s3);letter-spacing:.01em}
.pHero{position:relative;background:linear-gradient(150deg,color-mix(in srgb,var(--primary) 14%,var(--surface)),var(--surface) 62%);
  border:1px solid color-mix(in srgb,var(--primary) 26%,var(--border));border-radius:var(--r-xl);
  padding:var(--s6);box-shadow:var(--shadow-2)}
@media(min-width:768px){.pHero{padding:var(--s8)}}

.pLabel{font-size:14px;font-weight:600;color:var(--text-secondary);line-height:1.3}
.pHero .pLabel{font-size:14px;font-weight:600;color:var(--text-secondary)}
.pHero .pAmount{font-family:var(--data);font-weight:600;letter-spacing:-.025em;
  font-size:clamp(40px,7vw,62px);line-height:1.02;color:var(--text-primary);
  /* line-height below 1 makes the line box shorter than the glyphs, so the ink
     spills about 7.6px above and below at 62px. The margins have to clear that
     or the figure touches the label above and the sentence below. */
  margin:14px 0 10px}
.pHero .pWhen{font-size:14.5px;color:var(--text-secondary)}
.pTrackEnds{display:flex;justify-content:space-between;font-size:12.5px;color:var(--text-tertiary)}

/* ---- bottom navigation ---- */
/* Fixed height, so the row never grows from its content: the Add cell used
   to be taller than its neighbours and the whole bar stretched around it.
   The centre button sits in a bump drawn from the nav's own surface and
   hairline, so it is part of the bar rather than a sticker on it. */
/* The Add control is a squircle, not a circle: OWED is built out of rounded
   rectangles and a lone circle read as a foreign object dropped on the bar.
   The bump behind it uses the same curve at the same ratio, so the two are
   concentric shapes rather than two different ideas.
   Geometry is derived, never hand-tuned: the bump is the dot plus a ring of
   --pnav-ring on every side, the lift is exactly half the dot, so the dot's
   centre sits on the bar's top edge and its crown is inside the bump by the
   ring width. Change --pnav-dot alone and everything else follows. */
:root{
  --pnav-h:60px;
  /* 58px was a shade larger than it needed to be to read as the primary
     control: at 51px it still dominates its neighbours, which are 21px
     icons, and it stops crowding the two labels either side of it. */
  --pnav-dot:51px;
  --pnav-ring:7px;
  --pnav-bump:calc(var(--pnav-dot) + var(--pnav-ring) * 2);
  /* How far the control rises out of the bar.

     Half its own height put its equator on the bar's top edge: balanced on
     a ruler, floating away in the hand. A third of its height put it in
     no-man's-land, nine pixels below the edge and ten above the icons
     beside it, aligned with nothing, so the eye had no line to snap it to
     and it read as misplaced however carefully it was measured.

     The only reference that exists in that bar is the centreline of the
     four icons either side. The control's centre now sits on it: the fifth
     item in the row, larger than its neighbours, sharing their line. The
     crown still clears the bar's top edge by a few pixels, so it is seen
     to be the one control that rises, without leaving the row it belongs
     to. Everything else here is derived from this value, so the bump
     behind it and the clearance above it move with it. */
  /* The icon centreline is a fixed property of the bar: icons of a fixed
     size at a fixed padding. Deriving the lift from it, rather than from a
     fraction of the dot, means the control lands on that line whatever
     size it is, including the smaller one on narrow phones. A fraction
     was 2px off at 320 for exactly that reason. */
  --pnav-icon-line:19.7px;
  --pnav-lift:calc(var(--pnav-dot) / 2 - var(--pnav-icon-line));
  --pnav-r:34%;                       /* squircle: radius as a share of size */
}
@media(max-width:359px){:root{--pnav-dot:46px;--pnav-ring:6px}}
.pBottom{position:fixed;left:0;right:0;bottom:0;z-index:60;display:grid;grid-template-columns:repeat(5,1fr);
  grid-auto-rows:var(--pnav-h);
  /* Glass, but a navigation bar has to stay readable over whatever scrolls
     under it, so the transparency is bounded and the blur does the work. A
     saturation lift keeps colour from going grey behind the frost, and the
     hairline along the top edge is what makes it read as a pane of something
     rather than a translucent rectangle. */
  background:color-mix(in srgb,var(--surface) 82%,transparent);
  backdrop-filter:blur(22px) saturate(1.6);
  -webkit-backdrop-filter:blur(22px) saturate(1.6);
  border-top:1px solid var(--border);padding:0 6px env(safe-area-inset-bottom,0px);overflow:visible;isolation:isolate;
  box-shadow:inset 0 1px 0 color-mix(in srgb,#fff 42%,transparent),
             0 -6px 24px color-mix(in srgb,var(--text) 6%,transparent)}
/* Dark surfaces need the opposite highlight: white along the edge reads as a
   scratch rather than a shine. */
html[data-mode="dark"] .pBottom{
  background:color-mix(in srgb,var(--surface) 86%,transparent);
  box-shadow:inset 0 1px 0 color-mix(in srgb,#fff 12%,transparent),
             0 -6px 24px rgba(0,0,0,.34)}
/* Without backdrop-filter the transparency is just a legibility problem, so
   the pane becomes solid rather than degrading into a smear. */
/* Safari shipped unprefixed backdrop-filter only in 18.0, so this test was
   TRUE on iOS 16 and 17 and stripped the glass from phones where the
   prefixed property works perfectly. Ask about both spellings. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .pBottom{background:var(--surface)}
  html[data-mode="dark"] .pBottom{background:var(--surface)}}
/* Somebody who has asked their system for less transparency gets none. */
@media(prefers-reduced-transparency:reduce){
  .pBottom,html[data-mode="dark"] .pBottom{background:var(--surface);backdrop-filter:none;-webkit-backdrop-filter:none}
  .pBar{background:var(--background);backdrop-filter:none;-webkit-backdrop-filter:none}}
/* The ring used to be a pseudo-element of the NAV, centred by its own
   arithmetic. `*{box-sizing:border-box}` does not reach pseudo-elements, so
   its 1px border made it 67px wide on a 65px margin: its centre sat 1px
   right of the dot's, on every engine, and a phone at 3x with a pinch
   zoom shows that plainly. Two things centred separately can always
   disagree; the ring is now part of the button (see .pAdd::before), so
   they cannot. This strip only hides the bar's hairline under the ring. */
.pBottom::after{content:"";position:absolute;left:50%;top:-1px;box-sizing:border-box;width:calc(var(--pnav-bump) - 8px);
  margin-left:calc((var(--pnav-bump) - 8px) / -2);height:3px;background:inherit;z-index:-1}
@media(min-width:1024px){.pBottom{display:none}}
/* lifted clear of the software keyboard, so the nav is never "gone" when it
   is only hidden behind it (see keyboardInset in app.js) */
.pBottom{transform:translateY(calc(var(--kb,0px) * -1));transition:transform .18s var(--ease)}
#toasts{margin-bottom:var(--kb,0px)}
html[data-motion="reduced"] .pBottom{transition:none}
/* The bottom bar already carries a big Add. Two identical Add buttons on
   one phone screen (top bar and bottom bar) is noise. */
@media(max-width:1023px){#pQuickAdd{display:none}}
.pBottom a,.pBottom button{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:3px;padding:0 2px;border-radius:12px;color:var(--text-tertiary);font-size:11px;font-weight:600;
  min-height:0;height:var(--pnav-h);text-decoration:none;-webkit-tap-highlight-color:transparent}
.pBottom a:active,.pBottom button:active{transform:scale(.94)}
.pBottom a,.pBottom button{transition:transform .12s var(--ease),color .15s}
.pBottom button[aria-current="page"]{color:var(--primary-ink)}
.pBottom svg{width:21px;height:21px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.pBottom a[aria-current="page"]{color:var(--primary-ink)}
/* ============================================================
   THE ADD CONTROL

   Rebuilt as one element rather than patched. Every previous version put
   the purple dot INSIDE the nav's flex button and asked the button to
   place it — with align-items, then justify-content, then a negative
   margin, then an absolutely positioned child. A <button> is the one
   element whose insides engines lay out differently: WebKit wraps a
   button's content in an anonymous box of its own. So each fix was a
   guess about someone else's layout algorithm, and each was verified in
   the one engine that was never the problem.

   There is no algorithm left to disagree with:

   - The button IS the dot. No wrapper, no inner span, no flex, no grid.
   - It is positioned against the nav, which is a plain fixed element, and
     centred by arithmetic: left:50% less half its own width. `left`
     resolves against the nav's padding box, which spans the viewport, and
     the nav's centre grid column is centred on the same point, so the dot
     and the icons cannot drift apart however the columns are sized.
   - It is out of flow, so an empty .pAddSlot holds its column open and the
     four icons keep their positions.
   - The glyph is an <svg> stretched to the button's exact box, which
     centres its own contents through preserveAspectRatio — defined by the
     SVG spec, not by CSS layout, and therefore identical everywhere. It
     scales with the dot, and inherits the theme through currentColor.
   - Centring uses a margin, not a translate, so the press state is free to
     use transform again. It was given up to protect the old centring.
   ============================================================ */
.pBottom .pAddSlot{display:block}          /* holds the centre column open */
.pBottom .pAdd{
  position:absolute;left:50%;top:calc(var(--pnav-lift) * -1);
  margin:0 0 0 calc(var(--pnav-dot) / -2);box-sizing:border-box;
  width:var(--pnav-dot);height:var(--pnav-dot);min-height:0;min-width:0;
  display:block;padding:0;border:0;gap:0;background:none;box-shadow:none;
  border-radius:var(--pnav-r);color:var(--on-primary);overflow:visible;
  isolation:isolate;-webkit-tap-highlight-color:transparent;
  -webkit-appearance:none;appearance:none}
/* THE RING: the same box as the button, grown by the ring width on every
   side. Concentric by construction; nothing to align. It does not move
   when the face is pressed. */
.pBottom .pAdd::before{content:"";position:absolute;inset:calc(var(--pnav-ring) * -1);box-sizing:border-box;
  border-radius:var(--pnav-r);z-index:-1;pointer-events:none;
  background:color-mix(in srgb,var(--surface) 82%,transparent);
  -webkit-backdrop-filter:blur(22px) saturate(1.6);backdrop-filter:blur(22px) saturate(1.6);
  border:1px solid var(--border);
  box-shadow:0 0 0 0 color-mix(in srgb,var(--primary) 0%,transparent);
  transition:box-shadow .5s var(--ease)}
html[data-mode="dark"] .pBottom .pAdd::before{background:color-mix(in srgb,var(--surface) 86%,transparent)}
/* THE FACE: the purple square, exactly the button's box. It is what
   presses, springs and dims; the ring and the tap target stay put. */
.pBottom .pAdd::after{content:"";position:absolute;inset:0;box-sizing:border-box;z-index:0;
  border-radius:var(--pnav-r);
  background:linear-gradient(160deg,color-mix(in srgb,var(--primary) 88%,#fff),var(--primary) 55%,var(--primary-active));
  box-shadow:0 6px 16px color-mix(in srgb,var(--primary) 38%,transparent),
             0 2px 5px color-mix(in srgb,var(--text) 18%,transparent),
             inset 0 1px 0 color-mix(in srgb,#fff 34%,transparent);
  transition:transform .16s var(--spring),box-shadow .16s var(--ease)}
/* touch-down compresses the face into the bar; release springs it back */
.pBottom .pAdd:active::after{transform:scale(.9) translateY(2px);
  box-shadow:0 2px 6px color-mix(in srgb,var(--primary) 30%,transparent),
             inset 0 2px 6px color-mix(in srgb,var(--text) 26%,transparent)}
.pBottom .pAdd:focus-visible{outline:2px solid var(--focus-ring);outline-offset:calc(var(--pnav-ring) + 2px)}
/* the glyph: an svg stretched to the face, centring its own contents by
   viewBox, above the face, and it presses with it */
.pBottom .pAdd svg{position:absolute;inset:0;width:100%;height:100%;z-index:1;
  display:block;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;
  transform-origin:50% 50%;transition:transform .3s var(--spring)}
.pBottom .pAdd:active svg{transform:scale(.9) translateY(2px)}
/* while its sheet is open the plus is a close: it turns, the face settles
   in a touch, and the ring breathes once so the change is seen */
body.sheet-open .pBottom .pAdd svg{transform:rotate(135deg)}
body.sheet-open .pBottom .pAdd::after{transform:scale(.94)}
body.sheet-open .pBottom .pAdd::before{box-shadow:0 0 0 6px color-mix(in srgb,var(--primary) 14%,transparent)}
html[data-motion="reduced"] .pBottom .pAdd::before,html[data-motion="reduced"] .pBottom .pAdd::after,
html[data-motion="reduced"] .pBottom .pAdd svg{transition:none}

/* ---- bottom sheet ---- */
.sheet{position:fixed;inset:0;z-index:130;display:none}
.sheet.on{display:block}
.sheet .sheetBg{position:absolute;inset:0;background:color-mix(in srgb,var(--text-primary) 42%,transparent);
  animation:fadeIn .22s var(--ease)}
html[data-mode="dark"] .sheet .sheetBg,html[data-mode="dark"] .sheetBg{background:rgba(0,0,0,.58)}
.sheet .sheetIn{position:absolute;left:0;right:0;bottom:0;background:var(--surface);border-radius:var(--r-xl) var(--r-xl) 0 0;
  padding:var(--s5) var(--s5) calc(var(--s6) + env(safe-area-inset-bottom,0px));
  box-shadow:var(--shadow-2);animation:sheetUp .28s var(--ease);max-height:88dvh;overflow-y:auto}
@media(min-width:700px){.sheet .sheetIn{left:50%;right:auto;bottom:auto;top:50%;transform:translate(-50%,-50%);
  width:min(520px,92vw);border-radius:var(--r-xl);animation:fadeIn .22s var(--ease)}}
@keyframes sheetUp{from{transform:translateY(100%)}to{transform:none}}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
/* the handle is a real grab target now, not a 4px decoration: the pill is
   still 40x4 but it sits in a 44px-tall strip that takes the gesture */
.sheet .sheetGrab{width:40px;height:4px;border-radius:999px;background:var(--border-strong);
  margin:0 auto var(--s4);position:relative;cursor:grab}
.sheet .sheetGrab::after{content:"";position:absolute;left:-40px;right:-40px;top:-20px;bottom:-20px}
.sheet .sheetGrab:active{cursor:grabbing;background:var(--text-tertiary)}
/* vertical panning is ours to arbitrate; the browser keeps horizontal */
.sheet .sheetIn{touch-action:pan-y}
@media(min-width:700px){.sheet .sheetGrab{display:none}}
.sheetList{display:grid;gap:8px}
.sheetList button{display:flex;align-items:center;gap:12px;padding:15px 16px;border-radius:var(--r);
  background:var(--surface-muted);color:var(--text-primary);font-size:15.5px;font-weight:500;text-align:left;min-height:56px}
.sheetList button:hover{background:color-mix(in srgb,var(--primary) 10%,var(--surface-muted))}

/* realtime highlight */

/* ============ THEME + APPEARANCE PREVIEW CARDS ============ */
.themeGrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(148px,1fr));gap:var(--s3);margin-top:var(--s4)}
.modeGrid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s3);margin-top:var(--s4)}
.tCard{border:2px solid var(--border);border-radius:var(--r-lg);overflow:hidden;text-align:left;padding:0;
  background:var(--tp-bg);transition:border-color .2s var(--ease),transform .14s,box-shadow .2s}
@media(hover:hover){.tCard:hover{transform:translateY(-2px);box-shadow:var(--shadow-2)}}
.tCard[aria-pressed="true"]{border-color:var(--tp-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--tp-primary) 26%,transparent)}
.tCard .tp{padding:12px 12px 10px;display:grid;gap:7px}
.tCard .tpBar{height:7px;border-radius:4px;background:var(--tp-surface);border:1px solid color-mix(in srgb,var(--tp-ink) 16%,transparent);width:60%}
.tCard .tpMini{background:var(--tp-surface);border:1px solid color-mix(in srgb,var(--tp-ink) 14%,transparent);border-radius:9px;padding:9px}
.tCard .tpNum{font-family:var(--data);font-size:15px;font-weight:600;color:var(--tp-ink);line-height:1}
.tCard .tpRow{height:5px;border-radius:3px;background:color-mix(in srgb,var(--tp-ink) 22%,transparent);width:70%;margin-top:6px}
.tCard .tpBtn{height:16px;border-radius:6px;background:var(--tp-primary);width:52%;margin-top:8px}
.tCard .tpBtn.pride{background:var(--gradient-pride)}
.tCard .tpFoot{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:9px 12px;
  background:var(--surface);border-top:1px solid var(--border);font-size:13px;font-weight:600;color:var(--text-primary)}
.tCard .tpDots{display:flex;gap:4px}
.tCard .tpDots i{width:10px;height:10px;border-radius:50%;display:block}
.tCard[aria-pressed="true"] .tpFoot::after{content:"\2713";color:var(--primary-ink);font-weight:700}
.mCard{border:2px solid var(--border);border-radius:var(--r-lg);padding:0;overflow:hidden;text-align:left;background:var(--surface);
  transition:border-color .2s var(--ease),transform .14s}
@media(hover:hover){.mCard:hover{transform:translateY(-2px)}}
.mCard[aria-pressed="true"]{border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 24%,transparent)}
.mCard .mPrev{height:56px;display:grid;place-items:center;font-size:19px}
.mCard.light .mPrev{background:#F7F5F0;color:#19171D}
.mCard.dark .mPrev{background:#1E1C23;color:#F7F4FA}
.mCard.system .mPrev{background:linear-gradient(90deg,#F7F5F0 50%,#1E1C23 50%);color:var(--text-primary)}
.mCard .mName{padding:9px 12px;font-size:13.5px;font-weight:600;color:var(--text-primary);border-top:1px solid var(--border)}


/* ---- pricing: the trial as seven filled days ---- */
.trialCal{display:grid;grid-template-columns:repeat(8,minmax(28px,44px));gap:6px;justify-content:center;margin:26px auto 6px}
.trialCal .tc{aspect-ratio:1;display:grid;place-items:center;border-radius:8px;font-family:var(--data);font-size:14px;font-weight:600}
.trialCal .tc.free{background:color-mix(in srgb,var(--income) 18%,var(--surface));color:var(--text-primary);border:1px solid color-mix(in srgb,var(--income) 45%,var(--border))}
.trialCal .tc.bill{background:transparent;color:var(--text-secondary);border:1.5px dashed var(--border-strong)}
.trialCal .tcNote{grid-column:1/-1;text-align:center;font-size:13.5px;color:var(--text-secondary);margin:8px 0 0}
@media(prefers-reduced-motion:no-preference){
  .trialCal.rv.in .tc.free{animation:tcPopSpring .35s var(--spring) backwards}
  .trialCal.rv.in .tc.free:nth-child(1){animation-delay:.05s}.trialCal.rv.in .tc.free:nth-child(2){animation-delay:.12s}
  .trialCal.rv.in .tc.free:nth-child(3){animation-delay:.19s}.trialCal.rv.in .tc.free:nth-child(4){animation-delay:.26s}
  .trialCal.rv.in .tc.free:nth-child(5){animation-delay:.33s}.trialCal.rv.in .tc.free:nth-child(6){animation-delay:.4s}
  .trialCal.rv.in .tc.free:nth-child(7){animation-delay:.47s}
}
/* tcPop superseded by tcPopSpring (motion v5) */


/* ---- portal motion moments (all aria-hidden, all reduced-motion safe) ---- */
/* The dashboard's own mark, on the dashboard's own card. It replaced an
   envelope that appeared on one day in fourteen and said nothing about the
   money: this is a miniature of the thing you are looking at, and it is drawn
   from the same figures. The ring is the share of the cycle still safe to
   spend; the bars are the categories underneath it, tallest last.
   Everything moves with transform and opacity, so a phone composites the
   whole mark instead of repainting it. */
.hDash{width:112px;max-width:100%;line-height:0;margin:0 0 14px;animation:hdIn .55s var(--ease) backwards}
.hDash svg{display:block;width:100%;height:auto}
@keyframes hdIn{from{opacity:0;transform:translateY(6px)}}
.hdPane{fill:var(--surface);stroke:color-mix(in srgb,var(--primary) 28%,var(--border));stroke-width:1}
.hdHead{fill:color-mix(in srgb,var(--primary) 12%,var(--surface))}
.hdTitle{fill:color-mix(in srgb,var(--primary) 42%,transparent)}
.hdLed{fill:color-mix(in srgb,var(--primary) 38%,transparent)}
.hdLed2{fill:var(--income);animation:hdBlink 2.8s var(--ease) infinite}
@keyframes hdBlink{0%,100%{opacity:.3}50%{opacity:1}}
.hdGrid,.hdBase{fill:none;stroke:var(--border);stroke-width:1}
.hdGrid{opacity:.6}
.hdTrack{fill:none;stroke:color-mix(in srgb,var(--primary) 15%,transparent);stroke-width:5}
.hdArc{fill:none;stroke:var(--primary);stroke-width:5;stroke-linecap:round;
  animation:hdSweep .95s var(--ease) .2s backwards}
@keyframes hdSweep{from{stroke-dasharray:0 75.4}}
/* over-committed is the one state the mark says out loud */
.hdRing.over .hdArc{stroke:var(--danger)}
.hdCap{fill:color-mix(in srgb,var(--primary) 24%,transparent)}
.hdCol{fill:var(--primary);opacity:calc(.44 + var(--i,0) * .15);
  transform-box:fill-box;transform-origin:50% 100%;
  animation:hdGrow .5s var(--spring,var(--ease)) calc(.3s + var(--i,0) * .09s) backwards}
@keyframes hdGrow{from{transform:scaleY(0)}}
.hdSheen{fill:var(--primary);opacity:.07;animation:hdSheen 7s linear 1.4s infinite}
@keyframes hdSheen{from{transform:translateX(0) skewX(-16deg)}to{transform:translateX(178px) skewX(-16deg)}}
html[data-motion="reduced"] .hDash,html[data-motion="reduced"] .hDash *{animation:none}
html[data-motion="reduced"] .hdSheen{display:none}
@media(prefers-reduced-motion:reduce){.hDash,.hDash *{animation:none}.hdSheen{display:none}}

@keyframes peSlide{from{transform:translateY(16px);opacity:0}to{transform:translateY(0);opacity:1}}
@keyframes peFlap{from{transform:scaleY(.2)}to{transform:scaleY(1)}}
/* savings bar: a small vertical measure that fills to the saved share */
.savBar{display:inline-block;width:6px;height:16px;border-radius:3px;background:var(--surface-muted);
  margin-right:8px;vertical-align:-2px;position:relative;overflow:hidden}
.savBar b{position:absolute;left:0;right:0;bottom:0;background:var(--saving);border-radius:3px;
  transition:height .8s var(--ease)}
/* SAVED stamp: presses on, then lets go */
.savedStamp{position:fixed;top:50%;left:50%;z-index:95;pointer-events:none;
  width:92px;height:92px;border-radius:50%;display:grid;place-items:center;
  border:3px solid var(--income);color:var(--income);font-family:var(--display);
  font-weight:700;font-size:17px;letter-spacing:.08em;background:color-mix(in srgb,var(--income) 8%,var(--surface));
  transform:translate(-50%,-50%) rotate(-12deg);animation:stampIn .45s var(--ease) both}
.savedStamp.gone{transition:opacity .4s;opacity:0}
@keyframes stampIn{0%{transform:translate(-50%,-50%) rotate(-12deg) scale(1.7);opacity:0}
  60%{transform:translate(-50%,-50%) rotate(-12deg) scale(.94);opacity:1}
  100%{transform:translate(-50%,-50%) rotate(-12deg) scale(1)}}
html[data-motion="reduced"] .savedStamp{display:none}
@media(prefers-reduced-motion:reduce){.savedStamp{animation:none}.savBar b{transition:none}}


/* privacy points: three sentences in the open — the flip cards hid them */
.checkRow{display:grid;gap:var(--s5);max-width:720px}
@media(min-width:820px){.checkRow{grid-template-columns:repeat(3,1fr);max-width:none}}
.checkItem b{display:block;font-family:var(--display);font-size:16.5px;margin-bottom:6px}
.checkItem p{margin:0;font-size:14.5px;color:var(--muted);line-height:1.55}


.avatarInitials{font-family:var(--display);font-weight:700;font-size:.72em;letter-spacing:.02em;
  color:var(--primary-ink);display:grid;place-items:center;width:100%;height:100%;
  background:color-mix(in srgb,var(--primary) 14%,var(--surface));border-radius:inherit}




/* year total counts up as the cards deal out */
.fan .fanTotal{font-family:var(--data);font-size:13px;letter-spacing:.06em;color:var(--muted);margin:18px 0 0}
.fan .fanTotal .num{font-size:20px;font-weight:600;color:var(--income)}
/* The blank-band bug this block once fixed came from the strip's intrinsic
   width sizing the grid track. The strip is gone; the fan's cards are
   absolutely positioned, so they have no intrinsic width to size anything.
   minmax(0,1fr) stays as the guard it always was. */
@media(max-width:760px){
  .fan{grid-template-columns:minmax(0,1fr)}
  .fan .inner{width:100%;min-width:0}
}


/* ---- donuts, subscription card, trial ring ---- */
.donutWrap{display:flex;gap:20px;align-items:center;flex-wrap:wrap}
.donut{width:120px;height:120px;flex:none}
.donut .dSeg{transition:stroke-dasharray .8s var(--ease);transition-delay:calc(var(--i,0)*120ms)}
.donut .dCentre{font-family:var(--data);font-size:13px;font-weight:600;fill:var(--text-primary)}
.donutKey{display:grid;gap:7px;font-size:13.5px;color:var(--text-secondary);min-width:0}
.donutKey span{display:flex;align-items:center;gap:8px;min-width:0}
.donutKey i{width:10px;height:10px;border-radius:3px;flex:none}
.calcDonut{margin:18px 0 4px}
html[data-motion="reduced"] .donut .dSeg{transition:none}
@media(prefers-reduced-motion:reduce){.donut .dSeg{transition:none}}
.subCard{display:flex;justify-content:space-between;align-items:center;gap:var(--s5);flex-wrap:wrap}
.subCard.gift{border-color:color-mix(in srgb,var(--accent) 55%,var(--border));
  background:linear-gradient(150deg,color-mix(in srgb,var(--accent) 7%,var(--surface)),var(--surface) 70%)}
.trialRing{flex:none}
.trialRing .trFill{transition:stroke-dashoffset .9s var(--ease)}
.trialRing .trDays{font-family:var(--data);font-size:12px;font-weight:600;fill:var(--text-primary)}
html[data-motion="reduced"] .trialRing .trFill{transition:none}


/* G5 calendar: the page folds down to payday */
.calFlip{overflow:visible;display:block;margin-top:18px}
.calFlip .cfOld{transform-origin:44px 32px;transform-box:view-box}
.rv.in .calFlip .cfOld{animation:cfFold .45s ease-in .3s forwards}
@keyframes cfFold{to{transform:scaleY(0);opacity:0}}
.calFlip .cfNew{transform:scaleY(0);transform-origin:44px 32px;transform-box:view-box}
.rv.in .calFlip .cfNew{animation:cfUnfold .45s var(--ease) .75s forwards}
@keyframes cfUnfold{to{transform:scaleY(1)}}
html[data-motion="reduced"] .calFlip .cfOld{transform:scaleY(0);opacity:0;animation:none}
html[data-motion="reduced"] .calFlip .cfNew{transform:scaleY(1);animation:none}
@media(prefers-reduced-motion:reduce){.calFlip .cfOld{transform:scaleY(0);opacity:0}.calFlip .cfNew{transform:scaleY(1)}}

/* G6: the card noses into the wallet slot */
/* two cards approach, high-five, bounce, settle overlapped; six small
   stars at the contact. 1.6s once, transform and opacity only. */
.hi5{overflow:visible;flex:none}
.hi5 .h5a,.hi5 .h5b{transform-box:view-box;transform-origin:70px 46px;opacity:0}
/* Triggered by the drawing itself coming into view, not by the block
   around it, and slowed to something you can actually watch. It ran in
   1.6s from the top edge of a wrapper that is taller than a phone
   screen, so it was over before it was on screen. Now the two cards take
   just under three seconds to meet. */
.hi5.seen .h5a{animation:h5a 2.8s var(--ease) .35s both}
.hi5.seen .h5b{animation:h5b 2.8s var(--ease) .35s both}
@keyframes h5a{0%{opacity:0;transform:translate(-30px,12px) rotate(0)}20%{opacity:1}
 45%{transform:translate(0,0) rotate(11deg)}56%{transform:translate(-5px,3px) rotate(7deg)}
 70%{transform:translate(-1px,0) rotate(5deg)}100%{opacity:1;transform:translate(0,1px) rotate(4deg)}}
@keyframes h5b{0%{opacity:0;transform:translate(30px,-12px) rotate(0)}20%{opacity:1}
 45%{transform:translate(0,0) rotate(-11deg)}56%{transform:translate(5px,-3px) rotate(-7deg)}
 70%{transform:translate(1px,0) rotate(-5deg)}100%{opacity:1;transform:translate(-6px,-1px) rotate(-4deg)}}
.hi5 .h5burst use{opacity:0;transform-box:fill-box;transform-origin:center}
.hi5.seen .h5burst use{animation:h5pop .8s var(--ease) 1.9s both}
.hi5 .p1{--tx:-16px;--ty:-13px}.hi5 .p2{--tx:0px;--ty:-19px}.hi5 .p3{--tx:16px;--ty:-12px}
.hi5 .p4{--tx:-14px;--ty:10px}.hi5 .p5{--tx:15px;--ty:11px}.hi5 .p6{--tx:2px;--ty:18px}
@keyframes h5pop{0%{opacity:0;transform:translate(0,0) scale(.2)}
 30%{opacity:.9;transform:translate(calc(var(--tx) * .6),calc(var(--ty) * .6)) scale(1)}
 100%{opacity:0;transform:translate(var(--tx),var(--ty)) scale(.55)}}
html[data-motion="reduced"] .hi5 .h5a,html[data-motion="reduced"] .hi5 .h5b{animation:none;opacity:1}
html[data-motion="reduced"] .hi5 .h5a{transform:rotate(4deg) translateY(1px)}
html[data-motion="reduced"] .hi5 .h5b{transform:rotate(-4deg) translate(-6px,-1px)}
html[data-motion="reduced"] .hi5 .h5burst{display:none}
@media(prefers-reduced-motion:reduce){.hi5 .h5a,.hi5 .h5b{animation:none;opacity:1}
 .hi5 .h5a{transform:rotate(4deg) translateY(1px)}.hi5 .h5b{transform:rotate(-4deg) translate(-6px,-1px)}.hi5 .h5burst{display:none}}

/* G11: the verify envelope reuses the payEnv choreography */
.vfEnv{display:block;overflow:visible}
.vfEnv .peNote{animation:peSlide .9s var(--ease) .3s backwards}
.vfEnv .peFlap{transform-origin:32px 15px;animation:peFlap .5s var(--ease) both}
html[data-motion="reduced"] .vfEnv .peNote,html[data-motion="reduced"] .vfEnv .peFlap{animation:none}
@media(prefers-reduced-motion:reduce){.vfEnv .peNote,.vfEnv .peFlap{animation:none}}


/* G2: the calculator reacts to being used */
.calcObj{overflow:visible;flex:none}
.calcObj .coKey{transform-box:fill-box;transform-origin:center}
.calcObj.tap .coKey:nth-of-type(1){animation:keyPress .18s var(--ease)}
.calcObj.tap .coKey:nth-of-type(3){animation:keyPress .18s var(--ease) .09s}
.calcObj.tap .coKey:nth-of-type(5){animation:keyPress .18s var(--ease) .18s}
.calcObj.tap .coKey:nth-of-type(6){animation:keyPress .22s var(--ease) .3s}
@keyframes keyPress{50%{transform:scale(.72)}}
.calcObj.tap .coBar{animation:coBarFill .5s var(--ease) .35s forwards}
@keyframes coBarFill{to{width:32px}}
html[data-motion="reduced"] .calcObj.tap .coKey,html[data-motion="reduced"] .calcObj .coBar{animation:none}

/* the old slip is pulled back up into the printer before the next prints */
.printer.retract .printed{transform:translateY(-101%);transition:transform .45s cubic-bezier(.5,0,.75,.5)}
.printer.retract .printed .rline{transition:none}

/* the budget gate leads with the Southern Cross, the same mark the
   header carries, instead of the old coin jar */
.gateSx{color:var(--primary);margin-bottom:6px}
.gateSx .sxBig{display:block}

/* ---- verified: the check draws itself ---- */
.ecOk{margin:0 auto;overflow:visible;display:block}
.ecOk .ecRing{fill:none;stroke:var(--success);stroke-width:3;stroke-linecap:round;stroke-dasharray:163.4;stroke-dashoffset:163.4}
.ecOk .ecTick{fill:none;stroke:var(--success);stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:35;stroke-dashoffset:35}
.ecOk.draw .ecRing{animation:ecRing .6s var(--ease) .1s forwards}
.ecOk.draw .ecTick{animation:ecTick .35s var(--ease) .65s forwards}
.ecOk.draw{animation:ecPulse .5s var(--ease) 1.05s 1}
@keyframes ecRing{to{stroke-dashoffset:0}}
@keyframes ecTick{to{stroke-dashoffset:0}}
@keyframes ecPulse{0%,100%{transform:scale(1)}45%{transform:scale(1.07)}}
html[data-motion="reduced"] .ecOk .ecRing,html[data-motion="reduced"] .ecOk .ecTick{stroke-dashoffset:0;animation:none}
@media(prefers-reduced-motion:reduce){.ecOk .ecRing,.ecOk .ecTick{stroke-dashoffset:0;animation:none}}

/* ---- logo constellation + wordmark animation ----
   The Southern Cross: each star wakes in turn, the whole constellation
   gives one soft pulse, then it rests. Slow and quiet on purpose. */
.sxOrb{width:15px;height:17px;display:block;flex:none;color:var(--logo-orb);
  animation:sxPulse 7.5s var(--ease) infinite}
.sxOrb .sx{fill:currentColor;opacity:.55;transform-box:fill-box;transform-origin:center;
  animation:sxStar 7.5s var(--ease) infinite}
.sxOrb .s1{animation-delay:0s}
.sxOrb .s2{animation-delay:.45s}
.sxOrb .s3{animation-delay:.9s}
.sxOrb .s4{animation-delay:1.35s}
@keyframes sxStar{0%{opacity:.55;transform:scale(1)}4%{opacity:1;transform:scale(1.3)}10%{opacity:.62;transform:scale(1)}100%{opacity:.55}}
@keyframes sxPulse{0%,25%{transform:scale(1)}30%{transform:scale(1.06)}36%,100%{transform:scale(1)}}
html[data-motion="reduced"] .sxOrb,html[data-motion="reduced"] .sxOrb .sx{animation:none;opacity:.9}
@media(prefers-reduced-motion:reduce){.sxOrb,.sxOrb .sx{animation:none;opacity:.9}}
/* logo hover: letters rise one after another, then settle in reverse.
   The delays flip between states so the wave always travels left→right
   on the way up and right→left on the way down. */
.wordmark .wl{display:inline-block;transition:transform .3s var(--ease);
  transition-delay:calc((3 - var(--li,0))*38ms)}
.mark:hover .wl,.pBrand:hover .wl{transform:translateY(-3px);
  transition-delay:calc(var(--li,0)*38ms)}
html[data-motion="reduced"] .wordmark .wl{transition:none;transform:none}
@media(prefers-reduced-motion:reduce){.wordmark .wl{transition:none;transform:none}}
.mark:hover .wordmark{transform:translate(1px,1px)}

/* ---- footer: light, readable, theme-aware ---- */
footer{background:var(--surface-muted);color:var(--text-secondary);border-top:1px solid var(--border)}
footer .mark,footer .wordmark{color:var(--text-primary)}
footer .lede,footer p{color:var(--text-secondary);font-size:15px}
.fcol h4{color:var(--text-tertiary)}
.fcol button{color:var(--text-secondary);font-size:15px}
.fcol button:hover{color:var(--primary-ink)}
.fbar{border-top:1px solid var(--border);color:var(--text-tertiary);font-family:var(--body);
  font-size:15px;letter-spacing:0;text-transform:none}
.fbar a{color:var(--text-secondary)}
.flag svg{border-color:var(--border)}
footer :focus-visible{outline-color:var(--focus-ring)}

/* ============ PROOF STRIP ============
   Four equal claims. Icons animate once on entry, never rotate. */
/* The strip sits inside the hero's LEFT column, so a viewport media query
   was the wrong instrument: at 1600px it forced four columns into ~500px,
   giving 116px cards where "Built for New Zealand" wrapped one word per
   line and the row grew to 258px tall. Size to the container instead. */
/* auto-fit gave 3+1 inside the hero's half-width column. Deliberate 2x2
   on desktop, 4-up where the hero is single-column and wide, 2-up small,
   one column narrow. */
.proofStrip{display:grid;grid-template-columns:1fr;gap:var(--s3);margin-top:var(--s8);max-width:620px}
@media(min-width:600px){.proofStrip{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(min-width:768px) and (max-width:999.98px){.proofStrip{grid-template-columns:repeat(4,minmax(0,1fr));max-width:none}}
@media(min-width:1000px){.proofStrip{grid-template-columns:repeat(2,minmax(0,1fr));max-width:none}}
.proof{align-content:start}
.proof{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);
  padding:var(--s4);display:grid;gap:6px;align-content:start}
.proof b{text-wrap:balance}
.proof b{font-size:14.5px;font-weight:700;color:var(--text-primary);letter-spacing:-.01em}
.proof span{font-size:12.5px;color:var(--text-secondary);line-height:1.45}
.pIcon{width:30px;height:30px;margin-bottom:4px;stroke:var(--primary);fill:none;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.pIcon .pScreen{fill:color-mix(in srgb,var(--primary) 18%,transparent);stroke:none}
.pIcon .pKey{fill:var(--primary);stroke:none;opacity:.35}
.rv.in .pIcon .pKey{animation:keySettle .4s var(--ease) forwards}
.rv.in .proof:nth-child(1) .pIcon .pKey:nth-of-type(3){animation-delay:.1s}
.rv.in .proof:nth-child(1) .pIcon .pKey:nth-of-type(5){animation-delay:.2s}
@keyframes keySettle{to{opacity:1}}
.pIcon .pHand,.pIcon .pHand2{transform-origin:16px 16px}
.rv.in .pIcon .pHand{animation:handTick .7s var(--ease) forwards}
.rv.in .pIcon .pHand2{animation:handTick2 .7s var(--ease) .1s forwards}
@keyframes handTick{from{transform:rotate(0)}to{transform:rotate(60deg)}}
@keyframes handTick2{from{transform:rotate(-30deg)}to{transform:rotate(0)}}
.pIcon .pPin{fill:var(--accent);stroke:none;transform-origin:16px 14px;transform:scale(.4);opacity:0}
.rv.in .pIcon .pPin{animation:pinPop .5s var(--spring) .15s forwards}
@keyframes pinPop{to{transform:scale(1);opacity:1}}
.pIcon .pCheck{stroke:var(--success);stroke-width:2.2;stroke-dasharray:22;stroke-dashoffset:22}
.rv.in .pIcon .pCheck{animation:drawCheck .55s var(--ease) .2s forwards}
@keyframes drawCheck{to{stroke-dashoffset:0}}
html[data-motion="reduced"] .pIcon *{animation:none!important;opacity:1!important;transform:none!important;stroke-dashoffset:0!important}
@media(prefers-reduced-motion:reduce){.pIcon *{animation:none!important;opacity:1!important;transform:none!important;stroke-dashoffset:0!important}}

/* public budget page illustrations */
.splitDemo{margin-top:var(--s8);max-width:560px}
.sdTotal{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);padding:var(--s5);box-shadow:var(--shadow-1)}
.sdTotal span{display:block;font-size:14px;color:var(--text-secondary)}
.sdTotal b{display:block;font-size:34px;color:var(--text-primary);margin-top:4px}
.sdParts{display:grid;gap:8px;margin-top:var(--s3)}
@media(min-width:640px){.sdParts{grid-template-columns:repeat(2,1fr)}}
.sdPart{background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--c);border-radius:var(--r);
  padding:12px 14px;display:flex;justify-content:space-between;align-items:center;gap:12px;
  opacity:0;transform:translateY(-10px)}
.rv.in .sdPart{animation:sdDrop .5s var(--ease) forwards}
.rv.in .sdPart:nth-child(2){animation-delay:.12s}
.rv.in .sdPart:nth-child(3){animation-delay:.24s}
.rv.in .sdPart:nth-child(4){animation-delay:.36s}
@keyframes sdDrop{to{opacity:1;transform:none}}
.sdPart span{font-size:14px;color:var(--text-secondary)}
.sdPart b{font-size:16px;color:var(--text-primary)}
/* cross-device sync illustration */
.syncDemo{display:flex;align-items:center;gap:var(--s4);margin-top:var(--s8);flex-wrap:wrap}
.sdDesk,.sdPhone{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);padding:14px;box-shadow:var(--shadow-1)}
.sdDesk{width:min(240px,46vw)}
.sdPhone{width:112px}
.sdBar{height:6px;border-radius:3px;background:var(--primary);width:38%;margin-bottom:10px}
.sdRow{height:7px;border-radius:4px;background:var(--surface-muted);margin-bottom:6px}
.sdRow.short{width:62%}
.sdChip{font-size:11.5px;font-weight:600;color:var(--on-primary);background:var(--primary);border-radius:999px;
  padding:4px 9px;display:inline-block;margin-top:8px;opacity:0}
.rv.in .sdDesk .sdChip{animation:sdIn .4s var(--ease) .2s forwards}
.rv.in .sdPhone .sdChip{animation:sdIn .4s var(--ease) 1.05s forwards}
@keyframes sdIn{to{opacity:1}}
.sdPulse{width:44px;height:2px;background:var(--border-strong);border-radius:2px;position:relative;overflow:hidden}
.sdPulse::after{content:"";position:absolute;inset:0;width:40%;background:var(--primary);transform:translateX(-120%)}
.rv.in .sdPulse::after{animation:sdPulse .8s var(--ease) .5s forwards}
@keyframes sdPulse{to{transform:translateX(260%)}}
html[data-motion="reduced"] .sdPart,html[data-motion="reduced"] .sdChip{opacity:1;transform:none;animation:none}
@media(prefers-reduced-motion:reduce){.sdPart,.sdChip{opacity:1;transform:none;animation:none}}

/* ---- QA council fixes ---- */
/* MO1: hero words survive the in-app motion toggle */
html[data-motion="reduced"] .word{opacity:1!important;transform:none!important}
@media(prefers-reduced-motion:reduce){.word{opacity:1;transform:none}}
/* MO2: payslip detection callouts are shown, not stuck hidden */
html[data-motion="reduced"] .sda-line[data-k]::before,html[data-motion="reduced"] .sda-line[data-k]::after{opacity:1!important;animation:none}
@media(prefers-reduced-motion:reduce){.sda-line[data-k]::before,.sda-line[data-k]::after{opacity:1}}
/* M1: data-flow captions legible on phones (the svg scales down ~0.42) */

/* M2: allocation editor fits narrow phones */
@media(max-width:480px){.tplRow{grid-template-columns:20px 1fr 62px 78px;gap:6px}
  .tplRow .tplAmt{font-size:12px}}
/* M3: the portal hero amount shrinks before it clips */
@media(max-width:360px){.pHero .pAmount{font-size:clamp(26px,9vw,40px)}}
/* M4: admin detail rows wrap instead of clipping */
#admUserBox .row{flex-wrap:wrap}
/* MO4: the fan scroll cue finally pulses */
@media(prefers-reduced-motion:no-preference){.fanHint i{animation:hint 1.8s var(--ease) infinite}}

/* portal nav badge (pending invites) */
.pNavBadge{margin-left:auto;background:var(--accent);color:#fff;font-family:var(--data);font-size:11px;
  min-width:18px;height:18px;border-radius:9px;display:inline-grid;place-items:center;padding:0 5px}

/* ---- chart interaction: figure + readout + key ---- */
.chartReadout{font-family:var(--data);font-size:13px;color:var(--text-secondary);margin:8px 0 4px;min-height:18px}
.chartReadout b{color:var(--text-primary);font-size:15px}
.donutKey button{display:flex;align-items:center;gap:8px;border:0;background:none;padding:6px 8px;
  border-radius:var(--r-sm,8px);cursor:pointer;text-align:left;width:100%;color:var(--text-secondary);font-size:13.5px;min-height:32px}
.donutKey button[aria-pressed="true"]{background:var(--surface-muted);color:var(--text-primary)}
.chart[data-sel]:not([data-sel=""]) .dSeg{opacity:.3}
.chart .dSeg{transition:opacity .25s var(--ease),stroke-dasharray .8s var(--ease)}
.chart .dSeg.sel{opacity:1}
/* trend chart: six cycles, income reference line */
/* ---- info dots: "i" beside a heading, explainer on click ---- */
.infoDot{display:inline-flex;align-items:center;justify-content:center;
  width:18px;height:18px;margin-left:8px;padding:0;border-radius:50%;flex:0 0 auto;
  border:1.5px solid var(--faint);background:transparent;color:var(--faint);
  font-family:var(--data);font-size:11px;font-weight:700;line-height:1;
  cursor:pointer;vertical-align:middle;transition:color .2s var(--ease),border-color .2s var(--ease),background .2s var(--ease)}
.infoDot:hover,.infoDot[aria-expanded="true"]{color:var(--surface);background:var(--primary);border-color:var(--primary)}
.infoDot:focus-visible{outline:2px solid var(--primary);outline-offset:2px}
/* An 18px circle is the right SIZE for a mark beside a heading and the wrong
   TARGET for a thumb. The hit area is grown to 44px around it without moving
   anything: the dot still looks like a dot, and it can be hit like a button.
   Position is set on the element so the pseudo-element has something to
   anchor to, and the area is centred on the dot rather than hanging off it. */
.infoDot{position:relative}
.infoDot::before{content:"";position:absolute;left:50%;top:50%;
  width:44px;height:44px;transform:translate(-50%,-50%);border-radius:50%}
/* the dot's touch target, without letting an 18px circle set the line height */
.infoDot::after{content:"";position:absolute;inset:-13px}
.infoDot{position:relative}
.infoPop{position:fixed;z-index:9000;max-width:300px;
  background:var(--surface);color:var(--text);
  border:1px solid var(--rule);border-radius:var(--r-lg);
  padding:var(--s4) var(--s5);box-shadow:var(--shadow-3,0 12px 34px rgba(0,0,0,.18));
  animation:infoIn .18s var(--ease)}
.infoPop b{display:block;font-family:var(--display);font-size:14.5px;margin-bottom:6px}
.infoPop p{margin:0;font-size:13.5px;line-height:1.5;color:var(--text-secondary,var(--faint))}
@keyframes infoIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.infoPop{animation:none}}
/* the viewBox is drawn at the card's real width, so this is a backstop
   against the chart ever ballooning again on a wide screen */
.trend{width:100%;height:auto;display:block;overflow:visible;max-height:240px}
.trend .tBar rect{transform-box:fill-box;transform-origin:bottom;transform:scaleY(var(--k,1));transition:transform .6s var(--ease);transition-delay:calc(var(--i,0)*60ms)}
.trend .tIncome{stroke-dasharray:4 4}
.trend text{font-family:var(--data);font-size:11px;fill:var(--text-tertiary)}
/* compact quick actions (council R4) */
.pAction{display:flex;align-items:center;gap:10px;width:100%;min-height:48px;padding:0 14px;border-radius:var(--r);
  background:var(--surface-muted);color:var(--text-primary);font-size:15px;font-weight:500;text-align:left}
.pAction:hover{background:color-mix(in srgb,var(--primary) 8%,var(--surface-muted))}
/* premium hero (council R2): radial glow + 1px stroke + inner highlight, no blur */
.pHero{border:1px solid color-mix(in srgb,var(--primary) 26%,var(--border));
  background:radial-gradient(120% 90% at 85% -10%, color-mix(in srgb,var(--primary) 10%,transparent), transparent 60%),
    linear-gradient(150deg, color-mix(in srgb,var(--primary) 14%,var(--surface)), var(--surface) 62%);
  box-shadow:var(--shadow-2,0 8px 24px rgba(0,0,0,.06)), inset 0 1px 0 color-mix(in srgb,#fff 55%,transparent)}
html[data-mode="dark"] .pHero{box-shadow:var(--shadow-2,0 8px 24px rgba(0,0,0,.3)), inset 0 1px 0 color-mix(in srgb,#fff 7%,transparent)}
/* disabled buttons look disabled */
.solid:disabled,.btn:disabled,.outl:disabled,.pQuick:disabled,.pAction:disabled{opacity:.45;pointer-events:none}
/* txDel is visible intent, not hover-revealed */
.txDel{opacity:.75}
.txDel:hover{opacity:1}
/* dashboard customise panel */
.dashCustom{display:grid;gap:8px}
.dashCustom .dcRow{display:flex;align-items:center;gap:10px;min-height:40px}
.dashCustom .dcRow b{flex:1;font-weight:500;font-size:14.5px}
.dashCustom .dcRow .dcMove{min-width:34px;min-height:34px;border-radius:8px;background:var(--surface-muted)}
.dashCustom .dcRow .dcLock{font-size:12.5px;color:var(--text-tertiary)}

/* ============ PRIDE v5: motion that answers the user ============
   Law: every rainbow motion is one-shot and user-caused; the gradient
   moves as position, never hue-rotate; data colours are never replaced. */
/* P7: the gradient border travels once around the frame on hover */
html[data-theme="pride"] .solid,html[data-theme="pride"] .btn:not(.btn--ghost),html[data-theme="pride"] .pQuick{
  border:2px solid transparent;
  background:linear-gradient(var(--primary),var(--primary)) padding-box,
             linear-gradient(100deg,#E14855,#E87924,#D7A900,#2B9A66,#3478D4,#7655D9,#B64C96,#E14855) border-box;
  background-size:100% 100%,240% 100%;background-position:0 0,0 0}
@media(prefers-reduced-motion:no-preference){
  html[data-theme="pride"] .solid:hover,
  html[data-theme="pride"] .btn:not(.btn--ghost):hover,
  html[data-theme="pride"] .pQuick:hover{animation:prideBorderSweep .9s var(--ease) 1}
}
@keyframes prideBorderSweep{from{background-position:0 0,0 0}to{background-position:0 0,140% 0}}
html[data-theme="pride"] .solid::before{display:none}   /* one sweep per hover, not two */
/* P8: risen letters answer in flag colours */
html[data-theme="pride"] .mark:hover .wl:nth-child(1),html[data-theme="pride"] .pBrand:hover .wl:nth-child(1){color:#E14855}
html[data-theme="pride"] .mark:hover .wl:nth-child(2),html[data-theme="pride"] .pBrand:hover .wl:nth-child(2){color:#D7A900}
html[data-theme="pride"] .mark:hover .wl:nth-child(3),html[data-theme="pride"] .pBrand:hover .wl:nth-child(3){color:#2B9A66}
html[data-theme="pride"] .mark:hover .wl:nth-child(4),html[data-theme="pride"] .pBrand:hover .wl:nth-child(4){color:#3478D4}
/* P9: temporal progress (time, not money) may carry the flag */
html[data-theme="pride"] .pTrack i{background:var(--gradient-pride)}
html[data-theme="pride"] .bar i{background:var(--gradient-pride)}
/* P10: selected tabs seal with a gradient hairline */
html[data-theme="pride"] .seg button[aria-pressed="true"],
html[data-theme="pride"] .authTabs button[aria-selected="true"]{
  border-bottom:2px solid transparent;
  background:linear-gradient(var(--surface),var(--surface)) padding-box,var(--gradient-pride) border-box}
/* P11: pressed chips ring instead of flooding */
html[data-theme="pride"] .chip[aria-pressed="true"]{
  border:1.5px solid transparent;color:var(--text-primary);
  background:linear-gradient(var(--surface),var(--surface)) padding-box,var(--gradient-pride) border-box}
/* P12: the toast dot */
html[data-theme="pride"] .toast i{background:conic-gradient(from 0deg,#E14855,#E87924,#D7A900,#2B9A66,#3478D4,#7655D9,#B64C96,#E14855)}

/* ============ MOTION QUALITY PASS (council v5) ============ */
/* constellation: 6s cycle — noticeably more alive, still mostly at rest */
.sxOrb{animation-duration:6s}
.sxOrb .sx{animation-duration:6s}
.sxOrb .s2{animation-delay:.4s}.sxOrb .s3{animation-delay:.8s}.sxOrb .s4{animation-delay:1.2s}
@keyframes sxPulse{0%,30%{transform:scale(1)}36%{transform:scale(1.06)}44%,100%{transform:scale(1)}}
/* wordmark letters: colour joins the transition (P8 fades, not snaps) */
.wordmark .wl{transition:transform .3s var(--ease),color .3s var(--ease)}
/* trial calendar: free days land with spring; billing day merely exists */
@media(prefers-reduced-motion:no-preference){
  .trialCal.rv.in .tc.free{animation:tcPopSpring .35s var(--spring) backwards}
  .trialCal.rv.in .tc.bill{animation:tcFade .3s var(--ease) .65s backwards}
}
@keyframes tcPopSpring{from{transform:scale(.7) translateY(-4px);opacity:0}to{transform:none;opacity:1}}
@keyframes tcFade{from{opacity:0}to{opacity:1}}
/* calendar folds with gravity and unfolds with a kiss of overshoot */
.rv.in .calFlip .cfOld{animation:cfFold .4s cubic-bezier(.55,0,.85,.4) .3s forwards}
@keyframes cfUnfold{0%{transform:scaleY(0)}80%{transform:scaleY(1.04)}100%{transform:scaleY(1)}}
/* scan rows mark the instant the beam crosses them */
.scanDoc .scRow{transition:fill .3s var(--ease)}
.rv.in .scanDoc .scRow:nth-of-type(2){transition-delay:.38s}
.rv.in .scanDoc .scRow:nth-of-type(3){transition-delay:.5s}
.rv.in .scanDoc .scRow:nth-of-type(4){transition-delay:.62s}
.rv.in .scanDoc .scRow:nth-of-type(5){transition-delay:.78s}
.rv.in .scanDoc .scRow:nth-of-type(6){transition-delay:1s}
/* envelope: flap opens out of the way, note delivers, flap rests */
@keyframes peFlap{0%{transform:scaleY(1)}25%{transform:scaleY(.15)}80%{transform:scaleY(.15)}100%{transform:scaleY(1)}}
.vfEnv .peFlap{animation:peFlap 1.5s var(--ease) both}
@keyframes peSlide{0%{transform:translateY(16px);opacity:0}85%{transform:translateY(-2px);opacity:1}100%{transform:translateY(0);opacity:1}}
/* SAVED stamp: a mark on the document, not a modal */
.savedStamp{width:72px;height:72px;font-size:14px}
/* sheet exit: accelerate away */
#sheet.closing .sheetIn{transform:translateY(100%);transition:transform .24s cubic-bezier(.5,0,.75,.5)}
#sheet.closing .sheetBg{opacity:0;transition:opacity .2s}
/* mobile fan: cards enter, cue points sideways */
@media(max-width:760px){
  .rv.in .slipcard:nth-child(-n+6){animation:fadeUpCard .5s var(--ease) backwards}
  .rv.in .slipcard:nth-child(1){animation-delay:.0s}.rv.in .slipcard:nth-child(2){animation-delay:.045s}
  .rv.in .slipcard:nth-child(3){animation-delay:.09s}.rv.in .slipcard:nth-child(4){animation-delay:.135s}
  .rv.in .slipcard:nth-child(5){animation-delay:.18s}.rv.in .slipcard:nth-child(6){animation-delay:.225s}
}
@keyframes fadeUpCard{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
/* payslip detection boxes lock on, labels follow */
.sda-line[data-k]::before{transform:scale(1.15);transform-origin:left center;transition:transform .35s var(--ease),opacity .12s}

/* brand lockups + standalone constellations */
.authBrand{display:flex;align-items:center;gap:10px;justify-content:flex-start;margin:0 0 22px}
@media(max-width:560px){.authWrap .solid,#contact form .solid{width:100%;justify-content:center;min-height:52px}}
.authBrand .wordmark{color:inherit}
.cta{position:relative;overflow:hidden}
.ctaSx,.sxBig{display:block}
.cta .sxBig{position:absolute;right:4%;top:50%;transform:translateY(-50%);opacity:.14;color:var(--primary);pointer-events:none}
.pHero .sxBig{position:absolute;right:14px;top:12px;opacity:.08;color:currentColor;pointer-events:none}
.sxBig.sxOnce,.sxBig.sxOnce .sx{animation-iteration-count:1}
.rBrand{display:block;margin-top:6px;font-size:10.5px;letter-spacing:.14em;opacity:.7}




























/* ---- Turnstile mount. Collapses to nothing when the bot check is
   switched off, so the form spacing does not change. ---- */
.captchaSlot:empty{display:none}
.captchaSlot{margin-top:20px;min-height:0}
.captchaSlot iframe{max-width:100%}

/* ---- a provider that is not switched on yet ---- */
.oauthRow button.soon{opacity:.58;cursor:default}
.oauthRow button.soon:hover{background:inherit;border-color:var(--rule)}
.oauthRow button .soonTag{margin-left:auto;padding:3px 8px;border-radius:999px;
  background:var(--rule);color:var(--faint);
  font-family:var(--data);font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;
  white-space:nowrap}

/* ============ CALCULATOR: every period at once ============ */
/* A person comparing a fortnight against a month had to switch a chip
   and hold the first number in their head. The table shows both. */
/* Auth-aware calls to action. Both labels ship in the markup and CSS picks
   one, so nothing flashes and no page needs its own `if (user)`. The class
   is set before first paint from the stored token. */
[data-cta] .ctaIn{display:none}
html.has-account [data-cta="signup"] .ctaOut{display:none}
html.has-account [data-cta="signup"] .ctaIn{display:inline}
html.has-access [data-cta="trial"] .ctaOut{display:none}
html.has-access [data-cta="trial"] .ctaIn{display:inline}
.visually-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.calcMatrixWrap{margin-top:20px;overflow-x:auto;-webkit-overflow-scrolling:touch;
  border:1px solid var(--rule);border-radius:var(--r);background:var(--surface)}
.calcMatrix{width:100%;border-collapse:collapse;font-family:var(--data);font-size:14px;min-width:520px}
.calcMatrix th,.calcMatrix td{padding:11px 14px;text-align:right;white-space:nowrap}
.calcMatrix thead th{font-size:11px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--faint);font-weight:600;border-bottom:1px solid var(--rule)}
.calcMatrix tbody th{text-align:left;font-weight:500;color:var(--text);vertical-align:middle;font-family:var(--display);font-size:14.5px}
.calcMatrix tbody th i{display:inline-block;vertical-align:middle;margin-right:9px;width:9px;height:9px;border-radius:50%}
.calcMatrix tbody tr+tr th,.calcMatrix tbody tr+tr td{border-top:1px solid var(--rule-2)}
/* the row people came for */
.calcMatrix tr.cmNet th,.calcMatrix tr.cmNet td{background:var(--surface-muted);font-weight:700;
  border-top:1.5px solid var(--rule)}
.calcMatrix tr.cmNet td{color:var(--income)}
/* the first column stays put while the periods scroll on a phone */
@media(max-width:700px){
  .calcMatrix tbody th,.calcMatrix thead th:first-child{position:sticky;left:0;z-index:1;
    background:var(--surface);box-shadow:1px 0 0 var(--rule-2)}
  .calcMatrix tr.cmNet th{background:var(--surface-muted)}
}
/* the four figures worth knowing at a glance */
.calcStats{display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));
  gap:var(--s3);margin-top:18px}
.cStat{border:1px solid var(--rule);border-radius:var(--r);padding:13px 15px;background:var(--surface)}
.cStat b{display:block;font-size:23px;line-height:1.15;color:var(--text)}
.cStat span{display:block;margin-top:3px;font-size:12.5px;line-height:1.35;color:var(--faint)}

/* ---- one look for every locked feature ---- */
/* Whatever a person tries to reach without access, they meet the same
   panel: the mark, what it does, what it includes, and the way in. */
.lockPanel{text-align:left;max-width:620px;margin:0;padding:22px 0 4px}
.lockPanel h3,.lockPanel h2{font-family:var(--display);font-weight:700;
  font-size:clamp(24px,3.2vw,32px);letter-spacing:-.02em;line-height:1.15;
  margin:0 0 10px;color:var(--text)}
.lockPanel .lockLede{margin:0;font-size:16px;line-height:1.55;color:var(--muted);max-width:52ch}
.lockPanel .calc{max-width:460px}
.pCard>.lockPanel{padding-top:6px}


/* ============ PORTAL: hierarchy borrowed with care ============ */
/* time left becomes a shape: a ring on the hero, days remaining inside */
/* transaction rows carry a tinted category disc and a relative day */
.txRow{grid-template-columns:28px 1fr auto auto}
.txRow>.txWhat:first-child{grid-column:1/3}
.txRow .txDisc{width:28px;height:28px;border-radius:50%;display:grid;place-items:center;
  font-family:var(--data);font-size:12px;font-weight:600;flex:none}
.pCardHead{display:flex;justify-content:flex-start;align-items:center;gap:8px}
.pCardHead .lnk{margin-left:auto;order:2}
.pCardHead .infoDot{order:1;margin-left:0}
.pCardHead .pStatus{margin-left:0}
.pCardHead .lnk{font-size:13.5px;min-height:0;padding:0;--lnk-rule:-4px}
/* a finger and a mouse both need somewhere to land; this used to be inside a
   max-width media query, so the desktop link was a 78x22 target */
.pCardHead .lnk::before{content:"";position:absolute;inset:-12px -14px}

/* ============ ABOUT: the data journey, five steps ============ */
.dj{display:grid;grid-template-columns:minmax(0,1fr) 260px;gap:28px;align-items:start;max-width:740px;margin:0 0 6px}
/* ---- the journey, redrawn calm: one actor moves per step, nothing shares a
   rectangle with anything else, the only soft layer is the lock's shield ---- */
.djStage{width:100%;height:auto;display:block;aspect-ratio:400/200;overflow:visible}
.djLine,.djScreen,.djSlot,.djUnit rect,#djRow,.djLed circle,#djPkt rect,.djBody,.djRack{stroke:none}
.djBody{fill:var(--text);opacity:.92}
.djScreen{fill:var(--surface-muted)}
.djRack{fill:color-mix(in srgb,var(--text) 88%,var(--primary))}
.djUnit rect:first-child{fill:color-mix(in srgb,var(--surface) 12%,transparent);stroke:color-mix(in srgb,#fff 18%,transparent);stroke-width:1}
.djSlot{fill:color-mix(in srgb,#fff 30%,transparent)}
.djLed circle{fill:var(--income);opacity:.55}
.djLine{transform-box:fill-box;transform-origin:left center}
.djOnDevice rect{fill:color-mix(in srgb,var(--income) 18%,var(--surface))}
.djOnDevice text{font:700 7px var(--data);letter-spacing:.14em;fill:var(--income)}
.djOnDevice{opacity:0;transition:opacity .24s var(--ease)}
#djWire{fill:none;stroke:var(--rule);stroke-width:2;stroke-dasharray:4 6;stroke-linecap:round}
#djPkt{opacity:0;transform:translate(124px,100px)}
#djPkt rect{fill:var(--primary)}
.djPktLock{fill:none;stroke:var(--surface);stroke-width:1.6}
.djPktBody{fill:var(--surface)}
#djRow{fill:var(--income);opacity:0;transform-box:fill-box;transform-origin:center}
#djRowId{font:700 7.5px var(--data);letter-spacing:.12em;fill:var(--income);opacity:0}
#djShield{fill:color-mix(in srgb,var(--accent) 10%,var(--surface));stroke:color-mix(in srgb,var(--accent) 55%,transparent);stroke-width:1.5;opacity:0;transform-box:fill-box;transform-origin:center;transform:scale(.9)}
#djShackle{fill:none;stroke:var(--accent);stroke-width:2;transform-box:fill-box;transform-origin:bottom;transform:translateY(-7px)}
#djLockIn>rect{fill:var(--accent)}
#djYou{font:700 9.5px var(--data);fill:var(--surface);letter-spacing:.06em}
#djLock{opacity:0;transition:opacity .24s var(--ease)}
#djSrv{opacity:.35;transition:opacity .38s var(--ease)}
.djDash{opacity:0;transition:opacity .24s var(--ease)}
/* step 1: the lines type in, the badge appears */
.dj[data-step="1"] .djLine{animation:djTypeIn .4s var(--ease) backwards}
.dj[data-step="1"] .djLine:nth-of-type(2){animation-delay:.06s}.dj[data-step="1"] .djLine:nth-of-type(3){animation-delay:.12s}
.dj[data-step="1"] .djLine:nth-of-type(4){animation-delay:.18s}.dj[data-step="1"] .djLine:nth-of-type(5){animation-delay:.24s}.dj[data-step="1"] .djLine:nth-of-type(6){animation-delay:.3s}
.dj[data-step="1"] .djOnDevice{opacity:1}
/* step 2: the packet crosses, alone */
.dj[data-step="2"] #djSrv,.dj[data-step="3"] #djSrv,.dj[data-step="4"] #djSrv{opacity:1}
.dj[data-step="2"] #djPkt{animation:djTravel 1s var(--ease) forwards}
/* step 3: the row lands, and is named */
.dj[data-step="3"] #djRow,.dj[data-step="4"] #djRow,.dj[data-step="5"] #djRow{opacity:1}
.dj[data-step="3"] #djRow{animation:djLand .24s var(--ease)}
.dj[data-step="3"] #djRowId,.dj[data-step="4"] #djRowId,.dj[data-step="5"] #djRowId{animation:djFade .24s .3s forwards}
/* step 4: the shield grows, the lock shuts */
.dj[data-step="4"] #djLock,.dj[data-step="5"] #djLock{opacity:1}
.dj[data-step="4"] #djShield,.dj[data-step="5"] #djShield{animation:djShield .38s var(--ease) forwards}
.dj[data-step="4"] #djShackle,.dj[data-step="5"] #djShackle{animation:djShut .24s var(--ease) .2s forwards}
/* step 5: the packet returns, the rack rests, the screen becomes the dashboard */
.dj[data-step="5"] #djSrv{opacity:.45}
.dj[data-step="5"] #djPkt{animation:djReturn 1s var(--ease) forwards}
.dj[data-step="5"] .djPlan{opacity:0;transition:opacity .24s var(--ease) .98s}
.dj[data-step="5"] .djDash{opacity:1;transition:opacity .24s var(--ease) .98s}
@keyframes djTypeIn{from{opacity:0;transform:scaleX(.4)}}
@keyframes djTravel{0%{opacity:0;transform:translate(124px,100px)}12%{opacity:1;transform:translate(124px,100px)}84%{opacity:1;transform:translate(236px,100px)}100%{opacity:0;transform:translate(236px,100px)}}
@keyframes djReturn{0%{opacity:0;transform:translate(236px,100px)}12%{opacity:1;transform:translate(236px,100px)}84%{opacity:1;transform:translate(124px,100px)}100%{opacity:0;transform:translate(124px,100px)}}
@keyframes djLand{from{opacity:0;transform:translateX(-10px)}to{opacity:1;transform:none}}
@keyframes djShut{to{transform:translateY(0)}}
@keyframes djFade{to{opacity:1}}
@keyframes djShield{from{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}
html[data-motion="reduced"] .dj *{animation:none!important;transition:none!important}
html[data-motion="reduced"] .dj[data-step="2"] #djPkt,html[data-motion="reduced"] .dj[data-step="5"] #djPkt{opacity:0}
html[data-motion="reduced"] .dj[data-step="3"] #djRowId,html[data-motion="reduced"] .dj[data-step="4"] #djRowId,html[data-motion="reduced"] .dj[data-step="5"] #djRowId{opacity:1}
html[data-motion="reduced"] .dj[data-step="4"] #djShield,html[data-motion="reduced"] .dj[data-step="5"] #djShield{opacity:1;transform:none}
html[data-motion="reduced"] .dj[data-step="4"] #djShackle,html[data-motion="reduced"] .dj[data-step="5"] #djShackle{transform:none}

.djRail{list-style:none;margin:0;padding:0;display:grid;gap:4px;counter-reset:dj}
.djRail li{margin:0}
.djRail button{all:unset;box-sizing:border-box;cursor:pointer;display:grid;grid-template-columns:24px 1fr;gap:10px;
  padding:9px 8px;border-radius:10px;color:var(--faint);width:100%;min-height:44px;align-items:start;
  transition:background .2s var(--ease),color .2s var(--ease)}
.djRail button:hover{background:var(--surface-muted)}
.djRail button:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}
.djRail button::before{counter-increment:dj;content:counter(dj);font:600 11px var(--data);width:24px;height:24px;border-radius:50%;
  display:grid;place-items:center;border:1.5px solid var(--rule);color:var(--faint);transition:background .2s,border-color .2s,color .2s}
.djRail button b{display:block;font-weight:600;font-size:14.5px;line-height:24px;color:inherit}
.djRail button span{display:none;grid-column:2;font-size:13px;line-height:1.45;color:var(--text-secondary);margin-top:2px}
.djRail button[aria-current="step"]{color:var(--text)}
.djRail button[aria-current="step"]::before{background:var(--accent);border-color:var(--accent);color:var(--surface)}
.djRail button[aria-current="step"] span{display:block;min-height:57px}

@media(max-width:640px){
  .dj{grid-template-columns:1fr;gap:10px}
  .djStage{max-width:440px;margin:0 auto}
  .djRail{grid-template-columns:repeat(5,1fr);gap:6px;position:relative;padding-bottom:0}
  .djRail button{grid-template-columns:1fr;justify-items:center;padding:6px 0;gap:0}
  .djRail button b,.djRail button span{display:none}
  .djRail button[aria-current="step"] b,.djRail button[aria-current="step"] span{display:block;position:static;text-align:center}
  .djRail button[aria-current="step"]{grid-column:1/-1;grid-row:2;justify-items:center;padding:10px 4px 0;min-height:104px}
  .djRail button[aria-current="step"]::before{display:none}
  .djRail button[aria-current="step"] b{font-size:15px;line-height:22px}
  .djRail button[aria-current="step"] span{top:68px;min-height:40px;margin:0;padding:0 8px}
}

html[data-motion="reduced"] .dj *{animation:none!important;transition:none!important}
html[data-motion="reduced"] .dj[data-step="2"] #djPkt,html[data-motion="reduced"] .dj[data-step="5"] #djPkt{opacity:1;transform:translate(180px,100px)}
html[data-motion="reduced"] .dj[data-step="3"] .djLed circle,html[data-motion="reduced"] .dj[data-step="4"] .djLed circle{opacity:1}
html[data-motion="reduced"] .dj[data-step="4"] #djShackle,html[data-motion="reduced"] .dj[data-step="5"] #djShackle{transform:none}
html[data-motion="reduced"] .dj[data-step="4"] #djYou,html[data-motion="reduced"] .dj[data-step="5"] #djYou{opacity:1}
html[data-motion="reduced"] .dj[data-step="4"] #djKey,html[data-motion="reduced"] .dj[data-step="4"] #djKeyStem,html[data-motion="reduced"] .dj[data-step="5"] #djKey,html[data-motion="reduced"] .dj[data-step="5"] #djKeyStem{opacity:0}
@media(prefers-reduced-motion:reduce){
  .dj *{animation:none!important;transition:none!important}
  .dj[data-step="2"] #djPkt,.dj[data-step="5"] #djPkt{opacity:1;transform:translate(180px,100px)}
  .dj[data-step="3"] .djLed circle,.dj[data-step="4"] .djLed circle{opacity:1}
  .dj[data-step="4"] #djShackle,.dj[data-step="5"] #djShackle{transform:none}
  .dj[data-step="4"] #djYou,.dj[data-step="5"] #djYou{opacity:1}
  .dj[data-step="4"] #djKey,.dj[data-step="4"] #djKeyStem,.dj[data-step="5"] #djKey,.dj[data-step="5"] #djKeyStem{opacity:0}
}

/* ============ TOGGLE: an on/off switch, one for the whole product ============ */
/* A native checkbox with role=switch does the state; the track and knob
   are drawn. The knob stretches while it travels and the track gives a
   little on press, which is what makes it feel physical. */
.tgl{display:flex;align-items:center;gap:12px;cursor:pointer;color:var(--text);font-size:14.5px;line-height:1.35;
  -webkit-tap-highlight-color:transparent}
.tgl input{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
.tglTrack{position:relative;flex:none;width:48px;height:28px;border-radius:14px;background:color-mix(in srgb,var(--text-tertiary) 18%,var(--surface-muted));
  border:1.5px solid var(--rule-2);box-shadow:inset 0 1px 3px rgb(0 0 0/.10);
  transition:background .3s var(--spring,var(--ease)),border-color .3s var(--ease),transform .15s var(--ease)}
.tglTrack::before{content:"";position:absolute;top:calc(50% - 11px);left:2px;width:22px;height:22px;border-radius:50%;
  background:var(--surface);box-shadow:inset 0 0 0 1px rgb(255 255 255/.5),0 1px 3px rgb(0 0 0/.25);
  transform-origin:center;transition:transform .3s var(--spring,var(--ease)),background .3s var(--ease)}
.tgl input:checked+.tglTrack{background:var(--primary);border-color:var(--primary)}
.tgl input:checked+.tglTrack::before{transform:translateX(20px);background:var(--on-primary)}
.tgl:active .tglTrack{transform:scaleX(.97)}
.tgl:active .tglTrack::before{transform:translateX(3px) scaleX(1.3)}
.tgl.sm:active .tglTrack::before{transform:translateX(2px) scaleX(1.3)}
.tgl:active input:checked+.tglTrack::before{transform:translateX(16px) scaleX(1.3)}
.tgl.sm:active input:checked+.tglTrack::before{transform:translateX(12px) scaleX(1.3)}
.tgl input:focus-visible+.tglTrack{outline:2px solid var(--focus-ring);outline-offset:2px}
.tgl:has(input:disabled){cursor:not-allowed;opacity:.5}
.tgl.sm .tglTrack{width:40px;height:24px}.tgl.sm .tglTrack::before{width:18px;height:18px;top:calc(50% - 9px)}
.tgl{min-height:44px}
.tgl.sm{min-height:44px;min-width:44px;justify-content:center}
.dashCustom .dcRow{min-height:44px}
.dashCustom .dcRow b{flex:1}
.dashCustom .dcMove:disabled{opacity:.35;cursor:default}
.sw .tgl{flex:none;align-self:center}
@media(max-width:560px){.sw{flex-direction:column;gap:12px}}
.pBottom .isCur{color:var(--primary-ink)}
.mIco{display:inline-flex;width:20px;height:20px;flex:none;color:var(--faint)}
.mIco svg{width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.moreList{display:grid;grid-template-columns:1fr 1fr;gap:6px}
.moreList button{min-height:48px;padding:11px 12px;font-size:14.5px}
.moreList button:last-child{grid-column:1/-1}
/* under 360px two columns wrap half the labels onto two lines; one column
   keeps every row 48px */
@media(max-width:359px){.moreList{grid-template-columns:1fr}}
.tgl.sm input:checked+.tglTrack::before{transform:translateX(16px)}
.tglText{min-width:0}
.tglText small{display:block;color:var(--faint);font-size:13px;margin-top:1px}
html[data-mode="dark"] .tglTrack{box-shadow:inset 0 1px 3px rgb(0 0 0/.45)}
html[data-mode="dark"] .tglTrack::before{background:var(--text-secondary);box-shadow:inset 0 0 0 1px rgb(255 255 255/.14),0 1px 3px rgb(0 0 0/.5)}
html[data-motion="reduced"] .tglTrack,html[data-motion="reduced"] .tglTrack::before{transition-duration:.01ms}
html[data-motion="reduced"] .tgl:active .tglTrack,html[data-motion="reduced"] .tgl:active .tglTrack::before{transform:none}
html[data-motion="reduced"] .tgl:active input:checked+.tglTrack::before{transform:translateX(20px)}
html[data-motion="reduced"] .tgl.sm:active input:checked+.tglTrack::before{transform:translateX(16px)}

/* ============ PORTAL v2: the ring is the hero, cards answer one question each ============ */
.pPill{display:inline-flex;align-items:center;gap:6px;min-height:26px;padding:4px 12px;border-radius:999px;
  font-family:var(--data);font-size:12.5px;letter-spacing:.02em;color:var(--text-secondary);
  background:color-mix(in srgb,var(--primary) 12%,var(--surface));border:1px solid color-mix(in srgb,var(--primary) 22%,var(--border))}
.pHero{position:relative;overflow:hidden}
.pHeroTop{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:var(--s5);align-items:center}
.pRing{position:relative;width:148px;height:148px;display:grid;place-items:center;flex:none;justify-self:end}
.pRing svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.pRing circle,.goalRing circle{fill:none;stroke-width:11;stroke-linecap:round}
.pRingTrack{stroke:color-mix(in srgb,var(--primary) 9%,var(--rule));stroke-width:9}
.pRingArc{transition:stroke-dasharray 1.1s var(--ease) .15s}
.pRing .pRingTrack,.goalRing .pRingTrack{stroke-width:9}
.pRingIn{position:relative;text-align:center;line-height:1.02}
.pRingIn b{display:block;font-family:var(--data);font-size:30px;font-weight:600;color:var(--text);letter-spacing:-.02em}
.pRingIn span{display:block;font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);margin-top:3px}
.pRingIn .rAmt{display:none}
/* on a phone the ring carries the amount: one shape, one number */
@media(max-width:640px){
  .pHeroTop{grid-template-columns:1fr;justify-items:center;text-align:center;gap:var(--s3)}
  .pHeroTop .pHeroText{display:none}
  .pRing{width:212px;height:212px;justify-self:center;order:-1}
  .pRing circle{stroke-width:12}.pRing .pRingTrack{stroke-width:10}
  .pRingIn{max-width:160px}
  .pRingIn .rDays{display:none}
  .pRingIn b.rAmt{display:block;font-size:clamp(22px,8.4vw,32px);letter-spacing:-.02em;white-space:nowrap}
  .pRingIn span.rAmt{display:block}
  .pRingIn small.rAmt{display:block;font-family:var(--data);font-size:11.5px;color:var(--text-secondary);margin-top:8px}
  .pHero .pWhen{text-align:center;margin-top:var(--s3)}
  .pHero .pTrackEnds{display:none}
}
.pSplit{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin-top:var(--s5)}
.pSplit .pS{background:color-mix(in srgb,var(--primary) 6%,var(--surface));border:1px solid color-mix(in srgb,var(--primary) 12%,var(--border));border-radius:var(--r);padding:11px 10px;min-width:0}
.pSplit .pS span{display:block;font-size:11.5px;color:var(--text-tertiary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pSplit .pS{container-type:inline-size;padding:10px 8px}
.pSplit .pS b{display:block;font-family:var(--data);font-size:clamp(14px,17cqi,18px);font-variant-numeric:tabular-nums;color:var(--text-primary);margin-top:3px;white-space:nowrap;letter-spacing:-.01em}
.pSplit .pS.hero{background:color-mix(in srgb,var(--income) 10%,var(--surface));border-color:color-mix(in srgb,var(--income) 25%,var(--border))}
.pSplit .pS.hero b{color:var(--income)}
/* one status word per card */
.pStatus{display:inline-flex;align-items:center;min-height:22px;padding:2px 9px;border-radius:999px;margin-left:8px;vertical-align:middle;
  font-family:var(--data);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;font-weight:600;
  background:color-mix(in srgb,var(--primary) 12%,var(--surface));color:var(--text-secondary)}
.pStatus[data-tone="good"]{background:color-mix(in srgb,var(--income) 15%,var(--surface));color:var(--income)}
.pStatus[data-tone="warn"]{background:color-mix(in srgb,var(--debt) 16%,var(--surface));color:var(--debt)}
.num .numSub{font-family:var(--display);font-size:13px;font-weight:500;color:var(--faint);letter-spacing:0}
/* cards lift a little under the pointer; families share a tint */
.pCard{transition:transform .2s var(--ease),box-shadow .2s var(--ease)}
@media(hover:hover){.pCard:hover{transform:translateY(-2px);box-shadow:var(--shadow-2)}}
[data-mod="catprog"] .pCard{background:linear-gradient(160deg,color-mix(in srgb,var(--flexible) 9%,var(--surface)),var(--surface) 72%)}
[data-mod="trend"] .pCard,[data-mod="cycle_compare"] .pCard{background:linear-gradient(160deg,color-mix(in srgb,var(--primary) 9%,var(--surface)),var(--surface) 72%)}
[data-mod="recent"] .pCard,[data-mod="top_merchants"] .pCard{background:linear-gradient(160deg,color-mix(in srgb,var(--accent) 8%,var(--surface)),var(--surface) 72%)}
[data-mod="savings_goal"] .pCard,[data-mod="net_position"] .pCard{background:linear-gradient(160deg,color-mix(in srgb,var(--saving) 10%,var(--surface)),var(--surface) 72%)}
[data-mod="income_stability"] .pCard,[data-mod="paycheck"] .pCard{background:linear-gradient(160deg,color-mix(in srgb,var(--income) 9%,var(--surface)),var(--surface) 72%)}
[data-mod="recurring"] .pCard,[data-mod="bills_ahead"] .pCard{background:linear-gradient(160deg,color-mix(in srgb,var(--commitment) 9%,var(--surface)),var(--surface) 72%)}
[data-mod="spend_rhythm"] .pCard{background:linear-gradient(160deg,color-mix(in srgb,var(--cat-2) 9%,var(--surface)),var(--surface) 72%)}
/* the phone dashboard: two columns, hero and wide cards full width */
@media(max-width:1023px){
  .pGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
  .pGrid>[class*="span"]{flex:none;min-width:0}
  .pGrid>.span12,.pGrid>.span8,.pGrid>.span7,.pGrid>[data-mod="hero"],.pGrid>[data-mod="actions"],.pGrid>[data-mod="recent"],.pGrid>[data-mod="trend"]{grid-column:1/-1}
  .pGrid>[class*="span"]:not([data-mod]){grid-column:1/-1}   /* two columns are for the Overview tiles only */
  .pSplit .pS span{white-space:normal}
  #pBody .checkOpts .field{width:100%!important}
  body.in-portal .remainPanel{padding:14px}
  .acct-grid .eyebrow{display:flex;flex-direction:column;gap:2px}
  /* 148px keeps the two-column Overview tiles on an even grid. A card that
     already spans the full width has nothing to line up with, so the rule
     only padded short ones: 54px of guaranteed white under a single
     sentence. Give it to the tiles that need it and nobody else. */
  .pCard{padding:14px}
  /* Only [data-mod] cells ever sit two-to-a-row here; every other span is
     already grid-column:1/-1 two rules above. So the even-tile rule belongs
     to the modules and nothing else. */
  .pGrid>[data-mod] .pCard{min-height:148px}
  .pCard h3{font-size:14px}
  /* five icon tiles in one row, short labels; the long names live on desktop */
  [data-mod="actions"] .qaGrid{grid-template-columns:repeat(5,minmax(0,1fr));gap:6px}
  [data-mod="actions"] .pAction{flex-direction:column;justify-content:center;gap:6px;min-height:66px;padding:8px 2px;font-size:11px;text-align:center;line-height:1.1;letter-spacing:.01em}
  [data-mod="actions"] .pAction svg{width:22px;height:22px}
  [data-mod="actions"] .mLong{display:none}[data-mod="actions"] .mShort{display:inline}
  .pCardHead{flex-wrap:wrap;align-items:center;row-gap:6px;gap:8px}
  .pCardHead .pPill,.pCardHead .pStatus{white-space:nowrap;margin-left:0;letter-spacing:.06em}
  [data-mod="actions"] .pAction{font-size:12px}
  .pCard.pEmpty{min-height:0;padding:12px 14px}
  .pCardHead .lnk{white-space:nowrap;margin-left:auto;order:2}
  .pCardHead .infoDot{order:1}
  .pCardHead .mLong{display:none}.pCardHead .mShort{display:inline}
}
@media(max-width:640px){
  /* a paragraph in a half-width column reads like a ransom note; empty
     states and the two-column comparisons get the full width */
  .pGrid>[data-mod]:has(.pEmpty),.pGrid>[data-mod="cycle_compare"],.pGrid>[data-mod="income_stability"],.pGrid>[data-mod="spend_rhythm"],.pGrid>[data-mod="savings_goal"]{grid-column:1/-1}
}
.qaGrid{display:grid;gap:6px;margin-top:6px}
.pAction svg{width:18px;height:18px;flex:none;stroke:var(--primary);fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.mShort{display:none}
/* chart system: lines draw, areas fade, bars grow, dots answer */
.spark{width:100%;height:auto;max-height:140px;display:block;overflow:visible;margin-top:8px}
.spark .cTick{font-family:var(--data);font-size:10px;fill:var(--text-tertiary)}
.cLine{stroke-dasharray:1 1;stroke-dashoffset:1;transition:stroke-dashoffset .9s var(--ease) .1s}
.cArea{opacity:0;transition:opacity .7s var(--ease) .4s}
.cDot{opacity:0;transition:opacity .3s var(--ease) .9s,r .15s var(--ease);cursor:pointer;outline:none}
.cHalo{opacity:0;transform-box:fill-box;transform-origin:center}
.rv.in .cLine,.chart.in .cLine{stroke-dashoffset:0}
.rv.in .cArea,.chart.in .cArea{opacity:1}
.rv.in .cDot,.chart.in .cDot{opacity:1}
.rv.in .cHalo,.chart.in .cHalo{animation:cHalo 1.2s var(--ease) 1s 1}
@keyframes cHalo{0%{opacity:.6;transform:scale(1)}100%{opacity:0;transform:scale(3.2)}}
.cDot.sel,.cDot:hover,.cDot:focus-visible{r:5.5}
.cDot:focus-visible{filter:drop-shadow(0 0 0 3px color-mix(in srgb,var(--focus-ring) 45%,transparent))}
.trend .tBar rect{transform-box:fill-box;transform-origin:bottom;transform:scaleY(var(--k,1));transition:transform .55s var(--ease),filter .15s var(--ease);transition-delay:calc(var(--i,0)*60ms)}
.trend .tBar rect.now{opacity:.55;stroke:var(--primary);stroke-dasharray:3 3;stroke-width:1.5;fill-opacity:.35}
.trend .tBar{cursor:pointer;outline:none}
.trend .tBar:hover rect,.trend .tBar.sel rect{filter:brightness(1.08) saturate(1.1)}
.trend .tBar:focus-visible rect{filter:drop-shadow(0 0 0 3px color-mix(in srgb,var(--focus-ring) 45%,transparent))}
.chart[data-sel]:not([data-sel=""]) .tBar:not(.sel) rect{opacity:.45}
.chart .dSeg{stroke-linecap:round}
.chart .dSeg:focus-visible{outline:none;filter:drop-shadow(0 0 0 3px color-mix(in srgb,var(--focus-ring) 45%,transparent))}
.chart .dSeg:hover{stroke-width:13}
.goalRing{position:relative;width:96px;height:96px;display:grid;place-items:center;margin:10px 0 0}
.goalRing svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.goalRing .pRingArc{transition:stroke-dasharray 1s var(--ease) .2s}
.goalRing b{position:relative;font-size:19px}
.cmpBars{display:grid;gap:6px;margin:8px 0 4px}
.cmpBars .cmpRow{display:grid;grid-template-columns:34px 1fr;align-items:center;gap:8px;font:600 11px var(--data);letter-spacing:.06em;text-transform:uppercase;color:var(--text-tertiary)}
.cmpBars i{display:block;height:8px;border-radius:999px;background:var(--c);width:var(--w);transition:width .8s var(--ease) .2s}
html[data-motion="reduced"] .cLine{stroke-dashoffset:0;transition:none}
html[data-motion="reduced"] .cArea,html[data-motion="reduced"] .cDot{opacity:1;transition:none}
html[data-motion="reduced"] .cHalo{animation:none}
html[data-motion="reduced"] .pRingArc,html[data-motion="reduced"] .trend .tBar rect,html[data-motion="reduced"] .cmpBars i{transition:none}

/* ============ THE ANIMATED DASHBOARD (dz), THE CTA, HOW-TO ============ */
/* Static styles are the FINAL frame; keyframes declare only "from", with
   fill-mode backwards, so reduced motion shows the finished dashboard. */
.dzWrap{aspect-ratio:16/10;width:100%;max-width:520px;margin:0 auto}
.dz{width:100%;height:auto;display:block;overflow:visible}
.dz *{transform-box:fill-box;transform-origin:center}
.dz-bg{fill:var(--surface-muted);stroke:var(--rule)}
.dz-halo circle{fill:none;stroke:var(--rule-2);stroke-dasharray:3 6;opacity:.35}
.dzCta .dz,.hsFrame .dz{overflow:hidden}
.hsFrame .dz-bg{stroke:none}
.dz-side rect:first-child{fill:var(--surface);stroke:var(--rule)}
.dz-hero>rect:first-child,.dz-card>rect:first-child,.dz-nav>rect:first-child{fill:var(--surface);stroke:var(--rule)}
.dz-hero>rect{fill:color-mix(in srgb,var(--primary) 8%,var(--surface))}
.dz-lab{font:600 9px var(--data);letter-spacing:.14em;fill:var(--faint)}
.dz-when{font:500 12px var(--display);fill:var(--text-secondary)}
.dz-cur,.dz-odo text{font:600 30px var(--data);fill:var(--text);letter-spacing:-.02em}
.dz-odo{translate:0 calc(var(--n) * -34px)}
.dz-track{fill:none;stroke:var(--rule);stroke-width:7;stroke-dasharray:1 5}
.dz-arc{fill:none;stroke:var(--primary);stroke-width:7;stroke-linecap:round;stroke-dasharray:64 100}
.dz-ring text{font:600 14px var(--data);fill:var(--text);text-anchor:middle}
.dz-bar{transform-origin:bottom}
.dz-area{fill:color-mix(in srgb,var(--accent) 16%,transparent)}
.dz-line{fill:none;stroke:var(--accent);stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1 1}
.dz-dot{fill:var(--rule-2)}
.dz-add{fill:var(--primary)}.dz-plus{stroke:var(--on-primary);stroke-width:2.5;stroke-linecap:round}
.dz-cursor{fill:var(--text);transform:translate(393px,360px) scale(.85)}
@keyframes dzScale{from{opacity:0;transform:scale(.86)}}
@keyframes dzFlip{from{opacity:0;transform:translateX(-14px) scaleY(.2)}}
@keyframes dzRight{from{opacity:0;transform:translateX(18px)}}
@keyframes dzUp{from{opacity:0;transform:translateY(24px)}}
@keyframes dzGrow{from{transform:scaleY(0)}}
@keyframes dzArc{from{stroke-dasharray:0 100}}
@keyframes dzDraw{from{stroke-dasharray:0 1}}
@keyframes dzFade{from{opacity:0}}
@keyframes dzOdo{from{translate:0 0}}
@keyframes dzCursor{0%{opacity:0;transform:translate(560px,140px) scale(1)}20%{opacity:1}80%{transform:translate(393px,360px) scale(1)}100%{transform:translate(393px,360px) scale(.85)}}
@keyframes dzPop{from{transform:scale(1)}60%{transform:scale(1.25)}}
@keyframes dzFloat{from{transform:translateY(0)}50%{transform:translateY(-8px)}to{transform:translateY(0)}}
.dzWrap.in .dz-side{animation:dzScale .5s var(--spring,var(--ease)) 0s backwards}
.dzWrap.in .dz-mi{animation:dzFlip .4s var(--ease) calc(.25s + var(--i) * 90ms) backwards}
.dzWrap.in .dz-hero{animation:dzScale .55s var(--spring,var(--ease)) .7s backwards}
.dzWrap.in .dz-hero .dz-lab,.dzWrap.in .dz-hero .dz-when,.dzWrap.in .dz-card .dz-lab{animation:dzRight .5s var(--ease) 1s backwards}
.dzWrap.in .dz-arc{animation:dzArc 1s var(--ease) 1s backwards}
.dzWrap.in .dz-odo{animation:dzOdo 1.1s var(--ease) 1.3s backwards}
.dzWrap.in .dz-c1{animation:dzScale .5s var(--spring,var(--ease)) 1.6s backwards}
.dzWrap.in .dz-c2{animation:dzScale .5s var(--spring,var(--ease)) 1.72s backwards}
.dzWrap.in .dz-bar{animation:dzGrow .45s var(--spring,var(--ease)) calc(1.95s + var(--i) * 70ms) backwards}
.dzWrap.in .dz-area{animation:dzFade .6s var(--ease) 2.6s backwards}
.dzWrap.in .dz-line{animation:dzDraw .8s var(--ease) 2.3s backwards}
.dzWrap.in .dz-nav{animation:dzUp .45s var(--ease) 2.6s backwards}
.dzWrap.in .dz-cursor{animation:dzCursor 1s var(--ease) 3s backwards}
.dzWrap.in .dz-add{animation:dzPop .35s var(--spring,var(--ease)) 3.8s}
.dzWrap.in .dz-halo circle{animation:dzFloat 9s ease-in-out 1}
@media(prefers-reduced-motion:reduce){.dz *{animation:none!important}}
html[data-motion="reduced"] .dz *{animation:none!important}
.dzCta .dzGrid{display:grid;gap:36px;text-align:left;align-items:center}
.dzActs{display:flex;gap:16px 28px;flex-wrap:wrap;margin-top:28px;align-items:center}
@media(min-width:900px){.dzCta .dzGrid{grid-template-columns:1.05fr 1fr;gap:56px}}
@media(max-width:640px){.dzCta .dzWrap{max-width:100%;order:-1}.dzActs .solid{width:100%}}
.hs{list-style:none;padding:0;margin:var(--s10) 0 0;display:grid;gap:var(--s10)}
.hsStep{display:grid;grid-template-columns:32px 1fr;gap:var(--s5);align-items:start}
.hsStep .bullet{width:32px;height:32px;border-radius:50%;display:grid;place-items:center;font:600 13px var(--data);
  border:1.5px solid var(--rule);color:var(--faint);transition:background .25s var(--ease),color .25s,border-color .25s}
.hsStep[aria-current="step"] .bullet{background:var(--accent);border-color:var(--accent);color:var(--surface)}
.hsStep h3{margin:2px 0 8px;font-size:20px}
.hsStep p{margin:0 0 12px;color:var(--muted);font-size:15.5px;max-width:52ch}
.hsFrame{grid-column:2;position:relative;aspect-ratio:16/10;margin:0;border:1px solid var(--rule);border-radius:var(--r-lg);overflow:hidden;background:var(--surface-muted)}
.hsFrame img,.hsFrame .dzWrap{width:100%;height:100%;max-width:none;object-fit:cover;object-position:top;display:block}
.hsFrame picture{display:block;width:100%;height:100%}
.hsFrame img[data-shot]{cursor:zoom-in}
/* a desktop capture squeezed into 296px is not a screenshot, it is a
   texture: on a phone the frame takes the phone capture, upright, at a
   height that keeps the step readable, and a tap opens it full size */
@media(max-width:600px){
  .hsFrame:has(img[data-shot]){aspect-ratio:9/13;max-width:300px;margin:0 auto}
}
@media(min-width:601px) and (max-width:1023px){
  .hsFrame:has(img[data-shot]){aspect-ratio:3/4;max-width:520px;margin:0 auto}
}
.shotDlg{border:0;padding:0;background:transparent;max-width:min(96vw,1200px);max-height:96vh;width:auto;margin:auto}
.shotDlg::backdrop{background:rgba(0,0,0,.72);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.shotDlg img{display:block;max-width:96vw;max-height:92vh;width:auto;height:auto;border-radius:14px;box-shadow:0 30px 80px rgba(0,0,0,.4)}
.shotDlg button{position:absolute;top:10px;right:10px;width:40px;height:40px;border-radius:50%;background:#fff;color:#19171D;font-size:20px;line-height:40px;text-align:center;box-shadow:0 4px 14px rgba(0,0,0,.25)}
html[data-mode="dark"] .hsFrame img{filter:brightness(.82) contrast(1.05)}
.hsFrame .hsPlaceholder{display:none;position:absolute;inset:0;place-items:center;font:600 11px var(--data);letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.hsFrame.hsPending img{display:none}.hsFrame.hsPending .hsPlaceholder{display:grid}
@media(min-width:900px){.hsStep{grid-template-columns:32px 1fr 1fr}.hsFrame{grid-column:3;grid-row:1}}


/* ---- shared spaces ---- */
/* Eight activity lines is a lot of low-value text to scroll past on a phone.
   Three answer "did they see it"; the rest are one tap away. */
.spMore>summary{cursor:pointer;list-style:none;font-size:13px;color:var(--primary);
  padding:8px 0;min-height:44px;display:flex;align-items:center}
.spMore>summary::-webkit-details-marker{display:none}
.spMore>summary::after{content:'';width:7px;height:7px;margin-left:8px;border-right:2px solid currentColor;
  border-bottom:2px solid currentColor;transform:rotate(45deg) translateY(-2px);transition:transform var(--m-fast,.18s) var(--ease-release,ease)}
.spMore[open]>summary::after{transform:rotate(-135deg) translateY(-1px)}
/* An entry being corrected in place: the same fields it was created with,
   set into the list rather than over it, so the row it belongs to is obvious. */
.seEdit{border:1px solid var(--border);border-radius:12px;padding:12px;margin:8px 0;
  background:var(--surface-muted)}
/* 42x44 was two pixels under target on its narrow axis. */
.txRow .txDel{min-width:44px;min-height:44px}

/* ============================================================
   ADMIN CONSOLE
   The rest of the product is written to be read; this is written
   to be worked in. Dense rows, tabular figures, status as colour
   plus a word, and no motion beyond what confirms a click. It is
   the one screen where scanning beats reading.
   ============================================================ */
.admConsole{margin-top:26px;font-size:13px;color:var(--text)}
#admin .inner:has(.admConsole){max-width:1400px}
.admConsole .hidden{display:none!important}

.admBar{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;
  border:1px solid var(--rule);border-radius:10px;padding:10px 14px;background:var(--surface)}
.admMetrics{display:flex;gap:22px;flex-wrap:wrap}
.admMetric{display:flex;align-items:baseline;gap:6px}
.admMetric b{font-family:var(--data);font-size:18px;font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.admMetric i{font-style:normal;font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.admMetric[data-alert="1"] b{color:var(--amber)}
.admBarRight{display:flex;align-items:center;gap:12px}
.admWho{font-size:12px;color:var(--muted)}
.admBtn{border:1px solid var(--rule);background:var(--surface);color:var(--text);
  border-radius:8px;padding:7px 12px;font:inherit;font-size:12.5px;cursor:pointer;min-height:34px;
  transition:border-color var(--m-fast,.18s) var(--ease-release,ease),background var(--m-fast,.18s) var(--ease-release,ease)}
.admBtn:hover{border-color:var(--accent)}
.admBtn:active{transform:translateY(1px)}
.admBtn[data-danger]{color:var(--danger);border-color:color-mix(in srgb,var(--danger) 35%,var(--rule))}
.admBtn[data-danger]:hover{background:color-mix(in srgb,var(--danger) 8%,var(--surface))}
.admBtn[disabled]{opacity:.5;cursor:default}

.admTabs{display:flex;gap:2px;margin:18px 0 0;border-bottom:1px solid var(--rule)}
.admTab{appearance:none;background:none;border:0;border-bottom:2px solid transparent;
  font:inherit;font-size:13px;color:var(--muted);padding:10px 14px;cursor:pointer;min-height:44px;
  display:flex;align-items:center;gap:7px}
.admTab[aria-selected="true"]{color:var(--text);border-bottom-color:var(--accent);font-weight:600}
.admCount{font-family:var(--data);font-size:11px;background:var(--amber);color:#1b1503;
  border-radius:999px;padding:1px 6px;min-width:18px;text-align:center}
.admCount:empty{display:none}
.admPane{padding-top:16px}

.admToolbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px}
.admSearch{flex:1 1 280px;min-width:0;border:1px solid var(--rule);border-radius:8px;
  background:var(--surface);color:var(--text);font:inherit;font-size:13px;padding:9px 12px;min-height:38px}
.admSearch:focus{outline:2px solid var(--accent);outline-offset:-1px}
.admFilters{display:flex;gap:6px;flex-wrap:wrap}
.admChip{appearance:none;border:1px solid var(--rule);background:var(--surface);color:var(--muted);
  border-radius:999px;font:inherit;font-size:12px;padding:6px 11px;cursor:pointer;min-height:32px}
.admChip[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:var(--accent-ink);font-weight:600}
.admResult{font-size:12px;color:var(--muted);margin-left:auto}

/* the working area: a list you scan and one account you act on */
.admSplit{display:grid;grid-template-columns:minmax(0,1fr) 380px;gap:18px;align-items:start}
.admSplit:not(:has(.admDetail>*)){grid-template-columns:minmax(0,1fr)}
.admSplit:not(:has(.admDetail>*)) .admDetail{display:none}
.admTableWrap{border:1px solid var(--rule);border-radius:10px;overflow:hidden;background:var(--surface)}
.admTable{width:100%;border-collapse:collapse;font-size:12.5px}
.admTable th{text-align:left;font-weight:600;font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);padding:9px 12px;border-bottom:1px solid var(--rule);background:var(--surface-2);
  position:sticky;top:0;z-index:1}
.admTable td{padding:8px 12px;border-bottom:1px solid var(--rule-2,var(--rule));vertical-align:middle}
.admTable tbody tr:last-child td{border-bottom:0}
.admTable tbody tr{cursor:pointer}
.admTable tbody tr:hover{background:var(--surface-2)}
.admTable tbody tr[aria-selected="true"]{background:color-mix(in srgb,var(--accent) 10%,var(--surface))}
.admTable tbody tr:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.admNum{text-align:right;font-family:var(--data);font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--muted)}
.admWhoCell b{display:block;font-weight:600;color:var(--text)}
.admWhoCell span{display:block;font-size:11.5px;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:34ch}
.admQuiet{padding:26px 12px!important;color:var(--muted);text-align:center}

/* status reads as a word first and a colour second, so it survives
   colour blindness and a black and white screenshot in a support thread */
.admTag{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;line-height:1;
  border:1px solid var(--rule);border-radius:999px;padding:4px 8px;color:var(--muted);white-space:nowrap}
.admTag::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.admTag[data-tone="good"]{color:var(--success,var(--income));border-color:color-mix(in srgb,currentColor 35%,transparent)}
.admTag[data-tone="warn"]{color:var(--amber);border-color:color-mix(in srgb,currentColor 40%,transparent)}
.admTag[data-tone="bad"]{color:var(--danger);border-color:color-mix(in srgb,currentColor 40%,transparent)}
.admTag[data-tone="key"]{color:var(--accent-strong);border-color:color-mix(in srgb,currentColor 40%,transparent)}
.admTags{display:flex;gap:5px;flex-wrap:wrap}

/* the account panel: sticky beside the list, full width under it on a phone */
.admDetail{position:sticky;top:78px;border:1px solid var(--rule);border-radius:10px;
  background:var(--surface);max-height:calc(100dvh - 110px);overflow:auto;overscroll-behavior:contain}
.admDetail:empty{display:none}
.admDHead{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;
  padding:14px;border-bottom:1px solid var(--rule);position:sticky;top:0;background:var(--surface);z-index:2}
.admDHead h3{margin:0;font-size:15px;line-height:1.25}
.admDHead .admId{display:block;font-family:var(--data);font-size:11px;color:var(--muted);margin-top:3px;word-break:break-all}
.admDBody{padding:14px}
.admDTabs{display:flex;gap:2px;padding:0 14px;border-bottom:1px solid var(--rule)}
.admDTab{appearance:none;background:none;border:0;border-bottom:2px solid transparent;font:inherit;
  font-size:12.5px;color:var(--muted);padding:9px 10px;cursor:pointer;min-height:40px}
.admDTab[aria-selected="true"]{color:var(--text);border-bottom-color:var(--accent);font-weight:600}
.admKV{display:grid;grid-template-columns:minmax(88px,auto) 1fr;gap:6px 12px;font-size:12.5px}
.admKV dt{color:var(--muted)}
.admKV dd{margin:0;color:var(--text);word-break:break-word}
.admKV dd.admMono{font-family:var(--data);font-variant-numeric:tabular-nums}
.admActs{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}
.admSect{margin-top:18px;padding-top:14px;border-top:1px solid var(--rule)}
.admSect h4{margin:0 0 8px;font-size:11px;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);font-weight:600}
.admNote{font-size:11.5px;color:var(--muted);margin:10px 0 0;line-height:1.5}
.admLog{font-family:var(--data);font-size:11.5px;line-height:1.7}
.admLog div{display:flex;gap:10px;padding:4px 0;border-bottom:1px solid var(--rule-2,var(--rule))}
.admLog time{color:var(--muted);flex:none;font-variant-numeric:tabular-nums}
.admLog span{min-width:0;overflow-wrap:anywhere}

/* support and activity */
.admMsg{border:1px solid var(--rule);border-radius:10px;padding:14px;background:var(--surface);margin-bottom:10px}
.admMsg header{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.admMsg header b{font-size:13.5px}
.admMsg .admFrom{font-family:var(--data);font-size:11.5px;color:var(--muted)}
.admMsg p{margin:8px 0 0;font-size:13px;color:var(--text);white-space:pre-wrap}
.admBars{display:grid;gap:7px}
.admBarRow{display:grid;grid-template-columns:minmax(120px,1fr) minmax(0,3fr) auto;gap:10px;align-items:center;font-size:12.5px}
.admBarRow i{display:block;height:7px;border-radius:4px;background:var(--accent);min-width:2px}
.admBarRow b{font-family:var(--data);font-variant-numeric:tabular-nums;font-weight:600}

@media(max-width:1023px){
  .admSplit{grid-template-columns:minmax(0,1fr)}
  .admDetail{position:static;max-height:none;order:-1}
  .admTable thead{display:none}
  .admTable tbody tr{display:grid;grid-template-columns:1fr auto;gap:2px 10px;padding:10px 12px;
    border-bottom:1px solid var(--rule-2,var(--rule))}
  .admTable td{border:0;padding:0}
  .admTable td:nth-child(1){grid-column:1;grid-row:1/span 2}
  .admTable td:nth-child(2){grid-column:2;grid-row:1;text-align:right}
  .admTable td:nth-child(3){grid-column:2;grid-row:2;text-align:right}
  .admTable td:nth-child(4),.admTable td:nth-child(5){grid-column:1/-1;text-align:left;font-size:11.5px}
  .admTable td:nth-child(4)::before{content:'Joined '}
  .admTable td:nth-child(5)::before{content:'Last seen '}
  .admTags{justify-content:flex-end}
}
/* the console is a tool, so it starts near the top of the window rather than
   after a page of marketing air */
.admSection{padding-block:clamp(20px,3vw,34px)!important}
.admTitle{font-size:19px!important;font-weight:700;letter-spacing:-.01em;margin:0 0 4px!important;line-height:1.2}
@media(max-width:640px){
  /* four metrics on two tidy lines rather than three and a stray */
  .admMetrics{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;width:100%}
  .admBar{align-items:stretch}
  .admBarRight{width:100%;justify-content:space-between;padding-top:10px;border-top:1px solid var(--rule)}
  .admTabs{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
  .admTabs::-webkit-scrollbar{display:none}
  .admTab{white-space:nowrap}
}
/* an announcement in the sheet: prose, not a form */
.noticeBody{font-size:14.5px;line-height:1.55;color:var(--text-secondary,var(--muted))}
.noticeBody p{margin:0 0 10px}
.noticeBody strong{color:var(--text-primary,var(--text))}
.noticeBody ul{margin:0 0 10px;padding-left:18px}
.noticeBody li{margin:0 0 5px}

/* ---- the headline, and the parts it is made of ---- */
/* The figure is a button. It has to still read as the figure, so it takes no
   button chrome until a pointer or a keyboard says somebody means it. */
/* `font:inherit` outranked `.pHero .pAmount` (same specificity, later in the
   file) and reset the hero figure to 16.5px body type: the biggest number in
   the product was not there. A button needs its family and weight unset from
   the UA sheet, never its size. */
.pAmount.heroTap{appearance:none;background:none;border:0;padding:0;margin:0;position:relative;
  /* No font properties at all. `font:inherit` reset the hero figure to the
     parent paragraph's 16.5px body type, and `font-size:inherit` does exactly
     the same thing: it inherits from the PARENT, not from `.pHero .pAmount`,
     which has equal specificity and loses on source order. Author rules beat
     the UA button style on their own, so saying nothing here is what lets the
     figure be the size the hero rule asks for. */
  color:inherit;text-align:left;cursor:pointer;display:inline-flex;align-items:center;gap:8px;
  border-radius:10px;transition:opacity var(--m-fast,.18s) var(--ease-release,ease)}
/* the figure stays the size it is; the target grows around it */
.pAmount.heroTap::after{content:"";position:absolute;left:-8px;right:-8px;top:50%;height:44px;transform:translateY(-50%)}
.pAmount.heroTap:hover{opacity:.82}
.pAmount.heroTap:active{transform:scale(.985)}
.pAmount.heroTap:focus-visible{outline:2px solid var(--primary);outline-offset:4px}
.heroTapIcon{width:18px;height:18px;flex:none;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;opacity:.42;transition:opacity var(--m-fast,.18s) var(--ease-release,ease)}
.pAmount.heroTap:hover .heroTapIcon{opacity:.75}
.heroParts{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.heroPart{display:inline-flex;align-items:baseline;gap:6px;border:1px solid var(--border);
  border-radius:999px;padding:5px 11px;background:var(--surface)}
.heroPart i{font-style:normal;font-size:11.5px;letter-spacing:.04em;color:var(--text-tertiary,var(--muted))}
.heroPart b{font-family:var(--data);font-size:13px;font-variant-numeric:tabular-nums;color:var(--text-primary,var(--text))}

/* ---- the headline's derivation ---- */
.hbTotal{font-family:var(--data);font-size:32px;font-weight:700;letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;margin:0 0 6px;color:var(--text-primary,var(--text))}
.hbTotal.hbNeg{color:var(--danger)}
.hbList{border-top:1px solid var(--border)}
.hbHead{margin:14px 0 6px;font-size:11px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--text-tertiary,var(--muted));font-weight:600}
.hbRow{display:flex;align-items:baseline;justify-content:space-between;gap:14px;
  padding:7px 0;border-bottom:1px solid var(--border)}
.hbWhat{font-size:14px;color:var(--text-primary,var(--text));min-width:0}
.hbWhat small{display:block;font-size:11.5px;color:var(--text-tertiary,var(--muted));margin-top:2px}
.hbAmt{font-family:var(--data);font-size:14px;font-variant-numeric:tabular-nums;white-space:nowrap}
.hbAmt.hbIn{color:var(--income)}
.hbAmt.hbOut{color:var(--text-secondary,var(--muted))}

/* ---- arriving on a page ---- */
/* Short and small on purpose: long enough to say something happened, too
   short to be something you wait for. Content only, never the chrome, so the
   bar and the navigation stay put while the page under them changes. */
@keyframes pageEnter{
  from{opacity:0;transform:translate3d(0,8px,0)}
  to{opacity:1;transform:none}}
.pEnter{animation:pageEnter var(--m-page,280ms) var(--ease,cubic-bezier(.2,.6,.2,1))}
html[data-motion="reduced"] .pEnter{animation:none}

/* ---- waiting for a page ---- */
/* The shimmer moves left to right at reading speed. Two bars in a row are
   offset so the page does not pulse as one block, which reads as a broken
   screen rather than a loading one. */
.pSkelWrap .pCard{pointer-events:none}
.pSkelWrap .skel{margin-bottom:9px}
.pSkelWrap .skel:last-child{margin-bottom:0}
.pSkelWrap .skel:nth-child(2n){animation-delay:.09s}
.pSkelWrap .skel:nth-child(3n){animation-delay:.16s}
.skel.skelBig{height:40px;margin-top:12px;border-radius:9px}
.skelRow{display:flex;gap:10px;margin-top:12px}
.skelRow .skel{flex:1;margin-bottom:0}

/* ============================================================
   SWIPE A ROW AWAY

   The first version drew the red panel as a pseudo-element of the row
   with z-index:-1 and called that "behind". A row with position:relative
   and z-index:1 is a stacking context, and inside one a negative z-index
   paints ABOVE the element's own background, below only its text. So the
   panel sat on top of every row's amount at rest, and when the row was
   swiped the panel, being its child, moved with it. An auditor found it
   live at 2.4:1 over the amount column.

   Now the row is wrapped at wire time. The panel is the wrapper's child,
   behind; the row is opaque, in front, and translates over it; the wrapper
   clips whatever the row vacates on the left. Nothing is painted anywhere
   until a finger reveals it. The wrapper keeps pan-y, so a vertical drag
   started on a row is still a scroll: only the horizontal axis is claimed.
   ============================================================ */
.swipeWrap{position:relative;overflow:hidden;touch-action:pan-y;border-radius:10px}
/* the wrapper clips what the row vacates, which also clipped the Remove
   button's focus ring to nothing against its right edge */
.swipeWrap:focus-within{overflow:visible}
.swipeWrap:focus-within .swipePanel{display:none}
.swipeWrap .swipePanel{position:absolute;top:0;bottom:0;right:0;width:86px;
  display:grid;place-items:center;z-index:0;pointer-events:none;
  font-size:13px;font-weight:600;letter-spacing:.01em;
  color:#fff;background:color-mix(in srgb,var(--danger) calc(62% + 38% * var(--swipe,0)),#7a1a13)}
/* the panel becomes a control the moment it is fully open */
.swipeWrap.swipe-open .swipePanel{pointer-events:auto;cursor:pointer}
.swipeWrap .txRow[data-swipe]{position:relative;z-index:1;background:var(--surface);margin:0;
  /* .txRow animates in with accIn, whose keyframes set transform. A running
     animation beats an inline style, so while it played the swipe moved
     nothing at all — and wrapping the row re-inserts it, which restarts the
     animation at exactly the moment swipe is wired. The wrapper does the
     entrance now and the row's transform belongs to the finger. */
  animation:none}
.swipeWrap{animation:accIn .35s var(--ease)}
.swipeWrap .txRow[data-swipe].swiping{will-change:transform}
/* the row's own delete control is redundant once the panel is open, and two
   ways to do the same thing an inch apart is how a mis-tap happens */
.swipeWrap.swipe-open .txDel{opacity:.35;pointer-events:none}
/* White on this red is 2.2:1 in the dark themes. Dark ink on a lighter red
   clears 4.5:1 in every palette. */
html[data-mode="dark"] .swipeWrap .swipePanel{
  color:#1b0a08;background:color-mix(in srgb,var(--danger) calc(70% + 30% * var(--swipe,0)),#ffd6d1)}
/* under reduced motion the gesture is not wired, so there must be no panel
   to reveal, or it is a red block that explains nothing */
html[data-motion="reduced"] .swipeWrap .swipePanel{display:none}
@media(prefers-reduced-motion:reduce){.swipeWrap .swipePanel{display:none}}

/* ---- Recently deleted ---- */
.binRow{display:grid;grid-template-columns:1fr auto auto;gap:10px 14px;align-items:center;
  padding:10px 0;border-bottom:1px solid var(--border)}
.binRow:last-child{border-bottom:0}
.binRow .binWhat{min-width:0}
.binRow .binWhat b{display:block;font-weight:600;font-size:14.5px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.binRow .binWhat small{display:block;font-size:11.5px;color:var(--text-tertiary,var(--muted))}
.binRow .binAmt{font-family:var(--data);font-variant-numeric:tabular-nums;font-size:14px;color:var(--text-secondary)}


/* ============================================================
   THE TOUR — shown once after sign-up, and again from Account.
   Same vocabulary as every overlay: scrim, surface card, --r-xl, 300ms.
   Every scene is an SVG whose static styles are the FINISHED frame; the
   keyframes declare only "from", so reduced motion shows the end state.
   ============================================================ */
#tour{position:fixed;inset:0;z-index:135;display:grid;place-items:center;padding:16px;visibility:hidden;pointer-events:none;transition:visibility 0s .24s}
#tour.open{visibility:visible;pointer-events:auto;transition:none}
/* Whichever way the hand-over goes, the full-screen card is the layer on top:
   arriving, it covers the spotlight it replaces; leaving, it dissolves to
   reveal the spotlight already in place, instead of being darkened by the
   spotlight's own scrim fading in over it. Keyed off hidden rather than open,
   because the class goes the instant the fade starts. */
#tour:not([hidden]){z-index:137}
.tourBg{position:absolute;inset:0;background:color-mix(in srgb,var(--text) 46%,transparent);opacity:0;transition:opacity .2s var(--ease)}
html[data-mode="dark"] .tourBg{background:rgba(0,0,0,.62)}
#tour.open .tourBg{opacity:1}
.tourCard{position:relative;width:min(560px,100%);max-height:calc(100dvh - 32px);overflow:auto;background:var(--surface);color:var(--text);
  border-radius:var(--r-xl);box-shadow:0 30px 80px color-mix(in srgb,var(--text) 28%,transparent);
  transform:translateY(16px) scale(.98);opacity:0;transition:transform .2s var(--ease),opacity .2s var(--ease)}
#tour.open .tourCard{transform:none;opacity:1;transition:transform var(--m-sheet) var(--spring,var(--ease)),opacity var(--m-sheet) var(--ease)}
.tourSkip{position:absolute;top:12px;right:12px;z-index:2;min-height:36px;padding:0 12px;border-radius:999px;font-size:13.5px;font-weight:600;
  color:var(--text-secondary);background:color-mix(in srgb,var(--surface) 78%,transparent);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}
.tourSkip:hover{color:var(--text)}
.tourScene{aspect-ratio:16/10;background:color-mix(in srgb,var(--primary) 6%,var(--surface-muted));border-bottom:1px solid var(--rule);overflow:hidden}
.tourScene svg{width:100%;height:100%;display:block;overflow:visible}
.tourScene svg *{transform-box:fill-box;transform-origin:center}
.tourBody{padding:20px 24px 4px}
/* Not uppercased: in the data face "6 OF 17" reads as "6 0F 17", and the one
   label whose whole job is to say where you are should not be a puzzle. */
.tourStep{margin:0 0 6px;font:600 12px var(--data);letter-spacing:.06em;color:var(--text-tertiary)}
.tourBody h2{margin:0 0 8px;font-size:22px;letter-spacing:-.02em;line-height:1.2}
.tourBody p#tourText{margin:0;color:var(--text-secondary);font-size:15.5px;line-height:1.5;min-height:3.1em}
.tourFoot{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;padding:16px 24px calc(20px + env(safe-area-inset-bottom,0px))}
.tourFoot .outl,.tourFoot .solid{min-height:44px;padding:0 18px}
.tourFoot .outl[disabled]{visibility:hidden}
.tourDots{list-style:none;margin:0;padding:0;display:flex;justify-content:center;gap:6px}
.tourDots li{width:6px;height:6px;border-radius:999px;background:var(--rule-2);transition:width .25s var(--ease),background .25s}
.tourDots li[aria-current="step"]{width:18px;background:var(--primary)}
/* scene vocabulary */
.tsCard{fill:var(--surface);stroke:var(--rule)}
.tsLab{font:600 8px var(--data);letter-spacing:.14em;fill:var(--faint)}
.tsBig{font:600 26px var(--data);fill:var(--text);letter-spacing:-.02em}
.tsTxt{font:500 11px var(--display);fill:var(--text-secondary)}
.tsChip{fill:color-mix(in srgb,var(--primary) 10%,var(--surface))}
.tsChipT{font:600 8.5px var(--display);fill:var(--primary-ink)}
.tsStar{fill:var(--accent)}
.tsPlus{fill:var(--primary)}.tsPlusG{stroke:var(--on-primary);stroke-width:2.4;stroke-linecap:round}
.tsRing{fill:none;stroke:var(--rule);stroke-width:6;stroke-dasharray:1 4}
.tsArc{fill:none;stroke:var(--primary);stroke-width:6;stroke-linecap:round}
.tsBar{transform-origin:left center}
.tsDoc{fill:var(--surface);stroke:var(--rule-2);stroke-width:1.5}
.tsDocL{stroke:var(--rule-2);stroke-width:2;stroke-linecap:round}
.tsLock{fill:var(--accent)}.tsShackle{fill:none;stroke:var(--accent);stroke-width:3;stroke-linecap:round}
@keyframes tsIn{from{opacity:0;transform:translateY(14px)}}
@keyframes tsPop{from{opacity:0;transform:scale(.7)}}
@keyframes tsGrow{from{transform:scaleX(0)}}
@keyframes tsArc{from{stroke-dasharray:0 100}}
@keyframes tsTwinkle{0%,100%{opacity:.35;transform:scale(.8)}50%{opacity:1;transform:scale(1.15)}}
@keyframes tsRise{from{opacity:0;transform:translateY(60px)}}
@keyframes tsFly{from{opacity:0;transform:translate(-90px,-40px) rotate(-8deg)}}
@keyframes tsShut{from{transform:translateY(-8px)}}
@keyframes tsPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.12)}}
@keyframes tsCount{from{translate:0 0}}
.ts-in{animation:tsIn .55s var(--ease) calc(var(--i,0) * 90ms) backwards}
.ts-pop{animation:tsPop .5s var(--spring,var(--ease)) calc(var(--i,0) * 90ms) backwards}
.ts-grow{animation:tsGrow .8s var(--ease) calc(.3s + var(--i,0) * 120ms) backwards}
.ts-arc{animation:tsArc 1.1s var(--ease) .4s backwards}
.ts-twinkle{animation:tsTwinkle 2.2s var(--ease) calc(var(--i,0) * 300ms) infinite}
.ts-rise{animation:tsRise .55s var(--spring,var(--ease)) .5s backwards}
.ts-fly{animation:tsFly .6s var(--ease) calc(.4s + var(--i,0) * 110ms) backwards}
.ts-shut{animation:tsShut .5s var(--spring,var(--ease)) .8s backwards}
.ts-pulse{animation:tsPulse 1.6s var(--ease) .6s infinite}
.tsOdo{translate:0 -68px;animation:tsCount 1.2s var(--ease) .5s backwards}
html[data-motion="reduced"] .tourScene svg *,html[data-motion="reduced"] #tour,html[data-motion="reduced"] .tourCard,html[data-motion="reduced"] .tourBg{animation:none!important;transition:none!important}
@media(prefers-reduced-motion:reduce){.tourScene svg *{animation:none!important}}


/* ============================================================
   THE dza-* VOCABULARY — the How page's animation, as a reusable kit.
   The rule is always the same: the static styles ARE the finished frame,
   keyframes only say where a thing comes from, fill-mode backwards holds
   it there until its turn, and --i staggers the turn. .dzWrap.in starts
   it (the .rv observer adds .in when the block is seen; portal scenes
   carry .in from the first paint). Reduced motion gets the finished frame.
   ============================================================ */
.dzWrap.in .dza-in{animation:dzUp .55s var(--ease) calc(var(--i,0) * 90ms) backwards}
.dzWrap.in .dza-up{animation:dzUp .6s var(--spring,var(--ease)) calc(var(--i,0) * 90ms) backwards}
.dzWrap.in .dza-pop{animation:dzScale .5s var(--spring,var(--ease)) calc(var(--i,0) * 90ms) backwards}
.dzWrap.in .dza-grow{transform-origin:left center;animation:dzGrowX .8s var(--ease) calc(.2s + var(--i,0) * 110ms) backwards}
.dzWrap.in .dza-fly{animation:tsFly .6s var(--ease) calc(.4s + var(--i,0) * 110ms) backwards}
@keyframes dzGrowX{from{transform:scaleX(0)}}
.dzMini{max-width:320px;margin:0 auto 12px}
.dzMini .dz{overflow:visible}
html[data-motion="reduced"] .dzWrap .dza-in,html[data-motion="reduced"] .dzWrap .dza-up,html[data-motion="reduced"] .dzWrap .dza-pop,
html[data-motion="reduced"] .dzWrap .dza-grow,html[data-motion="reduced"] .dzWrap .dza-fly{animation:none}


/* ============================================================
   MONEY MOMENTS — small animated scenes built from three objects:
   New Zealand notes, coins and cards. Each one is a .dzWrap, so it
   inherits the dza-* vocabulary above: the static styles ARE the
   finished frame, keyframes only say where a thing comes from, and
   .in starts it. On top of that a few idle loops keep a scene alive
   once it has arrived, all of them transform or opacity only.

   The notes are stylised, not reproductions: a rounded panel in the
   colour of its denomination, its number, and a blank oval where a
   portrait would be. Nothing on them could be mistaken for currency.
   ============================================================ */
.moScene{width:100%;margin:0 auto}
.moScene .mo{display:block;width:100%;height:auto;overflow:visible}
.moScene .moLab{font:600 12px var(--data);letter-spacing:.02em;fill:var(--text-tertiary)}
.moScene .moKey{font:700 13px var(--data);fill:var(--text-secondary)}
.moScene .moRule{stroke:var(--border);stroke-width:1;fill:none}
/* Below 380 the whole drawing scales down with its container, and a 12px
   label inside a 400-unit viewBox lands near 8px on the glass. The scene is
   allowed to run a little wider than the text column rather than shrink its
   own captions out of legibility. */
@media(max-width:380px){.moScene{width:calc(100% + 16px);margin-inline:-8px}}

/* ---- a note ---- */
.mo .nt{--note:var(--primary)}
.mo .n5{--note:#D9701C}.mo .n10{--note:#1478B4}.mo .n20{--note:#1C7A45}
.mo .n50{--note:#6B4A9C}.mo .n100{--note:#BE2B31}
/* the same twenty-dollar note, on its way out: greyed rather than recoloured,
   because giving it another denomination's colour would say it was a
   different note and break the seven-in-ten the brackets underneath claim */
.mo .nOut{--note:var(--text-tertiary)}
.mo .nBody{fill:color-mix(in srgb,var(--note) 17%,var(--surface));stroke:var(--note);stroke-width:1.4}
.mo .nOval{fill:color-mix(in srgb,var(--note) 28%,transparent)}
.mo .nNum{font:700 12px var(--data);fill:var(--note)}
.mo .nBar{stroke:color-mix(in srgb,var(--note) 42%,transparent);stroke-width:2;stroke-linecap:round;fill:none}

/* ---- a coin: gold in both themes, because money is ---- */
.mo .cn{--coin:#D8A33F}
.mo .cnDisc{fill:var(--coin);stroke:#9C6F1C;stroke-width:1.3}
.mo .cnRim{fill:none;stroke:color-mix(in srgb,#fff 55%,transparent);stroke-width:1.2}
.mo .cnSign{font:700 12px var(--data);fill:#7A560F}

/* ---- a card ---- */
.mo .cdBody{fill:color-mix(in srgb,var(--primary) 82%,#fff);stroke:var(--primary-active,var(--primary));stroke-width:1.3}
.mo .cdStripe{fill:color-mix(in srgb,var(--primary-active,var(--primary)) 70%,#000)}
.mo .cdChip{fill:#E7C567;stroke:#A9832C;stroke-width:1}
.mo .cdLine{fill:color-mix(in srgb,#fff 60%,transparent)}

/* ---- panels the scenes are built on ---- */
.mo .moPane{fill:var(--surface);stroke:var(--border);stroke-width:1.2}
.mo .moHead{fill:color-mix(in srgb,var(--primary) 11%,var(--surface))}
.mo .moFill{fill:color-mix(in srgb,var(--primary) 16%,transparent)}
.mo .moInk{fill:var(--primary)}
.mo .moSoft{fill:color-mix(in srgb,var(--primary) 34%,transparent)}
.mo .moGood{fill:var(--income,#1C7A45)}

/* ---- idle loops: kept to transform and opacity ---- */
.mo .mo-bob{transform-box:fill-box;transform-origin:50% 50%;
  animation:moBob 4.6s ease-in-out infinite;animation-delay:calc(var(--i,0) * .35s)}
@keyframes moBob{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}
/* A deduction is a note that leaves every pay, so it keeps leaving. It never
   goes fully transparent: a still frame of this scene, which is what a
   thumbnail and a paused screen are, must still show all ten notes rather
   than a bracket pointing at nothing. The negative delays are a third of the
   loop apart, so the three never lift together either. */
.dzWrap.in .mo-leave{transform-box:fill-box;transform-origin:50% 100%;
  animation:moLeave 5.2s var(--ease) infinite;animation-delay:calc(var(--i,0) * -1.73s)}
@keyframes moLeave{0%,58%{transform:translateY(0) rotate(0);opacity:1}
  78%{transform:translateY(-22px) rotate(7deg);opacity:.2}
  90%,100%{transform:translateY(0) rotate(0);opacity:1}}
/* a coin travelling from one place to another, over and over */
.dzWrap.in .mo-toss{transform-box:fill-box;transform-origin:50% 50%;
  animation:moToss 3.4s var(--ease) infinite;animation-delay:calc(var(--i,0) * .42s)}
@keyframes moToss{0%{transform:translate(0,0) scale(.6);opacity:0}
  14%{transform:translate(0,0) scale(1);opacity:1}
  50%{transform:translate(calc(var(--tx,60px) * .5),calc(var(--ty,0px) - 20px)) scale(1.06)}
  86%{transform:translate(var(--tx,60px),var(--ty,0px)) scale(1);opacity:1}
  100%{transform:translate(var(--tx,60px),var(--ty,0px)) scale(.6);opacity:0}}
/* a coin turning on the spot: scaleX reads as a flip in two dimensions */
.mo .mo-flip{transform-box:fill-box;transform-origin:50% 50%;
  animation:moFlip 5.5s var(--ease) infinite;animation-delay:calc(var(--i,0) * .6s)}
@keyframes moFlip{0%,62%,100%{transform:scaleX(1)}72%{transform:scaleX(.08)}82%{transform:scaleX(1)}}
.mo .mo-twinkle{transform-box:fill-box;transform-origin:50% 50%;
  animation:moTwinkle 3s ease-in-out infinite;animation-delay:calc(var(--i,0) * .5s)}
@keyframes moTwinkle{0%,100%{opacity:.15;transform:scale(.7)}50%{opacity:.9;transform:scale(1)}}
/* a stamp pressing down on a finished period */
.dzWrap.in .mo-stamp{transform-box:fill-box;transform-origin:50% 50%;
  animation:moStamp 4.4s var(--ease) infinite}
@keyframes moStamp{0%,30%{transform:scale(1.9) rotate(-14deg);opacity:0}
  44%{transform:scale(.96) rotate(-12deg);opacity:1}52%,88%{transform:scale(1) rotate(-12deg);opacity:1}
  100%{transform:scale(1) rotate(-12deg);opacity:0}}
/* a countdown line that refills on payday */
.dzWrap.in .mo-refill{transform-box:fill-box;transform-origin:left center;
  animation:moRefill 5.6s var(--ease) infinite}
@keyframes moRefill{0%{transform:scaleX(1)}55%{transform:scaleX(.12)}62%{transform:scaleX(.12)}
  78%,100%{transform:scaleX(1)}}

/* ---- pieces the individual scenes are made of ---- */
/* one dollar across the brackets: five slices of the same hue, darkening */
.mo .moSeg{transform-box:fill-box;transform-origin:left center}
.mo .moSeg.s1{fill:color-mix(in srgb,var(--primary) 20%,transparent)}
.mo .moSeg.s2{fill:color-mix(in srgb,var(--primary) 34%,transparent)}
.mo .moSeg.s3{fill:color-mix(in srgb,var(--primary) 50%,transparent)}
.mo .moSeg.s4{fill:color-mix(in srgb,var(--primary) 68%,transparent)}
.mo .moSeg.s5{fill:var(--primary)}
/* the one line on a payslip everything else is working towards */
.mo .moNet{fill:color-mix(in srgb,var(--income) 17%,transparent);stroke:color-mix(in srgb,var(--income) 45%,transparent);stroke-width:1}
.mo .moNetKey{font:700 12px var(--data);fill:var(--income)}
.mo .moTrack{fill:none;stroke:color-mix(in srgb,var(--primary) 16%,transparent);stroke-width:5}
.mo .moArc{fill:none;stroke:var(--primary);stroke-width:5;stroke-linecap:round}
.mo .moArrow{fill:none;stroke:var(--text-tertiary);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.mo .moSave{font:700 12px var(--data);fill:var(--income)}
/* a stamp pressed on a period that has stopped changing */
.mo .moStamp{fill:color-mix(in srgb,var(--income) 10%,transparent);stroke:var(--income);stroke-width:2}
.mo .moTick{fill:none;stroke:var(--income);stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.mo .moStampT{font:700 13px var(--data);fill:var(--income)}
/* payday: the coin arrives as the cycle refills, on the same 5.6s */
.dzWrap.in .mo-drop{transform-box:fill-box;transform-origin:50% 50%;animation:moDrop 5.6s var(--ease) infinite}
@keyframes moDrop{0%,46%{transform:translateY(-18px) scale(.6);opacity:0}
  58%,88%{transform:translateY(0) scale(1);opacity:1}
  100%{transform:translateY(-18px) scale(.6);opacity:0}}
/* dza-grow is scaleX, for a bar that runs along the page; a column stands up */
.moScene.dzWrap.in .dza-rise{transform-box:fill-box;transform-origin:50% 100%;
  animation:dzGrow .5s var(--spring,var(--ease)) calc(.25s + var(--i,0) * 80ms) backwards}
/* the dza-* kit sizes its steps for a 16:10 diagram; these scenes are small
   and wide, so their entrances are quicker and the stagger is tighter */
.moScene.dzWrap.in .dza-pop{animation-duration:.42s;animation-delay:calc(var(--i,0) * 65ms)}
.moScene.dzWrap.in .dza-in,.moScene.dzWrap.in .dza-up{animation-duration:.45s;animation-delay:calc(var(--i,0) * 65ms)}
.moScene.dzWrap.in .dza-grow{animation-duration:.6s;animation-delay:calc(.15s + var(--i,0) * 90ms)}

html[data-motion="reduced"] .mo [class*="mo-"]{animation:none;opacity:1;transform:none}
html[data-motion="reduced"] .mo .mo-twinkle{display:none}
@media(prefers-reduced-motion:reduce){
  .mo [class*="mo-"]{animation:none;opacity:1;transform:none}
  .mo .mo-twinkle{display:none}}

/* ============================================================
   THE WALK — spotlight steps of the tour, on the real dashboard
   ============================================================ */
#walk{position:fixed;inset:0;z-index:136;visibility:hidden;pointer-events:none;transition:visibility 0s .24s}
#walk.open{visibility:visible;transition:none}
.walkScrim{position:absolute;inset:0;pointer-events:auto;background:color-mix(in srgb,var(--text) 58%,transparent);opacity:0;transition:opacity .28s var(--ease)}
html[data-mode="dark"] .walkScrim{background:rgba(0,0,0,.7)}
#walk.open .walkScrim{opacity:1}
/* the halo sits on the hole: a soft ring that breathes, so the eye finds it */
/* The breathing used to be an animated box-shadow spread, which repaints a
   blurred area the size of the card on every frame of every step. The ring is
   static now and a second ring scales and fades over it, so the pulse is a
   compositor job and the tour stays smooth on a phone. */
.walkHalo{position:absolute;pointer-events:none;
  box-shadow:0 0 0 3px var(--primary),0 0 0 9px color-mix(in srgb,var(--primary) 20%,transparent),0 12px 40px color-mix(in srgb,var(--primary) 28%,transparent);
  transition:left .34s var(--ease),top .34s var(--ease),width .34s var(--ease),height .34s var(--ease)}
.walkHalo::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  box-shadow:0 0 0 12px color-mix(in srgb,var(--primary) 26%,transparent);
  animation:walkBreathe 1.9s var(--ease) infinite}
/* opacity only: a scale would move a wide halo's sides far further than its
   top and bottom, and the ring would breathe lopsidedly */
@keyframes walkBreathe{0%,100%{opacity:.15}50%{opacity:1}}
.walkCard{position:absolute;pointer-events:auto;width:min(440px,calc(100vw - 24px));background:var(--surface);color:var(--text);
  border-radius:var(--r-xl);padding:18px 20px 16px;box-shadow:0 24px 70px color-mix(in srgb,var(--text) 30%,transparent);
  transform:translateY(10px);opacity:0;
  transition:transform .28s var(--spring,var(--ease)),opacity .28s var(--ease),
             top .34s var(--ease),left .34s var(--ease),bottom .34s var(--ease)}
/* the card is placed for the first time, or the window resized: no glide */
.walkCard.noGlide{transition:transform .28s var(--spring,var(--ease)),opacity .28s var(--ease)}
/* the words cross-fade while the halo travels */
.walkCard #walkStep,.walkCard #walkTitle,.walkCard #walkText,.walkCard #walkPrompt,.walkCard .walkFoot{
  transition:opacity .15s var(--ease)}
.walkCard.swapping #walkStep,.walkCard.swapping #walkTitle,.walkCard.swapping #walkText,
.walkCard.swapping #walkPrompt,.walkCard.swapping .walkFoot{opacity:0}
#walk.open .walkCard{transform:none;opacity:1}
.walkCard h2{margin:0 0 8px;font-size:21px;line-height:1.25;letter-spacing:-.015em}
.walkCard #walkText{margin:0;font-size:17px;line-height:1.5;color:var(--text-secondary)}
.walkPrompt{margin:12px 0 0;padding:10px 14px;border-radius:12px;font-weight:600;font-size:15.5px;
  background:color-mix(in srgb,var(--primary) 10%,var(--surface));color:var(--primary-ink)}
.walkPrompt::before{content:"👉 "}
.walkFoot{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:14px}
.walkFoot .outl,.walkFoot .solid{min-height:44px;padding:0 14px;font-size:15.5px;white-space:nowrap}
.walkFoot .outl[disabled]{visibility:hidden}
/* Skip sits apart on the left; the three that move you through the walk are
   one group on the right, and when the card is too narrow the whole group
   drops to its own line rather than Next alone landing under Skip */
.walkSkip{position:static;margin:0}
.walkActs{display:flex;align-items:center;gap:8px;margin-left:auto;min-width:0}
/* The shot is a peek at the top of the real screen, so it is cropped. A hard
   cut through a line of type reads as text that failed to load; a fade reads
   as a screen that carries on below, which is what it is. */
.tourScene.isShot{background:var(--surface-muted);position:relative}
.tourScene.isShot::after{content:"";position:absolute;left:0;right:0;bottom:0;height:38px;pointer-events:none;
  background:linear-gradient(to bottom,transparent,color-mix(in srgb,var(--surface) 92%,transparent))}
.tourScene .tourShot{display:block;width:100%;height:100%;object-fit:cover;object-position:top}
body.touring .pBottom{z-index:60}
html[data-motion="reduced"] .walkHalo,html[data-motion="reduced"] .walkCard,html[data-motion="reduced"] .walkScrim{animation:none;transition:none}

/* ---- phone review fixes ---- */
@media(max-width:360px){.tlRow{grid-template-columns:64px minmax(0,1fr) auto;gap:8px}.tlRow .tlDate{font-size:11px;letter-spacing:.02em}}
.tlRow .tlWhat span{overflow-wrap:anywhere}
/* 44px is the floor for anything a finger is asked to hit; where the glyph
   must stay small the hit area grows around it without moving the layout */
.seg button{min-width:44px;min-height:44px}
/* position:relative here would destroy .tourSkip's absolute placement, which
   is how it once ended up outside the card's left edge */
.pMobileMark,#pAvBtn,.infoDot,.donutKey button{position:relative}
.pMobileMark::after,#pAvBtn::after,.infoDot::after{content:"";position:absolute;left:50%;top:50%;width:44px;height:44px;transform:translate(-50%,-50%)}
.donutKey button{min-height:44px}
#tourSkip,#walkSkip,.tourSkip{min-height:44px}
/* A finger needs 44px; an underline needs the box it was measured against.
   Padding gave the first and broke the second: .lnk draws its rule with an
   absolutely positioned ::after at bottom:6px of the PADDING box, so ten
   pixels of padding put every underline ten pixels lower, on top of the next
   line of text. The hit area is a ::before overlay instead, which changes no
   geometry.

   But an overlay has the same appetite as padding did. A 44px band centred on
   an INLINE link reaches about 11px past its own 22px line box, which in
   running text is the line ABOVE: a tap on plain body text opened the privacy
   policy. A link inside a sentence is exempt from the target-size floor for
   exactly this reason, because growing it can only come out of its
   neighbours. The band stays where a link stands alone on its own line. */
.authLink,.checkNote .lnk.sm,p .lnk.sm{position:relative}
.authLink::before,.checkNote .lnk.sm::before,p .lnk.sm::before{
  content:"";position:absolute;left:0;right:0;top:50%;height:44px;transform:translateY(-50%);pointer-events:auto}
.checkNote .authLink::before,.legal p .authLink::before,p .authLink::before{content:none}
/* the fan's outer cards drop lower than the centre one; the section must hold them */
@media(max-width:400px){.fan{padding-bottom:calc(clamp(200px,32vh,280px) + 48px)}}
/* scene type at phone scale: a 640-wide drawing shown at 350px halves every
   label, so the labels are set for that; the How hero keeps its own sizes */
.dzWrap:not(.dzHow) .dz-lab{font-size:12px}.dzWrap:not(.dzHow) .dz-when{font-size:15px}
.dzMini .dz-lab{font-size:9px}
.tsLab{font-size:9.5px}.tsTxt{font-size:12.5px}.tsChipT{font-size:9.5px}
/* the portal's empty-state scene fits its card, not a 16:10 frame */
.dzMini{aspect-ratio:auto;height:auto;max-width:300px}
.dzMini .dz{height:auto;aspect-ratio:320/120;max-height:120px}

/* seventeen dots do not fit a phone; the "3 of 17" line already says where you are */
@media(max-width:600px){
  /* visibility, not display: display:none removes the grid item, and Next
     then stretches across the freed column and stops short of the card edge */
  .tourDots{visibility:hidden;height:0;margin:0}
}
/* A card that carries a scene is as tall as the scene and its note. Cards in
   a grid slot are `flex:1 1 0%`, which gives them a zero basis and makes them
   share the row's height: with a drawing inside, the note fell out of the
   bottom. These selectors match the depth of the rules they override. */
/* basis auto so the card is as tall as its content, but still able to
   shrink: in a row slot, 0 0 auto let it keep its max-content width and
   pushed the page sideways */
.pGrid [class*="span"] > .pCard:has(.dzMini){flex:0 1 auto;min-width:0}
.pGrid [data-mod] .pCard:has(.dzMini){height:auto;min-height:0;max-height:none;flex-direction:column}
.pCard:has(.dzMini)>.dzMini,.pCard:has(.dzMini)>.checkNote{flex:none}

/* ---- components review ---- */
/* The address is the thing these screens exist to let someone check. At 320 a
   long one measured 337.6px wide in a 320px viewport and overflow-x:clip ate
   the end of it in silence. */
#apVerify strong,#apExists strong,#apResetSent strong,.apAddr{overflow-wrap:anywhere;word-break:break-word;max-width:100%}

/* ---- portal review ---- */
/* .rpAmt sets line-height:1.05, so its line box is shorter than its glyphs and
   the figure's ink reached up into the label. .rpMain had no rule at all. */
.rpMain{display:flex;flex-direction:column;gap:8px}
/* Above 1024 a flex line can pair one short card against a stack of four, and
   stretch then draws a 540px card around two lines of text. */
@media(min-width:1024px){.pGrid{align-items:start}}
/* A stack inside one cell uses the same rhythm as the gap between cells. */
.pGrid>[class*="span"]:has(>*+*){gap:var(--s6)}
/* A status slot that is empty still carried its margin, which is why two cards
   sat 18-20px deeper than every other card. */
#pBody .pCard>div:empty{display:none}
/* Five 44px key rows beside a fixed 120px donut read as a double-spaced list
   rather than a chart with a legend. */
.donut{width:clamp(120px,40%,190px);height:auto;aspect-ratio:1;flex:none}
/* The middle swatch dot is the theme's own background: 1.06:1 against the card
   foot in both modes. Ring all three the same way. */
.tCard .tpDots i{width:10px;height:10px;border-radius:50%;display:block;box-sizing:border-box;
  box-shadow:0 0 0 1px color-mix(in srgb,var(--text) 30%,transparent)}
/* U+25E7 is one colour centred on a hard light/dark boundary, so half of it
   always disappears into the half it sits on. Draw the two faces instead. */
.mCard.system .mPrev{color:transparent;position:relative}
.mCard.system .mPrev::before{content:"\2600";position:absolute;left:25%;top:50%;transform:translate(-50%,-50%);color:#19171D}
.mCard.system .mPrev::after{content:"\263D";position:absolute;left:75%;top:50%;transform:translate(-50%,-50%);color:#F7F4FA}
/* ---- components review ---- */
/* two 44px hit bands on stacked links overlapped each other by 42x9.6px */
#apExists .authLink,#apVerify .authLink,#apResetSent .authLink{display:inline-block}
#apExists .authLink::before,#apVerify .authLink::before,#apResetSent .authLink::before{height:40px}
/* a date field was 2px taller than every other field in the same form */
.field input[type="date"],.field input[type="datetime-local"],.field input[type="month"],.field input[type="time"]{padding-block:0}
/* the reject chip wrapped on every phone under 390, leaving a 67px and a 46px
   button side by side in one action group */
@media(max-width:389px){.cookieActs{display:grid;grid-template-columns:1fr;gap:8px}
  .cookieActs button{width:100%;justify-content:center}}

/* ---- public review ---- */
/* The payslip scene's label moves above its line on phones, but the grid gap
   stayed 14px: a 16.8px label in a 14px gap landed 4px onto the bar above and
   6px onto that bar's ring, striking every label through. */
@media(max-width:640px){
  .sda-doc{gap:32px}
  .sda-line[data-k]::after{left:auto;right:8px;top:-26px;transform:none;font-size:10.5px}
}
/* At 320 the About allocation row needed 269px of the 230px it had, so
   overflow:hidden cut 14px off every figure and "32% · $640" read "32% · $6". */
@media(max-width:380px){
  /* the columns are set inline on the row, so only !important reaches them */
  #abAllocOut .tplRow{grid-template-columns:minmax(0,1fr) 110px!important;row-gap:6px}
  #abAllocOut .tplRow>*:last-child{grid-column:1/-1;text-align:left}
}
/* The rule is the link's underline, so it ends where the words end, not under
   the arrow. 26px = the 15px arrow + the 9px flex gap + 2px padding-right. */
.lnk:has(.arw)::after{right:26px}
/* A scene's wrapper reserved a 16:10 box whatever the drawing's own ratio, so
   a 640x150 strip left 137px of dead space under it at 390. The height belongs
   to the drawing. The .dzHow wrappers really are 16:10 and keep their box. */
.dzWrap:not(.dzHow):not(.dzMini){aspect-ratio:auto}
