:root {
  /* PWE / Paradise family identity. Functional colours below remain semantic
     UI tokens and must not be replaced by brand amber. */
  /* Golden-ratio layout contract. Phi is used for hierarchy rather than
     decoration: 61.8/38.2 splits, Fibonacci spacing, and restrained motion. */
  --ui-phi: 1.618;
  --ui-golden-major: 61.8%;
  --ui-golden-minor: 38.2%;
  --ui-golden-columns: minmax(0, 1.618fr) minmax(0, 1fr);
  --ui-golden-columns-reverse: minmax(0, 1fr) minmax(0, 1.618fr);
  --ui-content-max: 89rem;
  --ui-reading-measure: 55ch;
  --ui-space-1: 5px;
  --ui-space-2: 8px;
  --ui-space-3: 13px;
  --ui-space-4: 21px;
  --ui-space-5: 34px;
  --ui-space-6: 55px;
  --ui-space-7: 89px;
  --ui-type-xs: 13px;
  --ui-type-sm: 16px;
  --ui-type-md: 21px;
  --ui-type-lg: 34px;
  --ui-type-xl: 55px;
  --ui-duration-exit: 144ms;
  --ui-duration-enter: 233ms;
  --ui-touch-target: 44px;
  --ui-control-height: 46px;
  --ui-touch-gap: 8px;
  --ui-scrim: rgba(15, 23, 42, .60);
  --pwe-family-navy: #0e1729;
  --pwe-family-navy-raised: #16233d;
  --pwe-family-amber: #f5b335;
  --pwe-family-amber-text: #a16207;
  --pwe-warm-paper: #f7f5f2;
  --ui-primary: #2563eb;
  --ui-secondary: #0f766e;
  --ui-accent: #f59e0b;
  --ui-bg: #f8fafc;
  --ui-surface: #ffffff;
  --ui-text: #0f172a;
  --ui-muted: #64748b;
  --ui-border: #e2e8f0;
  --ui-error: #dc2626;
  --ui-success: #16a34a;
  /* brand-system.css has referenced --ui-warning since it was written, and the
     name was never defined here — so `var(--ui-warning, #5B421F)` always took
     its fallback and a literal shipped wearing a token's clothes. The value is
     the amber that already clears 4.9:1 on this surface (it is the same one
     --pwe-family-amber-text carries); the point of defining it is that the
     reference now resolves and moves when the palette moves. */
  --ui-warning: #a16207;
  --ui-radius-control: 8px;
  --ui-radius-card: 13px;
  --ui-radius-sheet: 21px;
  --ui-focus: 0 0 0 3px rgba(37, 99, 235, 0.28);
}

/* `summary` and `[tabindex]` were missing: a <details> toggle and any div
   made focusable by hand are both Tab stops that had no focus style at all.
   Still :where(), so a component can override without a specificity war —
   but a component that overrides it has to put a ring back. */
:where(button, a, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 2px;
  box-shadow: var(--ui-focus);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* One carve-out, and it is not cosmetic. `animation-iteration-count: 1`
     stops every looping animation after a single pass, which turns a busy
     indicator into a frozen glyph — the CMS boot spinner sits beside
     "连接中…" and is the only thing distinguishing "connecting" from "hung".
     Reduce-motion is about vestibular triggers, and the trigger there is the
     rotation, not the fact of animating: so the rotation is replaced by an
     opacity pulse rather than removed. `[data-motion="essential"]` is the
     hook for anything else that means "still working". */
  .sp, [role="progressbar"], [data-motion="essential"] {
    animation: essential-pulse 1.2s ease-in-out infinite !important;
    animation-iteration-count: infinite !important;
  }
}

@keyframes essential-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }

/* ── 主从分栏，只在放得下的时候才分 ────────────────────────────────
 *
 * φ 是给「宽屏上确实有主次」的版面用的。手机上没有两栏的余地：把 375px
 * 按 1:1.618 切开，次栏剩 143px —— 日期框溢出、表格被截断、按钮换行成
 * 一列。这不是审美问题，是这一版筛选栏落到账单页时当场看见的。
 *
 * 面板原本各自写内联 style 调用这个令牌，于是每个面板都得自己记得加断点。
 * 收成一个类：默认单栏堆叠，md 以上才启用 φ。AGENTS.md §8 那条「不许把 φ
 * 用在平级控件上」不受影响 —— 这里是主从，不是平级。
 */
.ui-golden-split {
  display: grid;
  gap: var(--ui-space-3, 13px);
  align-items: start;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 768px) {
  .ui-golden-split {
    grid-template-columns: var(--ui-golden-columns-reverse);
  }
}

/* ── Glass, and what happens when it is not available ──────────────────────
 *
 * 28 surfaces in this product paint a semi-transparent ground and blur what
 * is behind it — sticky navigation, the totop button, sheet headers, the
 * mobile submit bar. Not one of them had a fallback: no `@supports`, no
 * `prefers-reduced-transparency`, no `prefers-contrast`, zero occurrences of
 * any of the three repo-wide.
 *
 * Two ways that breaks. A browser without backdrop-filter keeps the
 * translucent ground and no blur, so the page scrolls visibly through the
 * navigation bar and its labels sit on moving text. And a person who has
 * turned Reduce Transparency on has said, in the operating system, that this
 * exact effect is the thing they cannot read — Windows contrast themes do the
 * same. Both want the same answer: put the ground back and drop the blur.
 *
 * `--glass-opacity` is the hook. Pages mix their own surface colour with it
 * rather than writing 88% / 90% / 92% / 94% / 96% by hand, which is what five
 * different files were doing for one visual idea.
 */
:root {
  --glass-opacity: 88%;          /* chrome that floats over content */
  --glass-opacity-strong: 94%;   /* sheets and bars that carry controls */
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
  :root { --glass-opacity: 100%; --glass-opacity-strong: 100%; }
  *, *::before, *::after {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root { --glass-opacity: 100%; --glass-opacity-strong: 100%; }
}
