:root {
  color-scheme: light;
  --brand:      #14B8A0;  /* CTA, active tab, links (Splitwise-teal family) */
  --brand-deep: #0E8371;  /* hover/pressed, small text on brand */
  --brand-tint: #E6F7F3;  /* chips, selected rows, expense icon squares */
  --credit:     #16A34A;  /* is-owed amounts & bars - ONLY money is green */
  --debt:       #DC2626;  /* owes amounts & bars - red, not orange (EU convention) */
  --credit-bg:  #DCFCE7;
  --debt-bg:    #FEE2E2;
  --ink:        #1A2229;  /* primary text */
  --ink-2:      #5B6670;  /* secondary text, dates */
  --ink-3:      #9AA4AD;  /* placeholders, disabled */
  --line:       #E5E9EC;  /* hairline borders */
  --bg:         #F6F8F9;  /* app background */
  --surface:    #FFFFFF;  /* cards */
  --warn:       #B45309;  /* uncertain scanner assignments */
  --warn-bg:    #FEF3C7;
  --accent-2:   #8B4A9E;  /* landing-page hero subhead only - a second brand color, teal stays primary everywhere else */
}

/* Dark theme follows the operating system's appearance setting. */
@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --brand: #2DD4BE; --brand-deep: #14B8A0; --brand-tint: #123A34;
    --credit: #4ADE80; --debt: #F87171; --credit-bg: #14321F; --debt-bg: #3A1A1A;
    --ink: #ECF1F4; --ink-2: #A8B3BC; --ink-3: #6B7680;
    --line: #2A3238; --bg: #12171B; --surface: #1B2126;
    --warn: #FBBF24; --warn-bg: #3A2E12;
    --accent-2: #C77DDA;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  background: var(--bg);
  color: var(--ink);
  font-size: 15px;
}

.page {
  max-width: 480px;
  margin: 0 auto;
  padding: 24px 16px 80px;
}

header.top-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 24px;
}

header.top-bar h1 {
  font-size: 1.25rem;
  margin: 0;
  font-weight: 700;
  color: var(--ink);
}

.project-title {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Small borderless icon button, same visual weight as button.link but sized
   for a single emoji glyph instead of text. */
.icon-btn {
  background: none;
  color: var(--ink-2);
  border: none;
  padding: 4px;
  min-height: 0;
  min-width: 0;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}

.rename-project-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
}

.rename-project-form input {
  flex: 1;
  min-width: 120px;
}

.rename-project-form button {
  width: auto;
}

.rename-project-form .error {
  flex-basis: 100%;
  margin-top: 0;
}

h1 { font-size: 1.5rem; font-weight: 700; }
h2 { font-size: 1.1rem; margin-top: 0; font-weight: 700; }
h3 { font-size: 0.95rem; margin: 0 0 8px; font-weight: 700; }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 16px;
  margin-bottom: 16px;
  box-shadow: 0 1px 2px rgb(0 0 0 / .04);
}

@media (prefers-color-scheme: dark) {
  .card { box-shadow: none; }
}

/* Dashboard "your name" card once a name is already set (see dashboard.js
   demoteDisplayNameCard()) - de-emphasized into a small "edit name" row
   instead of the full-size onboarding card it is for a brand-new user. */
#display-name-card.compact {
  padding: 10px 14px;
}

#display-name-card.compact h2 {
  font-size: 0.85rem;
  color: var(--ink-2);
  margin-bottom: 6px;
}

#display-name-card.compact #display-name-hint {
  display: none;
}

#display-name-card.compact #display-name-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

#display-name-card.compact #display-name-input {
  flex: 1;
  min-width: 120px;
  min-height: 40px;
}

#display-name-card.compact #display-name-form button {
  width: auto;
  margin-top: 0;
  min-height: 40px;
}

#display-name-card.compact .error {
  flex-basis: 100%;
  margin-top: 0;
}

/* --- Landing hero (index.html, above the login/signup card) --- */

.hero {
  background: var(--brand-tint);
  border-radius: 20px;
  padding: 28px 18px 20px;
  margin-bottom: 16px;
  text-align: center;
}

.hero-headline {
  margin: 0 0 8px;
  font-size: 1.85rem;
  font-weight: 300;
  line-height: 1.25;
  color: var(--ink);
}

.hero-subhead {
  margin: 0 0 20px;
  font-size: 1rem;
  font-weight: 600;
  color: var(--accent-2);
}

.hero-features {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 16px;
}

.hero-feature-icon {
  width: 44px;
  height: 44px;
  margin: 0 auto 6px;
  border-radius: 14px;
  background: var(--surface);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  box-shadow: 0 1px 2px rgb(0 0 0 / .06);
}

.hero-feature-title {
  margin: 0 0 2px;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--ink);
}

.hero-feature-desc {
  margin: 0;
  font-size: 0.7rem;
  color: var(--ink-2);
  line-height: 1.3;
}

.hero-microcopy {
  margin: 0;
  font-size: 0.78rem;
  color: var(--ink-2);
}

/* Pro price line under the microcopy. Same quiet weight on purpose: it points
   at /preise, it does not try to sell the subscription on the login page. */
.hero-pro-line {
  margin: 0;
  font-size: 0.78rem;
  color: var(--ink-2);
}

/* Standalone USP callout (scanner "learns" assignment rules) - visually
   distinct from the 3 feature tiles above so it reads as a differentiator,
   not a 4th equal-weight tile. */
.hero-usp {
  display: flex;
  align-items: center;
  gap: 12px;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--accent-2);
  border-radius: 14px;
  padding: 12px 14px;
  margin-bottom: 16px;
}

.hero-usp-icon {
  flex: none;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: var(--brand-tint);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
}

.hero-usp-title {
  margin: 0 0 2px;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--accent-2);
}

.hero-usp-desc {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.35;
  color: var(--ink-2);
}

label {
  display: block;
  font-size: 0.85rem;
  color: var(--ink-2);
  margin-bottom: 4px;
  margin-top: 12px;
  font-weight: 600;
}

input, select {
  width: 100%;
  min-height: 48px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  font-size: 1rem;
  background: var(--surface);
  color: var(--ink);
}

input:focus, select:focus {
  outline: 2px solid var(--brand);
  outline-offset: -1px;
}

button {
  cursor: pointer;
  border: none;
  border-radius: 12px;
  min-height: 48px;
  padding: 10px 16px;
  font-size: 1rem;
  font-weight: 700;
  transition: background .15s, transform .1s;
}

button:active { transform: scale(.98); }

button.primary {
  background: var(--brand);
  color: white;
  width: 100%;
  margin-top: 16px;
}

button.primary:hover { background: var(--brand-deep); }

button.secondary {
  background: transparent;
  color: var(--brand);
  border: 2px solid var(--brand);
  width: 100%;
  margin-top: 8px;
}

button.secondary:hover { background: var(--brand-tint); }

/* "Install app" button + iOS instruction hint: see js/install-prompt.js.
   Lives outside .card on both index.html and dashboard.html (neither a Dusi
   action nor part of the auth form), but keeps the same vertical rhythm as
   the cards around it. */
.install-row { margin-bottom: 16px; }
.install-row #install-ios-hint { margin: 8px 0 0; text-align: center; }

button.link {
  background: none;
  color: var(--ink-2);
  padding: 4px 0;
  min-height: 0;
  font-weight: 600;
  border-radius: 0;
}

button.link.danger { color: var(--debt); }

button.small {
  padding: 6px 10px;
  min-height: 0;
  font-size: 0.85rem;
}

button:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }

.error {
  color: var(--debt);
  font-size: 0.9rem;
  margin-top: 8px;
  min-height: 1.2em;
  font-weight: 600;
}

.muted { color: var(--ink-2); font-size: 0.9rem; }

.fun-comment {
  color: var(--accent-2);
  font-size: 0.85rem;
  font-style: italic;
  margin-top: 8px;
}

/* Soft "this might already be logged" hint after a scan - advisory, not an
   error, so it borrows the scanner's existing --warn palette (used for
   uncertain item assignments) rather than --debt. */
.duplicate-warning {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
  background: var(--warn-bg);
  color: var(--warn);
  border-radius: 12px;
  padding: 10px 14px;
  margin-bottom: 12px;
  font-size: 0.9rem;
}

.duplicate-warning button.link {
  color: var(--warn);
  flex-shrink: 0;
  padding: 4px 0;
}

/* Scan couldn't read (or wasn't sure of) the purchase date - sits directly
   under the field, no background box, matching the other inline hint text. */
.date-warning {
  color: var(--warn);
  font-size: 0.85rem;
  margin-top: 4px;
}

.password-field {
  position: relative;
}

.password-field input {
  padding-right: 44px;
}

.password-toggle {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  min-height: auto;
  padding: 6px 8px;
  font-size: 1.1rem;
  line-height: 1;
  color: var(--ink-2);
}

.password-toggle:hover { color: var(--ink); background: none; }
.password-toggle:active { transform: translateY(-50%) scale(.92); }

#forgot-password-btn {
  display: block;
  margin-top: 8px;
}

.password-hints {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  font-size: 0.85rem;
}
.password-hints li {
  color: var(--ink-2);
  padding-left: 1.3em;
  position: relative;
}
.password-hints li::before {
  content: "\2715"; /* ✕ */
  position: absolute;
  left: 0;
  color: var(--debt);
}
.password-hints li.met {
  color: var(--credit);
}
.password-hints li.met::before {
  content: "\2713"; /* ✓ */
  color: var(--credit);
}

.consent-group {
  margin-top: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.checkbox-label {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  font-weight: 400;
  font-size: 0.85rem;
  color: var(--ink-2);
  cursor: pointer;
}

.checkbox-label input {
  width: auto;
  min-height: 0;
  margin: 2px 0 0;
  padding: 0;
  border-radius: 0;
  flex: none;
  accent-color: var(--brand);
}

/* --- Cloudflare Turnstile widget (index.html auth form) ---
   Turnstile's sizes are fixed by Cloudflare, not by us: data-size="flexible"
   fills its container but never renders narrower than 300px (the alternatives
   are "normal" 300x65 and "compact" 150x140). The auth card leaves
   viewport − 66px of inner width (.page padding 2x16 + .card padding 2x16 +
   2x1px border), so a 360px phone has only 294px (6px short) and the widget
   would push the whole page into horizontal scrolling.
   Fix in two steps, both measured rather than guessed:
   1. Below 366px, cancel the card's own horizontal padding for this one block.
      That buys back 32px (viewport − 34), which clears 300px from a 334px
      viewport upwards, so every mainstream phone width fits unscaled.
   2. Narrower still (Galaxy Fold cover screen at 280px and similar), the
      widget itself scrolls inside its box. The page never does - which is the
      property that actually matters.
   MEASURED against the deployed site (headless Chromium, 2026-07-30) rather
   than predicted: page scrollWidth == viewport at 280/320/360/390/414px, so no
   horizontal page scrolling at any of them, and the widget box comes out at
   exactly viewport−34 below 366px (326px at 360) and viewport−66 above it
   (324px at 390) - the arithmetic above holds.
   Known cosmetic residual, deliberately NOT "fixed": below ~334px the box is
   narrower than Turnstile's 300px floor (286px at 320, 246px at 280), so the
   widget's trailing edge (the Cloudflare logo and the Privacy/Help links) is
   clipped and only reachable by scrolling inside the box. The checkbox and its
   label stay fully visible and tappable, so the control still works. A
   `transform: scale()` shrink would make it pixel-perfect, but that puts a
   visual transform that cannot be verified headlessly onto the one control
   standing between real users and logging in - not a trade worth making for
   the trailing edge of a logo on pre-2016 screen widths. Revisit only if a
   real user reports it. */
.turnstile-wrap {
  margin-top: 14px;
}

.turnstile-wrap .cf-turnstile {
  max-width: 100%;
  overflow-x: auto;
}

@media (max-width: 365px) {
  .turnstile-wrap {
    margin-left: -16px;
    margin-right: -16px;
  }
}

/* The status line under the widget is advisory ("check still running"), never
   an error - it borrows .muted's tone and only takes space when it has text. */
#turnstile-status {
  margin: 6px 0 0;
  font-size: 0.85rem;
}

.checkbox-label a {
  color: var(--brand-deep);
}

#resend-box { margin-top: 8px; }
#resend-box .muted { margin: 4px 0 0; }

/* Manual fallback for the "Request Dusi deletion" mailto: link, shown once
   the button has been clicked and confirmed: see js/project.js. Readonly,
   not an input the user fills in, so it gets its own class rather than the
   generic input/select rule. */
#delete-project-fallback { margin-top: 12px; }
#delete-project-fallback p:first-child { margin-top: 0; }
.fallback-textarea {
  width: 100%;
  min-height: 120px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  font-size: 0.9rem;
  font-family: inherit;
  background: var(--surface);
  color: var(--ink);
  resize: vertical;
}
#delete-project-copy-status { margin: 6px 0 0; min-height: 1.2em; }

.project-list-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  color: var(--ink);
  font-weight: 600;
  transition: background .15s;
}

.project-list-item:last-child { border-bottom: none; }
.project-list-item:active { transform: scale(.98); }

.tabs {
  display: flex;
  gap: 6px;
  margin-bottom: 16px;
  background: var(--bg);
  border-radius: 999px;
  padding: 4px;
}

.tabs button {
  flex: 1;
  background: transparent;
  border: none;
  color: var(--ink-2);
  border-radius: 999px;
  min-height: 40px;
  padding: 8px 0;
}

.tabs button.active {
  background: var(--brand);
  color: white;
}

/* Keyboard focus ring for the tab buttons. The default UA outline is
   imperceptible on the active tab (teal-on-teal), so use a light inset outline
   that shows against the teal fill plus a contrasting deep-teal box-shadow ring
   that shows against the light tab track for the inactive tab. Scoped to
   :focus-visible so a mouse click doesn't leave a ring. */
.tabs button:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -4px;
  box-shadow: 0 0 0 2px var(--brand-deep);
}

/* DE/EN language toggle: header.top-bar on index.html and on the 6 legal
   pages (js/i18n.js and js/legal-lang.js wire up the same #lang-de/#lang-en
   markup). Same segmented-control look as .tabs above, but sized to sit
   inline next to the page's <h1> instead of spanning full width. */
.lang-toggle {
  display: inline-flex;
  gap: 2px;
  background: var(--bg);
  border-radius: 999px;
  padding: 3px;
  flex-shrink: 0;
}

.lang-toggle button {
  background: transparent;
  border: none;
  color: var(--ink-2);
  border-radius: 999px;
  min-height: 40px;
  min-width: 40px;
  padding: 6px 14px;
  margin: 0;
  font-size: 0.85rem;
  font-weight: 700;
}

.lang-toggle button.active {
  background: var(--brand);
  color: white;
}

.lang-toggle button:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -4px;
  box-shadow: 0 0 0 2px var(--brand-deep);
}

/* --- Balance hero + bidirectional bars --- */

.hero-balance {
  text-align: center;
  padding: 12px 0 16px;
  font-size: 1.6rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.hero-balance .amount { font-variant-numeric: tabular-nums; }
.hero-balance.credit { color: var(--credit); }
.hero-balance.debt { color: var(--debt); }
.hero-balance.even { color: var(--ink-2); }

.balance-bars { margin-top: 4px; }

.bar-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}

.bar-row:last-child { border-bottom: none; }

.bar-row .bar-name {
  width: 6.5rem;
  flex-shrink: 0;
  font-weight: 600;
  font-size: 0.85rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bar-track {
  position: relative;
  flex: 1;
  height: 20px;
  background: var(--bg);
  border-radius: 6px;
}

.bar-track .axis {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line);
}

.bar-track .bar {
  position: absolute;
  top: 2px;
  bottom: 2px;
  border-radius: 4px;
  transition: width .4s ease-out;
}

.bar-track .bar.credit { left: 50%; background: var(--credit); }
.bar-track .bar.debt { right: 50%; background: var(--debt); }

.bar-row .bar-amount {
  width: 5.5rem;
  flex-shrink: 0;
  text-align: right;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
}

.bar-row .bar-amount.credit { color: var(--credit); }
.bar-row .bar-amount.debt { color: var(--debt); }

.settle-suggestion-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
  gap: 8px;
}

.settle-suggestion-row:last-child { border-bottom: none; }

.split-with-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin: 4px 0 4px;
}

.split-with-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  color: var(--ink);
  font-weight: 600;
  cursor: pointer;
}

.split-with-item input {
  width: auto;
  min-height: 0;
  margin: 0;
  padding: 0;
  border-radius: 0;
  accent-color: var(--brand);
}

.expense-row, .settlement-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}

.expense-row:last-child, .settlement-item:last-child { border-bottom: none; }

.expense-row {
  cursor: pointer;
  border-radius: 8px;
}

.expense-row:hover, .expense-row:focus-visible {
  background: var(--brand-tint);
  outline: none;
}

.expense-row .receipt-indicator {
  font-size: 14px;
  margin-left: -2px;
}

.settlement-item { justify-content: space-between; }

.expense-icon {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: 12px;
  background: var(--brand-tint);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
}

.expense-main { flex: 1; min-width: 0; }

.expense-desc { font-weight: 700; }
.expense-meta { color: var(--ink-2); font-size: 0.85rem; }
.expense-amount { font-weight: 700; white-space: nowrap; margin-left: 12px; font-variant-numeric: tabular-nums; }

.date-header {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 16px 0 4px;
}

.date-header:first-child { margin-top: 0; }

.avatar {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 0.85rem;
}

.empty-state {
  text-align: center;
  padding: 24px 8px;
  color: var(--ink-2);
}

.empty-state .emoji {
  display: block;
  font-size: 2rem;
  margin-bottom: 8px;
}

.receipt-preview {
  max-width: 100%;
  border-radius: 14px;
  margin-top: 12px;
  display: none;
}

/* Scan-quota readout under the scan buttons. Both the counts and the limit
   come from get_project_scan_usage(), which shares its tier logic with the
   function that actually enforces the limit; so what this shows and what
   the scan endpoint allows cannot drift apart. */
.scan-usage { margin-top: 8px; }

.scan-usage-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  font-size: 0.8rem;
  color: var(--ink-2);
}

.scan-usage-count {
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.scan-usage-bar {
  height: 6px;
  margin-top: 5px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}

.scan-usage-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--brand);
  transition: width 0.3s ease;
}

/* Amber before the wall, red at it - the same warn/debt pair the rest of the
   app uses, so "you are close" and "you are out" read the same everywhere. */
.scan-usage-fill.is-high { background: var(--warn); }
.scan-usage-fill.is-full { background: var(--debt); }

.scan-usage-members {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 14px;
  margin-top: 6px;
  font-size: 0.8rem;
  color: var(--ink-2);
}

.scan-usage-members > span { white-space: nowrap; }

/* The cancellation notice (scanCancelledEndsAt) is appended as the last
   child of this flex-wrap row, right behind the last member's "You 1" span:
   with nothing but the row's gap between them it reads cramped (STATUS.md
   2026-08-23). flex-basis: 100% claims the full row width so flex-wrap has
   to start a new line for it, and margin-top adds the breathing room a plain
   wrap wouldn't. display: block is the DOM/CSS-only way to blockify a span
   without touching the markup that builds it (js/project.js renderScanUsage). */
.scan-usage-cancelled {
  display: block;
  flex-basis: 100%;
  margin-top: 6px;
}

.scan-usage-n {
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.scan-status {
  font-size: 0.85rem;
  color: var(--brand-deep);
  margin-top: 8px;
  min-height: 1.2em;
  font-weight: 600;
}

.invite-code {
  font-family: ui-monospace, monospace;
  background: var(--brand-tint);
  color: var(--brand-deep);
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 0.9rem;
  font-weight: 700;
}

.scan-btn {
  display: block;
  text-align: center;
  padding: 10px;
  margin-bottom: 8px;
  border: 1.5px dashed var(--brand);
  background: var(--brand-tint);
  color: var(--brand-deep);
  border-radius: 12px;
  cursor: pointer;
  font-weight: 700;
  font-size: 0.9rem;
}

.scan-btn:hover { background: var(--brand-tint); opacity: 0.85; }

/* The camera button relies on the file input's capture="environment", which
   only means anything on a device that actually has a camera picker: on a
   mouse-driven desktop it would just open a second, identical file dialog.
   Show it only where the primary pointer is touch. Its input is taken out of
   the tab order there too, so keyboard users never land on a focusable
   control whose label is hidden. */
.scan-btn-camera { display: none; }
#receipt-camera-input { display: none; }

@media (pointer: coarse) {
  .scan-btn-camera { display: block; }
  /* .visually-hidden keeps it reachable by keyboard while the label shows. */
  #receipt-camera-input { display: initial; }

  /* On a phone the camera picker already offers "choose an existing photo",
     so a separate upload button is a redundant second tap target. Hidden
     here, input included, for the same reason as above. */
  .scan-btn-upload { display: none; }
  #receipt-input { display: none; }
}

/* Pulse the scan button when arriving via the "Scan to Duesio" home-screen
   shortcut, so the one-tap start is obvious if the camera didn't auto-open. */
.scan-highlight { animation: scan-pulse 0.9s ease-in-out 3; }

@keyframes scan-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(20, 184, 160, 0); }
  50% { box-shadow: 0 0 0 6px rgba(20, 184, 160, 0.35); }
}

@media (prefers-reduced-motion: reduce) {
  .scan-highlight {
    animation: none;
    box-shadow: 0 0 0 4px rgba(20, 184, 160, 0.35);
  }
}

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

.hidden { display: none !important; }

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 100;
}

.modal-box {
  position: relative;
  background: var(--surface);
  border-radius: 16px;
  padding: 20px;
  max-width: 90vw;
  max-height: 85vh;
  overflow: auto;
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-box img {
  max-width: 100%;
  max-height: 75vh;
  border-radius: 10px;
  display: block;
}

.modal-box-expense {
  display: block;
  text-align: left;
  width: 420px;
}

.modal-title {
  margin: 0 32px 12px 0;
  font-size: 1.05rem;
}

.modal-items-list {
  margin-bottom: 12px;
}

.modal-items-list:empty { display: none; }

.modal-item-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
}

.modal-item-row:last-child { border-bottom: none; }

.modal-item-row .modal-item-desc {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.modal-item-row .modal-item-who {
  color: var(--ink-2);
  font-size: 0.85rem;
  white-space: nowrap;
  flex: 0 1 auto;
  /* A native <select>'s auto min-width is driven by its widest <option> (the
     longest member email), not the selected value: without min-width:0 it
     refuses to shrink below that and can eat nearly the whole row, squeezing
     modal-item-desc to 0px even though it has flex:1. Confirmed live via
     CSSOM injection on duesio.pages.dev: min-width:0 alone did nothing,
     max-width:42% alone did nothing, only both together fixed it. */
  min-width: 0;
  max-width: 42%;
}

.modal-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 999px;
  background: var(--ink);
  color: var(--surface);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  z-index: 1;
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 16px 0 8px;
  color: var(--ink-2);
  font-size: 0.85rem;
  font-weight: 600;
}

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

button.google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
}

.items-review {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px dashed var(--line);
}

/* Post-checkout banner (?checkout=success|cancelled on project.html): same
   flex/dismiss shape as .duplicate-warning, but color depends on outcome:
   green for success, amber for cancelled (set via a modifier class in JS). */
.checkout-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
  border-radius: 12px;
  padding: 10px 14px;
  margin-bottom: 12px;
  font-size: 0.9rem;
}

.checkout-banner.is-success { background: var(--credit-bg); color: var(--credit); }
.checkout-banner.is-cancelled { background: var(--warn-bg); color: var(--warn); }

.checkout-banner button.link {
  color: inherit;
  flex-shrink: 0;
  padding: 4px 0;
}

.upgrade-pro {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px dashed var(--line);
}

/* Heading + fold/hide control row - see js/project.js's "Upgrade-Pro
   collapse" section. */
.upgrade-pro-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}

.upgrade-pro-header h3 { margin: 0; }

.upgrade-hide-btn {
  flex-shrink: 0;
  font-size: 0.85rem;
}

/* #upgrade-pro-bottom-slot (project.html, right before .legal-footer): the
   card that holds a collapsed .upgrade-pro at the bottom of the page. It is
   the SAME .upgrade-pro node moved in via JS, so its own top
   border/margin/padding (meant to separate it from the scan-usage section
   above it, up at the top) would just be a redundant blank strip here where
   it is the card's only content - cancelled below. */
#upgrade-pro-bottom-slot .upgrade-pro {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

/* The one-line stand-in shown instead, once collapsed. Deliberately quiet -
   muted color, no button chrome - this is a dismissed ad, not a second call
   to action pulling attention back down. nowrap + ellipsis keeps it on a
   single line at any width covered by .page's 480px cap rather than wrapping
   or forcing horizontal scroll; the full text stays in the DOM either way,
   so a screen reader still gets all of it even where it is visually clipped. */
.upgrade-pro-collapsed {
  text-align: center;
}

.upgrade-pro-collapsed-btn {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  background: none;
  border: none;
  color: var(--ink-3);
  font-weight: 400;
  font-size: 0.85rem;
  min-height: 0;
  padding: 4px 0;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

/* Replaced the former .upgrade-pro-buttons pair (one button per plan) when the
   upgrade block became a § 312j BGB ordering situation with a radio group and
   a single order button. */
.order-fieldset {
  border: 0;
  margin: 12px 0 0;
  padding: 0;
  min-width: 0;
}

.order-fieldset legend {
  padding: 0;
  font-size: 0.8rem;
  color: var(--ink-3);
}

.item-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.item-row input[type="text"] {
  flex: 1;
  min-width: 120px;
}

.item-row input[type="number"] {
  width: 6rem;
  font-variant-numeric: tabular-nums;
}

.item-row select {
  width: auto;
}

/* Scanner wasn't sure who this item belongs to - nudge the user to check. */
.item-row.uncertain select {
  border-color: var(--warn);
  background-color: var(--warn-bg);
}

/* Scanner couldn't read (or wasn't confident about) the purchase date. */
#expense-date.uncertain {
  border-color: var(--warn);
  background-color: var(--warn-bg);
}

.item-rule-note {
  flex-basis: 100%;
  margin: 0;
  font-size: 0.85rem;
}

#training-input {
  width: 100%;
  min-height: 132px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1.5;
  background: var(--surface);
  color: var(--ink);
  resize: vertical;
}

#training-input:focus {
  outline: 2px solid var(--brand);
  outline-offset: -1px;
}

.training-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 10px;
}

.training-actions button { width: auto; }

/* Phone-only overrides. The balance bar row (avatar + name + track + amount)
   has ~250px of fixed-width children - fine in the 480px desktop column, but
   at phone widths it squeezes the track to near-nothing and forces harsh
   name truncation. Wrap the track onto its own full-width line instead. */
@media (max-width: 480px) {
  .bar-row {
    flex-wrap: wrap;
  }

  .bar-row .bar-name {
    order: 1;
    width: auto;
    flex: 1 1 auto;
    min-width: 0;
  }

  .bar-row .bar-amount {
    order: 2;
    width: auto;
  }

  .bar-track {
    order: 3;
    flex-basis: 100%;
    height: 14px;
  }
}

.scroll-hint {
  position: fixed;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 50;
  width: 38px;
  height: 38px;
  border-radius: 999px;
  background: var(--brand);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
  pointer-events: none;
  animation: scroll-hint-bounce 1.6s ease-in-out infinite;
  transition: opacity 0.2s ease;
}

.scroll-hint.hidden { display: none; }

@keyframes scroll-hint-bounce {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50% { transform: translateX(-50%) translateY(6px); }
}

@media (prefers-reduced-motion: reduce) {
  .scroll-hint { animation: none; }
}

/* Legal footer: Impressum/Datenschutz links, reachable from every page (§5 DDG "leicht erkennbar und unmittelbar erreichbar") */
.legal-footer {
  margin: 32px auto 12px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  text-align: center;
  font-size: 0.85rem;
  color: var(--ink-3);
}
.legal-footer a {
  color: var(--ink-2);
  text-decoration: none;
}
.legal-footer a:hover { color: var(--brand); text-decoration: underline; }
.legal-footer span { margin: 0 8px; color: var(--ink-3); }

/* preise.html: two stacked plan blocks rather than a comparison table, so
   nothing needs its own horizontal scroll container on a phone. */
.plan-block {
  margin: 18px 0;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
}

.plan-block h3 { margin: 0; }

.plan-price {
  margin: 4px 0 10px;
  font-weight: 600;
  color: var(--brand);
}

/* project.html: the § 312j BGB order block inside .upgrade-pro. */
.order-options {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.order-options .checkbox-label { min-width: 150px; flex: 1; }

.order-summary {
  margin: 12px 0 0;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  font-size: 0.9rem;
}

.order-summary dt {
  color: var(--ink-3);
  font-size: 0.8rem;
}

.order-summary dd {
  margin: 0 0 8px;
  color: var(--ink-1);
}

.order-summary dd:last-child { margin-bottom: 0; }

/* The order button stays focusable and clickable while consent is missing:
   only aria-disabled + this dimming signal the state, so the submit handler
   can still fire and explain what's missing (a real `disabled` button would
   swallow the click and tell a screen-reader user nothing). */
button[aria-disabled="true"] { opacity: 0.55; }
