/* ========== Design Tokens ========== */

:root {
  /* Spacing */
  --spacing-xxs: 2px;
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-smd: 12px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --spacing-xl: 32px;
  --spacing-xxl: 48px;

  /* Motion (mirrored by `Motion` in styles/index.ts; see /design/motion) */
  --motion-duration-fast: 120ms;
  --motion-duration-base: 200ms;
  --motion-duration-slow: 400ms;
  --motion-duration-pulse: 1200ms;
  --motion-ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --motion-ease-out: cubic-bezier(0.2, 0, 0, 1);
  --motion-reveal-distance: 4px;

  /* Border Radius */
  --radius-none: 0;
  --radius-interactive: 4px;
  --radius-media: 4px;
  --radius-full: 9999px;

  /* Typography */
  --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-mono: "Fragment Mono", "Courier New", monospace;

  /* Underlined links (Decision 2026-10): a hairline sitting 4px under the
     baseline, in the text colour at 60%. Mirrors `Underline` in styles/index.ts;
     keep the two in sync. */
  --underline-thickness: 1px;
  --underline-offset: 4px;
  --underline-color: color-mix(in srgb, currentColor 60%, transparent);
}

/* ========== Dark Mode ========== */
/* Values mirror Colors.dark in styles/index.ts — keep the two in sync. */

.dark {
  --color-primary: #f5f5f5;
  --color-background: #0e0e0e;
  --color-bg-elevated: #1a1a1a;
  --color-bg-muted: #222222;
  --color-bg-inset: #161616;
  --color-text: #f5f5f5;
  --color-text-secondary: #a0a0a0;
  --color-text-tertiary: #737373;
  --color-text-disabled: #3a3a3a;
  --color-text-inverse: #171717;
  /* Legacy generic alias; mirrors Colors.dark.border in styles/index.ts. */
  --color-border: #262626;
  --color-border-subtle: #222222;
  --color-border-default: #2e2e2e;
  --color-border-strong: #525252;
  --color-button: #f5f5f5;
  --color-button-hover: #e5e5e5;
  --color-button-press: #cecece;
  --color-button-disabled: #3a3a3a;
  --color-button-text: #171717;
  --color-button-text-disabled: #525252;
  --color-success: #50ca79;
  --color-error: #ff6b7a;
  --color-error-bg: #3a1418;
  --color-error-bg-hover: #4a1c22;
  --color-error-bg-press: #5a2028;
  --color-warning: #f0a050;
  --color-warning-bg: #3a2410;
  --color-info: #7faaff;
  --color-icon: #7faaff;
  --color-modal-border: #3a3a3a;
  --color-modal-blur: rgba(0, 0, 0, 0.4);
  --color-input-bg: var(--color-background);
  --color-input-border: var(--color-border);
  /* Gray scale (inverted: gray-25 ≈ page bg, gray-1000 = strongest contrast) */
  --gray-00: #000000;
  --gray-25: #080909;
  --gray-50: #0f0f0f;
  --gray-100: #181818;
  --gray-200: #252525;
  --gray-300: #353535;
  --gray-400: #454545;
  --gray-500: #808080;
  --gray-600: #a0a0a0;
  --gray-700: #b0b0b0;
  --gray-800: #cecece;
  --gray-900: #e3e3e3;
  --gray-925: #f5f5f5;
  --gray-950: #fafafa;
  --gray-1000: #ffffff;
}

/* ========== Light Mode ========== */
/* Values mirror Colors.light in styles/index.ts — keep the two in sync. */

.light {
  --color-primary: #181818;
  --color-background: #fafafa;
  --color-bg-elevated: #ffffff;
  --color-bg-muted: #ededed;
  --color-bg-inset: #f5f5f5;
  --color-text: #181818;
  --color-text-secondary: #808080;
  --color-text-tertiary: #a0a0a0;
  --color-text-disabled: #cecece;
  --color-text-inverse: #fafafa;
  /* Legacy generic alias; mirrors Colors.light.border in styles/index.ts. */
  --color-border: #cecece;
  --color-border-subtle: #cecece;
  --color-border-default: #a0a0a0;
  --color-border-strong: #808080;
  --color-button: #181818;
  --color-button-hover: #2e2926;
  --color-button-press: #141110;
  --color-button-disabled: #a0a0a0;
  --color-button-text: #fafafa;
  --color-button-text-disabled: #e3e3e3;
  --color-success: #50ca79;
  --color-error: #c8102e;
  --color-error-bg: #ffeaea;
  --color-error-bg-hover: #ffd5d5;
  --color-error-bg-press: #ffbdbd;
  --color-warning: #c26700;
  --color-warning-bg: #fff6eb;
  --color-info: #4f8ef7;
  --color-icon: #4f8ef7;
  --color-modal-border: #cecece;
  --color-modal-blur: rgba(255, 255, 255, 0.6);
  --color-input-bg: var(--color-background);
  --color-input-border: var(--color-border);
  /* Gray scale (gray-25 ≈ page bg, gray-1000 = strongest contrast) */
  --gray-00: #ffffff;
  --gray-25: #fafafa;
  --gray-50: #ededed;
  --gray-100: #e3e3e3;
  --gray-200: #cecece;
  --gray-300: #b0b0b0;
  --gray-400: #a0a0a0;
  --gray-500: #808080;
  --gray-600: #545454;
  --gray-700: #353535;
  --gray-800: #252525;
  --gray-900: #181818;
  --gray-925: #0f0f0f;
  --gray-950: #090909;
  --gray-1000: #000000;
}

/* ========== Base Styles ========== */

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* React Native compatibility: Views default to flex column */
div {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
}

/* Inline elements should not inherit flex layout */
span,
a,
strong,
em,
code,
label {
  display: inline;
}

/* Text elements should not inherit flex */
p,
h1,
h2,
h3,
h4,
h5,
h6 {
  display: block;
}

html,
body,
#root {
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  font-feature-settings: "ss07" 1; /* Inter square punctuation; ignored by Fragment Mono */
  font-optical-sizing: auto;
  text-rendering: optimizeLegibility;
  color: var(--color-text);
  background-color: var(--color-background);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* Shared onboarding privacy controls. The input remains a native range for
   keyboard and assistive-technology behavior; only its presentation is custom. */
.near-privacy-slider-input {
  --near-privacy-slider-progress: 0%;
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: 28px;
  margin: 0;
  background: transparent;
  cursor: pointer;
  touch-action: pan-y;
}

.near-privacy-slider-input::-webkit-slider-runnable-track {
  height: 2px;
  border-radius: var(--radius-full);
  background: linear-gradient(
    to right,
    var(--color-primary) 0 var(--near-privacy-slider-progress),
    var(--color-border-default) var(--near-privacy-slider-progress) 100%
  );
}

.near-privacy-slider-input::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 22px;
  height: 22px;
  margin-top: -10px;
  border: 2px solid var(--color-primary);
  border-radius: 50%;
  background: var(--color-background);
}

.near-privacy-slider-input::-moz-range-track {
  height: 2px;
  border-radius: var(--radius-full);
  background: var(--color-border-default);
}

.near-privacy-slider-input::-moz-range-progress {
  height: 2px;
  border-radius: var(--radius-full);
  background: var(--color-primary);
}

.near-privacy-slider-input::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border: 2px solid var(--color-primary);
  border-radius: 50%;
  background: var(--color-background);
}

/* ─────────────────────────────────────────────────────────────────────────
   App shell + the single home for safe-area insets. Authoritative copy
   (mirrored in index.html to avoid a pre-bundle flash — keep the two in sync).

   #root is a fixed shell sized to the visible viewport (dvh). The status bar is
   opaque (`default` in index.html, not black-translucent), which fixes the iOS
   standalone bottom-band bug AND makes dvh == the usable screen. #root insets
   the TOP + SIDES only. The BOTTOM is edge-to-edge so scroll lists fill the
   whole viewport (padding here would clip them, cutting off the last row).
   Bottom safe-area clearance lives on the fixed-CTA footer classes
   (.bottom, .screen-cta, .screen-footer) — the only elements that must clear
   the home indicator.
   ───────────────────────────────────────────────────────────────────────── */
html,
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  overscroll-behavior: none;
}
#root {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh; /* fallback for engines without dvh */
  height: 100dvh; /* the visible viewport (status bar is opaque `default`) */
  box-sizing: border-box;
  overflow: hidden;
  padding-top: env(safe-area-inset-top);
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

/* ========== Typography ========== */

.h1 {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 28px;
  line-height: 36px;
  letter-spacing: -0.027em;
  color: var(--color-text);
}

.h2 {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 20px;
  line-height: 24px;
  letter-spacing: -0.05em;
  color: var(--color-text);
}

.h3 {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 20px;
  line-height: 28px;
  color: var(--color-text);
}

.body {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 16px;
  line-height: 24px;
  color: var(--color-text);
}

.body-small {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 14px;
  line-height: 20px;
  color: var(--color-text);
}

.body-large {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 20px;
  line-height: 26px;
  color: var(--color-text);
}

.body-medium {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 16px;
  line-height: 24px;
  color: var(--color-text);
}

.body-secondary {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 16px;
  line-height: 24px;
  color: var(--color-text-secondary);
}

.caption {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 12px;
  line-height: 16px;
  color: var(--color-text-secondary);
}

.label-caps {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text);
}

.monospace {
  font-family: var(--font-mono);
}

.mono-md {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 16px;
  line-height: 22px;
}

.mono-sm {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 14px;
  line-height: 18px;
}

.mono-xs {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 12px;
  line-height: 17px;
}

.italic {
  font-style: italic;
}
.bold {
  font-weight: 700;
}
.underline {
  text-decoration-line: underline;
  text-decoration-thickness: var(--underline-thickness);
  text-underline-offset: var(--underline-offset);
  text-decoration-color: var(--underline-color);
  text-decoration-skip-ink: auto;
}
.centered-text {
  text-align: center;
}
.left-text {
  text-align: left;
}

/* ========== Layout ========== */

.screen {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 100%;
  height: 100%;
  overflow-y: auto;
  overscroll-behavior: none;
  background-color: var(--color-background);
}

.container {
  display: flex;
  flex-direction: column;
  flex: 1;
  background-color: var(--color-background);
}

.container-spaced {
  display: flex;
  flex-direction: column;
  flex: 1;
  justify-content: space-between;
  background-color: var(--color-background);
}

/* Insets handled once at #root (see index.html) — no safe-area here. */
.app-header {
  height: 44px;
  padding: 0 16px;
}

.centered-content {
  display: flex;
  flex-direction: column;
  flex: 1;
  width: 100%;
  max-width: 600px;
}

/* ── Screen shell (onboarding / host / invite screens) ───────────────────── */
/* Every screen is ONE scrollable flex column — see OnboardingShell. The CTA
   wrapper is the last child of the column and is `position: sticky; bottom: 0`
   (Decision 2026-10): it stays last in flow, `margin-top: auto` anchors it to
   the bottom of the visible area when content is short, and when content is
   taller than the screen it sticks to the bottom edge while the content scrolls
   beneath its 40px gradient. Because it is sticky rather than fixed, no content
   inset is needed: at the end of the scroll the content sits above the button.
   The CTA wrapper's padding is identical on every screen so the button sits the
   same distance from the bottom on every step. */
.screen-scroll {
  flex: 1;
  min-height: 0;
  width: 100%;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  overscroll-behavior: none;
}

/* Content between the header and the CTA. Grows so screens can vertically
   center or stretch their own content; never shrinks below its content. */
.screen-body {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  width: 100%;
  min-width: 0;
}

/* CTA wrapper: last in flow, auto top margin, sticky to the bottom of the
   scroll container. The 40px fade is the wrapper's own top padding, painted
   as a gradient from transparent to the page background, so it can only ever
   sit over content that is scrolling beneath the pinned wrapper: when content
   fits, or at the end of the scroll, the wrapper is in flow and the padding
   covers empty background, so the fade is invisible and the last line sits
   40px clear of the button. The cue leaves continuously as the content slides
   out from under it. No pseudo-element, no scroll-driven animation, no JS, so
   it behaves the same on browsers without scroll timelines (older iOS Safari).
   Bottom padding is the pre-sticky 32px. One full-height button per wrapper;
   a secondary action is a text link under it (ctaSecondaryLinkStyle), never
   a second bordered button. #root is edge-to-edge at the bottom, so this
   wrapper clears the home indicator itself. */
.screen-cta {
  position: sticky;
  bottom: 0;
  z-index: 1;
  margin-top: auto;
  flex-shrink: 0;
  width: 100%;
  box-sizing: border-box;
  padding-top: 40px;
  padding-bottom: max(32px, env(safe-area-inset-bottom));
  background: linear-gradient(to bottom, transparent, var(--color-background) 40px);
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.screen-cta--centered {
  align-items: center;
}

/* Fixed-height slot under the primary button for a quiet text action (Skip,
   Not now, a caption link). Always the same height, empty or not, so the
   primary button never moves between screens. */
.screen-cta__secondary {
  width: 100%;
  min-height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ── Sheet-modal layout (wallet Send / Fund / recovery sheets) ─────────────
   `.modal--sheet` is a bounded card, so these keep the content-scrolls /
   footer-stays split inside it. Screens use the shell above instead. */
.screen-content {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.screen-footer {
  flex-shrink: 0;
  padding: 16px;
  padding-bottom: max(32px, env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Screen heading block (wizard / fund flow) */
.screen-heading {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
/* ─────────────────────────────────────────────────────────────────────────── */

/* Member-list grid: 2 columns on narrow viewports, 3 at >= 480px. */
@media (min-width: 480px) {
  .member-chip-grid {
    grid-template-columns: repeat(3, 1fr) !important;
  }
}

.center {
  display: flex;
  align-items: center;
  justify-content: center;
}

.bottom {
  justify-content: flex-end;
  gap: 8px;
  padding: 20px;
  /* fixed CTA container — clear the home indicator (#root is edge-to-edge) */
  padding-bottom: max(20px, env(safe-area-inset-bottom));
}

.invite-contents {
  flex: 1;
  gap: 32px;
  padding-top: 74px;
  padding-left: 20px;
  padding-right: 20px;
  padding-bottom: 32px;
  align-items: center;
}

.submit-button {
  margin-bottom: 24px;
}

/* Login screens in a bottom-CTA frame (/front, the invite byop sign-in): the
   CTA is the last in-flow element, like OnboardingShell's, and the quiet links
   under it (12-word / Master Ticket / Passkey / Switch urbit, the passkey
   lookup) hang out-of-flow inside the frame's 64px bottom padding, in the same
   12px-gap / 20px slot every onboarding screen reserves under its CTA. The
   button then sits level with every other screen's. Embedded (modal) logins
   keep the in-flow layout. */
.login-flush .submit-button {
  margin-bottom: 0;
}
.login-flush .login-cta-aside {
  position: relative;
  height: 0;
  /* cancel the 8px flex gap .bottom puts before this zero-height anchor */
  margin-top: -8px;
}
.login-flush .login-cta-aside__row {
  position: absolute;
  top: 20px; /* 12px under the CTA, plus the 8px the anchor is pulled up */
  left: 0;
  right: 0;
  margin: 0 !important;
  min-height: 20px;
}

.bottom-buttons {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding-bottom: 20px;
  padding-left: 20px;
  padding-right: 20px;
}

.bottom-actions {
  width: 100%;
  align-items: center;
  gap: 24px;
  min-height: 82px;
  justify-content: center;
}

.header {
  position: relative;
  justify-content: space-between;
  align-items: center;
  padding-top: var(--spacing-lg); /* top safe-area handled by #root */
  padding-left: var(--spacing-lg);
  padding-right: var(--spacing-lg);
}

.bordered {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-none);
}

.list-item {
  padding: var(--spacing-md);
  border-bottom: 1px solid var(--color-border);
}

/* ========== Buttons ========== */

.btn {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-interactive);
  min-height: 48px;
  padding: 0 16px;
  cursor: pointer;
  background: transparent;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 20px;
  line-height: 1.3;
  color: var(--color-text-secondary);
  transition:
    border-color 0.12s,
    color 0.12s,
    opacity 0.12s;
}

.btn--elevated {
  background: var(--color-bg-elevated);
  border-color: var(--color-border);
}

.btn:hover:not(:disabled) {
  border-color: var(--color-border-subtle);
  color: var(--color-text-tertiary);
}

.btn:active:not(:disabled) {
  border-color: var(--color-border-strong);
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn-salient {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: var(--radius-interactive);
  min-height: 48px;
  padding: 0 16px;
  cursor: pointer;
  background-color: var(--color-button);
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 20px;
  line-height: 1.3;
  color: var(--color-button-text);
  transition: background-color 0.12s;
}

.btn-salient:hover:not(:disabled) {
  background-color: var(--color-button-hover);
}

.btn-salient:active:not(:disabled) {
  background-color: var(--color-button-press);
}

.btn-salient:disabled {
  background-color: var(--color-button-disabled);
  color: var(--color-button-text-disabled);
  cursor: not-allowed;
}

.btn-cancel {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-error);
  border-radius: var(--radius-interactive);
  min-height: 48px;
  padding: 0 16px;
  cursor: pointer;
  background-color: var(--color-error-bg);
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 20px;
  line-height: 1.3;
  color: var(--color-error);
  transition: background-color 0.12s;
}

.btn-cancel:hover:not(:disabled) {
  background-color: var(--color-error-bg-hover);
}

.btn-cancel:active:not(:disabled) {
  background-color: var(--color-error-bg-press);
}

.btn-cancel:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ========== Inputs ========== */

.input {
  width: 100%;
  height: 48px;
  padding: 8px 16px;
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 400;
  line-height: 21px;
  color: var(--color-text);
  background: var(--color-input-bg);
  border: 1px solid var(--color-input-border);
  border-radius: var(--radius-interactive);
  outline: none;
  transition: border-color 0.12s;
}

.input::placeholder {
  color: var(--color-text-tertiary);
}

.amount-input::placeholder {
  color: var(--color-text-tertiary);
}

.input:focus {
  border-color: var(--color-text);
}

.input.is-error {
  border-color: var(--color-error);
  background: var(--color-error-bg);
}

.input.is-error::placeholder {
  color: var(--color-text-tertiary);
}

/* Modifier: elevated white surface — the default for every input on a page
   background. The thin border is kept for focus and error states only. */
.input--elevated {
  background: var(--color-bg-elevated);
  border-color: transparent;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

.input--elevated:focus {
  border-color: var(--color-text);
}

.input--elevated.is-error {
  border-color: var(--color-error);
  background: var(--color-error-bg);
}

/* Modifier: input placed INSIDE an elevated card / CardGroup / modal. White on
   white would vanish, so it takes the muted fill and drops the shadow. Combine
   with .input--elevated (or use it alone with .input). */
.input--on-card,
.input--elevated.input--on-card {
  background: var(--color-bg-muted);
  border-color: transparent;
  box-shadow: none;
}

.input--on-card:focus,
.input--elevated.input--on-card:focus {
  border-color: var(--color-text);
}

.input--on-card.is-error,
.input--elevated.input--on-card.is-error {
  border-color: var(--color-error);
  background: var(--color-error-bg);
}

/* Modifier: Fragment Mono for ticket / @p / address fields */
.input--mono {
  font-family: var(--font-mono);
  font-size: 16px;
  line-height: 22px;
}

/* Modifier: tall textarea */
.input--lg {
  height: 96px;
  resize: vertical;
}

/* Labelled input group */
.input-group {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
}

.input-label {
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 400;
  line-height: 21px;
  color: var(--color-text-secondary);
}

.input-label.is-error {
  color: var(--color-error);
}

input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border: 1px solid rgba(128, 128, 128, 0.35);
  border-radius: var(--radius-interactive);
  background-color: var(--color-border);
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
}

input[type="checkbox"]:checked {
  background-color: var(--color-text);
  border-color: var(--color-text);
}

input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 45%;
  width: 25%;
  height: 55%;
  border: solid var(--color-background);
  border-width: 0 2px 2px 0;
  transform: translate(-50%, -50%) rotate(45deg);
}

/* ========== Status ========== */

.success-text {
  color: var(--color-success);
}
.error-text {
  color: var(--color-error);
}
.warning-text {
  color: var(--color-warning);
}
.info-text {
  color: var(--color-info);
}

/* ========== Components ========== */

/* Fixed overlay — lives outside #root, so it fills the physical screen (lvh)
   and applies the same safe-area inset boundary itself. Modal content centered
   within the safe zone; no per-modal env() needed. */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh; /* fallback */
  height: 100lvh;
  box-sizing: border-box;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom)
    env(safe-area-inset-left);
  background-color: var(--color-modal-blur);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.modal {
  background-color: var(--color-bg-elevated);
  border: 1px solid var(--color-modal-border);
  border-radius: var(--radius-none);
  padding: var(--spacing-lg); /* 24px — matches Figma canonical */
  margin: var(--spacing-xl);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 24px;
  box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.08);
}

/* /design/components Modal preview: the overlay is scoped to the preview box
   by a `transform` containing block, but `height: 100lvh` would still size it
   to the viewport and center the card outside the box. Fill the box instead. */
.design-modal-preview .modal-overlay {
  position: absolute;
  height: 100%;
  padding: 0;
}

/* Sheet modal — multi-step flows (Send, Receive, wizard steps).
 *
 * Mobile (default): fills the full viewport — `flex: 1` expands inside
 * `.modal-overlay → .centered-content`, both of which are `flex: 1`.
 * `overflow: hidden` lets `.screen-content` / `.screen-footer` handle
 * internal scrolling without a double scroll-container.
 *
 * Tablet / desktop (≥ 640 px): reverts to a floating centered card.
 * Height is capped at 90dvh; internal content still scrolls via
 * `.screen-content { overflow-y: auto }`.
 *
 * Usage: className="modal modal--sheet"
 * Never add margin, width, maxWidth, or minHeight as inline styles.
 */
.modal--sheet {
  width: 100%;
  max-width: 480px;
  margin: 0;
  flex: 1;
  min-height: 0;
  max-height: calc(100dvh - 48px);
  overflow: hidden;
}

@media (min-width: 640px) {
  .modal--sheet {
    flex: none;
    min-height: 480px;
    max-height: calc(100dvh - 48px);
  }
}

.divider {
  height: 1px;
  background-color: var(--color-border);
  margin: var(--spacing-md) 0;
}

.scroll-container {
  overflow-y: auto;
  flex: 1;
}

/* ========== Misc ========== */

.reversed-color {
  color: var(--color-background);
}

button {
  cursor: pointer;
  border: none;
  background: none;
  padding: 0;
  font-family: inherit;
}

/* ui/Row — interactive rows get a muted fill on hover/press. Hover is gated on
 * a real pointer so touch screens don't keep a row highlighted after a tap. */
.ui-row--interactive {
  cursor: pointer;
  background: none;
  color: inherit;
}
.ui-row--interactive:active {
  background-color: var(--color-bg-muted);
}
.ui-row--interactive:focus-visible {
  outline: 2px solid var(--color-text);
  outline-offset: -2px;
}
@media (hover: hover) {
  .ui-row--interactive:hover {
    background-color: var(--color-bg-muted);
  }
}

.header-tap-button {
  -webkit-tap-highlight-color: transparent;
}

.header-tap-button:focus {
  outline: none;
}

.header-tap-button:focus-visible {
  outline: 2px solid var(--color-text-tertiary);
  outline-offset: 2px;
  border-radius: var(--radius-interactive);
}

/* ========== Tab Bar ========== */

.tab-bar {
  display: flex;
  flex-direction: row;
  height: 42px;
  background-color: var(--color-background);
  flex-shrink: 0;
  position: relative;
}

/* full-width baseline — a pseudo-element (not a border) so it shares the
   same absolute-positioning coordinate space as the sliding indicator
   below and the two lines render flush instead of stacked. Height matches
   the indicator's so the active segment reads as *part of* the line
   rather than a thicker bar poking out above a thinner one. */
.tab-bar::before {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--color-border);
}

/* sliding indicator — position driven by --tab-ix / --tab-iw set via JS.
   Painted after ::before, so it overlaps the baseline where active. */
.tab-bar::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: var(--tab-ix, 0px);
  width: var(--tab-iw, 0px);
  height: 1px;
  background: var(--color-text);
  transition:
    left var(--motion-duration-base) var(--motion-ease-standard),
    width var(--motion-duration-base) var(--motion-ease-standard);
}

/* suppress animation on first render */
.tab-bar.tab-bar--init::after {
  transition: none;
}
@media (prefers-reduced-motion: reduce) {
  .tab-bar::after {
    transition: none;
  }
}

/* ========== Secondary Tab Bar ========== */

.secondary-tab-bar {
  display: flex;
  flex-direction: row;
  height: 42px;
  background-color: var(--color-background);
  flex-shrink: 0;
  position: relative;
}

/* full-width baseline — see .tab-bar::before for why this is a
   pseudo-element rather than a border, and why the height matches the
   indicator's (1px). */
.secondary-tab-bar::before {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--color-border);
}

.secondary-tab-bar::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: var(--secondary-tab-ix, 0px);
  width: var(--secondary-tab-iw, 0px);
  height: 1px;
  background: var(--color-text);
  transition:
    left var(--motion-duration-base) var(--motion-ease-standard),
    width var(--motion-duration-base) var(--motion-ease-standard);
}

.secondary-tab-bar.secondary-tab-bar--init::after {
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .secondary-tab-bar::after {
    transition: none;
  }
}

/* ========== Alert ========== */

.alert {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 8px;
  padding: 12px;
  border-radius: var(--radius-none);
  border: 1px solid transparent;
  font-family: var(--font-sans);
}

.alert__icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  display: flex;
}

.alert__body {
  font-size: 14px;
  line-height: 18px;
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
}

.alert--warning {
  background: var(--color-warning-bg);
  border-color: var(--color-warning);
  color: var(--color-warning);
}

.alert--error {
  background: var(--color-error-bg);
  border-color: var(--color-error);
  color: var(--color-error);
}

.alert--info {
  background: var(--color-bg-muted);
  color: var(--color-text-secondary);
}

/* ========== Settings info rows (responsive key/value stacking) ========== */

/* Key on the left, value on the right on comfortable widths; on narrow
   screens the value drops onto its own line below the key so long values
   (hostnames, etc.) don't collide with or crush the label. */
.info-row {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.info-row__value {
  text-align: right;
  overflow-wrap: anywhere;
}
@media (max-width: 480px) {
  .info-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
  }
  .info-row__value {
    text-align: left;
    max-width: 100%;
  }
}

/* ========== Animations ========== */

@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@keyframes pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.4;
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* ========== Motion patterns (documented at /design/motion) ========== */

@keyframes reveal-up {
  from {
    opacity: 0;
    transform: translateY(var(--motion-reveal-distance));
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* Fade transition: the leaving view fades out, the arriving view fades in. */
.motion-fade-out {
  opacity: 0;
  transition: opacity var(--motion-duration-fast) var(--motion-ease-out);
}
.motion-fade-in {
  animation: fade-in var(--motion-duration-base) var(--motion-ease-out) both;
}

/* Route changes that navigate with `viewTransition: true` follow the same
   rule: the old screen is dropped at once and the new one fades in over the
   page colour, so two titles never overprint mid-transition. */
::view-transition {
  background-color: var(--color-background);
}
::view-transition-old(root) {
  display: none;
}
::view-transition-new(root) {
  animation: fade-in var(--motion-duration-base) var(--motion-ease-out) both;
  mix-blend-mode: normal;
}

/* Step reveal: a new line or block arrives with a 4px rise. */
.motion-reveal {
  animation: reveal-up var(--motion-duration-slow) var(--motion-ease-out) both;
}

/* Settle in place: state changes recolor or fade in the same box, no movement. */
.motion-settle {
  transition:
    color var(--motion-duration-slow) var(--motion-ease-standard),
    background-color var(--motion-duration-slow) var(--motion-ease-standard),
    opacity var(--motion-duration-slow) var(--motion-ease-standard);
}

/* Button loading: the verb is followed by three dots that fade in one after
   another (opacity only, no layout shift: the dots always occupy their space). */
@keyframes btn-ellipsis {
  0%,
  20% {
    opacity: 0;
  }
  40%,
  100% {
    opacity: 1;
  }
}
.btn-ellipsis > span {
  animation: btn-ellipsis var(--motion-duration-pulse) var(--motion-ease-standard) infinite both;
}
.btn-ellipsis > span:nth-child(2) {
  animation-delay: calc(var(--motion-duration-pulse) / 6);
}
.btn-ellipsis > span:nth-child(3) {
  animation-delay: calc(var(--motion-duration-pulse) / 3);
}

@media (prefers-reduced-motion: reduce) {
  .btn-ellipsis > span {
    animation: none;
    opacity: 1;
  }
  .motion-fade-out,
  .motion-fade-in,
  .motion-reveal,
  .motion-settle {
    animation: none;
    transition: none;
  }
  ::view-transition-new(root) {
    animation: none;
  }
}
