:root {
      /* ── Primary Brand ── */
      --urban-red: #b22222;
      --urban-dark-red: #8b0000;
      --urban-bg-red: #700104;
      --uc-primary: #b22222;
      --uc-primary-dark: #8b0000;
      --uc-primary-darker: #7f1d1d;

      /* ── Accent / Gold ── */
      --uc-gold: #facc15;
      --uc-gold-dim: #d4a017;

      /* ── Neutrals ── */
      --urban-grey: #2c2c2c;
      --urban-light-grey: #e5e5e5;
      --urban-navy: #242d3c;
      --uc-surface: #ffffff;
      --uc-surface-soft: #f8f8f8;
      --uc-surface-muted: #f3f4f6;
      --uc-text: #111827;
      --uc-text-secondary: #4b5563;
      /* Darkened from #9ca3af (2.53:1) to meet WCAG AA large-text on white.
         Deliberate Classic change, owner decision Q8 — this is the "greyish,
         hard to read" text the theme work was opened for. */
      --uc-text-muted: #6b7280;
      --uc-border: #e5e7eb;
      --uc-border-strong: #111111;

      /* ── Semantic quads ──
         Each family ships four co-defined roles: base (solid fill), -surface
         (tinted background), -border (tinted edge), -text (readable on -surface).
         Themes override a whole family or none of it, so -text is always
         validated against -surface by the contrast gate. */
      /* Base stays #ef4444: `var(--uc-danger, #ef4444)` already has a live consumer
         in porch-canvas-editor.css, and Classic must not shift. The deeper solid
         reds (#b91c1c, #dc2626) are separate shades resolved in the manual pass. */
      --uc-danger: #ef4444;
      --uc-danger-surface: #fef2f2;
      --uc-danger-border: #fecaca;
      --uc-danger-text: #991b1b;

      --uc-success: #22c55e;
      --uc-success-surface: #ecfdf5;
      --uc-success-border: #bbf7d0;
      --uc-success-text: #166534;

      --uc-warning: #f59e0b;
      --uc-warning-surface: #fffbeb;
      --uc-warning-border: #fde68a;
      --uc-warning-text: #92400e;

      --uc-info: #3b82f6;
      --uc-info-surface: #eff6ff;
      --uc-info-border: #bfdbfe;
      --uc-info-text: #1d4ed8;

      /* ── Neutral ramps (palette layer) ──
         The product already leans on two full neutral scales. They are declared here
         so page CSS can reference them instead of hardcoding, and so a dark theme can
         invert the whole ramp in one place. Semantic roles above are still preferred;
         these exist because a ramp value is not always a role. */
      --uc-slate-50: #f8fafc;
      --uc-slate-100: #f1f5f9;
      --uc-slate-200: #e2e8f0;
      --uc-slate-300: #cbd5e1;
      --uc-slate-400: #94a3b8;
      --uc-slate-500: #64748b;
      --uc-slate-600: #475569;
      --uc-slate-700: #334155;
      --uc-slate-800: #1e293b;
      --uc-slate-900: #0f172a;

      --uc-gray-50: #f9fafb;
      --uc-gray-100: #f3f4f6;
      --uc-gray-200: #e5e7eb;
      --uc-gray-300: #d1d5db;
      --uc-gray-400: #9ca3af;
      --uc-gray-500: #6b7280;
      --uc-gray-600: #4b5563;
      --uc-gray-700: #374151;
      --uc-gray-800: #1f2937;
      --uc-gray-900: #111827;

      /* ── Structural roles ──
         The roles raw literals were standing in for. A fixed literal cannot
         respond to data-uc-theme; these can. */
      --uc-surface-raised: #ffffff;
      --uc-surface-sunken: #f3f4f6;
      --uc-surface-input: #ffffff;
      --uc-divider: #e5e7eb;
      /* Overlay alpha scale. One scrim value cannot cover veils, hairlines and
         elevation washes, and a fixed black is invisible over a dark surface. */
      --uc-scrim: rgba(0, 0, 0, 0.35);
      --uc-scrim-weak: rgba(0, 0, 0, 0.08);
      --uc-scrim-soft: rgba(0, 0, 0, 0.18);
      --uc-scrim-strong: rgba(0, 0, 0, 0.55);
      --uc-wash-weak: rgba(15, 23, 42, 0.06);
      --uc-wash-soft: rgba(15, 23, 42, 0.1);
      --uc-wash-strong: rgba(15, 23, 42, 0.18);
      /* Veils lighten. Scrims darken. Both are theme-invariant operations, so a
         veil stays light in every theme; only --uc-wash-* flips with the surface. */
      --uc-veil-weak: rgba(255, 255, 255, 0.08);
      --uc-veil-soft: rgba(255, 255, 255, 0.14);
      --uc-veil-strong: rgba(255, 255, 255, 0.22);
      /* Brand tints — theme-aware, so a red glow becomes a gold glow on Street. */
      --uc-primary-wash: rgba(178, 34, 34, 0.08);
      --uc-primary-wash-strong: rgba(178, 34, 34, 0.2);
      --uc-accent-wash: rgba(250, 204, 21, 0.2);
      --uc-accent-wash-strong: rgba(250, 204, 21, 0.4);
      /* Shadow ink only — the offset/blur geometry stays in --uc-shadow-*.
         Dark themes need a heavier ink because a soft black is invisible there. */
      --uc-shadow-ink-weak: rgba(0, 0, 0, 0.06);
      --uc-shadow-ink-soft: rgba(0, 0, 0, 0.12);
      --uc-shadow-ink-strong: rgba(0, 0, 0, 0.25);
      --uc-focus-ring: #b22222;
      --uc-state-hover: rgba(17, 24, 39, 0.04);
      --uc-state-active: rgba(17, 24, 39, 0.08);
      --uc-state-disabled: #9ca3af;
      /* Foreground for text sitting on --uc-primary. Street's primary is yellow,
         so a hardcoded #fff here measures 1.74:1 and fails WCAG AA. */
      --uc-text-on-primary: #ffffff;
      --uc-text-inverse: #ffffff;
      /* Gold/yellow fills stay bright in every theme — always use dark ink on them.
         Do not use --uc-text-on-primary (white) on gold; Heat measures ~1.3:1. */
      --uc-text-on-gold: #111111;
      /* Brand label ink on surfaces (not fills). Dark reds fail AA on Heat. */
      --uc-primary-ink: #7f1d1d;
      /* Always-dark shell chrome (app top bar, bottom tab bar, global footer).
         Distinct from --uc-gray-900: that ramp inverts on dark themes for text
         remapping, and will paint chrome white if reused as a background. */
      --uc-chrome: #111827;
      --uc-chrome-text: #f9fafb;
      /* Per-member chat tones. The hue is derived from the handle at runtime;
         lightness lives here so the same handle stays legible when the surface
         flips. Dark themes raise --uc-chat-tone-text-l and drop the fills. */
      --uc-chat-tone-sat: 70%;
      --uc-chat-tone-text-l: 30%;
      --uc-chat-tone-bg-l: 95%;
      --uc-chat-tone-border-l: 84%;
      --uc-chat-tone-accent-l: 60%;

      /* ── Games palette (modern-retro) ── */
      --uc-games-panel: #8b2616;
      --uc-games-header: #4d1108;
      --uc-games-base: #6b1e11;
      --uc-games-row: #7d3c33;
      --uc-games-accent: #a32e1c;

      /* ── Typography Scale ── */
      --uc-text-hero: 1.5rem;
      --uc-text-h1: 1.25rem;
      --uc-text-h2: 1.125rem;
      --uc-text-body: 0.875rem;
      --uc-text-small: 0.75rem;
      --uc-text-xs: 0.625rem;

      /* ── Spacing Scale ── */
      --uc-space-1: 4px;
      --uc-space-2: 8px;
      --uc-space-3: 12px;
      --uc-space-4: 16px;
      --uc-space-6: 24px;
      --uc-space-8: 32px;

      /* ── Elevation ── */
      --uc-dna-shadow-hard: 6px 6px 0 rgba(0, 0, 0, 0.32);
      --uc-dna-shadow-soft: 3px 3px 0 rgba(0, 0, 0, 0.2);
      --uc-shadow-soft: 3px 3px 0 rgba(0, 0, 0, 0.2);
      --uc-shadow-hard: 6px 6px 0 rgba(0, 0, 0, 0.32);
      --uc-shadow-elevated: 0 12px 24px rgba(15, 23, 42, 0.14);

      /* ── Border Radius ── */
      --uc-dna-card-radius: 8px;
      --uc-dna-chip-radius: 4px;
      --uc-radius-sm: 4px;
      --uc-radius-md: 8px;
      --uc-radius-lg: 12px;
      --uc-radius-xl: 16px;
      --uc-radius-full: 9999px;

      /* ── Animation ── */
      --uc-duration-fast: 120ms;
      --uc-duration-normal: 200ms;
      --uc-duration-slow: 400ms;
      --uc-easing-default: cubic-bezier(0.4, 0, 0.2, 1);
      --uc-easing-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);

      /* ── Legacy DNA primitives (preserve compat) ── */
      --uc-dna-border-strong: #111;
      --uc-dna-header-red: rgb(178, 34, 34);
      --uc-dna-card-surface: #fff;
      --uc-dna-card-surface-soft: #f8f8f8;
    }
