/* web/css/shell.css -- WP-13 PWA shell.
 *
 * Mobile-first (CLAUDE.md rule 19): every rule below is written for a ~390px viewport as the
 * baseline layout, not as a shrunk-down desktop page. No rule in this file uses
 * `backdrop-filter` or any other blur-based translucency (CLAUDE.md rule 20) -- every
 * translucent surface below (the rejected/warning/error panels) uses plain alpha (rgba)
 * against the page background instead, which works identically on every rendering engine.
 * `tests/test_pwa_shell.py::test_no_backdrop_filter_in_shell_css` greps this file (and every
 * other web/css/*.css file) to keep that true automatically.
 *
 * Palette: the token *values* below are a literal reuse of `src/fina/render/section1_chart.py`
 * own `--page`/`--surface`/`--ink`/`--line-real` tokens (see that module's <style> block), so
 * the shell's own chrome and the chart it hosts read as one coherent app even though they are
 * two separate documents (the shell page and the chart's own sandboxed iframe) that never
 * share a stylesheet and therefore have no custom-property collision to avoid in the first
 * place -- this is a *coherence* choice, not a technical necessity. Token names are prefixed
 * `--shell-` specifically so a reader never mistakes them for the chart's own `--page`/`--ink`
 * (which live in a wholly different document and could never collide with these regardless).
 */

:root {
  --shell-page: #f6f5f1;
  --shell-surface: #fcfcfb;
  --shell-ink: #14140f;
  --shell-ink-2: #55534a;
  --shell-muted: #8b897f;
  --shell-border: rgba(11, 11, 11, 0.12);
  --shell-accent: #2a78d6;
  --shell-accent-bg: rgba(42, 120, 214, 0.12);
  --shell-danger: #e34948;
  --shell-danger-bg: rgba(227, 73, 72, 0.12);
  --shell-warning: #9a6b12;
  --shell-warning-bg: rgba(201, 145, 33, 0.16);
}

@media (prefers-color-scheme: dark) {
  :root {
    --shell-page: #0d0d0d;
    --shell-surface: #1a1a19;
    --shell-ink: #ffffff;
    --shell-ink-2: #c3c2b7;
    --shell-muted: #898781;
    --shell-border: rgba(255, 255, 255, 0.14);
    --shell-accent: #3987e5;
    --shell-accent-bg: rgba(57, 135, 229, 0.16);
    --shell-danger: #e66767;
    --shell-danger-bg: rgba(230, 103, 103, 0.18);
    --shell-warning: #d9a53f;
    --shell-warning-bg: rgba(217, 165, 63, 0.18);
  }
}

* {
  box-sizing: border-box;
}

/* Found while building WP-16's update banner, which needs an element that is both toggled via
 * the `hidden` attribute AND declares its own `display` value for when it's shown (`display:
 * flex`, for the banner's layout). Per the CSS cascade, an author-origin rule that sets
 * `display` beats the UA stylesheet's own `[hidden] { display: none }` regardless of selector
 * specificity or source order -- so `.foo[hidden]` can silently keep rendering whenever `.foo`
 * itself declares `display`. This single rule makes `hidden` win unconditionally, for every
 * element in this file now and later; it also retroactively fixes the same latent defect on
 * `.status-section` below (pre-existing, only invisible today by
 * accident, because they happen to render no visible content while hidden). */
[hidden] {
  display: none !important;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--shell-page);
  color: var(--shell-ink);
}

body {
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  line-height: 1.4;
  /* Side gutter kept at every width -- never zeroed by a later shorthand rule. 8px (WP-22b,
   * owner's feedback: wider frames wasted a phone's width). */
  padding: 12px 8px 32px;
}

main {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 640px;
  margin: 0 auto;
}

.app-header {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  max-width: 640px;
  margin: 0 auto 10px;
  padding: 0 4px;
}

.app-title {
  flex: 1 1 auto;
  min-width: 0;
}

.app-header h1 {
  font-size: 22px;
  margin: 0 0 2px;
}

.app-tagline {
  margin: 0;
  color: var(--shell-ink-2);
  font-size: 13.5px;
}

/* WP-14 (PWA-1.4a): hidden by default -- shown only once a real `beforeinstallprompt` event
 * fires (see web/js/install.js). Plain solid fill, no backdrop-filter (rule 20). */
.install-button {
  margin-top: 2px;
  padding: 8px 14px;
  font-size: 13.5px;
  font-weight: 600;
  color: #ffffff;
  background: var(--shell-accent);
  border: none;
  border-radius: 8px;
  cursor: pointer;
}

.install-button:active {
  opacity: 0.85;
}

/* WP-22b: settings behind one icon; the panel opens over the page, below the header. */
.settings,
.install-button {
  flex: 0 0 auto;
}

.settings-toggle {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 20px;
  color: var(--shell-ink-2);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.settings-toggle::-webkit-details-marker {
  display: none;
}

.settings[open] > .settings-toggle {
  background: var(--shell-accent-bg);
  color: var(--shell-accent);
}

.settings-panel {
  position: absolute;
  z-index: 10;
  top: 48px;
  left: 0;
  right: 0;
  padding: 14px 12px;
  border-radius: 10px;
  border: 1px solid var(--shell-border);
  background: var(--shell-surface);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
}

/* WP-16 (PWA-4.4): the "update available -- reload to apply" prompt, hidden by default (see
 * web/js/sw-register.js and index.html's own comment on this element). Plain solid fill /
 * plain alpha background, no backdrop-filter (rule 20); sits above <header> in document order
 * so it reads as a top-of-app notice rather than competing with the import/stored-files cards
 * below it. */
.update-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  max-width: 640px;
  margin: 0 auto 12px;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--shell-accent-bg);
  border: 1px solid var(--shell-accent);
  font-size: 13.5px;
}

.update-banner-text {
  margin: 0;
  color: var(--shell-ink);
}

.update-reload-button {
  flex: 0 0 auto;
  padding: 6px 12px;
  font-size: 13px;
  font-weight: 600;
  color: #ffffff;
  background: var(--shell-accent);
  border: none;
  border-radius: 8px;
  cursor: pointer;
}

.update-reload-button:active {
  opacity: 0.85;
}

.update-reload-button:disabled {
  opacity: 0.6;
  cursor: default;
}

section {
  background: var(--shell-surface);
  border: 1px solid var(--shell-border);
  border-radius: 10px;
  padding: 12px;
}

h2 {
  font-size: 15px;
  margin: 0 0 8px;
}

.file-input-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--shell-ink-2);
}

.busy-indicator {
  margin: 0 4px;
  font-size: 13px;
  color: var(--shell-ink-2);
}

.rejected-files {
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--shell-danger-bg);
  border: 1px solid var(--shell-border);
  font-size: 13.5px;
}

/* A banner's heading row: title, then its close cross. */
.banner-head {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.banner-head > p {
  flex: 1 1 auto;
}

.banner-close {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  margin: -6px -6px 0 0;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--shell-ink-2);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}

.rejected-heading {
  margin: 0 0 6px;
  font-weight: 600;
  color: var(--shell-danger);
}

.rejected-files ul,
.warnings ul {
  margin: 0;
  padding-left: 18px;
}

.rejected-files li {
  margin-bottom: 4px;
  word-break: break-word;
}

/* WP-15/WP-22b: one stored file, inside its account's card (or "Other files"). The whole row
 * is one <label>, a bigger tap target than the 18px checkbox alone (rule 19); the filename
 * wraps instead of overflowing at a phone width. */
.files {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  border-top: 1px solid var(--shell-border);
}

.stored-file-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 7px 0 0;
  cursor: pointer;
}

.stored-file-checkbox {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  accent-color: var(--shell-accent);
}

.stored-file-info {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.stored-file-name {
  font-size: 13px;
  font-weight: 600;
  word-break: break-word;
}

.stored-file-meta {
  font-size: 12px;
  color: var(--shell-muted);
}

.stored-file-inactive .stored-file-name,
.stored-file-inactive .stored-file-meta {
  color: var(--shell-muted);
}

/* WP-17: backup/export. Deliberately reuses `.file-input-label`'s look for the restore
 * input's own label (§4 of that WP's build tasks: "placed sensibly ... usable and not visually
 * broken at 390px" -- consistency with the import section above it is the simplest way to meet
 * that bar) rather than inventing a second visual language for what is, functionally, the same
 * kind of action (pick a file). No `backdrop-filter` anywhere here (rule 20) -- the status line
 * is a plain solid-color text row, no translucent surface at all. */
.backup-description {
  margin: 0 0 12px;
  color: var(--shell-ink-2);
  font-size: 13.5px;
}

.backup-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 8px 14px;
  font-size: 13.5px;
  font-weight: 600;
  color: #ffffff;
  background: var(--shell-accent);
  border: none;
  border-radius: 8px;
  cursor: pointer;
}

.backup-button:active {
  opacity: 0.85;
}

.backup-button:disabled {
  opacity: 0.6;
  cursor: default;
}

.restore-controls {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 14px;
}

#restore-backup-input {
  display: block;
  width: 100%;
  max-width: 100%;
  font-size: 14px;
  color: var(--shell-ink);
  padding: 4px 0;
}

.backup-status {
  margin: 12px 0 0;
  font-size: 13px;
  color: var(--shell-ink-2);
  word-break: break-word;
}

.backup-status--error {
  color: var(--shell-danger);
}

.error-section {
  background: var(--shell-danger-bg);
  border-color: var(--shell-danger);
}

.error-section h2 {
  color: var(--shell-danger);
}

#error-message {
  margin: 0;
  font-size: 14px;
  word-break: break-word;
}

.status-section {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 0;
  background: transparent;
  border: none;
}

.warnings {
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--shell-warning-bg);
  border: 1px solid var(--shell-border);
  font-size: 13.5px;
}

.warnings-heading {
  margin: 0 0 6px;
  font-weight: 600;
  color: var(--shell-warning);
}

/* WP-22b: the Accounts section -- a <details> card whose groups are <details> too, collapsed
 * by default. Plain alpha backgrounds only, no backdrop-filter (rule 20). */
.accounts-section {
  background: var(--shell-surface);
  border: 1px solid var(--shell-border);
  border-radius: 10px;
  padding: 0 12px;
}

.import-button {
  flex: 0 0 auto;
  min-height: 32px;
  padding: 4px 14px;
  font-size: 13.5px;
  font-weight: 600;
  color: #ffffff;
  background: var(--shell-accent);
  border: none;
  border-radius: 8px;
  cursor: pointer;
}

.import-button:active {
  opacity: 0.85;
}

.import-button:disabled {
  opacity: 0.6;
  cursor: default;
}

.accounts-section[open] {
  padding-bottom: 14px;
}

.acc-summary,
.group-summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
}

.acc-summary::-webkit-details-marker,
.group-summary::-webkit-details-marker {
  display: none;
}

.acc-summary {
  padding: 14px 0;
}

.accounts-section[open] > .acc-summary {
  padding-bottom: 6px;
}

.acc-summary h2 {
  margin: 0;
  flex: 1 1 auto;
}

/* The expand/collapse arrow sits on the left, away from the header's Import button (owner's
 * request, 2026-09-27); a fixed width keeps each heading's text aligned whichever way it
 * points. */
.acc-summary::before,
.group-summary::before {
  content: "\203A";
  flex: 0 0 12px;
  text-align: center;
  font-size: 20px;
  line-height: 1;
  color: var(--shell-muted);
  transform: rotate(0deg);
  transition: transform 0.12s ease;
}

.accounts-section[open] > .acc-summary::before,
.acc-group[open] > .group-summary::before {
  transform: rotate(90deg);
}

.accounts-empty {
  margin: 0 0 4px;
  color: var(--shell-muted);
  font-size: 13.5px;
}

.acc-group {
  border-top: 1px solid var(--shell-border);
}

/* Statement accounts sit directly in the section, above its collapsible groups. */
.accounts-list--statements {
  margin-bottom: 12px;
}

.acc-group[open] {
  padding-bottom: 12px;
}

.group-summary {
  padding: 11px 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--shell-ink-2);
}

.group-label {
  flex: 1 1 auto;
}

.count {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--shell-muted);
}

.group-note {
  margin: 0 0 4px;
  font-size: 12.5px;
  color: var(--shell-muted);
}

.accounts-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.account {
  padding: 10px 12px;
  border: 1px solid var(--shell-border);
  border-radius: 8px;
  font-size: 13.5px;
}

.account p {
  margin: 0 0 3px;
  word-break: break-word;
}

.account-head {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.account-title {
  flex: 1 1 auto;
  min-width: 0;
}

.account-alias {
  font-weight: 700;
  font-size: 15px;
}

.account-sub {
  font-size: 12.5px;
  color: var(--shell-ink-2);
}

.iban {
  white-space: nowrap;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 12.5px;
  color: var(--shell-ink-2);
}

.badge {
  flex: 0 0 auto;
  font-size: 11.5px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  white-space: nowrap;
}

.badge--statement {
  background: var(--shell-accent-bg);
  color: var(--shell-accent);
}

.badge--maybe {
  background: var(--shell-border);
  color: var(--shell-ink-2);
}

.nowrap {
  white-space: nowrap;
}

.own-account-meta {
  font-size: 12.5px;
  color: var(--shell-muted);
  font-variant-numeric: tabular-nums;
}

.account .balance {
  margin-top: 4px;
  font-variant-numeric: tabular-nums;
}

.own-account-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}

.acc-rename {
  border: none;
  background: transparent;
  color: var(--shell-accent);
  font-size: 12.5px;
  font-weight: 600;
  padding: 0;
  cursor: pointer;
}

.alias-edit {
  display: flex;
  gap: 6px;
  margin: 0 0 6px;
}

.alias-input {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 15px;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--shell-accent);
  background: var(--shell-page);
  color: var(--shell-ink);
}

.acc-button {
  min-height: 36px;
  padding: 6px 14px;
  font-size: 13.5px;
  font-weight: 600;
  border-radius: 8px;
  cursor: pointer;
  border: 1px solid var(--shell-accent);
  color: var(--shell-accent);
  background: transparent;
}

.acc-button--primary {
  color: #ffffff;
  background: var(--shell-accent);
}

.acc-button--small {
  min-height: 30px;
  padding: 4px 12px;
  font-size: 13px;
}

.acc-button:active {
  opacity: 0.85;
}

.acc-button:disabled,
.acc-rename:disabled {
  opacity: 0.6;
  cursor: default;
}

/* WP-22a/WP-22b: the chart sits in a card like every other one (owner's feedback), with no
 * inner padding: the chart document keeps its own 12px text gutter and draws the plot edge to
 * edge inside the card. */
.chart-section {
  padding: 0;
  overflow: hidden;
}

/* PWA-5.3: a fixed-aspect-ratio first-paint fallback (390x520, roughly the whole chart document
 * ratio) shown until the chart document's own height-reporting script (see app.js) posts its
 * real rendered height back -- at which point app.js sets an inline `height` that overrides
 * this ratio (an explicit height always wins over `aspect-ratio` per spec once both are
 * present). This two-step sizing exists specifically so the tooltip, which can render taller
 * than the base 420px-tall chart, is never clipped by a height frozen at the base chart's own
 * ratio alone -- verified by rendering, not reasoned about here (see tests/test_pwa_shell.py).
 */
.chart-frame {
  display: block;
  width: 100%;
  aspect-ratio: 390 / 520;
  border: 0;
  border-radius: 0;
  background: var(--shell-surface);
  transition: height 0.12s ease, opacity 0.12s ease;
  /* Starts invisible (see app.js's showChart/message handler): the fallback aspect-ratio box
   * above is correctly proportioned for the chart's own SVG, but the full document (title,
   * subtitle, legend) is taller than that box until the real-height postMessage arrives --
   * on a slow device that round trip has been measured up to ~1s, long enough for a real user
   * to see a moment of clipped content otherwise. The box still reserves its layout space
   * (opacity, not display:none/visibility:hidden), so nothing around it jumps when it appears.
   */
  opacity: 0;
}

.chart-frame--sized {
  opacity: 1;
}

@media (min-width: 700px) {
  main,
  .app-header,
  .update-banner {
    max-width: 720px;
  }
}

/* R-9.18: the Prices card (web/js/prices.js), styled as the Accounts section it follows. */
.prices-section {
  margin-top: 12px;
}

.prices-body p {
  margin: 0 0 8px;
  font-size: 13.5px;
}

.prices-status {
  color: var(--shell-ink-2);
}

.prices-received {
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--shell-accent-bg);
  color: var(--shell-ink);
  word-break: break-word;
}

.prices-missing {
  margin: 0 0 8px;
  padding-left: 18px;
  font-size: 12.5px;
  color: var(--shell-ink-2);
  word-break: break-word;
}

.prices-body .prices-hint {
  margin-top: 8px;
  font-size: 12.5px;
  color: var(--shell-muted);
}

.prices-paste {
  display: flex;
  gap: 6px;
  align-items: flex-start;
  margin-top: 4px;
}

.prices-paste-input {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--shell-border);
  background: var(--shell-page);
  color: var(--shell-ink);
  resize: vertical;
}
