/* inter-cyrillic-ext-wght-normal */
@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2) format('woff2-variations');
  unicode-range: U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F;
}

/* inter-cyrillic-wght-normal */
@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2) format('woff2-variations');
  unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116;
}

/* inter-greek-ext-wght-normal */
@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2) format('woff2-variations');
  unicode-range: U+1F00-1FFF;
}

/* inter-greek-wght-normal */
@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(/assets/inter-greek-wght-normal-CkhJZR-_.woff2) format('woff2-variations');
  unicode-range: U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF;
}

/* inter-vietnamese-wght-normal */
@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2) format('woff2-variations');
  unicode-range: U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB;
}

/* inter-latin-ext-wght-normal */
@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2) format('woff2-variations');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* inter-latin-wght-normal */
@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(/assets/inter-latin-wght-normal-Dx4kXJAl.woff2) format('woff2-variations');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}/* dancing-script-vietnamese-wght-normal */
@font-face {
  font-family: 'Dancing Script Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 400 700;
  src: url(/assets/dancing-script-vietnamese-wght-normal-BsUJvdxu.woff2) format('woff2-variations');
  unicode-range: U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB;
}

/* dancing-script-latin-ext-wght-normal */
@font-face {
  font-family: 'Dancing Script Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 400 700;
  src: url(/assets/dancing-script-latin-ext-wght-normal-DJGK6qJa.woff2) format('woff2-variations');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* dancing-script-latin-wght-normal */
@font-face {
  font-family: 'Dancing Script Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 400 700;
  src: url(/assets/dancing-script-latin-wght-normal-CyISZbeS.woff2) format('woff2-variations');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}/*
  LDV Invoicing design tokens — "rose" theme.
  Deep rose as the primary, warm blush neutrals, antique gold as the secondary accent.
  Every colour is a variable — nothing is hardcoded per mode, so light/dark both work.
*/

:root {
  --bg: #fdf6f7;
  --bg-elevated: #ffffff;
  --surface: #ffffff;
  --surface2: #f9eff1;
  --surface3: #f2e2e6;
  --border: #ecdde1;
  --border-strong: #dcc4cb;

  --txt1: #2c1c22;
  --txt2: #6a5158;
  --txt3: #93777f;

  --brand: #ac3b5e;
  --brand-ink: #86294a;
  --brand-soft: #fbe7ec;
  --brand-contrast: #ffffff;

  --accent: #b0793f;
  --accent-soft: #f9ecdc;
  --gold: #a9803d;
  --gold-soft: #f8eeda;

  /* Money semantics: emerald = settled, red = still owed. Deliberately distinct from the
     rose brand colour so an amount is never mistaken for a heading. */
  --ok: #0f8a63;
  --ok-soft: #ddf1e8;
  --warn: #a3702a;
  --warn-soft: #f9edd8;
  --danger: #b3241f;
  --danger-soft: #f9dedd;

  --shadow-sm: 0 1px 2px rgba(44, 28, 34, 0.06), 0 1px 3px rgba(44, 28, 34, 0.04);
  --shadow-md: 0 4px 16px rgba(44, 28, 34, 0.1);
  --shadow-lg: 0 12px 32px rgba(44, 28, 34, 0.16);

  /* Frosted panels. The page paints two soft colour washes behind everything, and panels
     blur what is under them — that is what makes the glass read as glass. */
  --glass-bg: rgba(255, 255, 255, 0.74);
  --glass-border: rgba(255, 255, 255, 0.9);
  --glass-sheen: linear-gradient(180deg, rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0) 46%);
  --glass-ring: inset 0 1px 0 rgba(255, 255, 255, 0.85);
  --glass-blur: saturate(150%) blur(16px);
  /* Shared by the drawer scrim and the modal scrim so the no-blur fallback can darken both. */
  --scrim-bg: rgba(32, 18, 24, 0.42);
  --wash-1: rgba(172, 59, 94, 0.13);
  --wash-2: rgba(169, 128, 61, 0.12);

  /* The sign-in screen gets a warmer, brighter treatment than the app itself. */
  --login-base: #fbeff1;
  --login-glow-1: rgba(172, 59, 94, 0.2);
  --login-glow-2: rgba(201, 143, 60, 0.22);
  --login-glow-3: rgba(232, 139, 162, 0.24);

  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-xl: 22px;

  --header-h: 56px;
  /* iPhone notch / Dynamic Island and the home-bar area. The left/right insets are non-zero
     in landscape and around curved edges, so every edge-anchored element uses them. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);

  /* Inter everywhere — self-hosted via @fontsource, so it works offline and renders the
     same on every phone. No serif: elegance comes from weight, spacing and the palette. */
  --font: 'Inter Variable', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-display: var(--font);

  /* Script face for the brand wordmark only — never for UI text or numbers. */
  --font-script: 'Dancing Script Variable', 'Segoe Script', cursive;
}

/* Dark mode is a deep plum rather than neutral black — keeps the rose feeling warm. */
:root[data-theme='dark'] {
  --bg: #160f13;
  --bg-elevated: #1f1519;
  --surface: #1f1519;
  --surface2: #281a20;
  --surface3: #33222a;
  --border: #3a2731;
  --border-strong: #513743;

  --txt1: #f5eaee;
  --txt2: #c3a8b1;
  --txt3: #9d818a;

  --brand: #e88ba2;
  --brand-ink: #f3b4c3;
  --brand-soft: #351d27;
  --brand-contrast: #2b0d17;

  --accent: #dda96a;
  --accent-soft: #33251a;
  --gold: #d8ab6b;
  --gold-soft: #322818;

  --ok: #3ddba6;
  --ok-soft: #10322a;
  --warn: #d9ab6a;
  --warn-soft: #322818;
  /* A true red, not salmon — dark enough to read as "owed" against the plum surface,
     light enough to stay legible on it. */
  --danger: #e0322b;
  --danger-soft: #3a1917;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.45);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.6);

  --glass-bg: rgba(38, 25, 31, 0.62);
  --glass-border: rgba(255, 255, 255, 0.09);
  --glass-sheen: linear-gradient(180deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0) 46%);
  --glass-ring: inset 0 1px 0 rgba(255, 255, 255, 0.08);
  --glass-blur: saturate(140%) blur(18px);
  --scrim-bg: rgba(10, 5, 8, 0.52);
  --wash-1: rgba(232, 139, 162, 0.16);
  --wash-2: rgba(216, 171, 107, 0.12);

  /* Lifted well above the app background so signing in never feels like a black screen. */
  --login-base: #2a1c24;
  --login-glow-1: rgba(232, 139, 162, 0.34);
  --login-glow-2: rgba(216, 171, 107, 0.22);
  --login-glow-3: rgba(172, 59, 94, 0.38);
}

/*
  The frosted panels are only readable because the blur destroys the detail behind them. Where the
  engine can't blur — old browsers, GPU compositing disabled, a build that strips the property —
  translucency turns into three layers of overlapping text, so go nearly opaque instead.
*/
@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  :root {
    --glass-bg: rgba(255, 252, 253, 0.975);
    --scrim-bg: rgba(32, 18, 24, 0.62);
  }

  :root[data-theme='dark'] {
    --glass-bg: rgba(30, 20, 25, 0.975);
    --scrim-bg: rgba(10, 5, 8, 0.72);
  }
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

html,
body {
  margin: 0;
  padding: 0;
  min-height: 100%;
}

body {
  background: var(--bg);
  color: var(--txt1);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

/* Two fixed colour washes give the frosted panels something to blur. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(60% 45% at 12% 0%, var(--wash-1), transparent 70%),
    radial-gradient(55% 40% at 92% 100%, var(--wash-2), transparent 70%);
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
  border: none;
  background: none;
}

a {
  color: var(--brand);
  text-decoration: none;
}

/* ---------- App shell ---------- */

.app {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.appbar {
  position: sticky;
  top: 0;
  z-index: 30;
  padding-top: var(--safe-top);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-bottom: 1px solid var(--glass-border);
  /* Same treatment as the landing bar: a soft drop below the hairline so the header reads as
     floating over the page it scrolls under. Tinted with the brand rather than plain black, which
     greys the blush. */
  box-shadow: 0 6px 18px -8px rgba(172, 59, 94, 0.35);
}

:root[data-theme='dark'] .appbar {
  /* Rose does not register on a dark background; depth has to come from shadow instead. */
  box-shadow: 0 8px 20px -10px rgba(0, 0, 0, 0.65);
}

.appbar-row {
  height: var(--header-h);
  display: flex;
  align-items: center;
  gap: 4px;
  padding-left: calc(6px + var(--safe-left));
  padding-right: calc(6px + var(--safe-right));
}

/* Every screen title is set in the script face; subtitles stay in Inter for legibility. */
.appbar-title {
  flex: 1;
  text-align: center;
  font-family: var(--font-script);
  font-size: 25px;
  font-weight: 700;
  letter-spacing: 0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The brand name, wherever it appears. Script needs more size and weight than Inter to
   hold its own at these sizes. */
.wordmark {
  font-family: var(--font-script);
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.1;
}

/* Lockup: solid initials, script word. Inter's cap height is shorter than the script's
   ascender, so "LDV" is sized up to sit level with "Invoicing". */
.wordmark-solid {
  font-family: var(--font);
  font-weight: 800;
  font-size: 0.94em;
  letter-spacing: 0.01em;
  margin-right: 0.3em;
  vertical-align: 0.02em;
  color: var(--brand);
}

.appbar-title .wordmark {
  font-size: 25px;
  display: inline-block;
  vertical-align: -2px;
}

/* Opt-out for titles that are codes or numbers — a script face makes INV-000002 hard to read. */
.appbar-title .title-plain {
  font-family: var(--font);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.appbar-title small {
  display: block;
  font-family: var(--font);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0;
  margin-top: -3px;
  color: var(--txt2);
}

.iconbtn {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--txt1);
  flex: 0 0 auto;
}

.iconbtn:active {
  background: var(--surface2);
}

.iconbtn[disabled] {
  color: var(--txt3);
}

.page {
  flex: 1;
  padding-top: 12px;
  padding-bottom: calc(96px + var(--safe-bottom));
  padding-left: calc(14px + var(--safe-left));
  padding-right: calc(14px + var(--safe-right));
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
}

/* ---------- Drawer ---------- */

.scrim {
  /* Only shown while the mobile drawer is open; never on the desktop pinned sidebar. */
  display: none;
  position: fixed;
  inset: 0;
  z-index: 40;
  background: var(--scrim-bg);
  /* Frosted, not just dimmed — the content behind stays present but clearly out of focus. */
  backdrop-filter: blur(10px) saturate(115%);
  -webkit-backdrop-filter: blur(10px) saturate(115%);
  animation: fadeIn 0.2s ease;
}

.drawer {
  position: fixed;
  z-index: 41;
  top: 0;
  bottom: 0;
  left: 0;
  width: min(84vw, 320px);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-right: 1px solid var(--glass-border);
  box-shadow: var(--shadow-lg);
  padding: calc(18px + var(--safe-top)) 0 calc(18px + var(--safe-bottom)) var(--safe-left);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  /* Always mounted now — hidden off-canvas until .app.drawer-open slides it in (mobile). */
  transform: translateX(-100%);
  transition: transform 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}

.app.drawer-open .scrim {
  display: block;
}

.app.drawer-open .drawer {
  transform: translateX(0);
}

.drawer-head {
  padding: 0 18px 16px;
  border-bottom: 1px solid var(--border);
}

.drawer-brand {
  display: flex;
  align-items: center;
  gap: 10px;
}

.drawer-logo {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
  box-shadow: var(--shadow-sm);
}

.drawer-name {
  font-size: 15.5px;
  font-weight: 650;
  line-height: 1.25;
}

.drawer-name.wordmark {
  font-size: 20px;
  color: var(--brand-ink);
}

.drawer-sub {
  font-size: 12px;
  color: var(--txt2);
}

.drawer-nav {
  padding: 10px 8px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.drawer-item {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 14px;
  border-radius: var(--r-md);
  color: var(--txt1);
  font-weight: 550;
  text-align: left;
  width: 100%;
}

/* A badge sits with its label, not at the far edge: pushed right it read as a separate column and
   lost its connection to the word it qualifies. The negative margin trims the row's 14px gap to
   something that reads as attached. */
.drawer-item .badge {
  margin-left: -7px;
}

.drawer-item .ico {
  width: 22px;
  display: grid;
  place-items: center;
  color: var(--txt2);
}

/* A small in/out tag at the far end of the row: money coming in (Invoices) vs going out
   (Expenses). A direction, not a +/- value — an unpaid invoice is still the money-in side.
   Right-aligned so the two adjacent items line their tags up. */
.drawer-item .nav-flag {
  margin-left: auto;
  flex: 0 0 auto;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  line-height: 1.6;
}

.nav-flag.is-in {
  color: var(--ok);
  background: var(--ok-soft);
}

.nav-flag.is-out {
  color: var(--danger);
  background: var(--danger-soft);
}

.drawer-item[aria-current='true'] {
  background: var(--brand-soft);
  color: var(--brand-ink);
}

.drawer-item[aria-current='true'] .ico {
  color: var(--brand);
}

.drawer-foot {
  margin-top: auto;
  padding: 12px 18px 0;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--txt2);
}

/* ---------- Cards & lists ---------- */

.card {
  position: relative;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm), var(--glass-ring);
  overflow: hidden;
}

/* Light catch along the top edge — the detail that sells the glass. */
.card::before,
.list::before,
.stat::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--glass-sheen);
  opacity: 0.55;
}

.card + .card {
  margin-top: 12px;
}

.card-pad {
  position: relative;
  z-index: 1;
  padding: 14px;
}

.section-title {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--txt2);
  margin: 18px 2px 8px;
}

.list {
  position: relative;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm), var(--glass-ring);
  overflow: hidden;
}

/* Rows sit above the sheen layer. */
.list > * {
  position: relative;
  z-index: 1;
}

.row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 14px;
  width: 100%;
  text-align: left;
  background: none;
  border-bottom: 1px solid var(--border);
}

.row:last-child {
  border-bottom: none;
}

.row:active {
  background: var(--surface2);
}

.row-main {
  flex: 1;
  min-width: 0;
}

/* Tappable part of a row that also carries a trailing action button. */
.row-tap {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0;
  background: none;
  text-align: left;
}

.row-title {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.row-sub {
  font-size: 12.5px;
  color: var(--txt2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.row-end {
  text-align: right;
  flex: 0 0 auto;
}

/* Date range for the list: field first, then from/to. Wraps rather than shrinking the inputs —
   a squeezed native date input becomes unreadable long before it becomes unusable. */
.date-filter {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 10px;
}

.date-filter-field {
  flex: 0 0 auto;
  width: auto;
  padding: 8px 10px;
  font-size: 12.5px;
  font-weight: 600;
}

.date-filter-input {
  flex: 1 1 128px;
  min-width: 128px;
  padding: 8px 10px;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}

.date-filter-sep {
  flex: 0 0 auto;
  color: var(--txt3);
  font-size: 12px;
}

.date-filter-clear {
  flex: 0 0 auto;
}

/* The pair of figures at the top of the invoice list. One card, split — they are two halves of the
   same money, and two separate cards made them read as unrelated. */
.kpi-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  margin-bottom: 10px;
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.kpi {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 14px;
  min-width: 0;
}

/* A hairline between the halves rather than a gap: the divider says "same card, two sides". */
.kpi + .kpi {
  border-left: 1px solid var(--border);
}

.kpi-label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--txt3);
}

.kpi-value {
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}

.kpi-value.is-due {
  color: var(--danger);
}

.kpi-value.is-in {
  color: var(--ok);
}

.kpi-sub {
  font-size: 11.5px;
  color: var(--txt3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Spans both halves, flush to the bottom edge of the card. */
.kpi-meter {
  grid-column: 1 / -1;
  height: 4px;
  background: var(--danger-soft);
}

.kpi-meter-fill {
  display: block;
  height: 100%;
  background: var(--ok);
  transition: width 0.35s ease;
}

@media (prefers-reduced-motion: reduce) {
  .kpi-meter-fill {
    transition: none;
  }
}

/* One-line total for a filtered list: a quiet strip, not a card — the cards belong to All, where
   there are two numbers worth comparing. */
.list-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
  padding: 8px 14px;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--surface2);
  font-size: 12.5px;
  color: var(--txt2);
}

.list-total strong {
  font-size: 15px;
  font-weight: 800;
  color: var(--txt1);
  font-variant-numeric: tabular-nums;
}

.list-total strong.is-paid {
  color: var(--ok);
}

/* Small caps for the due label, so "DUE" reads as a tag rather than a shout. */
.row-due {
  letter-spacing: 0.02em;
}

/* Context for a part-paid invoice: the balance leads, the original total follows quietly. */
.row-of {
  font-size: 11px;
  color: var(--txt3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Deliberately not .row-sub: that truncates with an ellipsis, which is right for a reference
   number and wrong for a date the seller is chasing. */
.row-due {
  margin-top: 1px;
  font-size: 12px;
  font-weight: 600;
  color: var(--txt2);
  white-space: nowrap;
}

.row-due.is-late {
  color: var(--danger);
}

.row-amount {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-weight: 700;
  color: var(--brand-contrast);
  background: var(--brand);
  flex: 0 0 auto;
}

.avatar.rose {
  background: var(--brand-soft);
  color: var(--brand-ink);
  border: 1px solid var(--border-strong);
}

.avatar.gold {
  background: var(--gold-soft);
  color: var(--gold);
  border: 1px solid var(--border-strong);
}

.deleted .row-title {
  text-decoration: line-through;
  color: var(--txt2);
}

/* ---------- Badges & chips ---------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 3px 8px;
  border-radius: 999px;
  white-space: nowrap;
}

.badge-paid {
  background: var(--ok-soft);
  color: var(--ok);
}

/* Money is owed, but the due date has not passed — red, held quietly. */
.badge-unpaid {
  background: var(--danger-soft);
  color: var(--danger);
}

/* Something has been paid: amber says "in progress", not "in trouble". Distinguishing it from
   unpaid at a glance is the whole point of the colour. */
.badge-partial {
  background: var(--warn-soft);
  color: var(--warn);
}

/* The one status that needs chasing today, so it is the only solid badge in the list: filled red
   with a soft red glow, which carries across a screen of tinted pills where another pale badge
   would not. */
.badge-overdue {
  background: var(--danger);
  color: #fff;
  box-shadow: 0 2px 8px color-mix(in srgb, var(--danger) 45%, transparent);
}

.badge-draft {
  background: var(--surface3);
  color: var(--txt2);
}

.badge-brand {
  background: var(--brand-soft);
  color: var(--brand-ink);
}

.badge-deleted {
  background: var(--danger-soft);
  color: var(--danger);
}

/* Arrows sit beside the row, not over it. An overlay needed a gradient to fade the chips out
   underneath, and that gradient read as a dirty smudge across whichever chip it covered. */
/* ---------- Landing page ---------- */

/* Sits on the warmer sign-in background rather than the app's, so a stranger's first screen
   feels like a product page instead of a tool that is already open. */
.landing {
  min-height: 100dvh;
  background:
    radial-gradient(90% 55% at 50% -10%, var(--login-glow-1), transparent 70%),
    radial-gradient(70% 45% at 10% 105%, var(--login-glow-3), transparent 70%),
    radial-gradient(60% 40% at 95% 40%, var(--login-glow-2), transparent 70%),
    var(--login-base);
  padding: calc(28px + var(--safe-top)) calc(16px + var(--safe-right)) calc(28px + var(--safe-bottom))
    calc(16px + var(--safe-left));
}

.landing-hero,
.landing-section,
.landing-footer {
  max-width: 760px;
  margin: 0 auto;
}

.landing-footer {
  margin-top: 36px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 4px 10px;
  color: var(--txt3);
  font-size: 12.5px;
  text-align: center;
}
.landing-footer-sep {
  color: var(--txt3);
}
.landing-footer-link {
  padding: 0;
  border: none;
  background: none;
  color: var(--txt2);
  font-size: 12.5px;
  font-weight: 600;
  border-bottom: 1px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.landing-footer-link:hover {
  color: var(--brand);
  border-bottom-color: var(--brand);
}

/* Sticky glass bar. Negative margins pull it out of the page padding so the blur spans the full
   width, while the inner max-width keeps it aligned with the content. */
.landing-nav {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 10px;
  margin: calc(-28px - var(--safe-top)) calc(-16px - var(--safe-right)) 18px calc(-16px - var(--safe-left));
  padding: calc(8px + var(--safe-top)) calc(14px + var(--safe-right)) 8px calc(14px + var(--safe-left));
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-bottom: 1px solid var(--glass-border);
  /* A soft drop below the hairline so the bar reads as floating over the page rather than
     painted onto it. Tinted with the brand rather than plain black, which greys the blush. */
  box-shadow: 0 6px 18px -8px rgba(172, 59, 94, 0.35);
}

:root[data-theme='dark'] .landing-nav {
  /* Rose does not register on a dark background; depth has to come from shadow instead. */
  box-shadow: 0 8px 20px -10px rgba(0, 0, 0, 0.65);
}

.landing-nav-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  padding: 0;
  border: none;
  background: none;
  font-size: 17px;
}

.landing-nav-brand img {
  border-radius: 50%;
}

/* On mobile the five links don't fit, so they move into a burger dropdown (.landing-nav-menu)
   and this inline strip is hidden. It reappears as a single centred row from 640px up. */
.landing-nav-links {
  display: none;
  gap: 2px;
  flex: 1 1 auto;
  min-width: 0;
  justify-content: flex-end;
  margin-left: auto;
}

.landing-nav-brand {
  margin-right: auto;
}

/* Theme toggle and burger are the two round icon-buttons on the right of the bar — same size and
   shape so they line up cleanly next to each other. The burger is hidden once the inline links
   fit (640px). */
.landing-nav-theme,
.landing-nav-burger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  border: none;
  background: none;
  border-radius: 10px;
  color: var(--txt1);
  touch-action: manipulation;
}

.landing-nav-theme:active,
.landing-nav-burger:active {
  background: var(--surface2);
}

/* Dropdown of the section links, anchored under the bar on the right. */
.landing-nav-menu {
  position: absolute;
  top: 100%;
  right: calc(14px + var(--safe-right));
  margin-top: 6px;
  min-width: 210px;
  display: flex;
  flex-direction: column;
  padding: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  z-index: 31;
  animation: fabPop 0.18s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.landing-nav-menu .landing-nav-link {
  width: 100%;
  justify-content: flex-start;
  text-align: left;
  padding: 11px 12px;
  font-size: 14px;
}

.landing-nav-menu .btn {
  width: 100%;
}

.landing-nav-menu-divider {
  height: 1px;
  background: var(--border);
  margin: 6px 4px 8px;
}

/* Catches a tap outside the open dropdown to close it. Sits under the sticky bar and its menu. */
.landing-nav-scrim {
  position: fixed;
  inset: 0;
  z-index: 29;
  border: none;
  background: transparent;
}

@media (min-width: 640px) {
  .landing-nav-links {
    display: flex;
    flex-wrap: wrap;
  }
  .landing-nav-burger {
    display: none;
  }
}

.landing-nav-link {
  flex: 0 0 auto;
  padding: 7px 10px;
  border: none;
  border-radius: 999px;
  background: none;
  color: var(--txt2);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  /* No double-tap zoom delay, and the tap is never interpreted as the start of a pan — which is
     what let a press slide onto the neighbouring link. */
  touch-action: manipulation;
  min-height: 34px;
}

.landing-nav-link.active {
  background: var(--brand-soft);
  color: var(--brand);
}

/* The inline CTA rides with the inline links: hidden on mobile (it lives in the burger menu
   instead), shown from 640px up. Scoped to .landing-nav so it beats .btn's display:inline-flex. */
.landing-nav .landing-nav-cta {
  display: none;
  flex: 0 0 auto;
}

@media (min-width: 640px) {
  .landing-nav .landing-nav-cta {
    display: inline-flex;
  }
}

/* Desktop: brand hugs the left, theme + CTA hug the right, and the links sit dead-centre in the
   bar (absolute so their position ignores the differing widths of brand vs actions). */
@media (min-width: 900px) {
  .landing-nav-links {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    justify-content: center;
    flex: 0 0 auto;
    flex-wrap: nowrap;
    margin: 0;
  }
}

/* Clears the sticky bar when a link jumps to a section. */
.landing-section,
.landing-hero {
  scroll-margin-top: 64px;
}

.landing-hero {
  text-align: center;
  padding: 8px 0 30px;
}

.landing-logo {
  width: 92px;
  height: 92px;
  border-radius: 50%;
  object-fit: cover;
  box-shadow: var(--shadow-md);
}

.landing-wordmark {
  font-size: 34px;
  margin: 14px 0 0;
  line-height: 1.1;
}

.landing-lead {
  margin: 12px auto 0;
  max-width: 30ch;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--txt2);
}

.landing-cta {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 22px auto 0;
  max-width: 340px;
}

.btn-lg {
  padding: 14px 18px;
  font-size: 15px;
  border-radius: var(--r-lg);
}

.landing-note {
  margin: 14px 0 0;
  font-size: 12px;
  color: var(--txt3);
}

/* The final section must be tall enough to scroll its own top under the nav, otherwise the page
   runs out of scroll first and the section before it can never become current. */
.landing-section:last-of-type {
  min-height: calc(100vh - 180px);
}

.landing-section {
  padding: 26px 0 6px;
}

.landing-h2 {
  margin: 0 0 6px;
  font-size: 20px;
  line-height: 1.25;
  text-align: center;
}

.landing-sub {
  margin: 0 auto 18px;
  max-width: 40ch;
  text-align: center;
  font-size: 13.5px;
  color: var(--txt2);
}

.landing-grid {
  display: grid;
  gap: 12px;
  margin-top: 16px;
}

@media (min-width: 620px) {
  .landing-grid {
    grid-template-columns: 1fr 1fr;
  }
}

.landing-card {
  padding: 16px;
  border-radius: var(--r-xl);
  border: 1px solid var(--glass-border);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--shadow-sm);
}

.landing-icon {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 12px;
  background: var(--brand-soft);
  color: var(--brand);
  margin-bottom: 10px;
}

.landing-card-title {
  margin: 0 0 4px;
  font-size: 15px;
  font-weight: 700;
}

.landing-card-body {
  margin: 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--txt2);
}

/* What is on the invoice is the point of this screen, so the list owns a rose edge while the two
   add buttons below it stay quiet. They were reading as three cards of equal weight, which made
   the actual contents hard to pick out. */
.line-list {
  border-color: color-mix(in srgb, var(--brand) 55%, var(--border));
  box-shadow: var(--shadow-sm), 0 0 0 3px color-mix(in srgb, var(--brand) 12%, transparent);
}

/* An invoice line reads as a thing, not as a table row: a quantity chip leads it, the name carries
   the weight, and the amount closes it. Restrained on purpose — this list is read constantly. */
.line-row {
  gap: 10px;
}

.line-qty {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: baseline;
  gap: 1px;
  min-width: 34px;
  justify-content: center;
  padding: 5px 7px;
  border-radius: var(--r-sm);
  background: var(--brand-soft);
  color: var(--brand-ink);
  font-size: 13px;
  font-weight: 750;
  font-variant-numeric: tabular-nums;
}

.line-qty-x {
  font-size: 10.5px;
  font-weight: 600;
  opacity: 0.7;
}

.line-row .row-title {
  font-weight: 650;
}

.line-row .row-amount {
  font-size: 15px;
}

/* A hairline rule between lines, not a boxed row each: the list stays one block of information. */
.line-row + .line-row {
  border-top: 1px solid var(--border);
}

/* ---------- Adding an invoice line ---------- */

.add-line {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-top: 10px;
}

.add-line-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 12px;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  /* Transparent, not a surface: these are controls under the list, not more content in it. */
  background: transparent;
  text-align: left;
  min-width: 0;
}

/* Dashed for the one that invents something new — the same language a blank slot uses elsewhere.
   A stronger line than the solid card next to it: at the same weight the dashes read as a faded
   edge rather than a deliberate one. */
.add-line-btn.is-custom {
  border-style: dashed;
  border-color: var(--border-strong);
  background: transparent;
}

.add-line-btn:active {
  border-color: var(--brand);
  background: var(--brand-soft);
}

.add-line-mark {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--brand-soft);
  color: var(--brand-ink);
}

.add-line-btn.is-custom .add-line-mark {
  background: var(--surface3);
  color: var(--txt2);
}

.add-line-text {
  display: grid;
  min-width: 0;
}

.add-line-title {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--txt1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.add-line-sub {
  font-size: 11.5px;
  color: var(--txt3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Below this the two labels start truncating, so they stack instead. */
@media (max-width: 359px) {
  .add-line {
    grid-template-columns: 1fr;
  }
}

/* Shown when a name is already in the catalogue: an offer, not a rejection. */
.clash {
  margin-bottom: 12px;
  padding: 12px 14px;
  border-radius: var(--r-md);
  border: 1px solid var(--brand);
  background: var(--brand-soft);
}

.clash-title {
  font-weight: 700;
  color: var(--brand-ink);
}

.clash-body {
  margin: 4px 0 10px;
  font-size: 12.5px;
  color: var(--txt2);
}

.clash-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

/* Shortcut row above the cost box — small enough to read as helpers, not as the main control. */
.cost-quick {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 6px;
}

.cost-quick-btn {
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface2);
  color: var(--txt2);
  font-size: 11.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* The selected state comes from the value, not from the tap: an :active style lingers after a
   touch on mobile, so whichever button was pressed last looked permanently chosen. */
.cost-quick-btn[aria-pressed='true'] {
  background: var(--brand-soft);
  border-color: var(--brand);
  color: var(--brand-ink);
}

.cost-quick-btn:disabled {
  opacity: 0.45;
}

/* ---------- Guided tour ---------- */

.tour {
  position: fixed;
  inset: 0;
  z-index: 90;
}

/* Four panels around the lit element rather than one overlay with a punched hole: box-shadow
   spills are unreliable across browsers, and this leaves the highlighted control genuinely
   untouched underneath. */
.tour-shade {
  position: fixed;
  background: rgba(18, 10, 14, 0.62);
  animation: fadeIn 0.18s ease;
}

.tour-ring {
  position: fixed;
  border-radius: var(--r-md);
  border: 2px solid var(--brand);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 28%, transparent);
  pointer-events: none;
  animation: fadeIn 0.18s ease;
}

.tour-card {
  position: fixed;
  max-width: 460px;
  margin: 0 auto;
  padding: 16px;
  border-radius: var(--r-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  animation: slideUp 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}

.tour-step-count {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--brand);
}

.tour-title {
  margin-top: 4px;
  font-size: 16px;
  font-weight: 750;
  text-wrap: balance;
}

.tour-body {
  margin: 6px 0 14px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--txt2);
}

.tour-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.tour-actions-end {
  display: flex;
  gap: 8px;
}

@media (prefers-reduced-motion: reduce) {
  .tour-shade,
  .tour-ring,
  .tour-card {
    animation: none;
  }
}

/* ---------- Slide-to-fit captcha ---------- */

.puzzle {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--surface3);
  /* The image is 640x360, and the box keeps that ratio at any width so the piece lands true. */
  aspect-ratio: 16 / 9;
  touch-action: pan-y;
}

.puzzle-bg,
.puzzle-piece {
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

.puzzle-bg {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Positioned from the left and moved by the slider alone: the answer is a distance, never a
   coordinate the page knows. */
.puzzle-piece {
  position: absolute;
  left: 0;
  filter: drop-shadow(0 2px 6px rgba(30, 16, 22, 0.45));
  will-change: transform;
}

.puzzle-piece.is-dragging {
  filter: drop-shadow(0 4px 10px rgba(30, 16, 22, 0.55));
}

.puzzle-loading {
  width: 100%;
  height: 100%;
  background: linear-gradient(100deg, var(--surface3) 30%, var(--surface2) 50%, var(--surface3) 70%);
  background-size: 200% 100%;
  animation: shimmer 1.2s linear infinite;
}

.puzzle-track {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 46px;
  margin-top: 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface2);
  overflow: hidden;
  touch-action: pan-y;
}

.puzzle-track:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.puzzle-track.is-done {
  border-color: var(--ok);
  background: var(--ok-soft);
}

.puzzle-track.is-wrong {
  border-color: var(--danger);
  background: var(--danger-soft);
}

.puzzle-track-hint {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  color: var(--txt3);
  pointer-events: none;
}

.puzzle-track.is-done .puzzle-track-hint {
  color: var(--ok);
  font-weight: 600;
}

.puzzle-track.is-wrong .puzzle-track-hint {
  color: var(--danger);
  font-weight: 600;
}

/* A short shake on a miss: the piece is where you left it, so movement is what says "not there". */
.puzzle-piece.is-wrong {
  animation: puzzle-miss 0.35s ease;
}

@keyframes puzzle-miss {
  0%, 100% { margin-left: 0; }
  25% { margin-left: -5px; }
  75% { margin-left: 5px; }
}

@media (prefers-reduced-motion: reduce) {
  .puzzle-piece.is-wrong {
    animation: none;
  }
}

/* The handle is the only draggable thing; the piece above follows it. */
.puzzle-handle {
  position: absolute;
  left: 0;
  top: 0;
  display: grid;
  place-items: center;
  width: 52px;
  height: 100%;
  border: none;
  border-radius: 999px;
  background: var(--brand);
  color: var(--brand-contrast);
  cursor: grab;
  will-change: transform;
}

.puzzle-handle:active {
  cursor: grabbing;
}

.puzzle-handle:disabled {
  background: var(--ok);
  cursor: default;
}

@media (prefers-reduced-motion: reduce) {
  .puzzle-loading {
    animation: none;
  }
}

/* ---------- Paying for a plan ---------- */

/* The figure to send, stated once and unmissably: getting this wrong wastes a transfer. */
.pay-amount {
  display: grid;
  gap: 2px;
  justify-items: center;
  text-align: center;
  padding: 14px 12px;
  border-radius: var(--r-lg);
  background: var(--brand-soft);
  border: 1px solid var(--border);
}

.pay-amount strong {
  font-size: 28px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--brand-ink);
}

/* Separates the two ways to pay. A plain heading read as a section; a ruled "or" reads as a
   choice between equals, which is what it is. */
/* The second way to pay, when a gateway is also on offer. An outline button read as disabled
   next to the filled one; this is tinted and bordered in the brand so it is unmistakably a
   real action, while still yielding to the primary. */
.btn-soft {
  background: var(--brand-soft);
  border: 1px solid color-mix(in srgb, var(--brand) 32%, transparent);
  color: var(--brand-ink);
  font-weight: 700;
}

.btn-soft:active {
  background: color-mix(in srgb, var(--brand) 18%, var(--brand-soft));
}

/* The tick sits in its own disc so the button reads as "confirm what you did", not "submit". */
.btn-soft .btn-mark {
  display: inline-grid;
  place-items: center;
  width: 22px;
  height: 22px;
  margin-right: 2px;
  border-radius: 999px;
  background: var(--brand);
  color: var(--brand-contrast);
}

.pay-or {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 14px 0 2px;
  color: var(--txt3);
  font-size: 12px;
}

.pay-or::before,
.pay-or::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

.pay-card {
  margin-top: 12px;
}

.pay-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.pay-qr {
  display: grid;
  justify-items: center;
  gap: 6px;
  margin-bottom: 12px;
}

/* White ground: a QR with a transparent background cannot be scanned on a dark theme.
   Capped by height as well as width — owners upload a full phone screenshot as often as a cropped
   code, and an uncapped tall image pushes the account number and the button off the screen. */
.pay-qr img {
  width: auto;
  max-width: 100%;
  max-height: 230px;
  object-fit: contain;
  border-radius: var(--r-md);
  background: #fff;
  padding: 8px;
  border: 1px solid var(--border);
}

/* An inline action inside running text — "copy" beside a number. */
.linkbtn {
  border: none;
  background: none;
  padding: 0 0 0 6px;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  color: var(--brand);
  text-decoration: underline;
}

/* Tap target for the receipt, showing the chosen image in place rather than a file name. */
.upload-drop {
  display: grid;
  place-items: center;
  min-height: 120px;
  padding: 10px;
  border-radius: var(--r-md);
  border: 1px dashed var(--border);
  background: var(--surface3);
  cursor: pointer;
}

.upload-drop img {
  max-height: 220px;
  max-width: 100%;
  border-radius: var(--r-sm);
}

/* ---------- Subscription payments (admin) ---------- */

/* Two buttons of equal weight, side by side — approve/reject, back/confirm. */
.two-col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.channel-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* The QR thumbnail stands in for an avatar on a channel row. White ground because a QR with a
   transparent background is unreadable against the dark theme. */
.channel-qr {
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: #fff;
  object-fit: contain;
  padding: 2px;
}

.channel-qr-empty {
  display: grid;
  place-items: center;
  background: var(--surface3);
  color: var(--txt3);
}

/* The receipt as sent, capped so a tall phone screenshot cannot push the buttons out of reach —
   it opens full size in a new tab. */
.receipt-frame {
  display: block;
  max-height: 320px;
  overflow: hidden;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--surface3);
}

.receipt-frame img {
  display: block;
  width: 100%;
  height: auto;
  object-fit: contain;
}

/* ---------- Landing device preview ---------- */

/* Built from markup rather than screenshots: it follows the live theme and branding, and it can
   never drift out of date the way a captured PNG does. */
.devices {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  justify-content: center;
  margin-top: 18px;
}

/* Above mobile width, an odd device count otherwise wraps a lone phone onto its own row (three
   across, then one) — a fixed 2-column grid keeps every row balanced instead. */
@media (min-width: 620px) {
  .devices {
    display: grid;
    grid-template-columns: repeat(2, auto);
    justify-content: center;
  }
}

.device {
  position: relative;
  width: 216px;
  margin: 0;
  padding: 9px;
  background: linear-gradient(160deg, #3a2b32, #241a1f);
  box-shadow: 0 18px 40px rgba(32, 18, 24, 0.28);
}

.device.android {
  border-radius: 26px;
}

.device.iphone {
  border-radius: 34px;
  padding: 10px;
}

/* Camera cut-outs — the quickest way to say "Android" and "iPhone" without naming them. */
.device-punch {
  position: absolute;
  top: 16px;
  left: 50%;
  width: 6px;
  height: 6px;
  margin-left: -3px;
  border-radius: 50%;
  background: #0d0a0b;
  z-index: 2;
}

.device-notch {
  position: absolute;
  top: 15px;
  left: 50%;
  width: 62px;
  height: 15px;
  margin-left: -31px;
  border-radius: 999px;
  background: #0d0a0b;
  z-index: 2;
}

.device-screen {
  position: relative;
  height: 380px;
  overflow: hidden;
  padding: 24px 10px 10px;
  border-radius: 20px;
  background: var(--bg);
  color: var(--txt1);
}

.device.iphone .device-screen {
  border-radius: 26px;
}

.device-caption {
  margin-top: 10px;
  text-align: center;
  font-size: 11.5px;
  color: var(--txt3);
}

/* On a phone the mockups sit one per row with width going spare, and at 216px the figures inside
   were smaller than in the real app they are meant to be showing.
   `zoom` rather than a bigger width: it scales the whole frame — screen, rows, type, caption — in
   proportion, where widening alone would only stretch the frame and leave the contents the same
   size against a taller empty screen. Browsers without zoom simply keep the current size. */
@media (max-width: 620px) {
  .devices {
    gap: 22px;
  }

  .device {
    zoom: 1.4;
  }
}

/* The narrowest phones cannot take the full step without the frame touching the page edges. */
@media (max-width: 359px) {
  .device {
    zoom: 1.2;
  }
}

.mini-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-bottom: 8px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--border);
}

.mini-burger {
  width: 12px;
  height: 8px;
  border-top: 1.5px solid var(--txt2);
  border-bottom: 1.5px solid var(--txt2);
}

.mini-title {
  font-size: 13px;
  font-weight: 700;
  margin: 0 auto;
}

.mini-title.script {
  font-family: var(--font-script), cursive;
  font-size: 16px;
  color: var(--txt1);
}

.mini-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  margin-bottom: 8px;
}

.mini-stat,
.mini-card {
  padding: 7px 8px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--surface);
}

/* The grid already makes both tiles the same height; this pushes the amount to the bottom of
   each so a label that wraps onto two lines cannot leave the figures at different heights. */
.mini-stat {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 46px;
}

.mini-card {
  margin-bottom: 8px;
}

.mini-label {
  display: block;
  font-size: 7.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--txt3);
}

.mini-value {
  display: block;
  margin-top: auto;
  padding-top: 2px;
  font-size: 13px;
  font-weight: 800;
}

.mini-value.brand {
  color: var(--brand);
}

.mini-value.accent {
  color: var(--accent);
}

.mini-value.ok {
  color: var(--ok);
}

.mini-chips {
  display: flex;
  gap: 4px;
  margin-bottom: 8px;
}

.mini-chip {
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  font-size: 8px;
  color: var(--txt2);
}

.mini-chip.on {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-contrast);
}

.mini-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 7px 8px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--surface);
  margin-bottom: 6px;
}

.mini-row-title {
  font-size: 9.5px;
  font-weight: 700;
}

.mini-row-sub {
  font-size: 8px;
  color: var(--txt3);
}

.mini-row-end {
  text-align: right;
}

.mini-amount {
  font-size: 10px;
  font-weight: 800;
}

.mini-badge {
  display: inline-block;
  margin-top: 2px;
  padding: 1px 5px;
  border-radius: 999px;
  font-size: 7px;
  font-weight: 700;
}

.mini-badge.paid {
  background: var(--ok-soft);
  color: var(--ok);
}

.mini-badge.partial {
  background: var(--warn-soft);
  color: var(--warn);
}

.mini-badge.unpaid {
  background: var(--danger-soft);
  color: var(--danger);
}

.mini-fab {
  position: absolute;
  right: 14px;
  bottom: 14px;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--brand);
  color: var(--brand-contrast);
  font-size: 15px;
  font-weight: 700;
  box-shadow: var(--shadow-md);
}

.mini-chart {
  width: 100%;
  height: 40px;
  margin-top: 4px;
}

.mini-chart path {
  stroke: var(--brand);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.mini-meter-row {
  margin-top: 6px;
}

.mini-meter {
  display: block;
  height: 4px;
  margin-top: 3px;
  border-radius: 999px;
  background: var(--surface3);
  overflow: hidden;
}

.mini-meter-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--brand);
}

.landing-steps {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: grid;
  gap: 12px;
}

.landing-step {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 14px 16px;
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  background: var(--surface);
}

.landing-step-num {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--brand);
  color: var(--brand-contrast);
  font-size: 13px;
  font-weight: 800;
}

.landing-step-text {
  min-width: 0;
}

/* Title and estimate share a line, and the estimate drops below it when the title is long. */
.landing-step-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 2px;
}

.landing-step-title {
  font-weight: 700;
}

.landing-step-time {
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}

/* Detail under the step: quieter and smaller than the step's own line, so scanning the numbered
   titles still works and the extra reading is optional. */
.landing-step-points {
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 5px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--txt3);
}

.landing-step-points li {
  position: relative;
  padding-left: 14px;
}

.landing-step-points li::before {
  content: '';
  position: absolute;
  left: 3px;
  top: 8px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.55;
}

.landing-plans {
  display: grid;
  gap: 12px;
}

@media (min-width: 620px) {
  .landing-plans {
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  }
}

.landing-plan {
  position: relative;
  padding: 18px;
  border-radius: var(--r-xl);
  border: 1px solid var(--border);
  background: var(--surface);
}

.landing-plan.featured {
  border-color: var(--brand);
  box-shadow: var(--shadow-md);
}

.landing-tag {
  position: absolute;
  top: -10px;
  left: 18px;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--brand);
  color: var(--brand-contrast);
  font-size: 11px;
  font-weight: 700;
}

.landing-plan-name {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--txt2);
}

.landing-plan-price {
  margin: 4px 0 8px;
  font-size: 26px;
  font-weight: 800;
  color: var(--brand);
}

.landing-plan-per {
  font-size: 13px;
  font-weight: 600;
  color: var(--txt3);
}

/* Hairline-separated rows with a ticked disc, instead of disc bullets with a hanging indent. The
   dividers do the aligning, so a wrapped perk stays flush with the one above it. */
.landing-list {
  margin: 14px 0 0;
  padding: 0;
  list-style: none;
  font-size: 13px;
  color: var(--txt2);
}

.landing-list li {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 8px 0;
  line-height: 1.45;
  border-top: 1px solid var(--glass-border);
}

.landing-list li:first-child {
  border-top: none;
  padding-top: 0;
}

.landing-list li:last-child {
  padding-bottom: 0;
}

.landing-check {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  /* Nudged down so the tick centres on the first line of text, not on the whole wrapped block. */
  margin-top: 1px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
}

/* The featured card carries the accent already; a filled tick there keeps it from going busy. */
.landing-plan.featured .landing-check {
  background: var(--accent);
  color: #fff;
}

.landing-final {
  max-width: 760px;
  margin: 26px auto 0;
  padding: 26px 18px;
  border-radius: var(--r-xl);
  border: 1px solid var(--glass-border);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  text-align: center;
}

/* Sort pill: states the current order and opens the chooser. Reads as one control, so the
   field name is emphasised and the direction sits quietly beside it. */
.sortbar {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin-bottom: 10px;
  padding: 9px 12px;
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--txt2);
  font-size: 13px;
}

.sortbar-label {
  font-weight: 650;
  color: var(--txt1);
}

.sortbar-dir {
  margin-left: auto;
  color: var(--txt2);
}

.loadmore {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
}

.loadmore .tiny {
  margin: 0;
}

/* Input with a button beside it, e.g. the document number and its Generate action. */
.input-with-action {
  display: flex;
  /* stretch, not center: the button then takes the input's height instead of its own padding. */
  align-items: stretch;
  gap: 8px;
}

.input-with-action .input {
  flex: 1 1 auto;
  min-width: 0;
}

.input-with-action .btn {
  flex: 0 0 auto;
  gap: 6px;
  /* Let the flex line set the height; the button's own vertical padding would fight it. */
  height: auto;
  padding-top: 0;
  padding-bottom: 0;
  white-space: nowrap;
}

/* Bulk actions above the bin list. Sits outside .list so it does not read as another row. */
.bulkbar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
  padding: 0 2px;
}

.bulkbar .tiny {
  flex: 1 1 auto;
  text-align: center;
}

/* Per-row tick. Reuses .check-box so it matches every other boolean in the app. */
.row-select {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 40px;
  align-self: stretch;
  background: none;
  border: none;
  padding: 0;
}

.chips-wrap {
  display: flex;
  align-items: center;
  gap: 6px;
}

.chips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 2px 2px 6px;
  scrollbar-width: none;
  /* min-width lets the flex child actually shrink instead of pushing the arrows off-screen. */
  flex: 1 1 auto;
  min-width: 0;
}

/* Styled like a chip so the row still reads as one control, only round. Rendered solely on a
   side that has more content, so its presence is the hint. */
.chips-arrow {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  margin-bottom: 4px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--txt2);
}

.chips-arrow:active {
  background: var(--surface2);
  color: var(--txt1);
}

/* One icon, mirrored — no second glyph to keep in step with the first. */
.chips-arrow.left span {
  display: grid;
  transform: scaleX(-1);
}

.chips::-webkit-scrollbar {
  display: none;
}

.chip {
  padding: 7px 13px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--txt2);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  flex: 0 0 auto;
}

.chip[aria-pressed='true'] {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-contrast);
}

/* ---------- Segmented control ---------- */

/* Tab bar: a recessed track with a raised active tab, so the selected one is unmistakable. */
.segmented {
  display: flex;
  gap: 4px;
  padding: 4px;
  margin-bottom: 14px;
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 14px;
}

.segmented button {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 8px;
  border-radius: 10px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--txt2);
  font-weight: 650;
  font-size: 13.5px;
  transition: background 0.16s ease, color 0.16s ease, box-shadow 0.16s ease;
}

/* Underline tabs for four or more: no track, no pills, just labels with a rose rule under the
   active one. Fits a single row on a phone and reads as quieter chrome than the pill control. */
.segmented.tabs {
  display: flex;
  gap: 0;
  padding: 0 2px;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  border-radius: 0;
  margin-bottom: 16px;
  /* Safety valve: if a future tab set really is too wide, it scrolls rather than clipping. */
  overflow-x: auto;
  scrollbar-width: none;
}

.segmented.tabs::-webkit-scrollbar {
  display: none;
}

.segmented.tabs button {
  position: relative;
  /* Size to the label, then share the leftover space — equal thirds would starve the longest
     word while "App" sat in whitespace. */
  flex: 1 1 auto;
  min-width: max-content;
  gap: 5px;
  padding: 9px 6px 11px;
  border: none;
  border-radius: 0;
  background: none;
  font-size: 13px;
  letter-spacing: 0.01em;
}

/* Never abbreviate a tab label. */
.segmented.tabs button span {
  overflow: visible;
  text-overflow: clip;
}

.segmented.tabs button svg {
  opacity: 0.55;
}

.segmented.tabs button[aria-pressed='true'] {
  background: none;
  border: none;
  box-shadow: none;
  color: var(--brand-ink);
  font-weight: 750;
}

/* The active marker sits on the container's rule, so tabs never shift as you switch. */
.segmented.tabs button[aria-pressed='true']::after {
  content: '';
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: var(--brand);
}

.segmented.tabs button:active {
  transform: none;
  opacity: 0.75;
}

/* Very narrow phones: drop the icons rather than the words. */
@media (max-width: 359px) {
  .segmented.tabs button svg {
    display: none;
  }
}

.segmented button span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.segmented button svg {
  flex: 0 0 auto;
  opacity: 0.7;
}

.segmented button[aria-pressed='true'] {
  background: var(--surface);
  border-color: var(--brand);
  color: var(--brand-ink);
  box-shadow: var(--shadow-sm);
}

.segmented button[aria-pressed='true'] svg {
  opacity: 1;
  color: var(--brand);
}

.segmented button:active {
  transform: scale(0.99);
}

/* ---------- Forms ---------- */

.field {
  display: block;
  margin-bottom: 12px;
}

.field-label {
  display: block;
  font-size: 12.5px;
  font-weight: 650;
  color: var(--txt2);
  margin-bottom: 5px;
}

.field-hint {
  display: block;
  font-size: 11.5px;
  color: var(--txt2);
  margin-top: 4px;
}

/* Fields carry a visible edge — on a frosted card an unbordered fill reads as a hole. */
.input,
.select,
.textarea {
  width: 100%;
  background: var(--surface2);
  border: 1.5px solid var(--border-strong);
  border-radius: var(--r-md);
  padding: 12px 13px;
  color: var(--txt1);
  font-size: 15px;
  outline: none;
  transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
  appearance: none;
}

.input::placeholder,
.textarea::placeholder {
  color: var(--txt3);
}

.input:focus,
.select:focus,
.textarea:focus {
  border-color: var(--brand);
  background: var(--surface);
  box-shadow: 0 0 0 3px var(--brand-soft);
}

.textarea {
  min-height: 84px;
  resize: vertical;
}

.select {
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 19px) calc(50% + 2px), calc(100% - 13px) calc(50% + 2px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 34px;
}

.input.money {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* ---------- Inline form error ---------- */

.form-error {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0 0 14px;
  padding: 10px 12px;
  border-radius: var(--r-md);
  border: 1px solid var(--danger);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.45;
  animation: slideUpSm 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}

.form-error svg {
  flex: 0 0 auto;
  margin-top: 1px;
}

/* ---------- Captcha ---------- */

/* Label row carries the refresh control on the right. The label keeps its own class so there
   is no display:block / display:flex fight on the same element. */
.captcha-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}

.captcha-head .field-label {
  margin-bottom: 0;
}

.captcha-refresh {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  border-radius: 999px;
  background: var(--surface2);
  border: 1px solid var(--border);
  color: var(--brand);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.captcha-refresh:active {
  background: var(--brand-soft);
}

.captcha-image {
  display: block;
  width: 100%;
  height: auto;
  margin-bottom: 14px;
  border-radius: 18px;
  box-shadow: var(--shadow-sm);
}

.captcha-loading {
  aspect-ratio: 420 / 150;
  background: linear-gradient(90deg, var(--surface2), var(--surface3), var(--surface2));
  background-size: 200% 100%;
  animation: shimmer 1.2s ease-in-out infinite;
}

.captcha-input {
  text-align: center;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.36em;
  text-transform: uppercase;
}

/* One-time code: wide tracking so six digits are easy to check against the email. */
.input.code-input {
  text-align: center;
  font-size: 26px;
  font-weight: 750;
  letter-spacing: 0.32em;
  font-variant-numeric: tabular-nums;
  padding: 14px 13px;
}

.grid2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.picker {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px;
  border-radius: var(--r-md);
  background: var(--surface2);
  border: 1.5px dashed var(--border-strong);
  color: var(--txt2);
  font-weight: 600;
}

.picker.filled {
  border-style: solid;
  border-color: var(--border);
  color: var(--txt1);
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 13px 18px;
  border-radius: var(--r-md);
  font-weight: 700;
  font-size: 15px;
  transition: transform 0.08s ease, opacity 0.15s ease;
}

.btn:active {
  transform: scale(0.985);
}

.btn[disabled] {
  opacity: 0.55;
  pointer-events: none;
}

.btn-primary {
  background: var(--brand);
  color: var(--brand-contrast);
}

.btn-accent {
  background: var(--accent);
  color: #3d1a16;
}

.btn-ghost {
  background: var(--surface2);
  color: var(--txt1);
}

.btn-outline {
  background: transparent;
  border: 1.5px solid var(--border-strong);
  color: var(--txt1);
}

.btn-danger {
  background: var(--danger-soft);
  color: var(--danger);
}

.btn-block {
  width: 100%;
}

.btn-sm {
  padding: 8px 12px;
  font-size: 13.5px;
  border-radius: var(--r-sm);
}

.fab {
  position: fixed;
  right: 16px;
  right: calc(16px + var(--safe-right));
  bottom: calc(20px + var(--safe-bottom));
  z-index: 25;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 15px 20px;
  border-radius: 999px;
  background: var(--brand);
  color: var(--brand-contrast);
  font-weight: 700;
  box-shadow: var(--shadow-lg);
}

/* ---------- Speed-dial FAB: one button that reveals labelled actions ---------- */
.fab-dial {
  position: fixed;
  right: calc(16px + var(--safe-right));
  bottom: calc(20px + var(--safe-bottom));
  z-index: 25;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
}

/* The trigger reuses .fab's look but drops its own fixed positioning inside the dial. */
.fab-dial .fab {
  position: static;
  right: auto;
  bottom: auto;
}

/* The trigger's icon morphs from + to × as the menu opens. */
.fab-dial .fab .fab-icon {
  transition: transform 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}
.fab-dial.is-open .fab .fab-icon {
  transform: rotate(135deg);
}

.fab-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}

.fab-action {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  padding: 11px 16px 11px 14px;
  border-radius: 999px;
  background: var(--surface);
  color: var(--txt1);
  font-weight: 650;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-md);
  white-space: nowrap;
  animation: fabPop 0.2s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.fab-action:disabled {
  opacity: 0.6;
}

.fab-action .fab-action-ico {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--brand-soft);
  color: var(--brand);
  flex: 0 0 auto;
}

@keyframes fabPop {
  from {
    opacity: 0;
    transform: translateY(10px) scale(0.94);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* Invisible catch-layer: a tap anywhere else closes the open dial. */
.fab-scrim {
  position: fixed;
  inset: 0;
  z-index: 24;
  background: transparent;
  border: none;
}

/* Sticky save/action bar. Needs its own stacking level: the cards above it are positioned
   (for the glass sheen), so without a z-index they paint over it. */
.actionbar {
  position: sticky;
  bottom: 0;
  z-index: 20;
  margin-top: 18px;
  margin-left: calc(-14px - var(--safe-left));
  margin-right: calc(-14px - var(--safe-right));
  padding: 12px calc(14px + var(--safe-right)) calc(12px + var(--safe-bottom)) calc(14px + var(--safe-left));
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-top: 1px solid var(--glass-border);
  box-shadow: 0 -6px 20px rgba(32, 18, 24, 0.18);
  display: flex;
  gap: 10px;
}

/* ---------- Custom toggles (never native checkboxes) ---------- */

.check {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 2px;
  width: 100%;
  text-align: left;
}

.check-box {
  width: 22px;
  height: 22px;
  border-radius: 6px;
  border: 2px solid var(--border-strong);
  display: grid;
  place-items: center;
  color: transparent;
  font-size: 13px;
  font-weight: 900;
  flex: 0 0 auto;
  transition: background 0.15s ease, border-color 0.15s ease;
}

/* Any element carrying aria-checked, not just .check — the bin's per-row tick is its own
   control and was silently missing the checked styling. */
.check[aria-checked='true'] .check-box,
[aria-checked='true'] > .check-box {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-contrast);
}

.check-label {
  flex: 1;
  font-weight: 550;
}

.check-sub {
  display: block;
  font-size: 12px;
  color: var(--txt2);
  font-weight: 450;
}

.switch {
  width: 46px;
  height: 28px;
  border-radius: 999px;
  background: var(--surface3);
  border: 1px solid var(--border);
  padding: 2px;
  display: flex;
  flex: 0 0 auto;
  transition: background 0.18s ease, border-color 0.18s ease;
}

.switch-knob {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Standalone switch: the button itself carries the state. */
.switch[aria-checked='true'] {
  background: var(--brand);
  border-color: var(--brand);
}

.switch[aria-checked='true'] .switch-knob {
  transform: translateX(18px);
}

/* Labelled switch: the state lives on the wrapping .check button, and the track inside is
   aria-hidden — so it has to be styled from the parent or the knob never moves. */
.check[aria-checked='true'] .switch {
  background: var(--brand);
  border-color: var(--brand);
}

.check[aria-checked='true'] .switch .switch-knob {
  transform: translateX(18px);
}

/* ---------- Modals & sheets ---------- */

.modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--scrim-bg);
  backdrop-filter: blur(12px) saturate(115%);
  -webkit-backdrop-filter: blur(12px) saturate(115%);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  animation: fadeIn 0.2s ease;
}

.modal-scrim.center {
  align-items: center;
  padding: 18px;
}

.sheet {
  width: 100%;
  max-width: 560px;
  max-height: 88dvh;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-bottom: none;
  box-shadow: var(--shadow-lg), var(--glass-ring);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding-bottom: var(--safe-bottom);
  padding-left: var(--safe-left);
  padding-right: var(--safe-right);
  display: flex;
  flex-direction: column;
  animation: slideUp 0.22s cubic-bezier(0.16, 1, 0.3, 1);
  overflow: hidden;
}

.sheet.center {
  border-radius: var(--r-lg);
  animation: slideUpSm 0.2s cubic-bezier(0.16, 1, 0.3, 1);
  max-width: 420px;
}

.sheet.tall {
  height: 94dvh;
  max-height: 94dvh;
}

.sheet.tall .sheet-body {
  flex: 1;
  min-height: 0;
}

/* Generous hit area around a small visual bar — it is a real close button, and a drag handle. */
.sheet-grip {
  display: block;
  width: 100%;
  padding: 11px 0 7px;
  background: none;
  border: none;
  touch-action: none;
}

.sheet-grip-bar {
  display: block;
  width: 42px;
  height: 4px;
  border-radius: 999px;
  margin: 0 auto;
  background: var(--border-strong);
  transition: background 0.15s ease, width 0.15s ease;
}

.sheet-grip:active .sheet-grip-bar {
  background: var(--brand);
  width: 54px;
}

.sheet-head {
  padding: 10px 16px 12px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 10px;
}

.sheet-title {
  flex: 1;
  font-size: 16px;
  font-weight: 700;
}

.sheet-body {
  padding: 14px 16px;
  overflow-y: auto;
}

.sheet-body.flush {
  padding: 0;
  overflow: hidden;
}

/* ---------- PDF preview ---------- */

.pdf-wrap {
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--surface2);
}

.pdf-frame {
  flex: 1;
  width: 100%;
  border: 0;
  background: var(--surface2);
}

.pdf-formats {
  padding: 12px 16px 10px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.pdf-formats .segmented {
  margin-bottom: 8px;
}

.pdf-image-scroll {
  flex: 1;
  overflow: auto;
  padding: 14px;
  display: flex;
  justify-content: center;
  align-items: flex-start;
}

.pdf-image {
  width: 100%;
  max-width: 460px;
  height: auto;
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-md);
  background: #ffffff;
}

.pdf-fallback {
  padding: 10px 14px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--brand);
  background: var(--surface);
  border-top: 1px solid var(--border);
}

/* ---------- "or" divider ---------- */

.or-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 14px 0 12px;
  color: var(--txt2);
  font-size: 12px;
}

.or-divider::before,
.or-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* ---------- Numbered instructions ---------- */

.steps {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}

.steps li {
  counter-increment: step;
  position: relative;
  padding: 0 0 12px 32px;
  font-size: 13.5px;
  color: var(--txt1);
}

.steps li:last-child {
  padding-bottom: 0;
}

.steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 11.5px;
  font-weight: 750;
  color: var(--brand-ink);
  background: var(--brand-soft);
  border: 1px solid var(--border-strong);
}

/* ---------- Subscription banner ---------- */

.sub-banner {
  /* The banner's tone as a variable rather than via currentColor: the pill below fills itself from
     this, and deriving a fill from the element's own colour meant the label and the fill could
     never differ. */
  --sub-tone: var(--warn);
  width: 100%;
  display: flex;
  align-items: center;
  gap: 11px;
  text-align: left;
  padding: 11px 13px;
  margin-bottom: 12px;
  border-radius: var(--r-md);
  border: 1px solid var(--warn);
  background: var(--warn-soft);
  color: var(--warn);
}

.sub-banner.urgent,
.sub-banner.expired {
  --sub-tone: var(--danger);
  border-color: var(--danger);
  background: var(--danger-soft);
  color: var(--danger);
}

.sub-banner-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.sub-banner-text strong {
  font-size: 13.5px;
  font-weight: 750;
}

.sub-banner-text span {
  font-size: 11.5px;
  color: var(--txt2);
}

.sub-banner-cta {
  flex: 0 0 auto;
  font-size: 12.5px;
  font-weight: 750;
  padding: 6px 11px;
  border-radius: 999px;
  background: var(--sub-tone);
  /* Always light-on-tone. Both tones are saturated enough in either theme to carry white text, and
     white cannot collide with the fill the way a theme surface colour did. */
  color: #fff;
}

/* ---------- Payment QR ---------- */

.qr-block {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 12px;
  margin-bottom: 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface2);
}

.qr-preview {
  width: 84px;
  height: 84px;
  flex: 0 0 auto;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: #ffffff;
  border: 1px solid var(--border);
  display: grid;
  place-items: center;
}

.qr-preview img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.qr-placeholder {
  color: var(--txt3);
}

.pdf-loading {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
}

.sheet-foot {
  padding: 12px 16px;
  border-top: 1px solid var(--border);
  display: flex;
  gap: 10px;
}

/* ---------- Toasts ---------- */

.toasts {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(84px + var(--safe-bottom));
  z-index: 70;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: min(92vw, 460px);
}

.toast {
  padding: 12px 14px;
  border-radius: var(--r-md);
  background: var(--txt1);
  color: var(--bg);
  font-weight: 600;
  font-size: 14px;
  box-shadow: var(--shadow-lg);
  animation: slideUpSm 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}

.toast.ok {
  background: var(--ok);
  color: #ffffff;
}

.toast.err {
  background: var(--danger);
  color: #ffffff;
}

/* ---------- Stats & report ---------- */

.stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.stat {
  position: relative;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm), var(--glass-ring);
  padding: 13px 14px;
  overflow: hidden;
}

.stat > * {
  position: relative;
  z-index: 1;
}

.stat-label {
  font-size: 11.5px;
  font-weight: 650;
  color: var(--txt2);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.stat-value {
  font-size: 21px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  margin-top: 3px;
}

.stat.brand .stat-value {
  color: var(--brand);
}

.stat.accent .stat-value {
  color: var(--accent);
}

.stat.danger .stat-value {
  color: var(--danger);
}

/* Same green as a paid invoice: profit and money received should read alike. */
.stat.ok .stat-value {
  color: var(--ok);
}

/* ---------- Trend chart ---------- */

.chart-caption {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 10px;
}

.chart-caption-value {
  font-size: 17px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  display: block;
  line-height: 1.15;
}

.chart-caption-label {
  font-size: 11px;
  color: var(--txt2);
  letter-spacing: 0.02em;
}

.chart-plot {
  position: relative;
  height: 116px;
}

.chart-svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.chart-dot {
  position: absolute;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 50%;
  background: var(--brand);
  border: 2px solid var(--surface);
  box-shadow: var(--shadow-sm);
}

.chart-axis {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  font-size: 11px;
  color: var(--txt2);
}

.chart-bars {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  height: 116px;
}

.chart-bar-slot {
  flex: 1;
  min-width: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.chart-bar-track {
  flex: 1;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  border-bottom: 1px solid var(--border);
}

.chart-bar {
  width: 100%;
  max-width: 30px;
  min-height: 2px;
  border-radius: 6px 6px 0 0;
  background: linear-gradient(180deg, var(--brand) 0%, var(--brand-soft) 190%);
  opacity: 0.55;
  transition: height 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
}

.chart-bar.peak {
  opacity: 1;
}

.chart-bar-label {
  font-size: 10.5px;
  color: var(--txt2);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chart-empty {
  height: 116px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  font-size: 13px;
  color: var(--txt2);
}

.chart-empty-line {
  width: 100%;
  height: 1px;
  background: repeating-linear-gradient(
    90deg,
    var(--border-strong) 0 6px,
    transparent 6px 12px
  );
}

.meter {
  height: 7px;
  border-radius: 999px;
  background: var(--surface3);
  overflow: hidden;
}

.meter-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--brand);
}

/* ---------- Misc ---------- */

.empty {
  text-align: center;
  padding: 52px 20px;
  color: var(--txt2);
}

/* A soft brand-tinted disc gives the empty state a focal point without a cartoon emoji. */
.empty-icon {
  width: 64px;
  height: 64px;
  margin: 0 auto 14px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--brand);
  background: var(--brand-soft);
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow-sm), var(--glass-ring);
}

/* Trailing icons in rows: present, but quieter than the label. */
.row-icon {
  color: var(--txt3);
  flex: 0 0 auto;
}

.empty-title {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 600;
  color: var(--txt1);
  margin-bottom: 4px;
}

.muted {
  color: var(--txt2);
}

.tiny {
  font-size: 12px;
}

.right {
  text-align: right;
}

.rowline {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 5px 0;
}

.rowline.total {
  border-top: 1px solid var(--border);
  margin-top: 6px;
  padding-top: 10px;
  font-weight: 750;
  font-size: 18px;
}

/* What the customer still owes is the number the seller looks for first. */
.rowline.balance {
  align-items: center;
  margin-top: 2px;
  font-weight: 750;
  font-size: 15.5px;
}

.rowline.balance .val {
  font-size: 24px;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.rowline .val {
  font-variant-numeric: tabular-nums;
}

.spinner {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2.5px solid var(--border);
  border-top-color: var(--brand);
  animation: spin 0.7s linear infinite;
  margin: 32px auto;
}

.skeleton {
  height: 62px;
  border-radius: var(--r-md);
  background: linear-gradient(90deg, var(--surface2), var(--surface3), var(--surface2));
  background-size: 200% 100%;
  animation: shimmer 1.2s ease-in-out infinite;
  margin-bottom: 8px;
}

.upgrade {
  background: linear-gradient(135deg, var(--brand-soft), var(--gold-soft));
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 16px;
  text-align: center;
}

.divider {
  height: 1px;
  background: var(--border);
  margin: 14px 0;
}

.login-wrap {
  position: relative;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: calc(24px + var(--safe-top)) calc(22px + var(--safe-right)) calc(28px + var(--safe-bottom))
    calc(22px + var(--safe-left));
  max-width: 460px;
  margin: 0 auto;
}

/* ---------- Legal pages (privacy) ---------- */
.legal-wrap {
  max-width: 680px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  padding: calc(20px + var(--safe-top)) calc(22px + var(--safe-right)) calc(44px + var(--safe-bottom))
    calc(22px + var(--safe-left));
  color: var(--txt1);
}

.legal-back {
  align-self: flex-start;
  padding: 6px 2px;
  border: none;
  background: none;
  color: var(--txt2);
  font-size: 13px;
  font-weight: 600;
  transition: color 0.15s ease;
}
.legal-back:hover {
  color: var(--brand);
}

.legal-title {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 600;
  margin: 14px 0 2px;
  color: var(--txt1);
}
.legal-updated {
  margin: 0 0 26px;
  font-size: 12.5px;
  color: var(--txt3);
}

.legal-body {
  color: var(--txt2);
  line-height: 1.7;
  font-size: 14.5px;
}
.legal-body h2 {
  color: var(--txt1);
  font-size: 15px;
  font-weight: 700;
  margin: 26px 0 8px;
}
.legal-body p {
  margin: 0 0 12px;
}
.legal-body ul {
  margin: 0 0 12px;
  padding-left: 20px;
}
.legal-body li {
  margin: 0 0 6px;
}
.legal-body strong {
  color: var(--txt1);
  font-weight: 650;
}
.legal-mail {
  color: var(--brand);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.legal-mail:hover {
  border-bottom-color: var(--brand);
}

/* ---------- Sign-up stepper + plan picker ---------- */
.signup-steps {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 0 auto 18px;
}
.signup-step {
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--txt3);
  font-size: 12px;
  font-weight: 600;
}
.signup-step-dot {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--surface2);
  border: 1px solid var(--border);
  color: var(--txt2);
  font-size: 12px;
  flex: 0 0 auto;
}
.signup-step.current {
  color: var(--txt1);
}
.signup-step.current .signup-step-dot {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-contrast);
}
.signup-step.done .signup-step-dot {
  background: color-mix(in srgb, var(--brand) 18%, transparent);
  border-color: transparent;
  color: var(--brand);
}
.signup-step:not(:last-child)::after {
  content: '';
  width: 18px;
  height: 1px;
  background: var(--border);
  margin-left: 6px;
}

.plan-picker {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.plan-card {
  position: relative;
  display: block;
  width: 100%;
  text-align: left;
  padding: 14px 16px;
  border-radius: var(--r-md);
  border: 1.5px solid var(--border);
  background: var(--surface);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.plan-card.selected {
  border-color: var(--brand);
  background: var(--brand-soft);
}
.plan-card-check {
  position: absolute;
  top: 12px;
  right: 12px;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--brand);
  color: var(--brand-contrast);
  opacity: 0;
  transform: scale(0.8);
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.plan-card.selected .plan-card-check {
  opacity: 1;
  transform: scale(1);
}
.plan-card-name {
  font-weight: 700;
  font-size: 15px;
  color: var(--txt1);
}
.plan-card-price {
  font-size: 22px;
  font-weight: 800;
  color: var(--brand);
  margin-top: 2px;
}
.plan-card-per {
  font-size: 13px;
  font-weight: 600;
  color: var(--txt2);
}
.plan-card-trial {
  font-size: 12px;
  font-weight: 600;
  color: var(--ok);
  margin-top: 3px;
}
.plan-card-trial.muted {
  color: var(--txt3);
}
.plan-card-desc {
  font-size: 12.5px;
  color: var(--txt2);
  margin-top: 6px;
  line-height: 1.45;
}

/* Three overlapping washes on a lifted base — warm and lit rather than flat dark. */
.login-wrap::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(95% 55% at 50% -8%, var(--login-glow-1), transparent 68%),
    radial-gradient(75% 45% at 6% 104%, var(--login-glow-2), transparent 70%),
    radial-gradient(70% 45% at 104% 88%, var(--login-glow-3), transparent 70%),
    var(--login-base);
}

/* The real Lovely's Pasabuy badge. It is already circular, so the square PNG is
   simply clipped to a disc, with a soft halo lifting it off the background. */
.login-logo {
  display: block;
  width: 88px;
  height: 88px;
  border-radius: 50%;
  object-fit: cover;
  margin: 0 auto 14px;
  box-shadow:
    0 10px 34px rgba(44, 12, 24, 0.28),
    0 0 0 1px var(--glass-border),
    0 0 0 12px rgba(255, 255, 255, 0.06);
}

/* ---------- Animations ---------- */

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideUpSm {
  from {
    opacity: 0;
    transform: translateY(10px) scale(0.99);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes slideIn {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0);
  }
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes shimmer {
  0% {
    background-position: 200% 0;
  }
  100% {
    background-position: -200% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* Content column wrapper — sits beside the sidebar on desktop, full width on mobile. */
.app-main {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

/* Desktop: the menu becomes a pinned, in-flow sidebar (no scrim, no overlay). The appbar burger
   collapses/expands it rather than opening a modal. Mobile is untouched — it keeps the drawer. */
@media (min-width: 1024px) {
  .app {
    flex-direction: row;
    align-items: stretch;
  }

  .app .scrim {
    display: none !important;
  }

  .app .drawer {
    position: sticky;
    top: 0;
    height: 100dvh;
    flex: 0 0 272px;
    width: 272px;
    transform: none !important;
    box-shadow: none;
    transition: width 0.2s ease, flex-basis 0.2s ease, padding 0.2s ease;
  }

  /* Collapsed = fully tucked away; the appbar burger brings it back. */
  .app.nav-collapsed .drawer {
    flex-basis: 0;
    width: 0;
    padding-left: 0;
    padding-right: 0;
    overflow: hidden;
    border-right: none;
  }
}

/* Thin, elegant scrollbar tinted with the brand accent (translucent so it reads the same in light
   and dark). Applied to the sidebar and the main document scroller so both match. */
.drawer,
html {
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--accent) 42%, transparent) transparent;
}
.drawer::-webkit-scrollbar,
html::-webkit-scrollbar {
  width: 6px;
}
.drawer::-webkit-scrollbar-track,
html::-webkit-scrollbar-track {
  background: transparent;
}
.drawer::-webkit-scrollbar-thumb,
html::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--accent) 42%, transparent);
  border-radius: 999px;
}
.drawer::-webkit-scrollbar-thumb:hover,
html::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--accent) 68%, transparent);
}
