:root {
  color-scheme: dark;
  font-family:
    "Geist Pixel", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  background: var(--bg);
  color: var(--text);
  font-synthesis: none;
  text-rendering: geometricPrecision;
  -webkit-font-smoothing: antialiased;
  --bg: #0b0b0c;
  --panel: rgba(18, 18, 20, 0.78);
  --panel-2: rgba(255, 255, 255, 0.055);
  --panel-3: rgba(255, 255, 255, 0.09);
  --field: rgba(9, 9, 11, 0.82);
  --border: rgba(255, 255, 255, 0.13);
  --border-strong: rgba(255, 255, 255, 0.22);
  --text: #f6f2ea;
  --muted: #a8a39b;
  /* WCAG AA contrast against --bg (#0b0b0c) — bumped from #78736c (3.8:1)
     to #8d887e (5.7:1) to clear the 4.5:1 normal-text threshold. Light
     theme --dim is already compliant (~5.5:1 against the light panel). */
  --dim: #8d887e;
  --accent: #f6f2ea;
  --accent-soft: rgba(246, 242, 234, 0.22);
  --accent-softer: rgba(246, 242, 234, 0.08);
  /* Always contrasts against --accent — used for text/icons on filled
     accent backgrounds (primary buttons, current-look chip). Dark in
     dark mode (because --accent is light), light in light mode. */
  --on-accent: #0b0b0c;
  /* Shadow tokens — set to `none` in light theme so the UI reads as
     flat in light mode and only the dark canvas gets the lift cards. */
  --shadow-sm: 0 6px 14px rgba(0, 0, 0, 0.32);
  --shadow: 0 14px 36px rgba(0, 0, 0, 0.32);
  --shadow-md: 0 18px 54px rgba(0, 0, 0, 0.38);
  --shadow-lg: 0 24px 80px rgba(0, 0, 0, 0.44);
  --shadow-xl: 0 32px 80px rgba(0, 0, 0, 0.55);
  --shadow-thumb: 0 1px 2px rgba(0, 0, 0, 0.55);
  --shadow-up: 0 -16px 40px rgba(0, 0, 0, 0.55);
  /* Theme-aware faint stroke — used by the dashed dividers, slider track
     idle fill, and inset hairlines so they swap polarity in light mode
     instead of becoming invisible white-on-cream. */
  --stroke-faint: rgba(255, 255, 255, 0.18);
  --stroke-fill: rgba(255, 255, 255, 0.28);
  --stroke-empty: rgba(255, 255, 255, 0.08);
  /* Slider thumb fill — light in dark theme (visible against dark track),
     dark in light theme (visible against light track). */
  --thumb-fill: #ffffff;
  /* Surface hover/active overlays — soft white on dark, soft dark on
     light, so subtle hover states stay visible in both themes. */
  --surface-hover: rgba(255, 255, 255, 0.04);
  --surface-active: rgba(255, 255, 255, 0.06);
  /* Vignette tints used by the canvas edge gradient. Each represents
     "the bg color, but extra concentrated" — dark mode pulls toward
     deep black, light mode lifts to near-white. Light mode also dials
     down the opacity so the edges don't crush the dotted globe. */
  --vignette-strong: rgba(11, 11, 12, 0.58);
  --vignette-mid: rgba(11, 11, 12, 0.16);
  --vignette-soft: rgba(11, 11, 12, 0.08);
  --radius: 16px;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  --font-mono-2: "Geist Pixel", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}

/* iOS-style continuous corners (squircle / G2 continuity). Replaces the
   default circular-arc rounded corner with the superellipse curve Apple
   uses across iOS, macOS and SwiftUI. Browsers that don't yet support
   `corner-shape` ignore this and fall back to the regular border-radius. */
*,
*::before,
*::after {
  corner-shape: squircle;
  -webkit-corner-shape: squircle;
}

/* Light-theme UI tokens. Applied to <html data-theme="light">. Only the
   panel / picker / overlay chrome flips — the canvas stays on its own dark
   --bg so the artwork (dots, gradients, presets) reads the same regardless
   of which UI theme the user is in. */
[data-theme="light"] {
  color-scheme: light;
  /* Opaque panel surfaces — kills the alpha bleed-through that was
     letting the dotted-globe canvas show through the backdrop-filter
     as a soft gradient under every translucent panel. Light mode now
     reads as flat solid surfaces, matching how dark mode looks on a
     uniformly-dark canvas. */
  --panel: #fcfbf8;
  --panel-2: #f1efeb;
  --panel-3: #e8e6e1;
  --field: #ffffff;
  --border: rgba(0, 0, 0, 0.1);
  --border-strong: rgba(0, 0, 0, 0.18);
  --text: #18171a;
  --muted: #4a4742;
  --dim: #6d6962;
  --accent: #18171a;
  --accent-soft: rgba(24, 23, 26, 0.16);
  --accent-softer: rgba(24, 23, 26, 0.06);
  --on-accent: #f6f2ea;
  /* Canvas/body background — warm cream, lighter than --panel so panels
     still float visibly on top of the canvas in light mode. */
  --bg: #f4f1ea;
  /* Strip all card-level drop shadows in light mode — the cream-on-cream
     contrast doesn't need them, and they cast visible dark halos that
     fight the flat aesthetic. Slider thumb keeps a tiny shadow so the
     puck stays visible against the track. */
  --shadow-sm: none;
  --shadow: none;
  --shadow-md: none;
  --shadow-lg: none;
  --shadow-xl: none;
  --shadow-up: none;
  /* Flip stroke colors to dark-on-light so dashed dividers, slider
     track fills, and hairlines stay visible on the cream canvas. */
  --stroke-faint: rgba(0, 0, 0, 0.18);
  --stroke-fill: rgba(0, 0, 0, 0.36);
  --stroke-empty: rgba(0, 0, 0, 0.1);
  /* Slider thumb flips to dark so it pops against the light track. */
  --thumb-fill: #18171a;
  /* Hover overlays use dark wash on the cream canvas. */
  --surface-hover: rgba(0, 0, 0, 0.04);
  --surface-active: rgba(0, 0, 0, 0.06);
  /* Light-mode vignette uses warm dark tones at *very* low alpha — the
     cream bg doesn't need a heavy edge crush; a hint is enough. */
  --vignette-strong: rgba(60, 50, 30, 0.06);
  --vignette-mid: rgba(60, 50, 30, 0.02);
  --vignette-soft: transparent;
}

/* One-shot transition class applied to <html> for ~320ms when the user
   toggles dark↔light. Crossfades every color-bearing property across
   the entire tree so panels, text, borders, fills, and dashed strokes
   slide between themes instead of snapping. Respects reduced-motion. */
.is-theme-transitioning,
.is-theme-transitioning *,
.is-theme-transitioning *::before,
.is-theme-transitioning *::after {
  transition-property: background-color, background, color, border-color, fill, stroke, box-shadow !important;
  transition-duration: 320ms !important;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important;
}

@media (prefers-reduced-motion: reduce) {
  .is-theme-transitioning,
  .is-theme-transitioning *,
  .is-theme-transitioning *::before,
  .is-theme-transitioning *::after {
    transition: none !important;
  }
}

output,
.range-number,
.map-zoom-input {
  font-variant-numeric: tabular-nums;
}

* {
  box-sizing: border-box;
}

html,
body,
#root {
  min-height: 100%;
}

body {
  margin: 0;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
}

/* Static takeover pages (/docs, /brand, /404) need ordinary window
   scroll — the canvas app's overflow:hidden body would otherwise lock
   the page in place and hide content below the viewport. The class
   is toggled by useBodyScrollable on those pages. */
body.is-body-scrollable {
  overflow: auto;
}

button,
select,
input,
textarea {
  font: inherit;
}

button {
  color: inherit;
}

.lucide {
  flex: 0 0 auto;
  display: block;
  stroke-width: 1.75;
}

.app-shell {
  position: relative;
  min-height: 100svh;
  padding: 12px;
  overflow: hidden;
  background: var(--preview-bg, var(--bg));
}

/* On preset routes (/looks/:id), the long-form <PresetDetail/> content
   lives below the canvas. Allow vertical scroll so users can read it. */
.app-shell.has-preset-detail {
  overflow: visible;
  overflow-x: hidden;
}

/* Per-preset long-form content below the canvas. Rendered when the user
   lands on a /looks/:id URL — gives each preset page ~200+ words of
   designer-facing unique content for SEO + AI Overview citations. Lives
   below the fold so the canvas-as-hero impression stays intact for
   first-time visitors. Driven by src/data/preset-seo.js. */
.preset-detail {
  position: relative;
  z-index: 5;
  margin: 100svh auto 0;
  max-width: 720px;
  padding: 64px 32px 96px;
  color: var(--text);
  /* Inherit Geist Pixel from body for consistency with the takeover
     pages and the canvas chrome. */
  font-size: 15px;
  line-height: 1.65;
  background: linear-gradient(180deg, transparent 0%, var(--bg) 18%);
}

/* /brand, /docs, /changelog are static takeover pages — no canvas,
   no panel, no looks bar. Share the same vertical-rhythm system so
   they read as siblings. */
.brand-page,
.docs-page,
.changelog-page,
.integrations-page,
.privacy-page {
  position: relative;
  min-height: 100svh;
  max-width: 760px;
  margin: 0 auto;
  /* Reduced top padding since the sticky .takeover-nav now sits above
     the page and already provides visual top spacing. */
  padding: 32px 24px 96px;
  color: var(--text);
  /* Inherit Geist Pixel from body — keeps the typeface consistent
     across the canvas app and every takeover page. Line-height is
     a bit looser than the default to keep long-form copy readable. */
  font-size: 15px;
  line-height: 1.65;
}

.brand-page-header,
.docs-page-header,
.changelog-page-header,
.integrations-page-header,
.privacy-page-header {
  margin-bottom: 36px;
}

/* Brand mark + wordmark link in the takeover-page headers. Doubles as
   the "back to Globestudio" affordance — the entire row is the home
   link. Keeps the DottedGlobe icon visually anchored at every static
   route (/, /docs, /brand, /404 also uses the same mark). */
.brand-page-brand,
.docs-page-brand,
.changelog-page-brand,
.integrations-page-brand,
.privacy-page-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 22px;
  padding: 4px 0;
  color: var(--text);
  font-size: 15px;
  font-weight: 800;
  letter-spacing: -0.01em;
  text-decoration: none;
  transition: opacity 140ms var(--ease);
}

.brand-page-brand:hover,
.docs-page-brand:hover,
.changelog-page-brand:hover,
.integrations-page-brand:hover,
.privacy-page-brand:hover {
  opacity: 0.7;
  text-decoration: none;
}

.brand-page-title,
.docs-page-title,
.changelog-page-title,
.integrations-page-title,
.privacy-page-title {
  margin: 0 0 10px;
  font-size: 32px;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.brand-page-lede,
.docs-page-lede,
.changelog-page-lede,
.integrations-page-lede,
.privacy-page-lede {
  margin: 0;
  color: var(--muted);
  font-size: 16px;
  max-width: 60ch;
}

/* Full-width sticky header that pins to the viewport top once the page
   scrolls past it. Rendered as a sibling of <main> (not inside) so its
   100% width is the viewport width, not the 760px reading column. The
   inner wrapper re-centers the content to match the column width
   underneath. Translucent panel + backdrop-filter keeps the content
   beneath legible as it scrolls under. */
.takeover-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(11, 11, 12, 0.85);
  border-bottom: 1px solid var(--border);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
}

.takeover-nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  max-width: 760px;
  margin: 0 auto;
  padding: 10px 24px;
}

.takeover-nav-brand {
  display: inline-flex;
  align-items: center;
  padding: 4px;
  color: var(--text);
  text-decoration: none;
  /* Hidden by default; the TakeoverNav toggles `.has-brand` on the
     parent <nav> once the page-header brand mark scrolls out of view
     (via IntersectionObserver). Faded in/out + scale to avoid a hard
     pop. Pointer-events disabled while invisible so it can't catch
     stray clicks. */
  opacity: 0;
  transform: translateY(-2px) scale(0.92);
  pointer-events: none;
  transition:
    opacity 220ms var(--ease),
    transform 220ms var(--ease);
}

.takeover-nav.has-brand .takeover-nav-brand {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.takeover-nav-brand:hover {
  opacity: 0.7;
}

.takeover-nav-links {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  overflow-x: auto;
  scrollbar-width: none;
}

.takeover-nav-links::-webkit-scrollbar {
  display: none;
}

.takeover-nav-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  padding: 6px 10px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-decoration: none;
  /* Pixel-stepped corners on the chip itself so hover + active states
     read as proper pixel-art badges. 4px notch matches the kbd / code
     block scale at this small link size. */
  clip-path: polygon(
    0 4px,
    4px 4px,
    4px 0,
    calc(100% - 4px) 0,
    calc(100% - 4px) 4px,
    100% 4px,
    100% calc(100% - 4px),
    calc(100% - 4px) calc(100% - 4px),
    calc(100% - 4px) 100%,
    4px 100%,
    4px calc(100% - 4px),
    0 calc(100% - 4px)
  );
  transition:
    color 140ms var(--ease),
    background 140ms var(--ease);
}

.takeover-nav-link:hover {
  /* Cyan tint on hover so hover + active live in the same color family
     — the chip lights up slightly, signaling "this is interactive". */
  color: #8ddfff;
  background: rgba(141, 223, 255, 0.12);
}

/* Active link gets the full cyan accent that other takeover-page links
   use, so the "you are here" indicator stays consistent with the
   product's inline-link hue. */
.takeover-nav-link.is-active {
  color: #8ddfff;
  background: rgba(141, 223, 255, 0.22);
}

/* Theme toggle button — same `takeover-nav-link` visual weight but
   without the underline/active state, since it's a control not a link. */
.takeover-nav-theme {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 30px;
  height: 28px;
  margin-left: 4px;
  border: 0;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition:
    color 140ms var(--ease),
    background 140ms var(--ease);
}

.takeover-nav-theme:hover,
.takeover-nav-theme:focus-visible {
  color: var(--text);
  background: rgba(255, 255, 255, 0.05);
  outline: none;
}

/* Section heading with a hover-revealed deep-link anchor.
   The anchor is visually hidden until the heading is hovered so it
   doesn't compete with the title text on first read. */
.section-heading {
  display: flex;
  align-items: baseline;
  gap: 8px;
  scroll-margin-top: 16px;
}

.section-heading-text {
  flex: 0 1 auto;
}

.section-heading .section-heading-anchor {
  flex: 0 0 auto;
  opacity: 0;
  color: var(--dim);
  font-weight: 600;
  text-decoration: none;
  transition: opacity 140ms var(--ease), color 140ms var(--ease);
}

.section-heading:hover .section-heading-anchor,
.section-heading:focus-within .section-heading-anchor {
  opacity: 1;
}

.section-heading .section-heading-anchor:hover {
  color: #8ddfff;
}

/* Framed code block — pixel-cornered border, header bar with a
   language label on the left + copy button on the right, then the
   code in its own scrollable area. Replaces the prior bare <pre>. */
.code-block {
  position: relative;
  margin: 0 0 16px;
  background:
    linear-gradient(var(--panel-3), var(--panel-3)),
    var(--bg);
  /* Pixel-stepped corners matching the rest of the app's elevated
     surfaces (.kbd-shadow, .takeover-nav variants). 6px notch matches
     the 12px-radius scale used elsewhere on bigger cards. */
  clip-path: polygon(
    0 6px,
    6px 6px,
    6px 0,
    calc(100% - 6px) 0,
    calc(100% - 6px) 6px,
    100% 6px,
    100% calc(100% - 6px),
    calc(100% - 6px) calc(100% - 6px),
    calc(100% - 6px) 100%,
    6px 100%,
    6px calc(100% - 6px),
    0 calc(100% - 6px)
  );
}

.code-block-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  /* Slight padding-top so the header doesn't touch the pixel-corner
     cut at the top — keeps the language label/copy button optically
     centered inside the visible bar. */
  padding: 10px 14px 8px;
  border-bottom: 1px solid var(--border);
  background: rgba(0, 0, 0, 0.18);
}

.code-block-language {
  color: var(--dim);
  font-family: var(--font-mono-2);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.code-block-copy {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-family: var(--font-mono-2);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: color 140ms var(--ease);
}

.code-block-copy:hover,
.code-block-copy:focus-visible {
  color: #8ddfff;
  outline: none;
}

.code-block-pre {
  margin: 0;
  padding: 14px 16px 16px;
  font-family: var(--font-mono-2);
  font-size: 13px;
  line-height: 1.6;
  color: var(--text);
  overflow-x: auto;
  /* Tabs render as 2 spaces — long lines stay readable in the
     constrained 760px reading column. */
  tab-size: 2;
}

.code-block .code-block-pre code {
  /* Reset .docs-section code chip styling that would otherwise wrap
     each visible line of the <code> with a pill background (because
     inline elements paint their bg behind every line box). Specificity
     bumped so we win against the later `.docs-section code` rule. */
  display: block;
  padding: 0;
  background: transparent;
  border-radius: 0;
  font-size: inherit;
  color: inherit;
  white-space: pre;
}

/* Slim scrollbar styling for the horizontal overflow when a snippet
   exceeds the column width. Keeps the chrome quiet so long lines feel
   intentional, not broken. */
.code-block-pre::-webkit-scrollbar {
  height: 6px;
}

.code-block-pre::-webkit-scrollbar-thumb {
  background: var(--border-strong);
}

.code-block-pre::-webkit-scrollbar-track {
  background: transparent;
}

/* Right-rail TOC on /docs. Position-fixed so it floats next to the
   centered 760px reading column without widening it — keeps every
   takeover page the same content width. `top` accounts for the sticky
   header (54px nav + 24px breathing room). Collapses to display:none
   below 1080px so tablet/mobile gets a single column. */
.on-this-page {
  position: fixed;
  top: 78px;
  right: 24px;
  z-index: 10;
  width: 200px;
  padding: 14px 14px 14px 20px;
  border-left: 1px solid var(--border);
  max-height: calc(100vh - 100px);
  overflow-y: auto;
}

.on-this-page-title {
  margin: 0 0 10px;
  color: var(--dim);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.on-this-page-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.on-this-page-link {
  /* Inline-block so the ::before bullet sits inline next to the label
     and the link only grows to its content width. Padding gives a
     proper click target without making the whole row clickable. */
  position: relative;
  display: block;
  padding: 4px 0 4px 14px;
  color: var(--muted) !important;
  font-size: 12px;
  font-weight: 600;
  text-decoration: none !important;
  transition:
    color 220ms var(--ease),
    transform 220ms var(--ease);
}

.on-this-page-link::before {
  /* 2-pixel bullet that stays muted until the link is hovered or
     becomes active — gives the eye a clear "you are here" cue without
     a heavy underline or background chip. */
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 4px;
  height: 4px;
  margin-top: -2px;
  background: var(--dim);
  opacity: 0;
  transform: translateX(-2px);
  transition:
    opacity 220ms var(--ease),
    transform 220ms var(--ease),
    background 220ms var(--ease);
}

.on-this-page-link:hover {
  color: var(--text) !important;
  transform: translateX(1px);
}

.on-this-page-link:hover::before {
  opacity: 0.5;
  transform: translateX(0);
}

.on-this-page-link.is-active {
  color: #8ddfff !important;
  transform: translateX(2px);
}

.on-this-page-link.is-active::before {
  opacity: 1;
  background: #8ddfff;
  transform: translateX(0);
}

@media (prefers-reduced-motion: reduce) {
  .on-this-page-link,
  .on-this-page-link::before {
    transition: none;
  }
}

/* Hide the floating TOC below the threshold where it would overlap
   the 760px reading column (760 + ~240 for the rail + breathing room). */
@media (max-width: 1079px) {
  .on-this-page {
    display: none;
  }
}

/* Bottom-of-page prev/next pager. Replaces the old multi-link footer
   with a single forward-momentum nudge to the next surface (and an
   optional "go back" affordance on the left). Grid keeps prev pinned
   left and next pinned right even when only one is present. */
.page-pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin-top: 64px;
  padding-top: 32px;
  border-top: 1px solid var(--border);
}

.page-pager-link {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 20px;
  border: 1px solid var(--border);
  background:
    linear-gradient(var(--panel-3), var(--panel-3)),
    var(--bg);
  color: var(--text) !important;
  text-decoration: none !important;
  transition:
    border-color 140ms var(--ease),
    transform 220ms var(--ease);
  clip-path: polygon(
    0 4px,
    4px 4px,
    4px 0,
    calc(100% - 4px) 0,
    calc(100% - 4px) 4px,
    100% 4px,
    100% calc(100% - 4px),
    calc(100% - 4px) calc(100% - 4px),
    calc(100% - 4px) 100%,
    4px 100%,
    4px calc(100% - 4px),
    0 calc(100% - 4px)
  );
}

.page-pager-link:hover,
.page-pager-link:focus-visible {
  border-color: var(--border-strong);
  outline: none;
}

.page-pager-next {
  text-align: right;
  align-items: flex-end;
}

.page-pager-direction {
  color: var(--dim);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.page-pager-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.page-pager-next .page-pager-label {
  flex-direction: row-reverse;
}

/* On the cyan-link-using takeover pages, override the underline rule
   so the pager card doesn't inherit a stray underline from
   `.docs-section a` etc. The card chrome IS the affordance. */
.docs-section .page-pager-link,
.brand-section .page-pager-link,
.changelog-section .page-pager-link,
.privacy-section .page-pager-link {
  text-decoration: none !important;
}

.brand-section,
.docs-section {
  margin-top: 40px;
}

/* Integration sections render as proper cards — pixel-cornered chrome,
   panel-3 bg, section title pinned at top, body + code below, and a
   trailing "Full recipe →" CTA in the panel's accent cyan. Reads as
   a stack of equally-weighted recipes the visitor can scan.
   No border — the pixel-corner clip-path + panel-2 bg already
   defines the card edge; the stroke was creating a 1px outline
   that read as extra visual weight. */
.integrations-section {
  margin-top: 24px;
  padding: 24px 24px 16px;
  background:
    linear-gradient(var(--panel-2), var(--panel-2)),
    var(--bg);
  border: 0;
  border-radius: 0;
  clip-path: polygon(
    0px 9px,
    0px 3px,
    3px 3px,
    3px 0px,
    9px 0px,
    calc(100% - 9px) 0px,
    calc(100% - 3px) 0px,
    calc(100% - 3px) 3px,
    calc(100% - 0px) 3px,
    calc(100% - 0px) 9px,
    calc(100% - 0px) calc(100% - 9px),
    calc(100% - 0px) calc(100% - 3px),
    calc(100% - 3px) calc(100% - 3px),
    calc(100% - 3px) calc(100% - 0px),
    calc(100% - 9px) calc(100% - 0px),
    9px calc(100% - 0px),
    3px calc(100% - 0px),
    3px calc(100% - 3px),
    0px calc(100% - 3px),
    0px calc(100% - 9px)
  );
}

.integrations-section-header {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 8px;
}

.integrations-section-logo {
  flex: 0 0 auto;
  /* 40×40 chip with the brand color as bg, icon centered.
     Pixel-cornered like every other elevated surface in the app. */
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  clip-path: polygon(
    0 6px,
    6px 6px,
    6px 0,
    calc(100% - 6px) 0,
    calc(100% - 6px) 6px,
    100% 6px,
    100% calc(100% - 6px),
    calc(100% - 6px) calc(100% - 6px),
    calc(100% - 6px) 100%,
    6px 100%,
    6px calc(100% - 6px),
    0 calc(100% - 6px)
  );
}

.integrations-section-title {
  margin: 0;
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--text);
  flex: 1 1 auto;
}

.integrations-section p {
  margin: 0 0 14px;
}

.integrations-recipe {
  margin: 4px 0 0;
  font-size: 13px;
}

.integrations-recipe a {
  font-weight: 700;
}

.brand-section-title,
.docs-section-title,
.integrations-section-title {
  margin: 0 0 12px;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.brand-section p,
.docs-section p,
.integrations-section p {
  margin: 0 0 12px;
  color: var(--muted);
  max-width: 64ch;
}

/* Inline links inside takeover-page paragraphs read as a distinct
   cyan accent against the warm-white body copy — same hue the
   atmosphere glow uses, so the takeover pages quietly carry the
   product's color identity. Underline stays so users can identify
   links by shape too, not just hue. */
.brand-section a,
.docs-section a,
.changelog-section a,
.integrations-section a,
.privacy-section a {
  color: #8ddfff;
  text-decoration: underline;
  text-decoration-color: rgba(141, 223, 255, 0.36);
  text-underline-offset: 3px;
  transition: color 140ms var(--ease), text-decoration-color 140ms var(--ease);
}

.brand-section a:hover,
.docs-section a:hover,
.changelog-section a:hover,
.integrations-section a:hover,
.privacy-section a:hover,
.brand-section a:focus-visible,
.docs-section a:focus-visible,
.changelog-section a:focus-visible,
.integrations-section a:focus-visible,
.privacy-section a:focus-visible {
  color: #c8ecff;
  text-decoration-color: #c8ecff;
}

.brand-section code,
.docs-section code,
.integrations-section code {
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--field);
  font-family: var(--font-mono-2);
  font-size: 13px;
  color: var(--text);
}

/* Logo cards on /brand. */
.brand-logo-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-bottom: 12px;
}

.brand-logo-card {
  flex: 1 1 240px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 32px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--field);
  color: var(--text);
}

.brand-logo-card--light {
  background: #f6f2ea;
  color: #0b0b0c;
  border-color: rgba(0, 0, 0, 0.08);
}

.brand-card-caption {
  font-size: 12px;
  color: var(--text-soft);
}

.brand-logo-card--light .brand-card-caption {
  color: rgba(0, 0, 0, 0.72);
}

.brand-link,
.docs-link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 36px;
  padding: 8px 12px;
  background: var(--field);
  color: var(--text);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  transition: background 140ms var(--ease), color 140ms var(--ease);
}

.brand-link:hover,
.docs-link:hover,
.docs-link:focus-visible {
  background: rgba(255, 255, 255, 0.09);
  text-decoration: none;
}

.brand-link svg,
.docs-link svg {
  flex: 0 0 auto;
  opacity: 0.7;
  transition: opacity 140ms var(--ease);
}

.brand-link:hover svg,
.docs-link:hover svg {
  opacity: 1;
}

.brand-caption {
  font-size: 13px;
  color: var(--muted);
}

/* OG card thumbnail grid on /brand. */
.brand-og-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
  margin-bottom: 12px;
}

.brand-og-tile {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--field);
  text-decoration: none;
  transition: border-color 140ms var(--ease), background 140ms var(--ease);
}

.brand-og-tile:hover {
  border-color: var(--border-strong);
  background: var(--surface-hover);
  text-decoration: none;
}

.brand-og-tile img {
  width: 100%;
  height: auto;
  border-radius: 8px;
  display: block;
}

.brand-og-label {
  padding: 0 4px 4px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  text-transform: lowercase;
}

/* Palette tiles on /brand. */
.brand-palette-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
}

.brand-palette-tile {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--field);
}

.brand-palette-swatch {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: 8px;
  border: 1px solid var(--border);
}

.brand-palette-meta {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.brand-palette-meta strong {
  font-size: 13px;
  font-weight: 700;
}

.brand-palette-meta code {
  padding: 0;
  background: transparent;
  font-size: 12px;
  color: var(--muted);
}

.brand-palette-meta span {
  font-size: 11px;
  color: var(--muted);
}

.brand-taglines {
  margin: 0;
  padding: 0 0 0 18px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  color: var(--text);
}

/* 2-column grid like the about modal so links line up evenly
   instead of wrapping at arbitrary points. Falls back to single
   column on narrow viewports. */
.brand-section-links,
.docs-section-links {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
}

@media (max-width: 540px) {
  .brand-section-links,
  .docs-section-links {
    grid-template-columns: 1fr;
  }

  /* The 160px key column is sized for desktop; on a ~345px-wide phone it
     leaves a big void between the badges and the descriptions and squeezes
     the labels into 2-line wraps. Narrow it to just past the widest badge
     group (the three-key "+ − 0" ≈ 86px) so the descriptions pull left and
     most fit on one line. Two-class selector so it outweighs the base
     `.docs-shortcut-row` rule, which is defined *later* in the file. */
  .docs-shortcuts .docs-shortcut-row {
    grid-template-columns: 100px 1fr;
    gap: 12px;
  }
}

/* /docs styling. */
.docs-code {
  margin: 0 0 12px;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--field);
  font-family: var(--font-mono-2);
  font-size: 13px;
  line-height: 1.55;
  color: var(--text);
  overflow-x: auto;
  white-space: pre;
}

.docs-subsection-title {
  margin: 24px 0 8px;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--text);
}

.docs-shortcuts {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.docs-shortcut-row {
  display: grid;
  grid-template-columns: 160px 1fr;
  align-items: center;
  gap: 16px;
  padding: 6px 0;
  border-bottom: 1px dashed var(--border);
}

.docs-shortcut-row:last-child {
  border-bottom: 0;
}

.docs-shortcut-keys {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

/* Pixel-cornered physical-key system.
   The HTML structure is <span class="kbd-frame"><kbd>...</kbd></span>
   (see KbdKey component). Why two elements:
     - The inner <kbd> has clip-path for pixel-stepped corners.
     - Clip-path crops any external shadow painted below the element,
       which is why we couldn't have pixel corners AND a 3D drop-shadow
       on a single element.
     - The outer .kbd-frame has NO clip-path. Its filter: drop-shadow
       generates the shadow from the kbd's alpha (which is already
       clipped to the pixel-cornered shape) and renders it freely.
   Result: real pixel corners + a real 3D drop-shadow that follows the
   stair-stepped silhouette. */
.kbd-frame {
  position: relative;
  /* New stacking context so .kbd-shadow's z-index: -1 stays scoped
     to this frame instead of going behind the page background. */
  isolation: isolate;
  display: inline-block;
  vertical-align: middle;
  /* Reserve space for the 4px offset drop so the frame's vertical
     bounding box includes the shadow region. Otherwise the shadow's
     bottom 4px can collide with the next text line. */
  margin-bottom: 4px;
}

/* Same pixel-cornered shape as the kbd, painted a few pixels below.
   `inset: 0` sizes it identically to the .kbd-frame (which wraps the
   kbd tightly), `transform: translateY(4px)` slides it down 4px, and
   `z-index: -1` puts it behind the kbd. Only the bottom 4px of the
   duplicate is visible, reading as the key's pixel-stepped drop. */
.kbd-shadow {
  position: absolute;
  inset: 0;
  transform: translateY(4px);
  /* Theme-aware shadow color. Dark theme: lighter band against the
     near-black bg reads as the key's "thickness". Light theme: darker
     band against the cream bg gives a proper drop-shadow. Overridden
     in the [data-theme="light"] block below. */
  background: rgba(255, 255, 255, 0.14);
  clip-path: polygon(
    0 4px,
    4px 4px,
    4px 0,
    calc(100% - 4px) 0,
    calc(100% - 4px) 4px,
    100% 4px,
    100% calc(100% - 4px),
    calc(100% - 4px) calc(100% - 4px),
    calc(100% - 4px) 100%,
    4px 100%,
    4px calc(100% - 4px),
    0 calc(100% - 4px)
  );
  z-index: -1;
  pointer-events: none;
}

.docs-shortcut-row kbd,
.docs-shortcuts kbd,
.docs-section kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 24px;
  padding: 0 8px;
  /* Opaque so the offset .kbd-shadow (z-index: -1, positioned behind)
     can't bleed through the translucent surface — bleed-through made
     the kbd body brighter than its top 4px (where the offset shadow
     didn't reach), creating a dark-top stripe. Stacking the same
     translucent token over an opaque page bg keeps the theme-aware
     color while making the kbd fully opaque. */
  background:
    linear-gradient(var(--panel-3), var(--panel-3)),
    var(--bg);
  color: var(--text);
  font-family: var(--font-mono-2);
  font-size: 12px;
  font-weight: 700;
  transform: translateY(-1px);
  clip-path: polygon(
    0 4px,
    4px 4px,
    4px 0,
    calc(100% - 4px) 0,
    calc(100% - 4px) 4px,
    100% 4px,
    100% calc(100% - 4px),
    calc(100% - 4px) calc(100% - 4px),
    calc(100% - 4px) 100%,
    4px 100%,
    4px calc(100% - 4px),
    0 calc(100% - 4px)
  );
  /* Inset top gloss survives clip-path (it paints inside the
     polygon, gets clipped at the corners to the pixel shape). */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

/* ===========================================================
   /examples page — REDESIGNED as full-screen company showcase.
   Six per-company hero sections styled in each brand's actual
   palette + typography vibe, then a cards gallery, stats
   gallery, and code reference. See examples-page.jsx.
   =========================================================== */

.examples-page--showcase {
  /* Override the takeover-page narrow max-width — showcases need
     full viewport to feel like landing pages. */
  max-width: none;
  padding: 0;
  /* Clip the full-bleed `100vw` bands (mesh gradient, logo marquee) so they
     can't overshoot by the scrollbar width and leave black space / a
     horizontal scroll on the right. `clip` (not `hidden`) avoids creating a
     nested scroll container or affecting vertical scrolling. */
  overflow-x: clip;
}

.examples-page--showcase .examples-hero {
  max-width: 760px;
  margin: 0 auto;
  padding: 36px 24px 28px;
  text-align: center;
}

.examples-hero-brand {
  display: inline-block;
  margin-bottom: 32px;
  color: var(--text);
  text-decoration: none;
}

.examples-hero-title {
  font-size: 56px;
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 16px;
  color: var(--text);
}

.examples-hero-lede {
  font-size: 17px;
  line-height: 1.6;
  color: var(--muted);
  margin: 0 auto;
  max-width: 620px;
}

/* Sticky chip nav — six company names that highlight as you
   scroll through the showcase stack. Sits just below the global
   TakeoverNav (which is also sticky at z-index 5). */
.showcase-chip-nav {
  position: sticky;
  top: 56px;
  z-index: 4;
  display: flex;
  justify-content: center;
  gap: 4px;
  padding: 10px 16px;
  background: rgba(11, 11, 12, 0.78);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
}
.showcase-chip-nav::-webkit-scrollbar {
  display: none;
}

.showcase-chip {
  flex: 0 0 auto;
  padding: 6px 14px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--muted);
  text-decoration: none;
  transition: color 140ms var(--ease), background 140ms var(--ease);
}
.showcase-chip:hover {
  color: var(--text);
  background: var(--field);
}
.showcase-chip.is-active {
  color: var(--text);
  background: var(--field);
  box-shadow: inset 0 0 0 1px var(--border);
}

.showcase-stack {
  display: flex;
  flex-direction: column;
}

/* Each .showcase fills (at least) the full viewport so it reads
   as a landing-page hero — visitors scroll one company at a time.
   Per-section background/color is set inline from the component;
   layout chrome lives here. */
.showcase {
  min-height: 100vh; /* fallback */
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  position: relative;
  /* No top padding: the global nav is non-sticky on this page (see the
     :has rule below), so each section's faked company nav sits flush at
     its top — stuck directly to whatever precedes it, no separation. */
  padding-top: 0;
}

.showcase-inner {
  flex: 1 1 auto;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 60px 32px 80px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  text-align: center;
}

/* Profound's signature cycling-engine headline — the AI engine name
   rotates through Perplexity → ChatGPT → Claude → Gemini → Grok →
   Copilot. A vertical "slot" reveals one engine at a time; the track
   translates up by one line per engine. The 7th item repeats the
   first so the loop reset is seamless. */
.profound-engine-cycle {
  display: inline-block;
  height: 1.15em;
  line-height: 1.15em;
  overflow: hidden;
  vertical-align: bottom;
  position: relative;
  top: 0.04em;
}
.profound-engine-track {
  display: flex;
  flex-direction: column;
  animation: profound-engine-cycle 10.5s infinite;
}
.profound-engine-track > span {
  height: 1.15em;
  line-height: 1.15em;
  white-space: nowrap;
}
@keyframes profound-engine-cycle {
  0%, 11%    { transform: translateY(0); }
  14.3%, 25% { transform: translateY(-1.15em); }
  28.6%, 39% { transform: translateY(-2.3em); }
  42.9%, 53% { transform: translateY(-3.45em); }
  57.2%, 67% { transform: translateY(-4.6em); }
  71.5%, 81% { transform: translateY(-5.75em); }
  85.8%, 100% { transform: translateY(-6.9em); }
}
@media (prefers-reduced-motion: reduce) {
  .profound-engine-track { animation: none; }
}

/* Faked-but-faithful company navbars — sit at the top of each
   showcase so the hero below feels like it's living on the real
   product page. Non-interactive: rendered as spans not links. */
.showcase-nav {
  width: 100%;
  flex: 0 0 auto;
  position: relative;
  z-index: 2;
}

.showcase-nav-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 16px 32px;
  display: flex;
  align-items: center;
  gap: 32px;
  font-size: 13px;
  font-weight: 500;
}

.showcase-nav-brand {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
}

.showcase-nav-items {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 22px;
  overflow: hidden;
}

.showcase-nav-item {
  font-size: 13px;
  font-weight: 500;
  opacity: 0.85;
  white-space: nowrap;
  cursor: default;
}

.showcase-nav-right {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 14px;
}

.showcase-nav-link {
  font-size: 13px;
  font-weight: 500;
  cursor: default;
}

.showcase-nav-cta {
  display: inline-flex;
  align-items: center;
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  cursor: default;
}

@media (max-width: 720px) {
  .showcase-nav-inner {
    padding: 12px 16px;
    gap: 16px;
    font-size: 12px;
  }
  .showcase-nav-items { display: none; }
  .showcase-nav-right { gap: 10px; }
  .showcase-nav-link { display: none; }
}

.showcase-inner--split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 64px;
  align-items: center;
  text-align: left;
}
.showcase-inner--split .showcase-copy {
  display: flex;
  flex-direction: column;
  gap: 20px;
  text-align: left;
}
.showcase-inner--split .showcase-eyebrow,
.showcase-inner--split .showcase-headline,
.showcase-inner--split .showcase-subhead,
.showcase-inner--split .showcase-ctas {
  text-align: left;
  justify-content: flex-start;
}

.showcase-eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.showcase-headline {
  font-size: clamp(40px, 6vw, 78px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0;
}

.showcase-subhead {
  font-size: clamp(16px, 1.4vw, 20px);
  line-height: 1.55;
  margin: 0;
  max-width: 660px;
}

.showcase-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
  margin-top: 8px;
}

.showcase-cta {
  display: inline-flex;
  align-items: center;
  padding: 12px 22px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  border: 1px solid transparent;
  transition: transform 120ms var(--ease), opacity 120ms var(--ease);
}
.showcase-cta:hover {
  transform: translateY(-1px);
}
.showcase-cta--primary { border-color: transparent; }
.showcase-cta--ghost  { background: transparent; }
/* The /examples heroes are real-product mockups, so their buttons opt out of
   the global pixel-corner flatten (the `* { border-radius: 0 !important }`
   nuclear rule) and keep rounded corners. Class specificity + !important beats
   the universal selector. */
.showcase--stripe .showcase-cta { border-radius: 16px !important; }
/* Same opt-out for the faked nav buttons (Sign in / Contact sales) — smaller
   buttons, so a smaller radius. */
.showcase--stripe .showcase-nav-cta { border-radius: 10px !important; }
/* Vercel hero: every button is a full pill. Opts out of the global
   border-radius flatten like the Stripe overrides above. */
.showcase--vercel .showcase-cta,
.showcase--vercel .showcase-nav-cta,
.showcase--vercel .vercel-pill {
  border-radius: 999px !important;
}
/* Small dropdown chevron on every Vercel nav item except the last (Pricing). */
.showcase--vercel .showcase-nav-item {
  display: inline-flex;
  align-items: center;
}
.showcase--vercel .showcase-nav-item:not(:last-child)::after {
  content: "";
  width: 5px;
  height: 5px;
  margin-left: 5px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translateY(-1px);
  opacity: 0.55;
}
/* Outlined (stroked) buttons — Log in + Get a Demo. Re-adds a border and pill
   radius that the global border-strip / radius-flatten rules remove. */
.showcase--vercel .vercel-outline {
  border: 1px solid rgba(255, 255, 255, 0.3) !important;
  border-radius: 999px !important;
}
/* Solid black disc behind the globe so its dots sit on pure black (the globe
   composites transparent, so the glow would otherwise bleed through). */
.vercel-globe-disc {
  border-radius: 50% !important;
  clip-path: none !important;
}
/* Animated Vercel-gradient glow behind the globe (adapted from the gist).
   A blurred, scaled, circular gradient that cycles its position. */
.vercel-globe-glow::after {
  content: "";
  position: absolute;
  inset: 0;
  clip-path: none !important;
  /* Fills the hero; colour reads blue (left) → green/yellow (centre) → red
     (right), like vercel.com. */
  filter: blur(70px);
  background: linear-gradient(95deg, #0a4dff 0%, #16b34a 30%, #ffd21e 52%, #ff7a00 72%, #ff1f1f 100%);
  background-size: 180% 100%;
  opacity: 0.78;
  /* Radiate from behind the globe (lower-centre) and fade to dark at the
     corners — broad atmospheric glow, not a tight halo. */
  -webkit-mask-image: radial-gradient(ellipse 56% 54% at 50% 82%, #000 10%, transparent 68%);
  mask-image: radial-gradient(ellipse 56% 54% at 50% 82%, #000 10%, transparent 68%);
  animation: vercel-glow 14s ease infinite;
}
@keyframes vercel-glow {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .vercel-globe-glow::after { animation: none; }
}
/* Ambient unicorn-blinds gradient wash behind the whole Vercel hero — soft,
   low opacity, slowly drifting. Sits behind everything (first child). */
.vercel-bg-wash {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: linear-gradient(-10deg, #f10d5e 0%, #ffb43e 25%, #43e03c 50%, #6528ff 100%);
  background-size: 200% 200%;
  filter: blur(28px);
  opacity: 0.42;
  /* Don't let it reach the top — fade out toward the nav/headline. */
  -webkit-mask-image: linear-gradient(to top, #000 35%, transparent 80%);
  mask-image: linear-gradient(to top, #000 35%, transparent 80%);
  animation: vercel-bg-slide 14s ease infinite;
}
@keyframes vercel-bg-slide {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .vercel-bg-wash { animation: none; }
}
/* "Ask AI" — looping rainbow gradient text. The gradient's first and last
   stops match so the panning background tiles seamlessly. */
.ai-gradient {
  background-image: linear-gradient(
    90deg,
    #6f9bff, #6fce93, #ffe487, #ffb273, #ff8a8a, #6f9bff
  );
  background-size: 220% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: ai-gradient-pan 5s linear infinite;
}
@keyframes ai-gradient-pan {
  to { background-position: 200% center; }
}
/* Hero contour-mesh gradient — sweeps side to side. !important on the size
   beats the inline `background` shorthand (which would reset size to auto). */
.vercel-mesh-gradient {
  background-size: 200% 100% !important;
  animation: vercel-mesh-pan 5s ease-in-out infinite alternate;
}
@keyframes vercel-mesh-pan {
  from { background-position: 0% 50%; }
  to { background-position: 100% 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .vercel-mesh-gradient { animation: none; }
}
@media (prefers-reduced-motion: reduce) {
  .ai-gradient { animation: none; }
}
/* Tiny rotating flag beside "195 countries" — flips in on each swap. */
.rotating-flag {
  transform-origin: center;
  backface-visibility: hidden;
  animation: rotating-flag-flip 760ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
@keyframes rotating-flag-flip {
  0% { transform: rotateY(52deg); opacity: 0; }
  40% { opacity: 1; }
  100% { transform: rotateY(0deg); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .rotating-flag { animation: none; }
}

.showcase-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 32px;
  width: 100%;
  max-width: 920px;
}
.showcase-stat {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: center;
  text-align: center;
}
.showcase-stat strong {
  font-size: clamp(40px, 4.5vw, 64px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
}
.showcase-stat span {
  font-size: 13px;
  letter-spacing: 0.02em;
}

/* Per-company small adjustments. The Earthscale section uses the
   globe as a full-bleed background, so its inner gets a tighter
   width for the floating copy. */
.showcase--earthscale .showcase-inner { max-width: 720px; }

.showcase-globe-backdrop iframe {
  filter: saturate(0.9);
}

/* Stripe customer-logo marquee — infinite horizontal scroll with soft
   edge fades. The track holds the logo list twice; translating -50% lands
   exactly on the duplicate so the loop is seamless. */
.stripe-logo-marquee {
  position: relative;
  overflow: hidden;
  width: 100%;
}
.stripe-logo-marquee-track {
  display: flex;
  align-items: center;
  gap: 52px;
  width: max-content;
  animation: stripe-logo-marquee 26s linear infinite;
}
@keyframes stripe-logo-marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .stripe-logo-marquee-track { animation: none; }
}

/* Stripe nav — matched to the live site: ~76px bar, 14px/400 #061b31
   items with dropdown carets and 24px gaps, a 25px-tall wordmark. */
.showcase--stripe .showcase-nav-inner {
  max-width: 1262px;
  padding: 18px 24px;
}
.showcase--stripe .showcase-nav-items {
  gap: 24px;
  margin-left: 8px;
}
.showcase--stripe .showcase-nav-item {
  font-size: 14px;
  font-weight: 600;
  color: #061b31;
  opacity: 1;
  display: inline-flex;
  align-items: center;
}
/* Dropdown caret on every item except the last (Pricing has none). */
.showcase--stripe .showcase-nav-item:not(:last-child)::after {
  content: "";
  width: 6px;
  height: 6px;
  margin-left: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translateY(-1px);
  opacity: 0.5;
}
.showcase--stripe .showcase-nav-right { gap: 20px; }

/* Stripe hero: a compact band (a little taller than pure content height),
   with the fake nav flush to the very top of the section. */
.showcase--stripe { min-height: 100vh; min-height: 100svh; padding-top: 0; }

/* Match Stripe's exact one-viewport height — the Vercel content otherwise
   runs ~150px taller. The bottom is the full-bleed globe band (already
   designed to crop), so capping + clipping keeps the heroes uniform. */
.showcase--vercel {
  height: 100vh; /* fallback */
  height: 100svh;
  min-height: 0;
  overflow: hidden;
}

/* On the examples showcase, the global site nav scrolls away (non-sticky)
   instead of sticking over the sections — so each section's own faked
   company nav reaches the very top of the viewport when you land on it,
   like a standalone page. The chip nav is hidden entirely: it rendered as
   a dark band sitting directly on top of the company nav, which read as an
   unwanted separation above the nav. */
/* The scroll container is the body (.is-body-scrollable sets overflow:auto on
   both axes). On the full-bleed showcase page that lets the 100vw bands give a
   horizontal scrollbar + black space on the right — clip the x axis here. */
body:has(.examples-page--showcase).is-body-scrollable {
  overflow-x: hidden;
}
body:has(.examples-page--showcase) .takeover-nav {
  position: relative;
}
body:has(.examples-page--showcase) .showcase-chip-nav {
  display: none;
}

/* --- Cards + Stats + Code Reference (regular scroll below showcases) --- */

.examples-block {
  max-width: 1100px;
  margin: 0 auto;
  padding: 80px 24px;
}

.examples-block-title {
  font-size: 36px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 0 0 12px;
  color: var(--text);
}

.examples-block-lede {
  font-size: 16px;
  color: var(--muted);
  margin: 0 0 40px;
  max-width: 620px;
}

.examples-cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 20px;
}

.examples-card {
  display: flex;
  flex-direction: column;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.06);
  box-shadow: 0 12px 32px -16px rgba(0, 0, 0, 0.3);
  transition: transform 180ms var(--ease);
}
.examples-card:hover {
  transform: translateY(-2px);
}

.examples-card-globe {
  display: block;
}

.examples-card-body {
  padding: 16px 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.examples-card-body strong {
  font-size: 15px;
  font-weight: 600;
}
.examples-card-body span {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.examples-stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
}

.examples-stat-tile {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 24px;
  border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.examples-stat-globe { display: block; }

.examples-stat-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.examples-stat-meta strong {
  font-size: 36px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
}
.examples-stat-meta span {
  font-size: 13px;
}

.examples-code-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.examples-code-item {
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--field);
  overflow: hidden;
}

.examples-code-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  cursor: pointer;
  font-weight: 600;
  font-size: 14px;
  color: var(--text);
  list-style: none;
}
.examples-code-summary::-webkit-details-marker { display: none; }
.examples-code-summary-hint {
  font-size: 12px;
  color: var(--muted);
  font-weight: 500;
}

.examples-code-body {
  padding: 0 12px 12px;
}

@media (max-width: 720px) {
  .showcase { padding: 64px 20px; }
  .showcase-inner--split {
    grid-template-columns: 1fr;
    text-align: center;
  }
  .showcase-inner--split .showcase-copy,
  .showcase-inner--split .showcase-eyebrow,
  .showcase-inner--split .showcase-headline,
  .showcase-inner--split .showcase-subhead,
  .showcase-inner--split .showcase-ctas {
    text-align: center;
    justify-content: center;
    align-items: center;
  }
  .examples-hero-title { font-size: 40px; }
  .showcase-chip-nav { top: 48px; padding: 8px 12px; }
}

/* ===========================================================
   Legacy /examples preview styles (kept for backwards-compat
   in case anything still references them, e.g. old shared
   URLs that linked deep into the old IDs). Safe to delete
   once we're sure nothing external uses them.
   =========================================================== */

/* /examples page — live previews of common embed layouts.
   Each .example-preview is a mini design composition: hero, card,
   stat tile, split, or footer band. Pixel-corner 9px polygon ties
   them visually to the rest of the docs surfaces. The iframes
   inside are live globes; the surrounding chrome is hand-styled
   so the preview matches what the copy-paste HTML produces. */
.example-preview {
  position: relative;
  margin: 16px 0;
  background: #0b0b0c;
  color: #f6f2ea;
  overflow: hidden;
  clip-path: polygon(
    0 9px, 9px 9px, 9px 0,
    calc(100% - 9px) 0, calc(100% - 9px) 9px, 100% 9px,
    100% calc(100% - 9px), calc(100% - 9px) calc(100% - 9px), calc(100% - 9px) 100%,
    9px 100%, 9px calc(100% - 9px), 0 calc(100% - 9px)
  );
}

.example-preview iframe {
  border: 0;
  display: block;
}

/* Hero — globe fills the entire frame, content sits on top with a
   left-side dark-vignette overlay to keep the headline legible. */
.example-preview--hero {
  height: 360px;
}

.example-preview--hero .example-preview-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.example-preview--hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    rgba(11, 11, 12, 0.85) 0%,
    rgba(11, 11, 12, 0.6) 35%,
    rgba(11, 11, 12, 0.05) 70%,
    rgba(11, 11, 12, 0) 100%
  );
  z-index: 1;
  pointer-events: none;
}

.example-preview-content {
  position: relative;
  z-index: 2;
  max-width: 60%;
  padding: 48px;
}

.example-preview-content h2 {
  margin: 0 0 8px;
  font-size: 28px;
  line-height: 1.15;
  letter-spacing: -0.01em;
}

.example-preview-content p {
  margin: 0 0 16px;
  color: #cdc8be;
  font-size: 15px;
}

.example-preview-cta {
  display: inline-block;
  padding: 10px 18px;
  background: #3df4ff;
  color: #0b0b0c;
  font-size: 13px;
  font-weight: 700;
}

/* Card — globe on top, body underneath. Two-row grid. */
.example-preview--card {
  display: grid;
  grid-template-rows: 200px auto;
  max-width: 340px;
  background: #131316;
  border: 1px solid #2a2a2c;
}

.example-preview--card iframe {
  width: 100%;
  height: 100%;
}

.example-preview-card-body {
  padding: 16px 20px 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.example-preview-card-body strong {
  font-size: 17px;
  font-weight: 700;
}

.example-preview-card-body span {
  color: #a8a39b;
  font-size: 14px;
}

/* Stat tile — globe to the left of a big number + label. */
.example-preview--stat {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 24px;
  background: #f6f2ea;
  color: #1a1a1c;
}

.example-preview--stat iframe {
  width: 140px;
  height: 140px;
  flex-shrink: 0;
  background: #0b0b0c;
}

.example-preview-stat-meta {
  display: flex;
  flex-direction: column;
}

.example-preview-stat-meta strong {
  font-size: 42px;
  font-weight: 800;
  color: #0b0b0c;
  line-height: 1;
}

.example-preview-stat-meta span {
  margin-top: 4px;
  color: #555;
  font-size: 14px;
}

/* Split — two equal columns. Stacks on narrow screens. */
.example-preview--split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  align-items: center;
  padding: 32px;
}

.example-preview-split-copy h2 {
  margin: 0 0 12px;
  font-size: 24px;
  letter-spacing: -0.01em;
}

.example-preview-split-copy p {
  margin: 0 0 12px;
  color: #cdc8be;
  font-size: 14px;
}

.example-preview-split-copy ul {
  margin: 0;
  padding-left: 18px;
  color: #cdc8be;
  font-size: 14px;
}

.example-preview-split-copy ul li {
  margin-bottom: 4px;
}

.example-preview-split-globe {
  width: 100%;
  aspect-ratio: 1;
}

@media (max-width: 640px) {
  .example-preview--split {
    grid-template-columns: 1fr;
  }
}

/* Footer — wide ambient band, globe at reduced opacity. */
.example-preview--footer {
  height: 200px;
}

.example-preview--footer iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0.55;
}

.example-preview-footer-content {
  position: relative;
  z-index: 1;
  padding: 80px 32px;
  text-align: center;
}

.example-preview-footer-content p {
  margin: 0;
  color: #f6f2ea;
  font-size: 14px;
}

.docs-preset-grid {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 8px;
}

.docs-preset-link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  background: var(--field);
  text-decoration: none;
  transition: background 140ms var(--ease);
}

.docs-preset-link:hover,
.docs-preset-link:focus-visible {
  background: rgba(255, 255, 255, 0.09);
  text-decoration: none;
}

/* Real-canvas thumbnail captured by scripts/capture-thumbnails.js.
   Same pixel-corner notch as the kbd/chip vocabulary so the preset
   card reads as part of the design system. */
.docs-preset-thumb {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: var(--bg);
  clip-path: polygon(
    0 4px, 4px 4px, 4px 0,
    calc(100% - 4px) 0, calc(100% - 4px) 4px, 100% 4px,
    100% calc(100% - 4px), calc(100% - 4px) calc(100% - 4px), calc(100% - 4px) 100%,
    4px 100%, 4px calc(100% - 4px), 0 calc(100% - 4px)
  );
  overflow: hidden;
}

.docs-preset-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
}

.docs-preset-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}

.docs-preset-link strong {
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.docs-preset-link span {
  font-size: 12px;
  color: var(--muted);
}

.docs-preset-text span {
  font-size: 12px;
  color: var(--muted);
}

/* /changelog — vertical list of dated sections. */
.changelog-section {
  margin-top: 40px;
}

.changelog-section-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.changelog-section-title {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.changelog-section-date {
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.changelog-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  color: var(--text);
}

/* Each row: pixel-stepped plus icon + the change. align-items:flex-start
   keeps the icon aligned to the FIRST line of a multi-line item. The
   icon's `margin-top` aligns its visual center with the first line of
   text — eyeballed to ~6px for the 12px icon at 1.55 line-height. */
.changelog-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  line-height: 1.55;
}

.changelog-list li > svg {
  flex: 0 0 auto;
  margin-top: 6px;
  color: #8ddfff;
}

.changelog-list li > span {
  flex: 1 1 auto;
}

.changelog-section p {
  margin: 0 0 12px;
  color: var(--muted);
  max-width: 64ch;
}

/* Inline-link styling for .changelog-section is now defined in the
   shared takeover-page block above (cyan accent + underline). The
   old rule that lived here was duplicate and overridden by the
   newer combined rule. */

/* /privacy page. Same vertical-rhythm as the other takeover pages —
   just adds list + opt-out button styling. */
.privacy-section {
  margin-top: 40px;
}

.privacy-section-title {
  margin: 0 0 12px;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.privacy-section p {
  margin: 0 0 12px;
  color: var(--muted);
  max-width: 64ch;
}

.privacy-section a {
  color: var(--text);
  text-decoration: underline;
  text-decoration-color: var(--border-strong);
  text-underline-offset: 3px;
}

.privacy-section a:hover {
  text-decoration-color: var(--text);
}

.privacy-section code {
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--field);
  font-family: var(--font-mono-2);
  font-size: 13px;
  color: var(--text);
}

.privacy-list {
  margin: 0 0 12px;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  color: var(--text);
}

/* Pixel-stepped plus icon + body, matching the changelog list. The icon
   is pinned to the FIRST line of multi-line items via flex-start so the
   bullet keeps its visual position when text wraps. */
.privacy-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  line-height: 1.55;
}

.privacy-list li > svg {
  flex: 0 0 auto;
  margin-top: 6px;
  color: #8ddfff;
}

.privacy-list li > span {
  flex: 1 1 auto;
}

.privacy-optout {
  margin-top: 18px;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--field);
}

.privacy-optout-button {
  width: 100%;
  padding: 10px 16px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--surface-hover);
  color: var(--text);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  transition: background 140ms var(--ease), border-color 140ms var(--ease);
}

.privacy-optout-button:hover {
  background: var(--panel-3);
  border-color: var(--text);
}

.privacy-optout-note {
  margin: 10px 0 0;
  color: var(--muted);
  font-size: 12px;
}

/* Shared footer for the static takeover pages. Sits at the bottom of
   /docs, /brand, /changelog, /404 so the chrome is consistent and
   every page has the same back-to-home / GitHub / license set of
   anchors. */
.takeover-footer {
  margin-top: 72px;
  padding-top: 28px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
}

.takeover-footer-brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: -0.01em;
  text-decoration: none;
}

.takeover-footer-brand:hover {
  text-decoration: none;
  opacity: 0.7;
}

.takeover-footer-links {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
}

.takeover-footer-links a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--muted);
  text-decoration: none;
  transition: color 140ms var(--ease);
}

.takeover-footer-links a:hover {
  color: var(--text);
}

/* 404 takeover. Centered, minimal — no canvas. */
.not-found-page {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 48px 24px;
  text-align: center;
  color: var(--text);
}

.not-found-mark {
  opacity: 0.75;
  color: var(--text);
}

.not-found-title {
  margin: 0;
  font-size: 28px;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.not-found-lede {
  margin: 0;
  max-width: 44ch;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.55;
}

.not-found-links {
  margin-top: 8px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}

.not-found-link {
  display: inline-block;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--field);
  color: var(--text);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  transition: background 140ms var(--ease), border-color 140ms var(--ease);
}

.not-found-link:hover {
  background: var(--surface-hover);
  border-color: var(--border-strong);
}

.preset-detail-header {
  position: relative;
  margin-bottom: 32px;
  padding-bottom: 24px;
}

.preset-detail-header::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 1px;
  background-image: linear-gradient(to right, var(--stroke-faint) 1px, transparent 1px);
  background-size: 2px 1px;
  background-repeat: repeat-x;
  pointer-events: none;
}

.preset-detail-title {
  margin: 0 0 8px;
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.preset-detail-blurb {
  margin: 0;
  color: var(--muted);
  font-size: 15px;
}

.preset-detail-body {
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.preset-detail-description {
  margin: 0;
  color: var(--text);
}

.preset-detail-subtitle {
  margin: 0 0 12px;
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}

.preset-detail-use-cases ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.preset-detail-use-cases li {
  position: relative;
  padding-left: 22px;
  color: var(--text);
}

.preset-detail-use-cases li::before {
  content: "→";
  position: absolute;
  left: 0;
  color: var(--muted);
}

.preset-detail-footer {
  position: relative;
  margin-top: 40px;
  padding-top: 24px;
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  justify-content: space-between;
  align-items: center;
  color: var(--muted);
  font-size: 14px;
}

.preset-detail-footer::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background-image: linear-gradient(to right, var(--stroke-faint) 1px, transparent 1px);
  background-size: 2px 1px;
  background-repeat: repeat-x;
  pointer-events: none;
}

.preset-detail-back {
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  padding-bottom: 2px;
}

.preset-detail-back:hover {
  border-bottom-color: var(--accent);
}

.preset-detail-share code {
  font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  background: var(--panel-2);
  padding: 2px 6px;
  border-radius: 8px;
  color: var(--muted);
}

@media (max-width: 600px) {
  .preset-detail {
    padding: 48px 20px 64px;
  }
  .preset-detail-title {
    font-size: 24px;
  }
}

/* Embed view — fills the viewport with no chrome. Used by /embed for
   iframe embeds in Framer / Webflow / external pages. The wrapper sets a
   stable height so parent iframes can use the postMessage resize protocol
   to size correctly. See src/components/embed-view.jsx. */
.embed-view {
  position: fixed;
  inset: 0;
  background: var(--bg);
  overflow: hidden;
}

/* When ?c=…&transparent=true is in the URL, the embed canvas paints
   without its own bg so the iframe is see-through to whatever the
   host page/iframe-wrapper sits behind it. Used by /examples to drop
   brand-tinted globes onto cream / off-white / brand-colored hero
   panels without the dark wrapper bleeding through. */
.embed-view[data-transparent="true"] {
  background: transparent;
}
.embed-view[data-transparent="true"] .embed-view-placeholder {
  background: transparent;
}
/* The WebGL canvas already clears at alpha 0, so the only thing painting
   a "black box" behind it is the .globe-background mount div's own
   var(--bg). Null it out (plus its soft radial glow ::before, which would
   otherwise leave a faint rectangular halo) so the spinning globe + its
   in-scene atmosphere glow composite straight onto the host page. */
.embed-view[data-transparent="true"] .globe-background {
  background: transparent;
}
.embed-view[data-transparent="true"] .globe-background::before {
  display: none;
}
/* The mobile breakpoint crops .globe-background up by 210px to clear the
   app's control-rail bottom sheet. The embed has no control rail, so when
   an embed iframe is narrower than that breakpoint (small dashboard cards,
   mobile) that crop wrongly collapses the globe to a ~30px sliver. The
   embed globe must always fill its iframe — this higher-specificity rule
   beats the mobile crop at every width. */
.embed-view .globe-background {
  inset: 0;
}

.embed-view-placeholder {
  position: fixed;
  inset: 0;
  background:
    radial-gradient(circle at 60% 50%, rgba(120, 140, 200, 0.04) 0%, transparent 38%),
    var(--bg);
}

.embed-view-fallback {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 32px;
  background: var(--bg);
  color: var(--text);
  font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  text-align: center;
}

.embed-view-fallback a {
  color: var(--text);
  text-decoration: underline;
}

/* Plugin-host shell — when the embed is loaded inside the Figma plugin
   (via ?plugin=figma), a bottom action bar surfaces an Insert button.
   Fixed-position over the canvas so it follows along when the embed
   resizes for different plugin panel heights. */
.embed-plugin-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
  display: flex;
  justify-content: center;
  padding: 12px 16px 16px;
  background: linear-gradient(180deg, transparent 0%, rgba(10, 10, 12, 0.86) 60%);
  pointer-events: none;
}

.embed-plugin-insert {
  pointer-events: auto;
  appearance: none;
  border: 0;
  border-radius: 10px;
  padding: 10px 18px;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: -0.005em;
  color: #0a0a0c;
  background: #f6f2ea;
  box-shadow:
    0 2px 0 rgba(0, 0, 0, 0.45),
    0 6px 18px rgba(0, 0, 0, 0.4);
  cursor: pointer;
  transition: transform 140ms var(--ease), box-shadow 140ms var(--ease), opacity 140ms var(--ease);
}

.embed-plugin-insert:hover {
  transform: translateY(-1px);
  box-shadow:
    0 3px 0 rgba(0, 0, 0, 0.45),
    0 10px 24px rgba(0, 0, 0, 0.45);
}

.embed-plugin-insert:active {
  transform: translateY(1px);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.45);
}

.embed-plugin-insert[disabled] {
  opacity: 0.6;
  cursor: progress;
  transform: none;
}

.app-shell::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(
      90deg,
      var(--vignette-strong),
      var(--vignette-mid) 24%,
      transparent 42%,
      transparent 58%,
      var(--vignette-mid) 76%,
      var(--vignette-strong)
    ),
    linear-gradient(180deg, var(--vignette-soft), var(--vignette-mid));
}

.map-background {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--preview-bg, var(--bg));
  cursor: grab;
  perspective: var(--map-perspective, 1000px);
  perspective-origin: center;
  touch-action: none;
  user-select: none;
}

.globe-background {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--preview-bg, var(--bg));
  cursor: grab;
  touch-action: none;
  user-select: none;
  animation: globe-background-enter 780ms cubic-bezier(0.2, 0.7, 0.2, 1) backwards;
}

/* One-shot entrance — runs the first time the lazy GlobeBackground
   resolves and the canvas takes over from the placeholder. The slight
   filter blur lets the dot field "focus in" rather than just popping
   on, which makes the handoff feel intentional. */
@keyframes globe-background-enter {
  from {
    opacity: 0;
    filter: blur(8px);
  }
  60% {
    opacity: 1;
    filter: blur(2px);
  }
  to {
    opacity: 1;
    filter: blur(0);
  }
}

/* Coordinated panel entrance — slides from the left a fraction after the
   globe entrance starts (120ms delay), so the canvas and chrome arrive
   together rather than the canvas blooming alone. transform-origin: top
   left already exists for the collapse animation; reuse it. */
@keyframes control-rail-enter {
  from {
    opacity: 0;
    transform: translateX(-18px) scale(0.985);
  }
  to {
    opacity: 1;
    transform: translateX(0) scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .globe-background,
  .control-rail {
    animation: none;
  }
}

.map-background-placeholder {
  position: fixed;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(circle at 60% 50%, rgba(120, 140, 200, 0.04) 0%, transparent 38%),
    var(--preview-bg, var(--bg));
}

/* Dev-only perf HUD. Rendered only in `vite dev` builds — Vite strips
   import.meta.env.DEV branches from the production bundle, so these styles
   are inert in deployed CSS but harmless. Sits in the top-right of the
   canvas; pointer-events: none lets globe drag/wheel events pass through. */
.perf-monitor {
  position: absolute;
  top: 12px;
  right: 12px;
  display: grid;
  grid-template-columns: auto auto;
  grid-template-columns: auto auto;
  gap: 2px 8px;
  padding: 0;
  background: transparent;
  color: var(--text);
  font: 700 10px/1.2 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: 0.02em;
  pointer-events: none;
  z-index: 10;
  border: 0;
  user-select: none;
  /* Background-colored halo so the readout stays legible against any
     canvas content (dotted globe, shader output, gradients) without
     needing an actual background chip. */
  text-shadow:
    0 0 6px var(--bg),
    0 0 3px var(--bg),
    0 0 1px var(--bg);
}

.perf-monitor-row {
  display: contents;
}

.perf-monitor-label {
  color: var(--dim);
  opacity: 0.7;
  text-transform: uppercase;
  font-size: 10px;
  align-self: center;
}

.perf-monitor-value {
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--dim);
  opacity: 0.7;
}

.map-background-placeholder::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 60%;
  width: 22vmin;
  height: 22vmin;
  margin: -11vmin 0 0 -11vmin;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background: radial-gradient(circle at 35% 35%, rgba(255, 255, 255, 0.04), transparent 60%);
  animation: globe-shimmer 2.4s ease-in-out infinite;
}

@keyframes globe-shimmer {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 0.8; }
}

.map-background-error {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 12px;
  padding: 24px;
  text-align: center;
  background: var(--preview-bg, var(--bg));
  color: var(--text, #e8e8ea);
  font-size: 15px;
}

.map-background-error p {
  margin: 0;
  max-width: 320px;
  opacity: 0.78;
}

/* Row of actions in the error fallback — Reload + Report this issue. */
.map-background-error-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
}

/* WebGL-unsupported fallback. Pre-flight: rendered instead of the canvas
   when hasWebGL() returns false on first mount. Three.js never loads in
   this path. Designed to feel intentional, not broken.
   Sits above the control panel + bottom chrome because none of those
   tools work without a live canvas — the user gets a clean still + a
   path to fix or report. */
.no-webgl {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: grid;
  place-items: center;
  padding: 32px 24px;
  overflow-y: auto;
  background: var(--bg);
  color: var(--text);
}

/* When the fallback is on, hide the control rail + bottom chrome since
   they're all canvas-dependent. Body gets `is-no-webgl` from App.jsx
   when hasWebGL() returns false — we use a class instead of CSS :has()
   so older browsers (Firefox < 121, Safari < 15.4) still get a clean
   fallback presentation. */
.is-no-webgl .control-panel,
.is-no-webgl .looks-bar,
.is-no-webgl .map-zoom,
.is-no-webgl .view-mode-switch,
.is-no-webgl .canvas-controls,
.is-no-webgl .perf-monitor {
  display: none !important;
}

.no-webgl-inner {
  max-width: 560px;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  text-align: center;
}

.no-webgl-image {
  width: 100%;
  max-width: 480px;
  height: auto;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--panel-2);
}

.no-webgl-title {
  margin: 4px 0 0;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.no-webgl-body {
  margin: 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--dim);
  max-width: 460px;
}

.no-webgl-details {
  align-self: stretch;
  text-align: left;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 14px;
  background: var(--panel);
  font-size: 13px;
  color: var(--dim);
}

.no-webgl-details summary {
  cursor: pointer;
  font-weight: 700;
  color: var(--text);
  padding: 2px 0;
}

.no-webgl-details ul {
  margin: 10px 0 4px;
  padding-left: 18px;
  line-height: 1.6;
}

.no-webgl-details li + li {
  margin-top: 6px;
}

.no-webgl-details code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  background: var(--panel-2);
  padding: 1px 5px;
  border-radius: 3px;
}

.no-webgl-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 4px;
}

/* Ghost button variant — used for the secondary "Report this" link
   next to a primary Reload button. Same dimensions + a11y as .button,
   transparent background, subtler border. */
.button-ghost {
  background: transparent !important;
  border-color: var(--border) !important;
  font-weight: 700 !important;
  text-decoration: none;
}

.button-ghost:hover {
  background: var(--panel-2) !important;
  border-color: var(--border-strong) !important;
}

.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;
}

/* Skip link — hidden offscreen until keyboard-focused, then animates into
   view top-left. WCAG 2.4.1 Bypass Blocks. The href="#globe-canvas" lands
   focus on the canvas wrapper so users can immediately interact with the
   globe via the existing keyboard handlers. */
.skip-link {
  position: fixed;
  top: 8px;
  left: 8px;
  z-index: 100;
  padding: 10px 14px;
  border-radius: 10px;
  background: var(--panel);
  color: var(--text);
  text-decoration: none;
  font-size: 14px;
  font-weight: 700;
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow);
  transform: translateY(-200%);
  transition: transform 180ms var(--ease);
}

.skip-link:focus,
.skip-link:focus-visible {
  transform: translateY(0);
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.globe-background::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Cool cyan core → blue bleed, matching the WebGL halo (#4c8bff) and the
     cyan canvas drop-shadow. No white/cream layer — a near-white radial
     here read as an off-palette "white glow" wash behind the hero globe. */
  background:
    radial-gradient(circle at 50% 48%, rgba(124, 210, 255, 0.05), transparent calc(var(--globe-glow-spread, 50%) * 0.7)),
    radial-gradient(circle at 50% 48%, rgba(76, 139, 255, 0.055), transparent var(--globe-glow-spread, 50%));
  opacity: var(--globe-bg-glow-opacity, 0.54);
  filter: blur(var(--globe-glow-blur, 0px));
}

.globe-background.look-borderless::before {
  background:
    radial-gradient(circle at 56% 42%, rgba(119, 222, 255, 0.16), transparent calc(var(--globe-glow-spread, 50%) * 0.55)),
    radial-gradient(circle at 46% 56%, rgba(168, 133, 255, 0.13), transparent calc(var(--globe-glow-spread, 50%) * 0.75)),
    radial-gradient(circle at 50% 50%, rgba(119, 222, 255, 0.05), transparent calc(var(--globe-glow-spread, 50%) * 1.05));
  mix-blend-mode: screen;
}

.globe-background canvas {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  outline: none;
  /* Spring-back from the drag scale lives here so release always animates
     even though .is-dragging removes the modifier instantly. */
  transition: transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1);
  transform-origin: 50% 50%;
  will-change: transform;
  /* Blur slider — multi-layer drop-shadow stack. Three Gaussian
     halos at progressive radii sum into a single smooth gradient
     that bleeds outward from the silhouette. Value computed in
     App.jsx so the radii + opacities can scale together with the
     slider. "none" when Glow is off → zero filter cost. */
  filter: var(--globe-canvas-halo, none);
}

.globe-background.is-dragging canvas {
  /* Stripe-style "picking it up" feel. Quick ease-out into the scale, then
     the spring above carries the release back to 1.0. */
  transform: scale(1.05);
  transition: transform 180ms cubic-bezier(0.2, 0.7, 0.2, 1);
}

/* Preset crossfade — when a preset is applied, the underlying state
   changes are synchronous (shader uniforms, colors, gradients all snap
   at once). Without this, switching presets reads as a hard cut. The
   fade dips canvas opacity to 40% in the first 40% of the animation
   (covering the swap) and rises back to 100% over the remaining 60%,
   so the user perceives a deliberate transition rather than a jump cut.
   `appliedLookId` is non-null for ~700ms after applyLook fires; the
   animation runs once and clears with the class.
   First-mount /looks/:id applications also fade — a fade-in from 40%
   on first paint feels considered. */
.app-shell.is-preset-applying .globe-background canvas {
  animation: preset-apply-crossfade 460ms cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes preset-apply-crossfade {
  0% { opacity: 1; }
  40% { opacity: 0.4; }
  100% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .app-shell.is-preset-applying .globe-background canvas {
    animation: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .globe-background canvas,
  .globe-background.is-dragging canvas {
    transition: none;
    transform: none;
  }
}

.globe-background canvas:focus-visible {
  outline: 2px solid var(--accent, #f6f2ea);
  outline-offset: -4px;
  border-radius: 8px;
}

/* Reduced-transparency: simplify backdrop filters so the panel doesn't lag
   on systems where compositing transparent layers is expensive. */
@media (prefers-reduced-transparency: reduce) {
  .control-rail,
  .panel-toggle,
  .social-link,
  .map-zoom-controls,
  .view-mode-switch {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    background: var(--panel) !important;
  }
}

.globe-background.look-borderless canvas {
  filter: saturate(1.2) contrast(1.06);
}

.globe-background.effect-chromatic canvas {
  filter: saturate(1.16) contrast(1.08);
}

.globe-background.effect-crt canvas {
  filter: contrast(1.1) saturate(1.12);
  animation: crt-flicker 3.6s steps(2, end) infinite;
}

.globe-background.effect-halftone canvas,
.globe-background.effect-pixel canvas,
.globe-background.effect-threshold canvas {
  filter: contrast(1.2);
}

.globe-background.is-dragging {
  cursor: grabbing;
}

.map-background.is-passive,
.globe-background.is-passive {
  pointer-events: none;
}

.app-shell.is-view-transitioning .globe-background,
.app-shell.is-view-transitioning .map-svg-layer,
.app-shell.is-view-transitioning .shader-canvas {
  will-change: opacity, transform, filter, clip-path;
}

/* Cinematic flat↔globe transition flourish — a brief saturation /
   brightness / blur pulse that peaks mid-morph. The duration matches
   GLOBE_MORPH_DURATION in src/config/globe-settings.js (1700ms). The
   Three.js scene handles the structural morph (dots wrapping, FOV
   breath, scale dip, Z roll); this layer adds the atmospheric pulse on
   top so the whole transition reads as one cohesive moment rather than
   a simple parameter swap. */
.app-shell.is-view-transitioning .globe-background canvas,
.app-shell.is-view-transitioning .shader-canvas {
  animation: morph-pulse 1700ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
}

@keyframes morph-pulse {
  0% {
    filter: saturate(1) brightness(1) blur(0);
  }
  35% {
    filter: saturate(1.18) brightness(1.06) blur(0.6px);
  }
  50% {
    filter: saturate(1.22) brightness(1.08) blur(0.8px);
  }
  70% {
    filter: saturate(1.12) brightness(1.04) blur(0.4px);
  }
  100% {
    filter: saturate(1) brightness(1) blur(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .app-shell.is-view-transitioning .globe-background canvas,
  .app-shell.is-view-transitioning .shader-canvas {
    animation: none;
  }
}

.app-shell.is-transparent-preview,
.app-shell.is-transparent-preview .map-background,
.app-shell.is-transparent-preview .globe-background {
  background-color: #f4f4f4;
  background-image: conic-gradient(#b8b8b8 25%, #f2f2f2 0 50%, #b8b8b8 0 75%, #f2f2f2 0);
  background-position: 0 0;
  background-size: 20px 20px;
}

.app-shell.is-transparent-preview::after,
.app-shell.is-transparent-preview.is-globe-mode::after,
.app-shell.is-transparent-preview.is-borderless-globe::after {
  background:
    linear-gradient(90deg, rgba(255, 255, 255, 0.18), transparent 24%, transparent 76%, rgba(0, 0, 0, 0.06)),
    linear-gradient(180deg, rgba(255, 255, 255, 0.12), transparent 46%, rgba(0, 0, 0, 0.04));
}

.app-shell.is-globe-mode::after {
  background:
    linear-gradient(
      90deg,
      var(--vignette-strong),
      var(--vignette-mid) 24%,
      transparent 47%,
      transparent 63%,
      var(--vignette-mid) 79%,
      var(--vignette-strong)
    ),
    linear-gradient(180deg, var(--vignette-soft), var(--vignette-mid));
}

.app-shell.is-borderless-globe::after {
  background:
    linear-gradient(
      90deg,
      rgba(5, 9, 18, 0.58),
      rgba(5, 9, 18, 0.13) 24%,
      transparent 47%,
      transparent 63%,
      rgba(10, 7, 23, 0.17) 79%,
      rgba(5, 7, 15, 0.58)
    ),
    linear-gradient(180deg, rgba(19, 31, 59, 0.03), rgba(9, 10, 20, 0.2));
}

.map-background .dot-map {
  grid-area: 1 / 1;
  display: block;
  width: min(1320px, 106vw);
  height: auto;
  max-height: 88svh;
  object-fit: contain;
  opacity: 0.92;
  transform: translate3d(var(--map-offset-x, 0px), var(--map-offset-y, 0px), 0)
    rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) scale(var(--map-zoom, 1));
  transform-origin: center;
  transform-style: preserve-3d;
  transition:
    filter 180ms ease,
    opacity 180ms ease,
    transform 280ms cubic-bezier(0.34, 1.3, 0.5, 1);
  will-change: filter, transform;
}

.map-svg-layer {
  grid-area: 1 / 1;
  z-index: 1;
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
}

.shader-canvas {
  grid-area: 1 / 1;
  z-index: 2;
  display: block;
  width: min(1320px, 106vw);
  height: auto;
  max-height: 88svh;
  object-fit: contain;
  pointer-events: none;
  transform: translate3d(var(--map-offset-x, 0px), var(--map-offset-y, 0px), 0)
    rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) scale(var(--map-zoom, 1));
  transform-origin: center;
  transform-style: preserve-3d;
  transition:
    opacity 180ms ease,
    transform 280ms cubic-bezier(0.34, 1.3, 0.5, 1);
  will-change: opacity, transform;
}

.map-background.effect-bloom .dot-map {
  filter: drop-shadow(0 0 var(--shader-glow, 8px) rgba(246, 242, 234, 0.18))
    drop-shadow(0 0 var(--shader-glow-wide, 16px) rgba(255, 255, 255, 0.18));
}

.map-background.effect-chromatic .dot-map {
  filter: drop-shadow(var(--shader-split, 7px) 0 0 rgba(255, 60, 148, 0.64))
    drop-shadow(var(--shader-split-neg, -7px) 0 0 rgba(64, 224, 255, 0.6));
}

.map-background.effect-crt .dot-map {
  filter: saturate(1.2) drop-shadow(0 0 var(--shader-glow, 8px) rgba(246, 242, 234, 0.16));
  animation: crt-flicker 3.6s steps(2, end) infinite;
}

.map-background.effect-halftone .dot-map {
  filter: contrast(1.18) saturate(1.05);
}

.map-background.effect-pixel .dot-map {
  filter: contrast(1.36);
  image-rendering: pixelated;
}

.map-background.effect-threshold .dot-map {
  filter: grayscale(0.6) contrast(2.2);
}

.map-background.has-webgl-shader .dot-map {
  opacity: 0.012;
  filter: none;
  animation: none;
}

.map-background.is-dragging,
.map-background.is-dragging .map-dot {
  cursor: grabbing;
}

.map-background.is-dragging .dot-map,
.map-background.is-dragging .shader-canvas {
  transition: none;
}

.map-dot {
  cursor: pointer;
  stroke: rgba(0, 0, 0, 0);
  stroke-width: 1;
  transition: opacity 120ms ease;
}

.map-dot:hover {
  opacity: 0.72;
}

@keyframes crt-flicker {
  0%,
  100% {
    opacity: 0.94;
  }

  48% {
    opacity: 0.9;
  }

  52% {
    opacity: 0.98;
  }
}

.space-background {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  background: #03030a;
}

.space-background-nebula {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 60% 50% at 28% 38%, rgba(36, 22, 78, 0.55) 0%, rgba(8, 6, 24, 0.18) 55%, transparent 100%),
    radial-gradient(ellipse 55% 45% at 74% 68%, rgba(8, 38, 64, 0.55) 0%, rgba(4, 14, 32, 0.18) 55%, transparent 100%),
    radial-gradient(ellipse 40% 35% at 55% 18%, rgba(64, 18, 50, 0.32) 0%, transparent 100%);
}

.space-background-svg {
  position: absolute;
  inset: 0;
  display: block;
}

.space-background-vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, transparent 55%, rgba(0, 0, 0, 0.5) 100%);
}

.app-shell.is-space-background {
  background: #03030a;
}

.app-shell.is-flow-background {
  background: #080714;
}

.app-shell.is-space-background .globe-background,
.app-shell.is-space-background .map-background,
.app-shell.is-space-background .map-background-placeholder,
.app-shell.is-space-background .map-background-error,
.app-shell.is-flow-background .globe-background,
.app-shell.is-flow-background .map-background,
.app-shell.is-flow-background .map-background-placeholder,
.app-shell.is-flow-background .map-background-error {
  background: transparent;
}

.control-rail {
  position: fixed;
  top: 16px;
  bottom: auto;
  left: 16px;
  z-index: 2;
  width: min(398px, calc(100vw - 32px));
  max-height: calc(100vh - 32px);
  display: grid;
  align-content: start;
  gap: 10px;
  /* Bottom padding gives focus outlines + native select hover shadows
     room to render at the last child without getting clipped by the
     rail's overflow: hidden auto. */
  padding: 4px 10px 18px;
  background: var(--panel);
  box-shadow: var(--shadow-lg);
  backdrop-filter: blur(18px);
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow: hidden auto;
  scrollbar-width: thin;
  scrollbar-color: var(--panel-3) transparent;
  transform-origin: top left;
  animation: control-rail-enter 540ms cubic-bezier(0.2, 0.7, 0.2, 1) 120ms backwards;
  transition:
    transform 320ms var(--ease),
    opacity 220ms var(--ease),
    filter 220ms var(--ease);
}

.control-rail.is-collapsed {
  transform: scale(0.6);
  opacity: 0;
  filter: blur(6px);
  pointer-events: none;
}

.panel-header-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.looks-bar {
  display: flex;
  gap: 8px;
  list-style: none;
  /* Negative horizontal margins extend the scroll container all the way
     to the card's inner edges (cancelling the .control-panel's 10px
     padding), so chips fade in and out at the card edges instead of 10px
     inside them. Padding then pushes the first chip back in to 18px from
     the card edge — same column as the panel-header text, looks-section
     summaries, and every OptionRow below. */
  margin: 0 -10px;
  padding: 2px 18px;
  /* overflow-x: auto implicitly forces overflow-y to non-visible (CSS
     spec rule — `auto` + `visible` is not a valid combination), which
     was clipping the chip hover shadow that extends ~16px below the
     chip. `overflow-y: clip` + `overflow-clip-margin` lets the shadow
     render 24px beyond the bar's vertical bounds while keeping the
     horizontal scroll behaviour. */
  overflow-x: auto;
  overflow-y: clip;
  overflow-clip-margin: 24px;
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* Soft edge fades hint at scrollable content. Mask gets composed from the
     two attribute states (at-start hides the left fade, at-end hides the
     right fade), so the indicator only appears in the direction you can
     actually scroll. */
  mask-image: linear-gradient(to right, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
}

.looks-item {
  flex: 0 0 auto;
  display: inline-flex;
}

.looks-bar[data-at-start="true"] {
  mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 24px), transparent 100%);
  -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 24px), transparent 100%);
}

.looks-bar[data-at-end="true"] {
  mask-image: linear-gradient(to right, transparent 0, #000 24px, #000 100%);
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 24px, #000 100%);
}

.looks-bar[data-at-start="true"][data-at-end="true"] {
  mask-image: none;
  -webkit-mask-image: none;
}

.looks-bar::-webkit-scrollbar {
  display: none;
}

.looks-chip {
  min-height: 36px;
  padding: 0 14px 0 4px;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--field);
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  overflow: hidden;
  transform: scale(1);
  transition: background 180ms var(--ease), border-color 180ms var(--ease),
    color 180ms var(--ease), transform 140ms cubic-bezier(0.2, 0.7, 0.2, 1);
}

/* Real-canvas thumbnail rendered on the left of each chip. Same
   pixel-corner notch as the kbd / command-palette-thumb surfaces;
   without the explicit width the <img> would render at its natural
   512×512 and blow up the chip. */
.looks-chip-thumb {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  background: var(--bg);
  clip-path: polygon(
    0 4px, 4px 4px, 4px 0,
    calc(100% - 4px) 0, calc(100% - 4px) 4px, 100% 4px,
    100% calc(100% - 4px), calc(100% - 4px) calc(100% - 4px), calc(100% - 4px) 100%,
    4px 100%, 4px calc(100% - 4px), 0 calc(100% - 4px)
  );
  overflow: hidden;
}

.looks-chip-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
}

.looks-chip-label {
  display: inline-block;
}

/* Chip's left-cell preview. Solid black backdrop with a 1px stroke on
   the right that separates it from the label area, like a postcard
   stamp on the chip. Hosts the globe SVG inside, no extra padding. */
.looks-chip-preview {
  flex: 0 0 auto;
  align-self: stretch;
  width: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #000;
  border-right: 1px solid var(--border);
  filter: saturate(0.8);
  transition: filter 220ms var(--ease), transform 220ms var(--ease);
}

.looks-chip:hover .looks-chip-preview,
.looks-chip.is-applied .looks-chip-preview {
  filter: saturate(1);
}

.looks-chip-preview-globe {
  width: 100%;
  height: 100%;
  display: block;
  transition: filter 220ms var(--ease);
}

/* Bitmap preview path — used when a preset carries a `previewImage`
   field that points at a PNG / WebP rendered from the actual canvas.
   Pixel-perfect representation; the SVG approximation is the fallback.
   No hover-scale on this path (the bitmap reads cleanly on its own —
   the SVG version needed the lift for affordance). Crop is biased
   slightly left of centre so the sphere sits a touch left in the chip
   instead of dead-centre. */
.looks-chip-preview-image {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: 30% center;
  transition: filter 220ms var(--ease);
  user-select: none;
  -webkit-user-drag: none;
}

.looks-chip:hover .looks-chip-preview-image {
  filter: brightness(1.06) saturate(1.06);
}

.looks-chip.is-applied .looks-chip-preview-image {
  filter: brightness(1.05) saturate(1.05);
}

/* Inner globe content (sphere shading, graticule, dots / shader overlay).
   Only filter animates on hover — no scale or tilt. */
.looks-chip-preview-globe .globe-preview-content {
  transition: filter 220ms var(--ease);
}

.looks-chip:hover .looks-chip-preview-globe .globe-preview-content {
  filter: brightness(1.08) saturate(1.06);
}

.looks-chip.is-applied .looks-chip-preview-globe .globe-preview-content {
  filter: brightness(1.06) saturate(1.05);
}

.looks-chip:hover {
  border-color: var(--border-strong);
  background: var(--panel-3);
  color: var(--text);
  transform: translateY(-1px);
  box-shadow: 0 6px 18px -8px rgba(0, 0, 0, 0.55);
}

.looks-chip:active {
  transform: scale(0.94);
  box-shadow: none;
}

/* Persistent "currently active preset" state — solid border + accent fill +
   contrasting text. Stays on while this preset is the one applied to the
   canvas, separate from the brief is-applied flash below. */
.looks-chip.is-current {
  border-style: solid;
  border-color: var(--accent);
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: 0 0 0 1px var(--accent), 0 8px 24px -10px rgba(246, 242, 234, 0.32);
}

.looks-chip.is-current:hover {
  background: var(--text);
  border-color: var(--text);
  color: var(--on-accent);
  box-shadow: 0 0 0 1px var(--text), 0 10px 26px -10px rgba(246, 242, 234, 0.4);
}

/* Sheen sweep across the active chip — a slow, low-contrast diagonal
   gradient that travels left → right on a 7s loop. Reads as a subtle
   "this is alive" cue without distracting from the canvas behind it.
   Respects prefers-reduced-motion: animation pauses to a single static
   frame for users who don't want motion. */
.looks-chip.is-current::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 30%,
    rgba(255, 255, 255, 0.14) 50%,
    transparent 70%
  );
  background-size: 220% 100%;
  background-position: -110% 0;
  animation: looks-chip-sheen 6.4s ease-in-out infinite;
  pointer-events: none;
  mix-blend-mode: overlay;
}

.looks-chip {
  position: relative;
}

@keyframes looks-chip-sheen {
  0%, 12% { background-position: -110% 0; }
  62%, 100% { background-position: 220% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .looks-chip.is-current::after {
    animation: none;
  }
}

/* Brief glow + lift when a preset is freshly applied — confirms the action
   without delaying anything. Animation is single-shot; the .is-applied class
   auto-clears after 700ms. */
.looks-chip.is-applied {
  animation: looks-chip-apply 620ms cubic-bezier(0.2, 0.7, 0.2, 1);
  color: var(--text);
  border-color: rgba(246, 242, 234, 0.45);
}

.looks-chip.is-applied .looks-chip-preview {
  animation: looks-chip-pop 620ms cubic-bezier(0.2, 0.7, 0.2, 1);
}

@keyframes looks-chip-apply {
  0% {
    transform: translateY(0) scale(1);
    box-shadow: 0 0 0 0 rgba(246, 242, 234, 0);
  }
  35% {
    transform: translateY(-1px) scale(1.025);
    box-shadow: 0 0 0 4px rgba(246, 242, 234, 0.16);
  }
  100% {
    transform: translateY(0) scale(1);
    box-shadow: 0 0 0 0 rgba(246, 242, 234, 0);
  }
}

@keyframes looks-chip-pop {
  0% { transform: scale(1); }
  35% { transform: scale(1.18) rotate(-3deg); }
  100% { transform: scale(1.06) rotate(0); }
}

/* Reset icon does a full counter-clockwise turn when the action fires.
   Matches the chevron's RotateCcw direction — feels like the settings
   physically rewinding. */
.panel-reset-button.is-resetting .lucide-rotate-ccw {
  animation: reset-spin 520ms cubic-bezier(0.4, 0, 0.2, 1);
}

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

/* Shuffle does a brief tumble — the arrows visually cross over and back, which
   evokes the "rearranging" semantics of the action. */
.panel-shuffle-button.is-shuffling .lucide-shuffle {
  animation: shuffle-tumble 620ms var(--ease);
}

@keyframes shuffle-tumble {
  0% { transform: rotate(0) scale(1); }
  30% { transform: rotate(-18deg) scale(1.1); }
  60% { transform: rotate(18deg) scale(1.1); }
  100% { transform: rotate(0) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .panel-shuffle-button.is-shuffling .lucide-shuffle,
  .panel-reset-button.is-resetting .lucide-rotate-ccw {
    animation: none;
  }
}

.panel-icon-button {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 180ms var(--ease), color 180ms var(--ease);
}

.panel-icon-button:hover {
  background: var(--panel-3);
  color: var(--text);
}

/* Primary variant — for the action the user most often wants from the
   panel header (export). Gives it filled chrome so it reads as a button
   instead of a ghost icon next to the other passive controls. */
.panel-icon-button--primary {
  background: var(--accent);
  color: var(--on-accent);
  border: 1px solid var(--accent);
  box-shadow: var(--shadow-sm);
}

.panel-icon-button--primary:hover {
  background: var(--text);
  color: var(--on-accent);
  border-color: var(--text);
}

.export-info {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 2px 4px;
  margin-bottom: 4px;
}

.export-info::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 1px;
  background-image: linear-gradient(to right, var(--stroke-faint) 1px, transparent 1px);
  background-size: 2px 1px;
  background-repeat: repeat-x;
  pointer-events: none;
}

.export-info-label {
  color: var(--text);
  font-size: 13px;
  font-weight: 700;
}

.export-info-value {
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

.export-info-x {
  color: var(--dim);
  margin: 0 2px;
}

.export-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  place-items: center;
  padding: 24px;
  background: transparent;
  animation: export-modal-fade-in 180ms var(--ease);
}

.export-modal {
  width: min(640px, 100%);
  max-height: calc(100vh - 48px);
  display: flex;
  flex-direction: column;
  /* Same translucent panel tint the control rail / color picker /
     dropdown use, so the modal reads as part of the same surface
     family instead of a noticeably lighter card. */
  background: var(--panel);
  backdrop-filter: blur(36px) saturate(160%);
  -webkit-backdrop-filter: blur(36px) saturate(160%);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
  animation: export-modal-rise-in 260ms cubic-bezier(0.2, 0.7, 0.2, 1);
}

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

@keyframes export-modal-rise-in {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

.export-modal-header {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  /* Left edge aligns with body (24px); right is a touch tighter to
     give the close button breathing room without orphaning it. */
  padding: 20px 20px 20px 24px;
}

.export-modal-title {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}

.export-modal-close {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 180ms var(--ease), color 180ms var(--ease), border-color 180ms var(--ease);
}

.export-modal-close:hover {
  background: var(--panel-3);
  color: var(--text);
  border-color: var(--border-strong);
}

.export-modal-tabs {
  position: relative;
  display: flex;
  gap: 4px;
  /* Sides align with header + body (24px) so the dashed divider runs
     across the modal's full content column without jogging in/out. */
  padding: 12px 24px 0;
}

/* Dashed divider line under the tab strip. */
.export-modal-tabs::before {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 1px;
  background-image: linear-gradient(to right, var(--stroke-faint) 1px, transparent 1px);
  background-size: 2px 1px;
  background-repeat: repeat-x;
  pointer-events: none;
}

/* Single shared active-tab underline indicator. Position + width are
   set as CSS variables from React after measuring the active tab; the
   transform-based transition makes it slide smoothly when the user
   switches tabs, rather than hard-cutting from one tab to the next. */
.export-modal-tabs::after {
  content: "";
  position: absolute;
  bottom: -1px;
  left: 0;
  height: 2px;
  width: var(--tab-indicator-width, 0);
  background: var(--accent);
  border-radius: 1px;
  opacity: var(--tab-indicator-opacity, 0);
  transform: translateX(var(--tab-indicator-left, 0));
  transition:
    transform 280ms cubic-bezier(0.4, 0, 0.2, 1),
    width 280ms cubic-bezier(0.4, 0, 0.2, 1),
    opacity 180ms ease;
  pointer-events: none;
  z-index: 1;
}

@media (prefers-reduced-motion: reduce) {
  .export-modal-tabs::after {
    transition: opacity 180ms ease;
  }
}

.shortcuts-overlay-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 24px;
  background: transparent;
  animation: export-modal-fade-in 180ms var(--ease);
}

.shortcuts-overlay {
  width: min(380px, 100%);
  display: flex;
  flex-direction: column;
  /* Matches the rest of the surface card family — same translucent
     panel tint in dark + light themes. */
  background: var(--panel);
  backdrop-filter: blur(36px) saturate(160%);
  -webkit-backdrop-filter: blur(36px) saturate(160%);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
  animation: export-modal-rise-in 260ms cubic-bezier(0.2, 0.7, 0.2, 1);
}

.shortcuts-overlay-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 16px 12px 20px;
}

.shortcuts-overlay-title {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.005em;
  color: var(--text);
}

.shortcuts-overlay-close {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 180ms var(--ease), color 180ms var(--ease), border-color 180ms var(--ease);
}

.shortcuts-overlay-close:hover {
  background: var(--panel-3);
  color: var(--text);
  border-color: var(--border-strong);
}

.shortcuts-overlay-list {
  margin: 0;
  padding: 4px 8px 16px;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.shortcuts-overlay-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 12px;
  border-radius: 8px;
  color: var(--muted);
  font-size: 13px;
}

.shortcuts-overlay-row:hover {
  background: var(--surface-hover);
  color: var(--text);
}

/* Physical-keyboard-key look — same recipe as every other kbd in
   the app. See .docs-shortcut-row kbd block for full reasoning. */
.shortcuts-overlay-key {
  flex: 0 0 auto;
  min-width: 28px;
  height: 26px;
  padding: 0 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background:
    linear-gradient(var(--panel-3), var(--panel-3)),
    var(--bg);
  color: var(--text);
  font-family: var(--font-mono-2);
  font-size: 12px;
  font-weight: 700;
  transform: translateY(-1px);
  clip-path: polygon(
    0 4px,
    4px 4px,
    4px 0,
    calc(100% - 4px) 0,
    calc(100% - 4px) 4px,
    100% 4px,
    100% calc(100% - 4px),
    calc(100% - 4px) calc(100% - 4px),
    calc(100% - 4px) 100%,
    4px 100%,
    4px calc(100% - 4px),
    0 calc(100% - 4px)
  );
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

.shortcuts-overlay-label {
  flex: 1 1 auto;
}

/* About modal — opens from the info icon in the panel header. Borrows
   the shortcuts-overlay frosted-glass card pattern; body is two short
   sections (the project + the maker) with link rows instead of the
   keyboard-row list. */
.about-overlay-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 24px;
  background: transparent;
  animation: export-modal-fade-in 180ms var(--ease);
}

.about-overlay {
  width: min(420px, 100%);
  display: flex;
  flex-direction: column;
  /* Matches the panel / picker / palette / export modal — one
     surface family across every overlay card. */
  background: var(--panel);
  backdrop-filter: blur(36px) saturate(160%);
  -webkit-backdrop-filter: blur(36px) saturate(160%);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
  animation: export-modal-rise-in 260ms cubic-bezier(0.2, 0.7, 0.2, 1);
}

.about-overlay-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 16px 12px 20px;
}

.about-overlay-title {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.005em;
  color: var(--text);
}

.about-overlay-close {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 180ms var(--ease), color 180ms var(--ease), border-color 180ms var(--ease);
}

.about-overlay-close:hover {
  background: var(--panel-3);
  color: var(--text);
  border-color: var(--border-strong);
}

.about-overlay-body {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 4px 20px 20px;
}

/* Brand mark at the top of the body — left-aligned so it anchors the
   reading order with the section titles below (which are also
   left-aligned). Slight breathing room above and below so it reads as
   a hero element separate from the sections below. Uses the same
   DottedGlobe icon as the panel header. */
.about-logo {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  padding: 12px 0 4px;
  color: var(--text);
}

.about-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.about-section + .about-section {
  /* Dashed-divider language used elsewhere in the app (1px-on / 1px-off
     via the linear-gradient + 2px bg-size technique). Separates the two
     sections without adding visual weight. */
  padding-top: 16px;
  background-image: linear-gradient(to right, var(--stroke-faint) 50%, transparent 50%);
  background-repeat: repeat-x;
  background-size: 4px 1px;
  background-position: top left;
}

.about-section-title {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text);
}

/* Maker section — avatar + heading + bio in a horizontal layout, with
   the avatar pinned to the top-left so it reads alongside the heading.
   On narrow modals the section stays readable because the text column
   wraps within its remaining space. */
.about-maker {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.about-avatar {
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  /* Slight indent below the heading row's baseline so the avatar
     visually aligns with the heading's cap height. */
  margin-top: 2px;
  object-fit: cover;
}

.about-maker-text {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.about-section-text {
  margin: 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--muted);
}

/* About modal link list — 2-column grid so links line up evenly
   instead of wrapping at arbitrary points. Each link sits as its
   own row-half with consistent height + padding so the modal reads
   as an organized index rather than a chip cloud. */
.about-section-links {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  margin-top: 8px;
}

.about-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  padding: 6px 10px;
  background: var(--field);
  color: var(--text);
  font-size: 12px;
  font-weight: 700;
  text-decoration: none;
  transition:
    background 180ms var(--ease),
    color 180ms var(--ease);
}

.about-link:hover,
.about-link:focus-visible {
  background: rgba(255, 255, 255, 0.09);
  color: var(--text);
}

.about-link svg {
  flex: 0 0 auto;
  opacity: 0.75;
}

.about-link:hover svg,
.about-link:focus-visible svg {
  opacity: 1;
}

/* Arrow glyph styling for the internal-link spans — kept as a
   simple character so the external-link icons (Github / Twitter
   SVGs) and internal-link arrows visually rhyme at the same size. */
.about-link > span[aria-hidden="true"] {
  display: inline-flex;
  width: 14px;
  justify-content: center;
  color: var(--dim);
  font-weight: 700;
}

.about-link:hover > span[aria-hidden="true"],
.about-link:focus-visible > span[aria-hidden="true"] {
  color: var(--text);
}

@media (max-width: 460px) {
  .about-section-links {
    grid-template-columns: 1fr;
  }
}

.follow-tooltip {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 60;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
  box-shadow: var(--shadow);
  color: var(--text);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate(0, 0);
  transition: opacity 120ms var(--ease);
}

.follow-tooltip.is-visible {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .follow-tooltip {
    transition: none;
  }
}

.keyboard-hint {
  position: fixed;
  left: 50%;
  bottom: 28px;
  z-index: 55;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px 8px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel);
  box-shadow: var(--shadow);
  color: var(--text);
  font-size: 13px;
  font-weight: 700;
  pointer-events: none;
  transform: translateX(-50%);
  animation: keyboard-hint-pop 1400ms var(--ease);
}

.keyboard-hint-key {
  min-width: 24px;
  height: 22px;
  padding: 0 7px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background:
    linear-gradient(var(--panel-3), var(--panel-3)),
    var(--bg);
  color: var(--text);
  font-family: var(--font-mono-2);
  font-size: 12px;
  font-weight: 700;
  transform: translateY(-1px);
  clip-path: polygon(
    0 4px,
    4px 4px,
    4px 0,
    calc(100% - 4px) 0,
    calc(100% - 4px) 4px,
    100% 4px,
    100% calc(100% - 4px),
    calc(100% - 4px) calc(100% - 4px),
    calc(100% - 4px) 100%,
    4px 100%,
    4px calc(100% - 4px),
    0 calc(100% - 4px)
  );
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

@keyframes keyboard-hint-pop {
  0% {
    opacity: 0;
    transform: translate(-50%, 6px) scale(0.96);
  }
  12% {
    opacity: 1;
    transform: translate(-50%, 0) scale(1);
  }
  85% {
    opacity: 1;
    transform: translate(-50%, 0) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, 4px) scale(0.98);
  }
}

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

/* First-visit onboarding hint — a single floating pill that surfaces
   the two highest-leverage shortcuts (S and [ ]). Sits at the top of
   the canvas area so it doesn't compete with the keyboard-hint toast
   at the bottom. Auto-dismisses on first interaction or after 12s. */
.onboarding-hint {
  position: fixed;
  top: 16px;
  left: 50%;
  z-index: 30;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.01em;
  pointer-events: none;
  box-shadow: 0 10px 28px -12px rgba(0, 0, 0, 0.5);
  transform: translateX(-50%);
  animation: onboarding-hint-in 460ms cubic-bezier(0.2, 0.7, 0.2, 1) backwards;
}

.onboarding-hint kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 22px;
  padding: 0 7px;
  background:
    linear-gradient(var(--panel-3), var(--panel-3)),
    var(--bg);
  color: var(--text);
  font-family: var(--font-mono-2);
  font-size: 11px;
  font-weight: 700;
  transform: translateY(-1px);
  clip-path: polygon(
    0 4px,
    4px 4px,
    4px 0,
    calc(100% - 4px) 0,
    calc(100% - 4px) 4px,
    100% 4px,
    100% calc(100% - 4px),
    calc(100% - 4px) calc(100% - 4px),
    calc(100% - 4px) 100%,
    4px 100%,
    4px calc(100% - 4px),
    0 calc(100% - 4px)
  );
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

.onboarding-hint-sep {
  opacity: 0.5;
  margin: 0 2px;
}

@keyframes onboarding-hint-in {
  from {
    opacity: 0;
    transform: translate(-50%, -6px);
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

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

/* Cmd+K command palette. Same frosted-glass language as the other
   modals; sits a touch higher (top: 18vh) so it reads as a docked
   surface rather than a centered modal — the canonical command-palette
   position. */
.command-palette-backdrop {
  position: fixed;
  inset: 0;
  z-index: 65;
  display: grid;
  place-items: start center;
  padding: 18vh 24px 24px;
  background: transparent;
  animation: export-modal-fade-in 180ms var(--ease);
}

.command-palette {
  width: min(560px, 100%);
  max-height: calc(100vh - 28vh);
  display: flex;
  flex-direction: column;
  /* Matches .control-rail / .color-picker / .export-modal so every
     surface-card reads as one design family. */
  background: var(--panel);
  backdrop-filter: blur(36px) saturate(160%);
  -webkit-backdrop-filter: blur(36px) saturate(160%);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
  animation: export-modal-rise-in 260ms cubic-bezier(0.2, 0.7, 0.2, 1);
}

.command-palette-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 14px 12px;
  border-bottom: 1px solid var(--border);
}

.command-palette-input {
  flex: 1 1 auto;
  min-width: 0;
  background: transparent;
  border: 0;
  outline: none;
  color: var(--text);
  font-size: 15px;
  font-weight: 600;
  padding: 4px 6px;
}

.command-palette-input::placeholder {
  color: var(--muted);
  font-weight: 500;
}

.command-palette-close {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 140ms var(--ease), color 140ms var(--ease);
}

.command-palette-close:hover {
  background: var(--surface-hover);
  color: var(--text);
}

.command-palette-list {
  flex: 1 1 auto;
  overflow-y: auto;
  margin: 0;
  padding: 6px;
  list-style: none;
  scrollbar-width: thin;
  scrollbar-color: var(--panel-3) transparent;
}

.command-palette-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: 8px;
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background 100ms linear;
}

.command-palette-row.is-selected {
  background: var(--surface-hover);
}

.command-palette-group {
  flex: 0 0 auto;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--field);
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

/* Real-shader thumbnail rendered to the left of preset-apply rows.
   Uses the same pixel-corner notch vocabulary as kbd / chip surfaces;
   sized to read as a glance preview without crowding the row text. */
.command-palette-thumb {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  background: var(--bg);
  clip-path: polygon(
    0 4px,
    4px 4px,
    4px 0,
    calc(100% - 4px) 0,
    calc(100% - 4px) 4px,
    100% 4px,
    100% calc(100% - 4px),
    calc(100% - 4px) calc(100% - 4px),
    calc(100% - 4px) 100%,
    4px 100%,
    4px calc(100% - 4px),
    0 calc(100% - 4px)
  );
  overflow: hidden;
}

.command-palette-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
}

.command-palette-label {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.command-palette-kbd,
.command-palette-footer kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 24px;
  padding: 0 8px;
  background:
    linear-gradient(var(--panel-3), var(--panel-3)),
    var(--bg);
  font-family: var(--font-mono-2);
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
  transform: translateY(-1px);
  clip-path: polygon(
    0 4px,
    4px 4px,
    4px 0,
    calc(100% - 4px) 0,
    calc(100% - 4px) 4px,
    100% 4px,
    100% calc(100% - 4px),
    calc(100% - 4px) calc(100% - 4px),
    calc(100% - 4px) 100%,
    4px 100%,
    4px calc(100% - 4px),
    0 calc(100% - 4px)
  );
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

.command-palette-empty {
  padding: 14px 12px;
  color: var(--muted);
  font-size: 13px;
  text-align: center;
}

.command-palette-footer {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 14px;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
}

.command-palette-footer span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.export-modal-tab {
  position: relative;
  padding: 12px 20px 10px;
  border: 0;
  background: transparent;
  color: var(--dim);
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  transition: color 160ms var(--ease);
}

.export-modal-tab:hover {
  color: var(--muted);
}

.export-modal-tab.is-active {
  color: var(--text);
}

.export-modal-body {
  /* The body is the scrollable wrapper. The actual vertical layout +
     gap live on .export-modal-pane below — the body has exactly ONE
     child (the keyed pane), so a flex+gap here would have nothing to
     apply between. */
  padding: 24px;
  overflow: auto;
}

/* Mount-fade on tab content + the real vertical layout for the
   section stack. Keyed re-render per tab swap (see export-modal.jsx
   <div key={tab}>) means the animation fires on each tab change.
   Cheap opacity + tiny lift; respects reduced motion.
   The 28px gap is what spaces the actual sections (Aspect pills,
   Quality pills, Dimension inputs, caption, CTA) — putting it here
   instead of on the body is the difference between "spacing works"
   and "the gap rule is wasted on a single-child wrapper". */
.export-modal-body > .export-modal-pane {
  display: flex;
  flex-direction: column;
  gap: 28px;
  animation: export-modal-pane-in 240ms cubic-bezier(0.2, 0.7, 0.2, 1);
}

@keyframes export-modal-pane-in {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .export-modal-body > .export-modal-pane {
    animation: none;
  }
}

.export-modal-field {
  display: flex;
  flex-direction: column;
  /* 12px label → control gap so the label sits as a clear section
     header above its pills / input, not glued to it. Combined with
     the body's 28px section gap, the hierarchy reads as
     "label → control → big break → next label". */
  gap: 12px;
}

.export-modal-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.export-modal-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.export-modal-pill {
  min-height: 32px;
  padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--field);
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  transition: background 160ms var(--ease), border-color 160ms var(--ease), color 160ms var(--ease);
}

.export-modal-pill:hover {
  border-color: var(--border-strong);
  background: var(--panel-3);
  color: var(--text);
}

.export-modal-pill.is-active {
  background: rgba(246, 242, 234, 0.08);
  border-color: rgba(246, 242, 234, 0.45);
  color: var(--text);
}

.export-modal-dimensions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.export-modal-dimension {
  display: flex;
  flex-direction: column;
  /* Matches .export-modal-field gap so the label → input distance is
     consistent across pill rows and number inputs. */
  gap: 12px;
}

.export-modal-input {
  width: 100%;
  height: 36px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--field);
  color: var(--text);
  font: 700 13px var(--font-mono-2);
  font-variant-numeric: tabular-nums;
  transition: border-color 160ms var(--ease), background 160ms var(--ease);
}

.export-modal-input:hover {
  border-color: var(--border-strong);
}

.export-modal-input:focus {
  outline: none;
  border-color: rgba(246, 242, 234, 0.5);
  background: var(--surface-hover);
}

.export-modal-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 18px;
  align-items: start;
}

.export-modal-caption {
  margin: 0;
  font-size: 13px;
  color: var(--dim);
  line-height: 1.5;
}

.export-modal-cta {
  width: 100%;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 0 16px;
  border: 1px solid rgba(246, 242, 234, 0.22);
  border-radius: 14px;
  background: rgba(246, 242, 234, 0.08);
  color: var(--text);
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  /* Extra 16px above the body's 28px gap = 44px above the CTA. The
     CTA is a distinct action, separated from the section stack by a
     clear vertical break — not just the next item in the rhythm.
     Subsequent CTAs (.is-secondary stacked under .is-primary) reset
     this so they cluster tightly. */
  margin-top: 16px;
  transition: background 160ms var(--ease), border-color 160ms var(--ease), transform 80ms var(--ease);
}

.export-modal-cta + .export-modal-cta {
  margin-top: 4px;
}

.export-modal-cta:hover:not(:disabled) {
  background: rgba(246, 242, 234, 0.14);
  border-color: rgba(246, 242, 234, 0.36);
}

.export-modal-cta:active:not(:disabled) {
  transform: translateY(1px);
}

.export-modal-cta:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.export-modal-cta.is-secondary {
  background: var(--field);
  border-color: var(--border);
}

.export-modal-cta.is-success {
  border-color: rgba(246, 242, 234, 0.45);
  color: var(--accent);
}

.export-modal-cta.is-recording {
  background: rgba(246, 242, 234, 0.04);
}

.export-modal-progress {
  height: 3px;
  border-radius: 999px;
  background: var(--surface-active);
  overflow: hidden;
}

.export-modal-progress-fill {
  height: 100%;
  background: var(--accent);
  transition: width 120ms linear;
}

.export-modal-dropzone {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  background: var(--surface-hover);
  color: var(--muted);
  cursor: pointer;
  transition: border-color 160ms var(--ease), background 160ms var(--ease), color 160ms var(--ease);
}

.export-modal-dropzone:hover,
.export-modal-dropzone:focus-visible {
  border-color: rgba(246, 242, 234, 0.45);
  background: var(--surface-hover);
  color: var(--text);
  outline: none;
}

.export-modal-dropzone-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 13px;
  color: var(--dim);
}

.export-modal-dropzone-text strong {
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
}

@media (max-width: 540px) {
  .export-modal-row {
    grid-template-columns: 1fr;
  }
  .export-modal-dimensions {
    grid-template-columns: 1fr;
  }
}

.export-actions {
  display: grid;
  gap: 4px;
}

.export-progress {
  height: 3px;
  border-radius: 999px;
  background: var(--surface-active);
  overflow: hidden;
  margin-top: 4px;
}

.export-progress-fill {
  height: 100%;
  background: var(--accent, #f6f2ea);
  transition: width 120ms linear;
}

.export-action-button:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.export-action-button {
  width: 100%;
  min-height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: 10px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--field);
  color: var(--text);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  transition: background 180ms var(--ease), border-color 180ms var(--ease), color 180ms var(--ease);
}

.export-action-button > .lucide {
  flex: 0 0 auto;
  color: var(--muted);
  transition: color 180ms var(--ease);
}

.export-action-button:hover {
  border-color: var(--border-strong);
  background: var(--panel-3);
}

.export-action-button:hover > .lucide {
  color: var(--text);
}

.export-action-button.is-primary {
  background: rgba(246, 242, 234, 0.08);
  border-color: rgba(246, 242, 234, 0.22);
}

.export-action-button.is-primary > .lucide {
  color: var(--text);
}

.export-action-button.is-primary:hover {
  background: rgba(246, 242, 234, 0.14);
  border-color: rgba(246, 242, 234, 0.36);
}

.export-action-button.is-success {
  border-color: rgba(246, 242, 234, 0.32);
  color: var(--accent);
}

.export-action-button.is-success > .lucide {
  color: var(--accent);
}

.export-action-button.is-manual {
  border-color: rgba(246, 242, 234, 0.32);
  color: var(--accent);
}

/* Mobile-only drag handle inside the control rail. Default hidden — the
   @media (max-width: 620px) block below turns the rail into a bottom sheet
   and reveals this handle. */
.mobile-drag-handle {
  display: none;
}

/* Accordion wrapper used by the panel sections that appear/disappear
   when the user flips between Flat and Globe view modes. Uses the
   grid-template-rows 0fr → 1fr technique so the wrapper animates to
   its child's natural height without us measuring anything. */
.collapsible-section {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 320ms cubic-bezier(0.34, 1.3, 0.5, 1),
    opacity 220ms var(--ease);
  opacity: 0;
}

.collapsible-section.is-open {
  grid-template-rows: 1fr;
  opacity: 1;
}

/* Closed Collapsibles still occupied a grid row in the parent and
   each contributed a 6px gap on either side — between Surface and
   Animations in flat mode that meant ~24px of dead space from three
   collapsed Globe-mode sections. display:none removes them from the
   grid layout entirely. Trade-off: the opening expansion doesn't
   animate from 0fr → 1fr anymore (display can't be transitioned),
   but the view-mode switch already has its own visual feedback. */
.collapsible-section:not(.is-open) {
  display: none;
}

.collapsible-section-inner {
  min-height: 0;
  overflow: hidden;
  /* Grid (not flex) so children — including <details> .option-block
     wrappers for full PanelSections like Globe + Network — always
     stretch to the cross-axis. Under flex column they were sizing to
     intrinsic content width (~150-220 px) instead of the panel's
     full ~376 px. Grid items default to align-self: stretch.
     Gap matches the parent .option-content rhythm so option rows
     wrapped here (Auto spin + Speed, Projection) get the same 14 px
     break as un-wrapped siblings. */
  display: grid;
  grid-auto-rows: max-content;
  gap: 14px;
}

@media (prefers-reduced-motion: reduce) {
  .collapsible-section {
    transition: none;
  }
}

.panel-toggle {
  position: fixed;
  top: 16px;
  left: 16px;
  z-index: 5;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 0;
  background: var(--panel);
  /* Replaced box-shadow with filter:drop-shadow so the shadow follows
     the pixel-corner clip-path silhouette instead of being sliced off
     at the polygon edges (clip-path clips box-shadow but NOT
     filter:drop-shadow, which is applied AFTER clipping per spec). */
  filter: drop-shadow(0 14px 36px rgba(0, 0, 0, 0.32));
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  color: var(--muted);
  cursor: pointer;
  animation: panel-toggle-in 280ms var(--ease);
  transition: background 180ms var(--ease), border-color 180ms var(--ease), color 180ms var(--ease);
  /* Same 9px 2-step staircase as the rest of the canvas-app chrome —
     applied locally instead of via the global selector list because
     the global rule strips borders, and this button needs its 1px
     border for visual definition against the dark canvas. */
  clip-path: polygon(
    0px 9px,
    0px 3px,
    3px 3px,
    3px 0px,
    9px 0px,
    calc(100% - 9px) 0px,
    calc(100% - 3px) 0px,
    calc(100% - 3px) 3px,
    calc(100% - 0px) 3px,
    calc(100% - 0px) 9px,
    calc(100% - 0px) calc(100% - 9px),
    calc(100% - 0px) calc(100% - 3px),
    calc(100% - 3px) calc(100% - 3px),
    calc(100% - 3px) calc(100% - 0px),
    calc(100% - 9px) calc(100% - 0px),
    9px calc(100% - 0px),
    3px calc(100% - 0px),
    3px calc(100% - 3px),
    0px calc(100% - 3px),
    0px calc(100% - 9px)
  );
}

@keyframes panel-toggle-in {
  from {
    opacity: 0;
    transform: scale(0.8);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.panel-toggle:hover {
  background: var(--panel-3);
  border-color: var(--border-strong);
  color: var(--text);
}

.social-links {
  position: fixed;
  bottom: 16px;
  right: 16px;
  z-index: 4;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.bug-link {
  position: fixed;
  bottom: 16px;
  left: 16px;
  z-index: 4;
}

/* Each link is its own self-contained chip — same panel chrome as the
   view-mode-switch / segmented controls (border, blur, shadow), just
   one icon wide. Reads as a cluster of independent shortcuts rather
   than a single grouped card. */
.social-link {
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--panel);
  box-shadow: var(--shadow);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  color: var(--muted);
  text-decoration: none;
  cursor: pointer;
  transition: background 180ms var(--ease), border-color 180ms var(--ease), color 180ms var(--ease);
}

.social-link:hover {
  background: var(--panel-3);
  border-color: var(--border-strong);
  color: var(--text);
}

.view-mode-switch {
  position: fixed;
  top: 12px;
  left: 50%;
  z-index: 4;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 92px));
  gap: 5px;
  padding: 5px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--panel);
  box-shadow: var(--shadow-md);
  backdrop-filter: blur(18px);
  transform: translateX(-50%);
}

/* Sliding indicator pill — sits behind the buttons and animates between
   the two grid slots driven by the .view-mode-switch[data-active="…"]
   attribute. Spring-y cubic-bezier gives it a SwiftUI segmented-control
   feel without overshooting past the slot edges. */
.view-mode-indicator {
  position: absolute;
  top: 5px;
  bottom: 5px;
  left: 5px;
  width: calc((100% - 15px) / 2);
  border-radius: 10px;
  border: 1px solid var(--accent);
  background: var(--accent);
  transform: translateX(0);
  transition: transform 360ms cubic-bezier(0.34, 1.3, 0.5, 1);
  pointer-events: none;
  z-index: 0;
}

.view-mode-switch[data-active="globe"] .view-mode-indicator {
  transform: translateX(calc(100% + 5px));
}

@media (prefers-reduced-motion: reduce) {
  .view-mode-indicator {
    transition: none;
  }
}

.view-mode-button {
  position: relative;
  z-index: 1;
  min-height: 34px;
  display: inline-flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: 7px;
  white-space: nowrap;
  border: 1px solid transparent;
  border-radius: 10px;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 780;
  cursor: pointer;
  transition: color 220ms var(--ease);
}

.view-mode-button:hover,
.view-mode-button:focus-visible {
  color: var(--text);
}

.view-mode-button.is-active {
  color: var(--on-accent);
}

/* Panel-collapsed = full-bleed canvas. When the user collapses the
   control panel (H key or the hide-panel button), the peripheral
   chrome — looks bar, view-mode switch, zoom controls, social links
   — fades out alongside it so the canvas reads as a single piece of
   artwork. The .panel-toggle button stays visible: it's the way back.
   Animation: 260ms fade + a small directional slide so the chrome
   reads as "stepping out of the way" rather than hard-cutting. */
.app-shell.is-panel-collapsed .looks-bar,
.app-shell.is-panel-collapsed .view-mode-switch,
.app-shell.is-panel-collapsed .map-zoom-controls,
.app-shell.is-panel-collapsed .social-link {
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms cubic-bezier(0.4, 0, 0.2, 1),
    transform 320ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* Perf HUD is visible in normal (paneled) mode for dev introspection,
   but hidden in the fullscreen / panel-collapsed ambient view so it
   doesn't intrude on the canvas-only artwork. */
.app-shell.is-panel-collapsed .perf-monitor {
  display: none !important;
}

.app-shell.is-panel-collapsed .map-zoom-controls,
.app-shell.is-panel-collapsed .view-mode-switch {
  transform: translate(-50%, 12px);
}

.app-shell.is-panel-collapsed .social-link {
  transform: translateY(-12px);
}

@media (prefers-reduced-motion: reduce) {
  .app-shell.is-panel-collapsed .looks-bar,
  .app-shell.is-panel-collapsed .view-mode-switch,
  .app-shell.is-panel-collapsed .map-zoom-controls,
  .app-shell.is-panel-collapsed .social-link {
    transition: opacity 0s;
    transform: none;
  }
}

.map-zoom-controls {
  position: fixed;
  left: 50%;
  bottom: 16px;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--panel);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  box-shadow: var(--shadow);
  transform: translateX(-50%);
}

.map-zoom-button {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 180ms var(--ease), color 180ms var(--ease);
}

.map-zoom-button:hover {
  background: var(--panel-3);
  color: var(--text);
}

.map-zoom-display {
  min-width: 54px;
  padding: 0 8px;
  text-align: center;
  color: var(--text);
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  outline: 0;
  transition: background 140ms var(--ease);
}

.map-zoom-display:hover {
  background: var(--surface-hover);
}

.map-zoom-display:focus-visible {
  outline: 2px solid var(--accent, #f6f2ea);
  outline-offset: 2px;
}

.export-menu {
  position: relative;
}

.export-menu-button {
  min-width: 124px;
}

.export-popover {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: min(340px, calc(100vw - 24px));
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
  box-shadow: var(--shadow-md);
  backdrop-filter: blur(18px);
}

.export-scale-group {
  position: relative;
  display: grid;
  gap: 8px;
  padding: 8px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 740;
}

.export-scale-group::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 1px;
  background-image: linear-gradient(to right, var(--stroke-faint) 1px, transparent 1px);
  background-size: 2px 1px;
  background-repeat: repeat-x;
  pointer-events: none;
}

.export-scale-options {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 4px;
}

.export-scale-option {
  min-height: 28px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--field);
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background 180ms var(--ease), border-color 180ms var(--ease), color 180ms var(--ease);
}

.export-scale-option:hover {
  border-color: var(--border-strong);
  color: var(--text);
}

.export-scale-option.is-active {
  border-color: rgba(246, 242, 234, 0.32);
  background: rgba(246, 242, 234, 0.05);
  color: var(--accent);
}

.copy-fallback {
  width: 100%;
  min-height: 112px;
  resize: vertical;
  padding: 9px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--field);
  color: var(--text);
  font-family: var(--font-mono-2);
  font-size: 12px;
  line-height: 1.45;
  outline: 0;
}

.button {
  position: relative;
  min-height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--field);
  color: var(--text);
  font-size: 14px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background 180ms var(--ease),
    border-color 180ms var(--ease),
    color 180ms var(--ease),
    transform 140ms var(--ease);
}

.button:hover,
.button-active {
  background: var(--panel-3);
  border-color: var(--border-strong);
}

.button:active {
  transform: translateY(1px);
}

.button:disabled {
  cursor: default;
}

.panel-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 6px 8px 4px;
  /* Sticky so the logo + action icons (theme, keyboard, hide, export)
     stay pinned to the top of the rail as the user scrolls through
     Surface / Globe / Shader / Background sections. The rail itself
     is the scroll container (overflow: hidden auto), so top: 0 lands
     the header at the rail's interior top edge.
     Margin counteracts the rail's 4px top padding so the header sits
     flush at the rail's top edge while sticky. */
  position: sticky;
  top: -4px;
  margin: -4px -10px 0;
  padding: 10px 16px 8px;
  z-index: 3;
  background: var(--panel);
  backdrop-filter: blur(18px) saturate(120%);
  -webkit-backdrop-filter: blur(18px) saturate(120%);
}

.panel-meta {
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
}

.panel-meta-icon {
  flex: 0 0 auto;
  width: 60px;
  height: 60px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text);
  position: relative;
}

/* Brand-icon acknowledgement when a preset lands — a quick scale pulse +
   an expanding accent ring that fades out. The `.is-applied` class auto-
   clears after 700ms (the same window the looks-chip uses), so this is
   a one-shot single-firing animation. Keeps the brand mark visually
   tied to the applied-state across chips, header, and brand. */
.panel-meta-icon.is-rippling {
  animation: panel-meta-pulse 620ms cubic-bezier(0.2, 0.7, 0.2, 1);
}

.panel-meta-icon.is-rippling::after {
  content: "";
  position: absolute;
  inset: 6px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  opacity: 0;
  animation: panel-meta-ripple 620ms cubic-bezier(0.2, 0.7, 0.2, 1);
  pointer-events: none;
}

@keyframes panel-meta-pulse {
  0% { transform: scale(1); }
  35% { transform: scale(1.08); }
  100% { transform: scale(1); }
}

@keyframes panel-meta-ripple {
  0% {
    opacity: 0.6;
    transform: scale(0.7);
  }
  100% {
    opacity: 0;
    transform: scale(1.5);
  }
}

@media (prefers-reduced-motion: reduce) {
  .panel-meta-icon.is-rippling,
  .panel-meta-icon.is-rippling::after {
    animation: none;
  }
}

/* .panel-reset-button is just a marker for the rotation animation now —
   visual styles come from .panel-icon-button. */

.control-panel {
  min-width: 0;
  display: grid;
  align-content: start;
  gap: 6px;
}

.option-block {
  align-self: start;
  padding: 8px 4px 12px;
}

/* Dashed 1px-on / 1px-off divider — pixel-art-style separation between
   panel sections. Rendered as a ::before so the dashes can be inset
   from the panel sides without affecting the option-block's content. */
.option-block + .option-block::before,
.collapsible-section + .option-block::before,
.collapsible-section .option-block::before {
  content: "";
  position: absolute;
  top: 0;
  left: 10px;
  right: 10px;
  height: 1px;
  background-image: linear-gradient(
    to right,
    var(--stroke-faint) 1px,
    transparent 1px
  );
  background-size: 2px 1px;
  background-repeat: repeat-x;
  pointer-events: none;
}

.option-block {
  position: relative;
}

/* Panel-internal segmented toggle. Same visual idiom as the canvas-level
   view-mode-switch (sliding pill behind two pressable buttons) but
   sized for inline placement at the top of a PanelSection. The width
   spans the full row so the two segments stretch evenly. */
.segmented-toggle {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 4px;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: 0;
  /* `--field` gives the toggle a darker, recessed bed that visibly
     contrasts with the control-rail's panel bg. Using `--panel` here
     made the toggle blend invisibly into the rail because both used
     the same color. Same pattern as .color-picker-fill /
     .color-picker-modes — keeps the toggle family on a single key. */
  background: var(--field);
  margin: 4px 0 10px;
}

.segmented-toggle-indicator {
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 4px;
  width: calc((100% - 12px) / 2);
  border-radius: 0;
  /* clip-path applied via shared selector list below. */
  border: 1px solid var(--accent);
  background: var(--accent);
  /* translateX(100%) is the indicator's own width, +4px is the gap;
     active-index 0 → left slot, 1 → right slot. */
  transform: translateX(calc(var(--active-index, 0) * (100% + 4px)));
  transition: transform 320ms cubic-bezier(0.34, 1.3, 0.5, 1);
  pointer-events: none;
  z-index: 0;
}

@media (prefers-reduced-motion: reduce) {
  .segmented-toggle-indicator {
    transition: none;
  }
}

.segmented-toggle-button {
  position: relative;
  z-index: 1;
  min-height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  border: 1px solid transparent;
  border-radius: 0;
  background: transparent;
  color: var(--muted);
  font-size: 12px;
  font-weight: 760;
  cursor: pointer;
  transition: color 220ms var(--ease);
}

.segmented-toggle-button:hover,
.segmented-toggle-button:focus-visible {
  color: var(--text);
}

.segmented-toggle-button.is-active {
  color: var(--on-accent);
}

.map-area-section {
  display: grid;
  gap: 8px;
  /* 8px horizontal to match every other panel block (option summary,
     option content, panel header) — keeps the country dropdown in the
     same column as the looks bar chips above and every OptionRow
     below. */
  padding: 4px 8px 8px;
}

.option-block-header {
  min-height: 28px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 8px;
  /* Whole row is clickable (matches old <summary> behavior) so the
     pointer cue should be a hand anywhere across the header. */
  cursor: pointer;
}

.option-block-disclosure {
  min-width: 0;
  flex: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--dim);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  user-select: none;
  transition: color 180ms var(--ease);
}

.option-block-disclosure:hover,
.option-block-disclosure:focus-visible {
  color: var(--muted);
}

.option-block-chevron {
  flex: 0 0 auto;
  opacity: 0.55;
  transition: transform 180ms var(--ease), opacity 180ms var(--ease);
}

/* Layer-visibility eye button inside a PanelSection header. Sits
   between the title and the chevron. The button's margin-left: auto
   pushes it to the right edge; the chevron follows naturally with
   a small gap (overriding the auto-margin from the .svg:last-child
   selector above). */
.option-block-header .option-block-eye {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: color 160ms var(--ease), background 160ms var(--ease);
}
.option-block-header .option-block-eye:hover,
.option-block-header .option-block-eye:focus-visible {
  color: var(--text);
  background: var(--field);
}
/* When the layer is hidden, the eye renders closed and dims a step
   further so the off state reads obviously. */
.option-block.is-layer-disabled .option-block-eye {
  color: var(--dim);
}
/* "Nothing to toggle yet" state — eye visible but non-interactive
   (e.g. Shaders section when no effect is picked). Dimmer than the
   normal off state and cursor reverts to default so it doesn't
   advertise itself as a control. The native title attribute still
   surfaces a tooltip on hover explaining why. */
.option-block-header .option-block-eye.is-disabled {
  color: var(--dim);
  opacity: 0.45;
  cursor: not-allowed;
}
.option-block-header .option-block-eye.is-disabled:hover,
.option-block-header .option-block-eye.is-disabled:focus-visible {
  background: transparent;
  color: var(--dim);
}
.option-block-header .option-block-eye + svg {
  /* Bumped from 4px → 10px so the eye's hover background (22×22
     square) has breathing room before the chevron — at 4px the
     two read as visually mashed together when the eye lights up. */
  margin-left: 10px !important;
}

/* When a section's layer toggle is off, dim the section header so
   users can scan the panel and see at a glance which layers are
   contributing. The body content (when expanded) is unaffected so
   they can still adjust settings while the layer is hidden. */
.option-block.is-layer-disabled .option-block-disclosure > span {
  opacity: 0.55;
}

.option-block-header:hover .option-block-chevron {
  opacity: 0.9;
}

.option-block[data-open="true"] .option-block-chevron {
  transform: rotate(180deg);
}

.option-content {
  display: grid;
  gap: 14px;
  padding: 14px 8px 2px;
}

/* Author `display: grid` above outweighs the UA stylesheet's
   `[hidden] { display: none }` (1 class beats 1 attribute), so the
   PanelSection's `hidden={!isOpen}` attribute was being ignored and
   every section stayed expanded. Re-assert hidden at matching
   specificity so collapse actually collapses. */
.option-content[hidden] {
  display: none;
}


.option-row {
  min-width: 0;
}

.option-row-inline {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
}

.option-row-stacked {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.option-row label {
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  color: var(--text);
  font-size: 13px;
  font-weight: 700;
}

.option-row-inline label {
  flex: 1 1 auto;
}

.option-row label > span:first-child {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.option-row output {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.option-control {
  min-width: 0;
}

/* Keep the dropdown column in inline option rows consistent across the
   panel — the native <select> (Look, Pass, Background) and the custom
   .shape-select (Shape) otherwise size to their content, leaving the
   smaller selects narrow and Shape wider in the same section. 160px is
   wide enough to fit the longest option ("Particle Grid") with its
   preview icon and chevron, so every control in the column renders at
   exactly the same width regardless of selection. The .segmented-control
   sits in the same column and matches via .option-row-inline below. */
.option-row-inline .option-control select,
.option-row-inline .option-control .shape-select,
.option-row-inline .option-control .segmented-control {
  min-width: 160px;
}

/* Inline segmented control — sliding pill that animates between options.
   Mirrors the top-of-screen ViewModeSwitch design (same spring-y motion,
   same indicator geometry), sized down to slot into an OptionRow. The
   --segment-count and --active-index variables, set on the wrapper by
   the SegmentedControl component, drive the indicator math so the same
   styles work for 2- and 3-option toggles. */
.segmented-control {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--segment-count, 2), 1fr);
  gap: 3px;
  padding: 3px;
  min-height: 32px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--field);
}

.segmented-control-indicator {
  position: absolute;
  top: 3px;
  bottom: 3px;
  left: 3px;
  /* indicator-width = (full row − padding ×2 − gap ×(count−1)) / count */
  width: calc((100% - 6px - 3px * (var(--segment-count, 2) - 1)) / var(--segment-count, 2));
  border-radius: 10px;
  background: var(--panel-3);
  border: 1px solid rgba(246, 242, 234, 0.22);
  /* translateX(100%) is the indicator's own width, +3px adds the gap so
     each step moves to the next slot perfectly. */
  transform: translateX(calc(var(--active-index, 0) * (100% + 3px)));
  transition: transform 360ms cubic-bezier(0.34, 1.3, 0.5, 1);
  pointer-events: none;
  z-index: 0;
}

@media (prefers-reduced-motion: reduce) {
  .segmented-control-indicator {
    transition: none;
  }
}

.segmented-control-button {
  position: relative;
  z-index: 1;
  min-height: 24px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  transition: color 220ms var(--ease);
}

.segmented-control-button:hover,
.segmented-control-button:focus-visible {
  color: var(--text);
}

.segmented-control-button.is-active {
  color: var(--text);
}

select {
  width: 100%;
  min-height: 36px;
  padding: 0 32px 0 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--field);
  color: var(--text);
  font-size: 13px;
  font-weight: 700;
  outline: 0;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%23a8a39b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 10px 10px;
  transition: border-color 180ms var(--ease), background-color 180ms var(--ease);
}

select:hover {
  border-color: var(--border-strong);
}

.shape-select {
  position: relative;
  width: 100%;
}

.shape-select-trigger {
  width: 100%;
  min-height: 36px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--field);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  transition: border-color 180ms var(--ease), background 180ms var(--ease);
}

.shape-select-trigger:hover {
  border-color: var(--border-strong);
}

.shape-select-trigger[aria-expanded="true"] {
  border-color: var(--border-strong);
  background: var(--surface-hover);
}

.shape-select-trigger .shape-preview {
  color: var(--text);
}

.shape-select-label {
  flex: 1 1 auto;
  text-align: left;
}

.shape-select-trigger svg:last-child {
  margin-left: auto;
  opacity: 0.6;
}

.shape-select-menu {
  /* Position is set inline by the component (portaled to body via
     position: fixed, calculated from the trigger's bounding rect).
     z-index needs to be above the canvas (z 2) and any panel chrome. */
  z-index: 100;
  margin: 0;
  padding: 4px;
  list-style: none;
  border: 1px solid var(--border);
  border-radius: 8px;
  /* More transparent bg + stronger blur than var(--panel) gave on its
     own — the dark var(--panel) at 0.78 alpha was opaque enough to
     hide most of the backdrop blur. Drop the alpha so the frosted
     effect actually reads. */
  background: rgba(18, 18, 20, 0.55);
  backdrop-filter: blur(22px) saturate(180%);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  box-shadow: var(--shadow-md);
  max-height: 240px;
  overflow-y: auto;
}

[data-theme="light"] .shape-select-menu {
  background: rgba(252, 251, 248, 0.6);
}

.shape-select-option {
  width: 100%;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 6px 8px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  text-align: left;
  transition: background 140ms var(--ease), color 140ms var(--ease);
}

.shape-select-option:hover {
  background: var(--surface-active);
  color: var(--text);
}

.shape-select-option.is-active {
  background: rgba(246, 242, 234, 0.05);
  color: var(--accent);
}

.shape-select-option .shape-preview {
  flex: 0 0 auto;
}

.searchable-select {
  position: relative;
  width: 100%;
  /* 250+ country labels — give the trigger room so country names don't get
     truncated and the popover sits over a comfortably wide search field. */
  min-width: 180px;
}

.searchable-select-trigger {
  width: 100%;
  min-height: 36px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--field);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  text-align: left;
  transition: border-color 180ms var(--ease), background 180ms var(--ease);
}

.searchable-select-trigger:hover {
  border-color: var(--border-strong);
}

.searchable-select.is-open .searchable-select-trigger {
  border-color: var(--border-strong);
  background: var(--surface-hover);
}

.searchable-select-value {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.searchable-select-trigger svg {
  flex: 0 0 auto;
  opacity: 0.6;
}

/* Positioning (left/top/width) is set inline by the SearchableSelect
   component, which portals this to <body> and updates the rect on
   scroll/resize so the popover can escape the rail's overflow:hidden
   and never gets clipped by the card edge. */
.searchable-select-popover {
  z-index: 50;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: 14px;
  /* Backdrop-blur + saturate so the dropdown reads as a frosted-
     glass card (same idiom as the color-picker card). The panel
     background var already carries the alpha — backdrop-filter
     pulls in what's behind it. */
  background: var(--panel);
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}

.searchable-select-search {
  width: 100%;
  min-height: 36px;
  padding: 0 12px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  outline: 0;
  /* Aggressive resets — Safari adds an inset shadow on inputs by default
     even when border + outline are zeroed. */
  appearance: none;
  -webkit-appearance: none;
  box-shadow: none;
}

.searchable-select-search::placeholder {
  color: var(--dim);
}

.searchable-select-list {
  margin: 0;
  padding: 8px 6px;
  list-style: none;
  max-height: 240px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--panel-3) transparent;
}

.searchable-select-option {
  padding: 6px 8px;
  border-radius: 10px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background 140ms var(--ease), color 140ms var(--ease);
}

.searchable-select-option.is-active {
  background: var(--surface-active);
  color: var(--text);
}

.searchable-select-option.is-selected {
  color: var(--accent);
}

.searchable-select-empty {
  padding: 10px 8px;
  color: var(--dim);
  font-size: 13px;
  text-align: center;
}

.ascii-symbol-input {
  width: 100%;
  min-height: 32px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--field);
  color: var(--text);
  font-family: var(--font-mono-2);
  font-size: 13px;
  font-weight: 700;
  text-align: center;
  letter-spacing: 0.05em;
  outline: 0;
  transition: border-color 180ms var(--ease);
}

.ascii-symbol-input:focus-visible {
  border-color: var(--border-strong);
}

.ascii-symbol-input::placeholder {
  color: var(--dim);
  font-weight: 700;
}

.custom-shape-upload {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.custom-shape-upload input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}

.custom-shape-pick {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 36px;
  padding: 4px 10px 4px 4px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--field);
  color: var(--text);
  font-size: 13px;
  font-weight: 700;
  text-align: left;
  cursor: pointer;
  transition: border-color 180ms var(--ease), background 180ms var(--ease);
}

.custom-shape-pick:hover {
  border-color: var(--accent-soft);
  background: var(--surface-hover);
}

.custom-shape-pick-icon {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: var(--surface-active);
  color: var(--muted);
  font-size: 16px;
  font-weight: 400;
  line-height: 1;
}

.custom-shape-pick-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--muted);
}

/* Logo → palette: upload button + extracted swatch chips. */
.logo-palette {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
}
.logo-palette-upload {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 36px;
  padding: 4px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--field);
  color: var(--text);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  transition: border-color 180ms var(--ease), background 180ms var(--ease);
}
.logo-palette-upload:hover {
  border-color: var(--accent, var(--text));
}
.logo-palette-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.logo-palette-swatch {
  width: 26px;
  height: 26px;
  border-radius: 6px;
  border: 1px solid var(--border-strong);
  padding: 0;
  cursor: pointer;
  transition: transform 120ms var(--ease);
}
.logo-palette-swatch:hover {
  transform: scale(1.1);
}

/* Data points — paste lat,lng,value to plot additive globe markers. */
.data-points-control {
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
}
.data-points-input {
  width: 100%;
  min-height: 76px;
  padding: 8px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--field);
  color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  line-height: 1.5;
  resize: vertical;
}
.data-points-input:focus-visible {
  outline: none;
  border-color: var(--accent, var(--text));
}
.data-points-sample {
  align-self: flex-start;
  padding: 3px 8px;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  font-size: 11px;
  cursor: pointer;
}
.data-points-sample:hover {
  color: var(--text);
  border-color: var(--accent, var(--text));
}
.data-points-meta {
  display: flex;
  align-items: center;
  gap: 8px;
}
.data-points-hint {
  margin: 0;
  font-size: 11px;
  color: var(--muted);
}

.custom-shape-thumb {
  width: 28px;
  height: 28px;
  object-fit: contain;
  border-radius: 8px;
  background: var(--surface-active);
  padding: 3px;
}

.custom-shape-clear {
  align-self: flex-start;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  transition: color 180ms var(--ease), border-color 180ms var(--ease);
}

.custom-shape-clear:hover {
  color: var(--text);
  border-color: var(--border-strong);
}

.custom-shape-error {
  margin: 0;
  color: #ff9ea0;
  font-size: 12px;
}

.custom-shape-paste {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.custom-shape-paste-input,
.custom-topology-input {
  width: 100%;
  min-height: 64px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--field);
  color: var(--text);
  font-family: var(--font-mono-2);
  font-size: 12px;
  line-height: 1.4;
  resize: vertical;
  outline: 0;
  transition: border-color 180ms var(--ease);
}

.custom-shape-paste-input::placeholder,
.custom-topology-input::placeholder {
  color: var(--dim);
  font-family: inherit;
}

.custom-shape-paste-input:focus-visible,
.custom-topology-input:focus-visible {
  border-color: var(--border-strong);
}

.custom-shape-apply {
  align-self: flex-start;
  padding: 5px 12px;
  border: 1px solid var(--accent-soft);
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  transition: filter 180ms var(--ease);
}

.custom-shape-apply:hover {
  filter: brightness(1.06);
}

select:focus,
input[type="range"]:focus-visible,
.range-number:focus-visible,
.map-zoom-input:focus-visible,
.button:focus-visible,
.transparent-toggle:focus-visible,
.toggle-control:focus-visible,
.color-swatch:focus-visible,
.step-button:focus-visible,
.export-menu-item:focus-visible,
.export-scale-option:focus-visible,
.option-block-disclosure:focus-visible,
.copy-fallback:focus-visible,
.view-mode-button:focus-visible,
.panel-toggle:focus-visible,
.panel-icon-button:focus-visible,
.panel-reset-button:focus-visible,
.looks-chip:focus-visible,
.export-action-button:focus-visible,
.map-zoom-button:focus-visible,
.searchable-select-trigger:focus-visible,
.social-link:focus-visible {
  outline: 2px solid var(--accent, #f6f2ea);
  outline-offset: 2px;
}

/* Search input inside the searchable-select popover has no focus
   outline — the popover itself is the focus indicator (it only appears
   on user action), the input is the sole interactive element on open,
   and the previous outer outline was clipping against the popover's
   overflow:hidden boundary and rendering as a stray horizontal line. */
.searchable-select-search:focus-visible {
  outline: 0;
}

.slider,
input[type="range"] {
  width: 100%;
  height: 22px;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  cursor: pointer;
}

.slider::-webkit-slider-runnable-track,
input[type="range"]::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 0;
  background: linear-gradient(
    to right,
    var(--stroke-fill) 0%,
    var(--stroke-fill) var(--fill, 50%),
    var(--stroke-empty) var(--fill, 50%),
    var(--stroke-empty) 100%
  );
}

.slider::-webkit-slider-thumb,
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  margin-top: -5px;
  width: 16px;
  height: 16px;
  border-radius: 0;
  /* 2px pixel-corner notches — small enough to keep the 16px thumb
     reading as a clear square, large enough that the stair-step is
     visible at normal viewing distance. 3px (kbd-scale) was too
     aggressive for the smaller thumb. */
  clip-path: polygon(
    0 2px,
    2px 2px,
    2px 0,
    calc(100% - 2px) 0,
    calc(100% - 2px) 2px,
    100% 2px,
    100% calc(100% - 2px),
    calc(100% - 2px) calc(100% - 2px),
    calc(100% - 2px) 100%,
    2px 100%,
    2px calc(100% - 2px),
    0 calc(100% - 2px)
  );
  background: var(--thumb-fill);
  border: 0;
  box-shadow: var(--shadow-thumb);
  cursor: grab;
  transition: transform 140ms var(--ease), box-shadow 140ms var(--ease);
}

.slider::-webkit-slider-thumb:hover,
input[type="range"]::-webkit-slider-thumb:hover {
  transform: scale(1.08);
}

.slider:active::-webkit-slider-thumb,
input[type="range"]:active::-webkit-slider-thumb {
  cursor: grabbing;
  transform: scale(1.14);
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.6),
    0 0 0 4px rgba(255, 255, 255, 0.08);
}

.slider::-moz-range-track,
input[type="range"]::-moz-range-track {
  height: 6px;
  border-radius: 0;
  background: var(--stroke-empty);
}

.slider::-moz-range-progress,
input[type="range"]::-moz-range-progress {
  height: 6px;
  border-radius: 0;
  background: var(--stroke-fill);
}

.slider::-moz-range-thumb,
input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 0;
  clip-path: polygon(
    0 2px,
    2px 2px,
    2px 0,
    calc(100% - 2px) 0,
    calc(100% - 2px) 2px,
    100% 2px,
    100% calc(100% - 2px),
    calc(100% - 2px) calc(100% - 2px),
    calc(100% - 2px) 100%,
    2px 100%,
    2px calc(100% - 2px),
    0 calc(100% - 2px)
  );
  background: var(--thumb-fill);
  border: 0;
  box-shadow: var(--shadow-thumb);
  cursor: grab;
  transition: transform 140ms var(--ease);
}

.slider::-moz-range-thumb:hover,
input[type="range"]::-moz-range-thumb:hover {
  transform: scale(1.08);
}

.slider:active::-moz-range-thumb,
input[type="range"]:active::-moz-range-thumb {
  cursor: grabbing;
  transform: scale(1.14);
}

.background-control {
  display: flex;
  align-items: center;
  gap: 10px;
}

.color-control {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.color-control-fallback {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
  pointer-events: none;
}

.color-swatch {
  /* 22×22 square — same height as the on/off toggle track so every
     control in the panel's right column sits on the same horizontal
     line. Also matches the 22×22 gradient-stop swatches inside the
     color picker. */
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: #fff;
  cursor: pointer;
  transition: border-color 180ms var(--ease);
}

.color-swatch:hover {
  border-color: var(--border-strong);
}

.transparent-grid {
  display: block;
  width: 100%;
  height: 100%;
  background-color: #f4f4f4;
  background-image: conic-gradient(#b8b8b8 25%, #f2f2f2 0 50%, #b8b8b8 0 75%, #f2f2f2 0);
  background-size: 10px 10px;
  background-position: 0 0;
}

.color-picker {
  position: absolute;
  z-index: 70;
  width: min(398px, calc(100vw - 32px));
  max-height: calc(100vh - 32px);
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: 0;
  background: var(--panel);
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  box-shadow: var(--shadow-xl);
  /* clip-path applied via the shared canvas-app pixel-corner selector
     list below so the popover card uses the same 9px 2-step polygon
     as every other panel control. */
  /* Horizontal stays hidden so the rounded card edge isn't broken
     by a scrollbar; vertical switches to auto so picker content
     can scroll internally when it overflows the viewport height
     (gradient mode + tall screens of inputs would otherwise clip
     the bottom rows off). */
  overflow-x: hidden;
  overflow-y: auto;
  animation: color-picker-pop 180ms var(--ease);
}

@keyframes color-picker-pop {
  from {
    opacity: 0;
    transform: translateY(-4px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.color-picker-header {
  /* 3-column layout: grip-left | title-center | X-right. Grid keeps
     the title centered relative to the card even when the grip and
     close buttons have different widths. */
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 4px 10px 6px;
  cursor: grab;
  user-select: none;
  flex: 0 0 auto;
}

.color-picker-title {
  /* Exact match for the panel section-title typography (SURFACE,
     GLOBE, GRID, ...) — same color, size, weight, tracking, transform
     so the picker header reads as part of the same family as the main
     card's section labels. Centered in its grid slot. */
  margin: 0;
  text-align: center;
  color: var(--dim);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.color-picker-header:active {
  cursor: grabbing;
}

/* Six-dot drag handle — 2 columns × 3 rows of pixel squares, like the
   six face of a die. Reads as a classic "grip" affordance and matches
   the pixel aesthetic of the rest of the UI. */
.color-picker-grip {
  display: inline-grid;
  grid-template-columns: repeat(2, 2px);
  grid-template-rows: repeat(3, 2px);
  gap: 2px;
  flex: 0 0 auto;
  opacity: 0.55;
  transition: opacity 140ms var(--ease);
}

.color-picker-grip > span {
  width: 2px;
  height: 2px;
  background: var(--muted);
  transition: background 140ms var(--ease);
}

.color-picker-header:hover .color-picker-grip {
  opacity: 1;
}

.color-picker-header:hover .color-picker-grip > span {
  background: var(--text);
}

.color-picker-title {
  flex: 1 1 auto;
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--text);
  text-transform: none;
}

.color-picker-close {
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 140ms var(--ease), color 140ms var(--ease), border-color 140ms var(--ease);
}

.color-picker-close:hover {
  background: var(--surface-active);
  color: var(--text);
  border-color: var(--border-strong);
}

.color-picker-body {
  display: grid;
  /* Explicit single-column grid with stretch alignment so every child
     (SV square, hue bar, alpha bar, mode tabs, value input) gets the
     exact same horizontal extent. `minmax(0, 1fr)` prevents grid items
     from overflowing their column when their intrinsic size is larger
     than the available width. */
  grid-template-columns: minmax(0, 1fr);
  justify-items: stretch;
  gap: 14px;
  padding: 10px;
  overflow: hidden auto;
  flex: 1 1 auto;
  min-height: 0;
  scrollbar-width: thin;
  scrollbar-color: var(--panel-3, rgba(255, 255, 255, 0.18)) transparent;
  /* Anchor scroll-y to nearest scroll container so the page doesn't scroll
     when the picker reaches its top/bottom limit. */
  overscroll-behavior: contain;
  box-sizing: border-box;
}

/* Every direct child of the picker body is a layout item — explicit
   width: 100% + box-sizing makes the alignment robust against any
   parent flex/grid quirks. */
.color-picker-body > * {
  box-sizing: border-box;
  min-width: 0;
}

.color-picker-body::-webkit-scrollbar {
  width: 8px;
}

.color-picker-body::-webkit-scrollbar-track {
  background: transparent;
}

.color-picker-body::-webkit-scrollbar-thumb {
  background: var(--stroke-faint);
  border-radius: 999px;
  border: 2px solid transparent;
  background-clip: padding-box;
}

.color-picker-body::-webkit-scrollbar-thumb:hover {
  background: var(--stroke-fill);
  background-clip: padding-box;
}

.color-picker-sv {
  position: relative;
  width: 100%;
  height: 180px;
  border-radius: 8px;
  cursor: crosshair;
  touch-action: none;
  user-select: none;
  /* overflow:visible so the cursor can extend past the pad's edge at
     extreme S/V values (corners). Previously `overflow:hidden` clipped
     the cursor to a quarter-circle at the corners — which made the
     selector almost invisible when the chosen color was pure black
     (bottom-left corner of the SV pad). The pad's gradient still
     respects the border-radius via background-clip default, so the
     swatch area stays nicely rounded. */
  overflow: visible;
  background-clip: padding-box;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}

/* Keyboard focus on the SV square gets an outer ring so designers can see
   where they are when navigating via Tab + arrow keys. */
.color-picker-sv:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.color-picker-sv-cursor {
  position: absolute;
  width: 12px;
  height: 12px;
  margin: -6px 0 0 -6px;
  border: 2px solid #ffffff;
  border-radius: 999px;
  /* Sandwich the white border between TWO black rings (one inset, one
     outset). Means the cursor stays visible whether the fill is black
     (outer black blends, white + inset visible), white (white blends,
     outer + inset black visible), or any color in between. Without
     the inset ring, a white fill would have only the 1px outer black
     to read against — almost invisible. */
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, 0.65),
    0 0 0 1px rgba(0, 0, 0, 0.65),
    0 2px 4px rgba(0, 0, 0, 0.45);
  pointer-events: none;
}

.color-picker-hue {
  position: relative;
  display: block;
  width: 100%;
  margin: 0;
  /* Bar height matches the 16 px thumb so the thumb sits fully inside
     the track (no vertical poke-out above / below the bar). */
  height: 16px;
  border-radius: 3px;
  background: linear-gradient(
    to right,
    #ff0000 0%,
    #ffff00 17%,
    #00ff00 33%,
    #00ffff 50%,
    #0000ff 67%,
    #ff00ff 83%,
    #ff0000 100%
  );
}

.color-picker-hue input[type="range"] {
  position: absolute;
  /* Inset by half the thumb width on each side so the 16 px thumb's
     edges stay flush with the bar's rounded corners at value extremes
     — native <input type="range"> centers the thumb on the value
     position, so at value=min it would overhang ~8 px past the bar's
     left edge (and ~8 px past the right at value=max) without this. */
  inset: 0 8px;
  width: calc(100% - 16px);
  height: 100%;
  margin: 0;
  padding: 0;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
}

/* Reset the global track styling but use FULL height for the
   runnable-track so the thumb centers on the bar's vertical middle.
   With height: 0 the browser places the (0-height) track at the
   bottom of the input area, which dragged the thumb visibly below
   the bar. Full-height transparent track keeps the visual clean while
   restoring proper vertical thumb positioning. */
.color-picker-hue input[type="range"]::-webkit-slider-runnable-track,
.color-picker-alpha input[type="range"]::-webkit-slider-runnable-track {
  height: 100%;
  background: transparent;
  border-radius: 0;
  border: 0;
}

.color-picker-hue input[type="range"]::-moz-range-track,
.color-picker-alpha input[type="range"]::-moz-range-track {
  height: 100%;
  background: transparent;
  border-radius: 0;
  border: 0;
}

.color-picker-hue input[type="range"]::-moz-range-progress,
.color-picker-alpha input[type="range"]::-moz-range-progress {
  height: 0;
  background: transparent;
}

.color-picker-hue input[type="range"]::-webkit-slider-thumb,
.color-picker-alpha input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  /* Bar is now 16px tall to match the thumb — no offset needed. */
  margin-top: 0;
  border-radius: 3px;
  background: var(--thumb-fill);
  box-shadow: var(--shadow-thumb);
  cursor: grab;
}

.color-picker-hue input[type="range"]::-moz-range-thumb,
.color-picker-alpha input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border: 0;
  border-radius: 3px;
  background: var(--thumb-fill);
  box-shadow: var(--shadow-thumb);
  cursor: grab;
}

.color-picker-hue input[type="range"]::-moz-range-track {
  height: 12px;
  background: transparent;
  border: 0;
}

.color-picker-alpha {
  position: relative;
  display: block;
  width: 100%;
  margin: 0;
  /* Matches .color-picker-hue height so the 16 px thumb fits flush. */
  height: 16px;
  border-radius: 3px;
  /* Checkerboard tile + color gradient on top so transparency is visible.
     `background-repeat: no-repeat, round` makes the checkerboard tile
     auto-scale so an integer number fits across the bar exactly —
     prevents the partial-last-tile column that used to make the alpha
     bar look like it ended a few pixels before the hue bar. */
  background-image:
    linear-gradient(to right, rgba(0, 0, 0, 0), var(--alpha-color, #ffffff)),
    conic-gradient(rgba(255, 255, 255, 0.6) 25%, rgba(180, 180, 180, 0.6) 0 50%, rgba(255, 255, 255, 0.6) 0 75%, rgba(180, 180, 180, 0.6) 0);
  background-size: 100% 100%, 8px 8px;
  background-position: 0 0, 0 0;
  background-repeat: no-repeat, round;
}

.color-picker-alpha input[type="range"] {
  position: absolute;
  /* Inset by half the thumb width on each side so the 16 px thumb's
     edges stay flush with the bar's rounded corners at value extremes
     — native <input type="range"> centers the thumb on the value
     position, so at value=min it would overhang ~8 px past the bar's
     left edge (and ~8 px past the right at value=max) without this. */
  inset: 0 8px;
  width: calc(100% - 16px);
  height: 100%;
  margin: 0;
  padding: 0;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
}

.color-picker-alpha input[type="range"]::-moz-range-track {
  height: 12px;
  background: transparent;
  border: 0;
}

.color-picker-modes {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 3px;
  padding: 3px;
  border: 0;
  border-radius: 0;
  background: var(--field);
  /* clip-path applied via shared canvas-app pixel-corner list below. */
}

.color-picker-mode {
  min-height: 28px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 180ms var(--ease), color 180ms var(--ease);
}

.color-picker-mode:hover,
.color-picker-mode:focus-visible {
  color: var(--text);
}

.color-picker-mode.is-active {
  background: var(--panel-3);
  color: var(--text);
}

.color-picker-fields {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 6px;
}

.color-picker-field {
  display: grid;
  gap: 3px;
  min-width: 0;
}

.color-picker-field.is-wide {
  grid-column: 1 / -1;
}

.color-picker-field > span {
  color: var(--dim);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.color-picker-field > input {
  width: 100%;
  min-height: 28px;
  padding: 0 8px;
  border: 0;
  border-radius: 8px;
  background: var(--field);
  color: var(--text);
  font-family: var(--font-mono-2);
  font-size: 12px;
  font-weight: 700;
  text-align: center;
  outline: 0;
  transition: background 180ms var(--ease);
}

.color-picker-field > input:focus-visible {
  background: var(--panel-3);
}

.color-picker-field.is-bare > input {
  min-height: 36px;
  font-size: 13px;
  letter-spacing: 0.04em;
}

.color-picker-field > input[type="number"] {
  -moz-appearance: textfield;
}

.color-picker-field > input[type="number"]::-webkit-outer-spin-button,
.color-picker-field > input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* Solid / Gradient toggle — same sliding-indicator pattern as the
   view-mode-switch (Flat / Globe). The indicator pill is positioned
   absolute behind the buttons and animates between the two slots via
   the data-active attribute on the parent. */
.color-picker-fill {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 5px;
  padding: 5px;
  border: 0;
  border-radius: 0;
  background: var(--field);
  /* clip-path applied via shared canvas-app pixel-corner list below. */
}

.color-picker-fill-indicator {
  position: absolute;
  top: 5px;
  bottom: 5px;
  left: 5px;
  width: calc((100% - 15px) / 2);
  border-radius: 0;
  /* clip-path applied via shared canvas-app pixel-corner list below. */
  border: 1px solid var(--accent);
  background: var(--accent);
  transform: translateX(0);
  transition: transform 360ms cubic-bezier(0.34, 1.3, 0.5, 1);
  pointer-events: none;
  z-index: 0;
}

.color-picker-fill[data-active="gradient"] .color-picker-fill-indicator {
  transform: translateX(calc(100% + 5px));
}

@media (prefers-reduced-motion: reduce) {
  .color-picker-fill-indicator {
    transition: none;
  }
}

.color-picker-fill-tab {
  position: relative;
  z-index: 1;
  min-height: 30px;
  padding: 0;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: color 220ms var(--ease);
}

.color-picker-fill-tab:hover,
.color-picker-fill-tab:focus-visible {
  color: var(--text);
}

.color-picker-fill-tab.is-active {
  color: var(--on-accent);
}

.color-picker-gradient {
  display: grid;
  gap: 10px;
}

.color-picker-gradient-track {
  width: 100%;
  height: 52px;
  border-radius: 8px;
}

.color-picker-stops {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  padding: 3px;
  border-radius: 10px;
  background: var(--field);
}

.color-picker-stop {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 8px;
  min-height: 34px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  text-align: left;
  transition: background 140ms var(--ease), color 140ms var(--ease);
}

.color-picker-stop:hover {
  color: var(--text);
}

.color-picker-stop.is-active {
  background: var(--panel-3);
  color: var(--text);
}

.color-picker-stop-swatch {
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  border-radius: 8px;
}

.color-picker-stop-meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.15;
}

.color-picker-stop-label {
  color: var(--dim);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.color-picker-stop-value {
  color: inherit;
  font-family: var(--font-mono-2);
  font-size: 12px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.color-picker-angle {
  display: grid;
  /* Label column needs to fit the widest label currently in use —
     "MIDPOINT" at uppercase + 0.08em letter-spacing measures ~62px
     so a 38px column overflowed it directly onto the slider track.
     64px gives "MIDPOINT" room and leaves "ANGLE" a little
     breathing space without making the slider feel crammed. */
  grid-template-columns: 64px 1fr 36px;
  align-items: center;
  gap: 6px;
}

.color-picker-angle > span {
  color: var(--dim);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.color-picker-angle > output {
  color: var(--muted);
  font-family: var(--font-mono-2);
  font-size: 12px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.color-picker-angle input[type="range"] {
  width: 100%;
  margin: 0;
}

.transparent-toggle {
  min-height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--field);
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: -0.005em;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background 180ms var(--ease),
    border-color 180ms var(--ease),
    color 180ms var(--ease);
}

.transparent-toggle:hover {
  border-color: var(--border-strong);
  color: var(--text);
}

.transparent-toggle.is-active {
  border-color: rgba(246, 242, 234, 0.32);
  color: var(--accent);
}

.toggle-control {
  display: inline-flex;
  align-items: center;
  /* 24×24 minimum hit area — WCAG 2.5.8 (Target Size). The visual track
     inside stays 36×22 for the existing design language; the wrapper padding
     expands the clickable + touchable target without changing how it reads. */
  min-height: 24px;
  min-width: 24px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

/* Binary toggle styled as a 2-cell segmented control — same visual
   language as the Flat/Globe and Shape/Solid switches. Track is the
   container, indicator fills one of the two halves (left = off, right
   = on) and slides with the same spring-y cubic-bezier the
   SegmentedControl uses. */
.toggle-track {
  position: relative;
  width: 36px;
  height: 22px;
  display: inline-flex;
  padding: 3px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--field);
  box-sizing: border-box;
  transition: border-color 180ms var(--ease), background 180ms var(--ease);
}

[data-theme="light"] {
  --toggle-track-off: rgba(0, 0, 0, 0.16);
  --toggle-track-border: rgba(0, 0, 0, 0.08);
}

/* Light-theme kbd shadow — DARKER than the cream surface so it reads
   as a proper drop-shadow, since the white-rgba shadow that works on
   the dark bg would be invisible against the cream paper. */
[data-theme="light"] .kbd-shadow {
  background: rgba(0, 0, 0, 0.14);
}

/* Light-theme sticky nav — dark-tinted backdrop won't work over the
   cream bg, so flip to a cream tint. */
[data-theme="light"] .takeover-nav {
  background: rgba(244, 241, 234, 0.85);
}

/* Hover bg in the light theme nav links — flip to a black tint for
   contrast since white-on-cream is invisible. */
[data-theme="light"] .takeover-nav-link:hover,
[data-theme="light"] .takeover-nav-theme:hover,
[data-theme="light"] .takeover-nav-theme:focus-visible {
  background: rgba(0, 0, 0, 0.05);
}

.toggle-knob {
  width: 50%;
  height: 100%;
  border-radius: 8px;
  background: var(--panel-3);
  border: 1px solid var(--border);
  transform: translateX(0);
  transition: transform 360ms cubic-bezier(0.34, 1.3, 0.5, 1),
    background 220ms var(--ease), border-color 220ms var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  .toggle-knob {
    transition: background 220ms var(--ease), border-color 220ms var(--ease);
  }
}

.toggle-control:hover .toggle-track {
  border-color: var(--border-strong);
}

.toggle-control:hover .toggle-knob {
  background: var(--panel-2);
}

.toggle-control.is-active .toggle-knob {
  /* Theme-aware ON state: --accent is off-white in dark mode, near-black
     in light mode → high contrast against the track in both themes. */
  background: var(--accent);
  border-color: var(--accent);
  /* indicator slides to the right half — translateX of its own width
     (50% of the track) lines it up with the on segment exactly. */
  transform: translateX(100%);
}

.toggle-control.is-active .toggle-track {
  background: var(--accent-soft);
  border-color: var(--border-strong);
}

@media (max-width: 620px) {
  body {
    overflow: auto;
  }

  .app-shell {
    min-height: 100svh;
    padding: 10px;
    overflow: visible;
  }

  .app-shell::after {
    background: var(--vignette-mid);
  }

  .map-background .dot-map,
  .shader-canvas {
    width: 220vw;
    max-width: none;
    max-height: 100svh;
  }

  .map-background .dot-map {
    opacity: 0.74;
  }

  .map-background.has-webgl-shader .dot-map {
    opacity: 0.012;
  }

  /* Crop the scene container to clear the bottom-sheet's peek (~200px)
     plus a 10px gap. Sized for the peek state — the state users see
     90% of the time. When the user drags the sheet open the globe gets
     partially covered, which is fine: at that point they're interacting
     with controls, not looking at the canvas.
     Tracking the rail's live height dynamically caused a drag glitch
     (transition on `bottom` fought the 1:1 finger tracking), so the
     value is intentionally static. */
  .globe-background,
  .map-background {
    bottom: 210px;
  }

  /* Mobile bottom-sheet pattern — control-rail is pinned to the bottom of
     the viewport and slides up/down. Drag handle replaces the desktop
     panel-toggle chip. Looks bar stays visible in the peek state so
     designers can still cycle presets without expanding the full sheet. */
  .control-rail {
    position: fixed;
    top: auto;
    bottom: 10px;
    left: 10px;
    right: 10px;
    width: auto;
    max-height: 82svh;
    margin-top: 0;
    padding: 6px 10px 18px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--panel);
    box-shadow: var(--shadow-up);
    transform: translateY(var(--drag-offset, 0px));
    transform-origin: bottom;
    transition:
      transform 360ms cubic-bezier(0.34, 1.3, 0.5, 1),
      opacity 220ms var(--ease),
      filter 220ms var(--ease);
    overflow: hidden auto;
    z-index: 8;
  }

  /* Shrink the brand-mark logo in the panel header on mobile so the
     header row is shorter — that brings the drag bar (which sits at
     the top) into visual alignment with the row between the logo and
     the right-side icon buttons. */
  .panel-meta-icon {
    width: 44px;
    height: 44px;
  }

  /* The desktop sticky behavior conflicts with the mobile drag handle:
     the handle is position: absolute; top: 0; height: 32px; z-index: 2
     and the sticky panel-header has z-index: 3, so it would steal the
     pointer events for the drag gesture. On mobile we drop sticky
     entirely (the rail is a bottom sheet, not a long-scrolling list,
     so a sticky header doesn't add value) and let the header flow
     beneath the drag bar in its natural position. Also drop the
     negative margins that pulled the header up into the drag-handle
     zone. */
  .panel-header {
    position: static;
    margin: 0;
    padding: 6px 8px 4px;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: transparent;
    /* Push the header below the 32px drag-handle strip so the handle
       gets the first 32px of pointer events uncontested. */
    margin-top: 28px;
  }

  /* On mobile, "collapsed" doesn't shrink-and-fade — it slides down to a
     small peek showing the drag handle + panel header + the looks bar
     for quick preset switching. Drag up to reveal the dropdown and
     the rest of the panel. The --drag-offset CSS var carries any live
     finger delta during a drag so the sheet follows the touch 1:1. */
  .control-rail.is-collapsed {
    transform: translateY(calc(100% - 200px + var(--drag-offset, 0px)));
    opacity: 1;
    filter: none;
    pointer-events: auto;
  }

  /* The desktop full-bleed rule fades the looks bar out when the panel is
     collapsed, but on mobile the collapsed peek exists precisely to keep
     the looks bar usable — same selector/specificity, later in source, so
     this wins and restores it inside the peek. */
  .app-shell.is-panel-collapsed .looks-bar {
    opacity: 1;
    pointer-events: auto;
  }

  /* During an active drag we suspend the spring transition so the sheet
     tracks the finger exactly; the transition kicks back in on release
     to handle the snap to the nearest state. */
  .control-rail.is-dragging {
    transition: none;
  }

  /* The drag handle is the entire top strip — wide tap target for the
     thumb-driven mobile flow. Override the default display:none from the
     desktop block so the handle is actually visible at the small viewport. */
  .mobile-drag-handle {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 32px;
    background: transparent;
    border: 0;
    padding: 0;
    cursor: grab;
    z-index: 2;
    touch-action: none;
  }

  .mobile-drag-handle:active {
    cursor: grabbing;
  }

  /* Visible grabber — iOS-style pill. Larger + more contrasted than the
     desktop --border-strong default so the affordance reads clearly on
     the small viewport. */
  .mobile-drag-handle-bar {
    display: block;
    width: 56px;
    height: 5px;
    margin: 12px auto 0;
    border-radius: 3px;
    background: var(--muted);
    opacity: 0.7;
    transition: background 180ms var(--ease), opacity 180ms var(--ease), width 220ms var(--ease);
  }

  .mobile-drag-handle:hover .mobile-drag-handle-bar,
  .mobile-drag-handle:focus-visible .mobile-drag-handle-bar {
    background: var(--text);
    opacity: 1;
  }

  /* During an active drag the bar widens slightly + goes full opacity
     for a "you're holding it" affordance, matching native iOS sheets. */
  .control-rail.is-dragging .mobile-drag-handle-bar {
    background: var(--text);
    opacity: 1;
    width: 72px;
  }

  /* The desktop chip toggle, keyboard shortcuts button, and panel-hide
     button all duplicate functionality the drag handle now owns. */
  .panel-toggle,
  .panel-icon-button--keyboard,
  .panel-icon-button--hide-panel {
    display: none;
  }

  /* Tooltips are a hover-discoverability pattern that doesn't translate to
     touch — every tap fires a "hover" and flashes a tooltip the user
     didn't ask for. Hide them entirely on mobile. */
  .follow-tooltip {
    display: none !important;
  }

  /* GitHub + bug-report links sit above the bottom sheet's collapsed peek
     line (200px + the sheet's 10px floating gap) so they stay visible at
     rest. When the user expands the sheet they're hidden under it, which
     is the same behavior as desktop when a modal opens — they reappear
     once the sheet collapses again. */
  .social-links {
    bottom: 224px;
    right: 14px;
  }

  .bug-link {
    bottom: 224px;
    left: 14px;
  }

  .top-actions {
    top: 10px;
    right: 10px;
    left: 10px;
  }

  .view-mode-switch {
    top: 16px;
    right: 10px;
    left: 10px;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    transform: none;
  }

  /* Perf HUD lives in the top-right corner on desktop. On mobile that
     collides with the centered view-mode-switch — move it below the
     toggle so the two read as a stack instead of fighting for the same
     row. */
  .perf-monitor {
    top: 76px;
    right: 10px;
  }

  .map-zoom-controls {
    bottom: 10px;
    grid-template-columns: 38px 62px 48px 38px;
  }

  .option-row {
    grid-template-columns: minmax(88px, 104px) minmax(0, 1fr);
    gap: 10px;
    padding: 9px 12px;
  }
}

@media (min-width: 521px) and (max-width: 620px) {
  body {
    overflow: hidden;
  }

  .app-shell {
    min-height: 100vh; /* fallback */
    min-height: 100svh;
    overflow: hidden;
  }
  /* Tablet-portrait range no longer overrides the bottom-sheet behavior.
     The base mobile media query above turns .control-rail into a fixed
     bottom sheet and that applies uniformly to anything <= 620px wide. */
}

@media (max-width: 460px) {
  .option-row {
    grid-template-columns: 1fr;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    transition-duration: 0.001ms !important;
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* =====================================================================
   PIXEL CORNERS (lukeb tool, radius=4 × multiplier=4)
   Smaller corner: quarter-circle approximated as 2 stair-stepped
   rows over a 16px footprint
   — pixelcorners.lukeb.co.uk/?radius=4&multiplier=4. Subtler than
   the previous radius=8 version. Border is zeroed on the clipped
   elements so the 1px stroke doesn't trace the staircase polygon
   edge — that's what made it "look weird"; with the border gone
   the clipped fill is the only edge.
   ===================================================================== */
.control-rail,
.color-picker,
.command-palette,
.export-modal,
.about-overlay,
.shortcuts-overlay,
.searchable-select-popover {
  border-radius: 0 !important;
  border: 0 !important;
  /* Polygon picked from the pixel-corners playground at
     /pixel-corners.html (radius=3, pixelSize=3, 9px footprint).
     One stair step per corner — subtle but visibly pixel-shaped.
     Same polygon used on every tier below so the whole UI shares
     one rounding language. */
  clip-path: polygon(
    0px 9px,
    0px 3px,
    3px 3px,
    3px 0px,
    9px 0px,
    calc(100% - 9px) 0px,
    calc(100% - 3px) 0px,
    calc(100% - 3px) 3px,
    calc(100% - 0px) 3px,
    calc(100% - 0px) 9px,
    calc(100% - 0px) calc(100% - 9px),
    calc(100% - 0px) calc(100% - 3px),
    calc(100% - 3px) calc(100% - 3px),
    calc(100% - 3px) calc(100% - 0px),
    calc(100% - 9px) calc(100% - 0px),
    9px calc(100% - 0px),
    3px calc(100% - 0px),
    3px calc(100% - 3px),
    0px calc(100% - 3px),
    0px calc(100% - 9px)
  );
}

/* =====================================================================
   PIXEL CORNERS — buttons / toggles / inputs / dropdown / modal
   (lukeb tool, radius=2 × multiplier=4) — 8px corner footprint.
   Applied to medium-sized "card-or-button" surfaces. Smaller controls
   (segmented toggles, switches, slider thumbs, swatches, eye button,
   inputs) get plain sharp 90° corners — the 8px corner would consume
   too much of their height. All listed elements lose their stroke
   so the clipped fill is the only visible edge (the 1px border was
   tracing the staircase polygon, which read as "weird" jagged trim).
   ===================================================================== */
.preset-card,
.look-card,
.look-tile,
.look-thumb,
.looks-chip,
.view-mode-button,
.panel-icon-button,
.top-button,
.ghost-button,
.action-button,
.button,
.button-ghost,
.export-modal-cta,
.export-modal-button,
.export-modal-pill,
.export-modal-tab,
.command-palette-row,
.color-picker-stop,
.color-picker-fill-tab,
.color-picker-header,
.color-picker-sv,
.about-card,
.toast,
.snackbar,
.map-zoom-button,
.privacy-optout-button,
.custom-shape-thumb,
.brand-logo-card,
.brand-og-tile,
.brand-palette-tile,
.social-link,
.bug-link,
.skip-link,
.map-zoom-controls,
.map-zoom-input,
.about-link,
.brand-link,
.docs-link,
.docs-preset-link,
.not-found-link,
.follow-tooltip,
.copy-fallback,
.preset-status,
.update-banner,
.about-avatar,
.looks-chip-preview-image,
.no-webgl-image,
.docs-preset-link,
/* Same 8px pixel-corner polygon for every "shaped" UI element —
   buttons, toggles, dropdown rows, inputs, panel sections. The
   previous attempt split these into pixel-corner vs sharp-corner
   tiers, which read as visually incoherent ("the buttons look weird
   with different corner in each"). Uniform polygon = single design
   language across cards (16px footprint above) + every control (8px
   footprint here). */
.option-block,
.option-block-eye,
.segmented-toggle,
.segmented-toggle-button,
.segmented-toggle-indicator,
.segmented-control,
.segmented-control-button,
.segmented-control-indicator,
.view-mode-switch,
.view-mode-indicator,
.toggle-track,
.toggle-thumb,
.toggle-control,
.color-swatch,
.color-control,
.color-control-fallback,
.color-picker-stop-swatch,
.transparent-toggle,
.searchable-select-trigger,
.searchable-select-search,
.searchable-select-option,
.command-palette-input,
.command-palette-kbd,
.command-palette-group,
.ascii-symbol-input,
.custom-topology-input,
.export-modal-input,
/* Color-picker chrome added here so the Solid/Gradient toggle, the
   HEX/RGB/HSB/HSL mode picker, and their active indicators share the
   same 9px 2-step staircase as every other panel control. The
   `.color-picker` popover card itself stays OUT — it has a real
   box-shadow that clip-path would slice off. */
.color-picker-fill,
.color-picker-fill-indicator,
.color-picker-modes,
.color-picker-mode,
input,
select,
textarea {
  border-radius: 0 !important;
  border: 0 !important;
  /* Same 9px / 1-stair-step polygon as the cards above. */
  clip-path: polygon(
    0px 9px,
    0px 3px,
    3px 3px,
    3px 0px,
    9px 0px,
    calc(100% - 9px) 0px,
    calc(100% - 3px) 0px,
    calc(100% - 3px) 3px,
    calc(100% - 0px) 3px,
    calc(100% - 0px) 9px,
    calc(100% - 0px) calc(100% - 9px),
    calc(100% - 0px) calc(100% - 3px),
    calc(100% - 3px) calc(100% - 3px),
    calc(100% - 3px) calc(100% - 0px),
    calc(100% - 9px) calc(100% - 0px),
    9px calc(100% - 0px),
    3px calc(100% - 0px),
    3px calc(100% - 3px),
    0px calc(100% - 3px),
    0px calc(100% - 9px)
  );
}

/* Slider tracks (6px tall) and thumbs (16px square) get a TIGHTER
   2px-notch polygon — the 9px polygon used everywhere else would
   self-intersect on these tiny elements (corner cuts at 9px overlap
   in the middle of a 16px thumb). 2px notch is the smallest valid
   pixel-step at this scale and still reads as part of the same
   stair-stepped family. */
input[type="range"]::-webkit-slider-runnable-track,
input[type="range"]::-webkit-slider-thumb,
input[type="range"]::-moz-range-track,
input[type="range"]::-moz-range-progress,
input[type="range"]::-moz-range-thumb,
.color-picker-hue input[type="range"]::-webkit-slider-runnable-track,
.color-picker-alpha input[type="range"]::-webkit-slider-runnable-track,
.color-picker-hue input[type="range"]::-webkit-slider-thumb,
.color-picker-alpha input[type="range"]::-webkit-slider-thumb,
.color-picker-hue input[type="range"]::-moz-range-track,
.color-picker-alpha input[type="range"]::-moz-range-track {
  border-radius: 0 !important;
  border: 0 !important;
  clip-path: polygon(
    0px 2px,
    2px 2px,
    2px 0px,
    calc(100% - 2px) 0px,
    calc(100% - 2px) 2px,
    calc(100% - 0px) 2px,
    calc(100% - 0px) calc(100% - 2px),
    calc(100% - 2px) calc(100% - 2px),
    calc(100% - 2px) calc(100% - 0px),
    2px calc(100% - 0px),
    2px calc(100% - 2px),
    0px calc(100% - 2px)
  ) !important;
}

/* On/off toggle's sliding KNOB — the user-facing "square inside" the
   pill. Bigger 3px notch + own !important to fight the 9px global rule
   that the parent .toggle-track gets. 18x16 footprint fits 3+3=6 < 16
   vertically, so the corners read clearly without self-intersection. */
.toggle-knob {
  border-radius: 0 !important;
  border: 0 !important;
  clip-path: polygon(
    0 3px,
    3px 3px,
    3px 0,
    calc(100% - 3px) 0,
    calc(100% - 3px) 3px,
    100% 3px,
    100% calc(100% - 3px),
    calc(100% - 3px) calc(100% - 3px),
    calc(100% - 3px) 100%,
    3px 100%,
    3px calc(100% - 3px),
    0 calc(100% - 3px)
  ) !important;
}

/* =====================================================================
   NUCLEAR FALLBACK — flatten rounded corners + strip borders on every
   non-card UI surface. The hand-picked selector list above was missing
   real class names (.looks-chip, .button, .export-modal-pill, etc.) so
   the targeted block didn't visually land. Universal `*` selector
   guarantees coverage; the 5 big cards above still keep their stepped
   pixel corners because clip-path is unaffected by border-radius: 0.
   ===================================================================== */
*,
*::before,
*::after {
  border-radius: 0 !important;
}

/* Border-stripping on common interactive class-name patterns. Substring
   matching catches the real names without us having to enumerate every
   class (the targeted list above missed too many). Inputs / form
   controls keep their structural borders where they're load-bearing
   for the layout — strip only on "shaped" elements. */
button,
[role="button"],
[role="switch"],
[role="tab"],
[role="radio"],
[role="option"],
[class*="-button"],
[class*="-chip"],
[class*="-card"],
[class*="-tile"],
[class*="-thumb"],
[class*="-pill"],
[class*="-tab"],
[class*="-trigger"],
[class*="-toggle"],
[class*="-swatch"],
[class*="-track"],
[class*="-row"],
[class*="-stop"],
[class*="-cta"],
[class*="-segmented"],
[class*="-select"],
[class*="-fill"],
[class*="-indicator"],
[class*="-handle"],
[class*="-bar"]:not(.looks-bar),
.option-block,
.option-block-eye,
.toggle-control,
.color-picker,
.command-palette,
.control-rail,
.export-modal,
.searchable-select-popover {
  border: 0 !important;
}

/* =====================================================================
   SMALL PIXEL CORNERS — single 4px corner notch.
   Override for compact controls (eye buttons, swatches, segmented
   buttons, icon buttons, kbd hints) where the 8px polygon above
   was too aggressive relative to the element's height — 8px on a
   22px eye button is 36% of the size. The 4px notch is a single
   pixel step per corner, still reads as pixel-art but lets the
   element keep most of its visual area.
   ===================================================================== */
.option-block-eye,
.color-swatch,
.color-picker-stop-swatch,
.color-picker-fill-tab,
.color-picker-close,
.about-overlay-close,
.command-palette-close,
.command-palette-kbd,
.export-modal-close,
.segmented-toggle-button,
.segmented-control-button,
.toggle-track,
.toggle-thumb,
.looks-chip,
.panel-icon-button,
.map-zoom-button,
.custom-shape-thumb,
.brand-palette-swatch {
  /* Same 9px / 1-stair-step polygon — uniform across every element. */
  clip-path: polygon(
    0px 9px,
    0px 3px,
    3px 3px,
    3px 0px,
    9px 0px,
    calc(100% - 9px) 0px,
    calc(100% - 3px) 0px,
    calc(100% - 3px) 3px,
    calc(100% - 0px) 3px,
    calc(100% - 0px) 9px,
    calc(100% - 0px) calc(100% - 9px),
    calc(100% - 0px) calc(100% - 3px),
    calc(100% - 3px) calc(100% - 3px),
    calc(100% - 3px) calc(100% - 0px),
    calc(100% - 9px) calc(100% - 0px),
    9px calc(100% - 0px),
    3px calc(100% - 0px),
    3px calc(100% - 3px),
    0px calc(100% - 3px),
    0px calc(100% - 9px)
  ) !important;
}
