/* ============================================================
   fb-ui.css — F. Binsabbar shared design system
   Tokens + component styles used across editor, env, materials,
   and viewer. Linked from ui-kit.html as the live preview.

   Pages keep their own page-layout CSS (max-widths, sidebars,
   safe-area logic, sheet positioning). This file is ONLY:
     - design tokens (:root)
     - global reset bits (focus, motion, tap-highlight)
     - component classes (buttons, inputs, switches, etc.)
   Adding it to a page is purely additive — nothing renames.
============================================================ */

:root {
  /* ---- core palette ---- */
  --bg: #0a0a0a;
  --canvas-bg: #edebe6;
  --accent: #f74827;
  --accent-soft: rgba(247, 72, 39, 0.18);
  --accent-yellow: #fee5a5;
  --champagne: #b19b7d;
  --text: #0e0e0e;
  --text-muted: #3a3a3a;
  --text-faint: #5a5a5a; /* WCAG AA on warm-beige */
  --hover: rgba(0,0,0,0.05);

  /* ---- semantic states ---- */
  --success: #2d8a4f;
  --success-soft: rgba(45, 138, 79, 0.16);
  --warning: #edb845;
  --warning-soft: rgba(237, 184, 69, 0.22);
  --error:   var(--accent);
  --error-soft: var(--accent-soft);

  /* ---- glass ---- */
  --glass-bg: rgba(249, 248, 246, 0.22);
  --glass-bg-strong: rgba(249, 248, 246, 0.42);
  --glass-blur: blur(18px) saturate(140%);
  --glass-shadow: 0 2px 8px rgba(0,0,0,0.06), 0 0 0 1px rgba(255,255,255,0.30), inset 0 1px 0 rgba(255,255,255,0.55);
  --glass-bg-inverse: rgba(20,20,20,0.42);
  --text-inverse: #f6f4ef;

  /* ---- radii ---- */
  --radius-pill: 999px;
  --radius-glass: 30px;
  --radius-card: 16px;
  --radius-control: 12px;
  --radius-md: 12px;
  --radius-sm: 8px;

  /* ---- 8pt spacing grid (Apple HIG) ---- */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 20px;
  --s-6: 24px;
  --s-7: 32px;
  --s-8: 40px;
  --s-9: 44px;   /* min tap target */
  --s-10: 56px;

  /* ---- Semantic control tokens ----
     Every form control on every page reads from THESE. Change a value
     here and the buttons, inputs, selects, sliders across the entire
     studio update with it. */
  --control-h:        var(--s-9);   /* 44 — default control height        */
  --control-h-sm:     var(--s-7);   /* 32 — compact (number steppers)     */
  --control-radius:   var(--radius-pill);
  --control-bg:       rgba(255,255,255,0.55);
  --control-bg-hover: rgba(255,255,255,0.85);
  --control-border:   rgba(0,0,0,0.10);
  --control-fs:       13px;
  --control-fs-sm:    12px;
  --control-tracking: 0.04em;
  --control-px:       var(--s-4);   /* horizontal padding (default)       */
  --control-px-sm:    var(--s-3);   /* compact horizontal padding         */

  /* Slider geometry — change here, every slider on every page updates */
  --slider-track-h:   4px;
  --slider-thumb-w:   38px;
  --slider-thumb-h:   28px;
  --slider-thumb-shadow: 0 3px 8px rgba(0,0,0,0.15), 0 1px 2px rgba(0,0,0,0.10);

  /* Select chevron — embedded SVG; recolor by replacing %23xxxxxx */
  --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230e0e0e' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");

  /* ---- Unified parameter-title style ----
     Every form-control TITLE/LABEL across every page reads from these
     vars: slider title, number-stepper title, radio group title, toggle
     label, color-pill title, texture-map title, etc. Override on :root
     to retune every label everywhere at once. */
  --param-title-fs:     11px;
  --param-title-tt:     uppercase;
  --param-title-ls:     0.10em;
  --param-title-color:  var(--text-muted);
  --param-title-weight: 400;
}

/* Single source of truth for parameter labels. Covers every container
   pattern in the kit + live pages. */
.field > label,
.slider-field > label,
.panel-row > label,
.opt-row label,
.check-row label,
.radio-row label,
.tex-row > label,
.field-label,
.tile .label {
  font-size: var(--param-title-fs);
  text-transform: var(--param-title-tt);
  letter-spacing: var(--param-title-ls);
  color: var(--param-title-color);
  font-weight: var(--param-title-weight);
}

/* ---- Unified hover affordance for inputs ----
   Text/number/select/text-shaped inputs + the OUTER color-hex-pill get
   a 2px accent ring on hover via outline (no layout shift). Sliders
   are excluded — the pill thumb already conveys interactivity, and a
   ring around the track reads as a glitch.
   For .color-hex-pill, only the OUTER wrapper paints the ring — the
   inner color/hex inputs explicitly suppress their own hover so we
   don't get nested borders. */
input[type=text]:hover,
input[type=number]:hover,
input[type=search]:hover,
input[type=email]:hover,
input[type=password]:hover,
select:hover,
textarea:hover,
.text-input:hover,
.v-input:hover,
.fb-select:hover,
.color-hex-pill:hover {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-color: var(--accent);
}
/* Suppress hover on inner pill children — only the wrapper shows it. */
.color-hex-pill > input[type=color]:hover,
.color-hex-pill > input.hex:hover,
.color-hex-pill > .hex:hover {
  outline: none;
  border-color: transparent;
}

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

/* Visible keyboard focus ring on every interactive element */
:focus { outline: none; }
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible,
.mat:focus-visible,
.iconbtn:focus-visible,
.fab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.primary.is-dirty:focus-visible,
.primary.success:focus-visible,
.secondary.danger:focus-visible,
.mat.selected:focus-visible {
  outline-color: var(--text);
}

/* iOS / touch hygiene — applies broadly but never visually */
* {
  -webkit-tap-highlight-color: transparent;
}

/* Smooth font rendering + Futura everywhere — defeats the user-agent default
   font-family on form controls (input/button/select/textarea) which would
   otherwise fall through to system-ui/-apple-system. */
html, body, button, input, select, textarea, .tip-bubble, .toast-static, .toast-live {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
button, input, select, textarea {
  font-family: inherit;
}

/* Buttons & labels — don't trigger long-press text selection on touch */
button, label, .mat, .tag, .count-pill, .toolbox, .iconbtn, .fab {
  -webkit-user-select: none;
  user-select: none;
}

/* All button glyphs centered, no inline-baseline gap */
button svg, .iconbtn svg, .fab svg, .toast-static .close svg, .toast-live .close svg {
  display: block;
  margin: auto;
}

/* Crisper SVG rendering — prevents anti-alias fuzz on small icons,
   especially at non-integer pixel positions inside flex layouts. */
svg {
  shape-rendering: geometricPrecision;
}
svg * {
  vector-effect: non-scaling-stroke;
}

/* Global icon-stroke weight — overrides inline SVG stroke-width attrs
   (presentation attributes have specificity 0) AND component-level CSS
   rules via !important. Tune this single value to retune the kit's
   icon weight everywhere at once. */
svg path,
svg line,
svg polyline,
svg circle,
svg rect,
svg polygon,
svg ellipse {
  stroke-width: 1 !important;
}

/* ---- Touch device overrides ----------------------------------
   On touch, :hover stays stuck after a tap. Neutralize the
   most visible hover styles and let :active carry the feedback. */
@media (hover: none) {
  .iconbtn:hover         { background: transparent; color: var(--text); }
  .iconbtn.active:hover  { background: rgba(177,155,125,0.18); color: var(--champagne); }
  .fab:hover             { background: var(--glass-bg); color: var(--text); }
  .mat:hover             { background: rgba(255,255,255,0.40); }
  .secondary:hover       { background: rgba(255,255,255,0.55); }
  .primary:hover         { opacity: 1; }
  .primary.is-dirty:hover,
  .primary.success:hover,
  .secondary.danger:hover{ filter: none; }
  .quality-pick label:hover { background: rgba(255,255,255,0.55); }
  .toast-static .close:hover,
  .toast-live .close:hover  { background: transparent; }
  .tip:hover::after { opacity: 0; }
}

/* Brand wordmark — always 500 weight, 0.10em tracking, uppercase */
.brand {
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
}

/* ============== GLASS CARD (utility) ============== */
.glass-card {
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-card);
  box-shadow: var(--glass-shadow);
  padding: var(--s-5);
}
.glass-card.strong {
  background: var(--glass-bg-strong);
}

/* ============== BUTTONS ============== */
button.primary {
  width: 100%;
  height: var(--s-9);
  padding: 0 var(--s-5);
  background: var(--text);
  color: #fff;
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
  font-family: inherit;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  transition: transform 0.08s, opacity 0.12s, filter 0.12s;
}
button.primary:hover { opacity: 0.85; }
button.primary:active { transform: scale(0.98); }
button.primary:disabled {
  background: rgba(0,0,0,0.10);
  color: rgba(0,0,0,0.45);
  cursor: not-allowed;
  opacity: 1;
}
button.primary.is-dirty { background: var(--accent); color: #fff; }
button.primary.is-dirty:hover { filter: brightness(1.12); opacity: 1; }
button.primary.success { background: var(--success); color: #fff; }
button.primary.success:hover { filter: brightness(1.10); opacity: 1; }

button.secondary {
  width: 100%;
  height: var(--s-9);
  padding: 0 var(--s-4);
  background: rgba(255,255,255,0.55);
  color: var(--text);
  border: 1px solid rgba(0,0,0,0.08);
  border-radius: var(--radius-pill);
  cursor: pointer;
  font-family: inherit;
  font-size: 12px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  transition: background 0.12s;
}
button.secondary:hover { background: rgba(255,255,255,0.85); }
button.secondary.danger {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
button.secondary.danger:hover { filter: brightness(1.10); }

/* Icon buttons (toolbox) — 44pt Apple min tap target */
.iconbtn {
  width: var(--s-9); height: var(--s-9);
  border-radius: 50%;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--text);
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.12s, transform 0.08s;
}
.iconbtn:hover { background: var(--accent); color: #fff; }
.iconbtn:active { background: var(--accent); color: #fff; transform: scale(0.94); }
.iconbtn.active { background: rgba(177,155,125,0.18); color: var(--champagne); }
.iconbtn.active:hover { background: var(--accent); color: #fff; }
.iconbtn svg { width: 22px; height: 22px; stroke: currentColor; stroke-width: 1.4; fill: none; }

/* FAB — 44pt frosted-glass action */
.fab {
  width: var(--s-9); height: var(--s-9);
  border-radius: 50%;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  border: 0;
  color: var(--text);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.12s, color 0.12s, transform 0.08s;
}
.fab:hover { background: var(--accent); color: #fff; }
.fab:active { background: var(--accent); color: #fff; transform: scale(0.94); }
.fab svg { width: 22px; height: 22px; stroke: currentColor; stroke-width: 1.4; fill: none; }

/* ============== FAB · fluid (morph-on-hover) ==============
   One component, three variants (primary · default · quiet). Circle
   at rest. Hover/focus expands to a pill with the label inline — no
   separate tooltip layer. Designed to drop in as a replacement for
   bespoke .signout-fab / .share-link / .publish-fab styles in editor.
   Height matches existing 40 px FABs (--s-8). */
.fab-fluid {
  display: inline-flex; align-items: center;
  height: var(--s-8);
  padding: 0;
  border: 0;
  border-radius: calc(var(--s-8) / 2);   /* always-pill since width grows >= height */
  background: rgba(255,255,255,0.55);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  color: var(--text);
  cursor: pointer;
  font: inherit; font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  overflow: hidden;
  transition: background 0.22s ease, box-shadow 0.22s ease, color 0.22s ease;
}
.fab-fluid .ico {
  width: var(--s-8); height: var(--s-8);
  flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
.fab-fluid .ico svg {
  width: 17px; height: 17px;
  stroke: currentColor; fill: none;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
}
.fab-fluid .ico .icon { --icon-size: 19px; }
.fab-fluid .lbl {
  max-width: 0;
  opacity: 0;
  white-space: nowrap;
  overflow: hidden;
  padding-right: 0;
  transition: max-width 0.34s cubic-bezier(0.34, 1.36, 0.64, 1),
              opacity 0.18s ease,
              padding-right 0.34s cubic-bezier(0.34, 1.36, 0.64, 1);
}
.fab-fluid:hover .lbl,
.fab-fluid:focus-visible .lbl {
  max-width: 200px;
  opacity: 1;
  padding-right: 16px;
}
.fab-fluid:hover {
  background: rgba(255,255,255,0.85);
  box-shadow: 0 6px 20px rgba(0,0,0,0.10);
}
.fab-fluid.fab-fluid-primary {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 6px 18px rgba(247,72,39,0.32);
}
.fab-fluid.fab-fluid-primary:hover {
  background: #d83a1c;
  box-shadow: 0 10px 28px rgba(247,72,39,0.42);
}
.fab-fluid.fab-fluid-quiet {
  background: rgba(255,255,255,0.40);
  box-shadow: none;
  color: var(--text-muted);
}
.fab-fluid.fab-fluid-quiet:hover {
  background: rgba(255,255,255,0.7);
  color: var(--text);
}
.fab-fluid:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.fab-fluid:disabled:hover {
  background: rgba(255,255,255,0.55);
  box-shadow: var(--glass-shadow);
}

/* Toolbox island (groups iconbtns) */
.toolbox {
  display: inline-flex;
  gap: var(--s-1);
  padding: var(--s-1);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  border-radius: var(--radius-pill);
}

/* ============== TEXT & NUMBER INPUTS ============== */
.text-input {
  width: 100%;
  height: var(--s-9);
  padding: 0 var(--s-4);
  background: rgba(255,255,255,0.55);
  border: 1px solid rgba(0,0,0,0.10);
  border-radius: var(--radius-pill);
  font-family: inherit;
  font-size: 13px;
  color: var(--text);
  outline: none;
  letter-spacing: 0.04em;
  transition: border-color 0.12s, background 0.12s;
}
.text-input::placeholder { color: var(--text-faint); }
.text-input:focus {
  background: rgba(255,255,255,0.75);
}

.v-input {
  width: 100%;
  height: 32px;
  padding: 0 var(--s-3);
  background: rgba(255,255,255,0.55);
  border: 1px solid rgba(0,0,0,0.10);
  border-radius: var(--radius-pill);
  font-family: inherit;          /* Futura PT, like every other control */
  font-size: 12px;
  font-variant-numeric: tabular-nums; /* keep digits column-aligned */
  color: var(--text);
  text-align: center;
  outline: none;
  transition: border-color 0.12s;
  -moz-appearance: textfield;
  appearance: textfield;
}
.v-input::-webkit-outer-spin-button,
.v-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* Touch: keep inputs ≥16px so iOS Safari doesn't auto-zoom on focus */
@media (hover: none) {
  .text-input, .v-input, .fb-select, .pers-input { font-size: 16px; }
  .v-input { height: 36px; padding: 0 var(--s-3); }
}

/* Underline-only personalization input */
.pers-input {
  width: 160px;
  padding: 5px 10px 7px;
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgba(0,0,0,0.10);
  border-radius: 0;
  text-align: center;
  font-family: inherit;
  font-weight: 400;
  font-size: 17px;
  letter-spacing: 0.10em;
  color: rgba(26,26,26,0.78);
  outline: none;
  transition: border-bottom-color 0.18s, color 0.18s;
}
.pers-input::placeholder { color: rgba(26,26,26,0.30); letter-spacing: 0.10em; }
.pers-input:focus {
  border-bottom-color: var(--champagne);
  color: #1a1a1a;
}
.pers-input:focus-visible { outline: none; }

/* ============== FIELD + RANGE SLIDER ============== */
.field {
  display: flex; flex-direction: column;
  gap: var(--s-3);            /* vertical breathing between stacked rows */
  width: 100%;
  margin-block: var(--s-2);   /* separation when fields stack */
}
.field + .field { margin-top: 0; } /* col gap handles stacking; avoid double */
.field .row-flex { display: flex; align-items: center; gap: var(--s-3); }

/* Slider field — flat markup variant.
   Two-row grid: label spans full width on row 1; range + .v-input share row 2.
   - column 1 = minmax(0,1fr) so the range cannot push past its column
   - column 2 = 60px fixed (matches .v-input width)
   - column-gap var(--s-4) (16px = thumb half-width) so the thumb at max value
     cannot graze the .v-input
   - label drops into the range row's upper padding via margin-bottom: -15px,
     leaving ~3px of breathing room between baseline and stripe */
.slider-field {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 60px;
  grid-template-rows: auto auto;
  column-gap: var(--s-4);
  row-gap: 0;
  width: 100%;
  margin-block: var(--s-2);
}
.slider-field > label {
  grid-column: 1 / -1;
  grid-row: 1;
  align-self: end;
  margin-bottom: -15px;
  pointer-events: none;          /* let clicks pass through to the slider */
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.10em;
  color: var(--text-muted);
  /* No z-index: title sits behind the slider track + thumb in DOM order. */
}
.slider-field > input[type=range] {
  grid-column: 1;
  grid-row: 2;
  align-self: center;
  width: 100%;
  min-width: 0;
}
.slider-field > .v-input {
  grid-column: 2;
  grid-row: 2;
  align-self: center;
  width: 100%;
}
.field label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.10em;
  color: var(--text-muted);
}
/* Slider — 4pt track + 32×24 pill thumb, with equal vertical breathing room */
.field input[type=range],
.slider-field input[type=range],
input[type=range].fb-range {
  -webkit-appearance: none;
  appearance: none;
  flex: 1;
  width: 100%;
  height: 36px;          /* taller so thumb has equal margin top/bottom */
  background: transparent;
  cursor: pointer;
}
.field input[type=range]::-webkit-slider-runnable-track,
.slider-field input[type=range]::-webkit-slider-runnable-track,
input[type=range].fb-range::-webkit-slider-runnable-track {
  height: 4px;
  background: rgba(0,0,0,0.12);
  border-radius: 2px;
}
.field input[type=range]::-moz-range-track,
.slider-field input[type=range]::-moz-range-track,
input[type=range].fb-range::-moz-range-track {
  height: 4px;
  background: rgba(0,0,0,0.12);
  border-radius: 2px;
}
.field input[type=range]::-webkit-slider-thumb,
.slider-field input[type=range]::-webkit-slider-thumb,
input[type=range].fb-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: var(--slider-thumb-w);
  height: var(--slider-thumb-h);
  border-radius: var(--radius-pill);
  /* Default state — global glass effect (matches FABs / panels / toolbox).
     Solid white reads as a flat element on the busy 3D viewport; glass
     tokens tie the thumb visually to the rest of the chrome. The
     accent-pill on :active still wins (rule below). */
  background: var(--glass-bg-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 0;
  box-shadow: var(--glass-shadow);
  margin-top: calc((var(--slider-track-h) - var(--slider-thumb-h)) / 2);
  /* Spring on transform so press / release bounce; keep fill + shadow snappy. */
  transition: transform 0.32s cubic-bezier(0.34, 1.55, 0.64, 1),
              box-shadow 0.12s,
              background 0.12s;
}
.field input[type=range]::-moz-range-thumb,
.slider-field input[type=range]::-moz-range-thumb,
input[type=range].fb-range::-moz-range-thumb {
  width: var(--slider-thumb-w);
  height: var(--slider-thumb-h);
  border-radius: var(--radius-pill);
  background: var(--glass-bg-strong);
  /* Firefox doesn't fully support backdrop-filter on form controls;
     it falls through to the glass-bg tint, which still reads as a
     translucent pill — same direction as Chrome / Safari. */
  backdrop-filter: var(--glass-blur);
  border: 0;
  box-shadow: var(--glass-shadow);
  transition: transform 0.32s cubic-bezier(0.34, 1.55, 0.64, 1),
              background 0.12s;
}
/* Held / dragging — solid red pill, scaled up with spring overshoot. */
.field input[type=range]:active::-webkit-slider-thumb,
.slider-field input[type=range]:active::-webkit-slider-thumb,
input[type=range].fb-range:active::-webkit-slider-thumb {
  background: var(--accent);
  box-shadow: none;
  transform: scale(1.18);
}
.field input[type=range]:active::-moz-range-thumb,
.slider-field input[type=range]:active::-moz-range-thumb,
input[type=range].fb-range:active::-moz-range-thumb {
  background: var(--accent);
  box-shadow: none;
  transform: scale(1.18);
}
.field .val { width: 60px; }

/* ============== COLOR + HEX PILL =================
   Single pill, split 50/50: native colour swatch on the left, monospace
   hex string on the right, divided by a hairline. Acts like a giant
   color input — clicking either half opens the OS picker (the wrapper
   is a <label> tied to the input), and the hex updates as the value
   changes. Used wherever a colour needs to be authored AND read at a
   glance: material Pill Color / Color / Tint, env diffuser color, etc.

   Markup:
     <label class="color-hex-pill">
       <input type="color" value="#caaf89">
       <span class="hex">#caaf89</span>
     </label>
================================================== */
.color-hex-pill {
  display: inline-flex;
  align-items: stretch;
  width: 100%;
  height: var(--control-h-sm);
  border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.55);
  border: 1px solid rgba(0,0,0,0.10);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.12s;
}
.color-hex-pill:hover { border-color: rgba(0,0,0,0.20); }
.color-hex-pill:focus-within { border-color: var(--accent); }
.color-hex-pill > input[type=color] {
  -webkit-appearance: none;
  appearance: none;
  flex: 1 1 50%;
  height: 100%;
  padding: 0;
  border: 0;
  border-radius: 0;            /* outer .color-hex-pill is the only rounded edge */
  /* No `background` rule — the inline style="background-color:VALUE" set
     by the template (and JS event handler) paints the swatch reliably,
     even when Safari refuses to render ::-webkit-color-swatch under
     -webkit-appearance:none. */
  cursor: pointer;
}
.color-hex-pill > input[type=color]::-webkit-color-swatch-wrapper {
  padding: 0;
  border: 0;
  border-radius: 0;
}
.color-hex-pill > input[type=color]::-webkit-color-swatch { border: 0; border-radius: 0; }
.color-hex-pill > input[type=color]::-moz-color-swatch    { border: 0; border-radius: 0; }
.color-hex-pill > input[type=color]::-moz-focus-inner     { border: 0; padding: 0; }
/* Right half — either a read-only <span class="hex"> or a typeable
   <input class="hex"> (editor diffuser uses the latter for hex paste). */
.color-hex-pill > .hex,
.color-hex-pill > input.hex {
  flex: 1 1 50%;
  height: 100%;
  display: flex; align-items: center; justify-content: center;
  border: none !important;        /* no divider on either variant — color + hex read as one pill */
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: var(--control-fs-sm);
  color: var(--text);
  letter-spacing: 0.04em;
  text-transform: lowercase;
  text-align: center;
}
.color-hex-pill > span.hex {
  user-select: none;
  pointer-events: none;          /* clicks fall through to the color input */
}
.color-hex-pill > input.hex {
  background: transparent;
  outline: none;
  padding: 0 var(--s-2);
}
.color-hex-pill > input.hex:focus { background: rgba(0,0,0,0.04); }

/* ============== SELECT / DROPDOWN ============== */
.fb-select {
  width: 100%;
  height: var(--s-9);
  padding: 0 var(--s-8) 0 var(--s-5);
  background-color: rgba(255,255,255,0.55);
  border: 1px solid rgba(0,0,0,0.10);
  border-radius: var(--radius-pill);
  font-family: inherit;
  font-size: 13px;
  color: var(--text);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230e0e0e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s-4) center;
  background-size: 14px 14px;
  cursor: pointer;
  outline: none;
  transition: border-color 0.12s;
}

/* ============== SEGMENTED / RADIO ============== */
.quality-pick {
  display: flex; gap: 4px;
  width: 100%;
  height: var(--s-7);
  padding: 2px;
  background: rgba(0,0,0,0.05);
  border-radius: var(--radius-pill);
}
.quality-pick label {
  flex: 1;
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  background: transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  border: 0;
  transition: background 0.12s, color 0.12s;
}
.quality-pick input { display: none; }
.quality-pick input:checked + label {
  background: #fff;
  color: var(--text);
  font-weight: 500;
  box-shadow: 0 1px 3px rgba(0,0,0,0.10), 0 0 0 0.5px rgba(0,0,0,0.04);
}

/* ============== PILL RADIO + CHECKBOX (custom) ============== */
/* Both controls share the same pill base — radio fills with an inner pill
   when checked, checkbox shows a check glyph. */
.radio-row { display: flex; gap: var(--s-4); flex-wrap: wrap; }
.radio-row label,
.check-row label {
  display: inline-flex; align-items: center; gap: var(--s-3);
  min-height: var(--s-9);
  font-size: 13px; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-muted); cursor: pointer;
}
.check-row { display: flex; flex-direction: column; gap: 0; }

.radio-row input[type=radio],
.check-row input[type=checkbox] {
  -webkit-appearance: none;
  appearance: none;
  width: 36px; height: 20px;
  margin: 0;
  border-radius: var(--radius-pill);
  border: 2px solid rgba(0,0,0,0.30);
  background: transparent;
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
  transition: border-color 0.15s, background 0.15s;
}
.radio-row input[type=radio]:hover,
.check-row input[type=checkbox]:hover {
  border-color: rgba(0,0,0,0.55);
}
.radio-row input[type=radio]:checked,
.check-row input[type=checkbox]:checked {
  border-color: var(--accent);
  background: var(--accent);
}
.radio-row input[type=radio]:disabled,
.check-row input[type=checkbox]:disabled {
  opacity: 0.45; cursor: not-allowed;
}

/* Checked state — same inner pill for radio and checkbox */
.radio-row input[type=radio]:checked::after,
.check-row input[type=checkbox]:checked::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: var(--radius-pill);
  background: #fff;
}

/* ============== SWITCH (Apple iOS 51×31, 27pt knob) ============== */
.switch {
  position: relative; display: inline-block;
  width: 60px; height: 31px; flex-shrink: 0;
}
.switch input { opacity: 0; width: 0; height: 0; }
.switch .slider {
  position: absolute; cursor: pointer; inset: 0;
  background: rgba(120,120,128,0.32);
  border-radius: 999px;
  transition: background 0.20s;
}
.switch .slider::before {
  content: ""; position: absolute;
  width: 32px; height: 23px;
  left: 4px; top: 4px;                /* equal 4px rest margin on left/top/bottom */
  background: #fff;
  border-radius: var(--radius-pill);
  box-shadow: 0 3px 8px rgba(0,0,0,0.15), 0 1px 2px rgba(0,0,0,0.10);
  transition: transform 0.20s;
}
.switch input:checked + .slider { background: var(--accent); }
/* Travel = track 60 − knob 32 − left 4 − right 4 = 20 → equal 4px rest margin on right too */
.switch input:checked + .slider::before { transform: translateX(20px); }

.switch-row {
  display: inline-flex; align-items: center; gap: var(--s-3);
  min-height: var(--s-9);
  cursor: pointer;
}
.switch-row .lbl {
  font-size: 13px; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-muted);
}

/* ============== MATERIAL PILL ==============
   Aliases: .mat (kit canonical), .mat-row (materials.html page class),
   .obj-row (env.html page class). All three render with the same pill style. */
.mat, .mat-row, .obj-row {
  display: inline-flex; align-items: center;
  height: var(--s-9);
  background: rgba(255,255,255,0.40);
  padding: 0 var(--s-4) 0 var(--s-2);
  font-size: 12px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border: 2px solid transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, box-shadow 0.12s;
}
.mat:hover, .mat-row:hover, .obj-row:hover {
  background: rgba(255,255,255,0.65);
}
.mat .swatch,
.mat-row .swatch,
.obj-row .swatch {
  width: 22px; height: 22px;
  border-radius: 50%;
  display: inline-block;
  box-shadow: 0 0 0 0.5px rgba(0,0,0,0.10);
  margin-right: var(--s-3);
  flex-shrink: 0;
}
.mat.selected,
.mat-row.selected,
.obj-row.selected {
  border-color: var(--accent);
  box-shadow: none;
}

/* ============== BADGES / COUNT PILLS ============== */
.count-pill {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px;
  padding: 0 var(--s-2);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.08);
  color: var(--text);
  font-size: 11px; font-weight: 400;
  letter-spacing: 0;
}
.count-pill.has-items { background: var(--accent); color: #fff; }

.tag {
  display: inline-flex; align-items: center;
  height: 24px;
  padding: 0 var(--s-3);
  border-radius: 999px;
  background: rgba(0,0,0,0.06);
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.10em;
}
.tag.accent  { background: var(--accent-soft);  color: var(--accent);  }
.tag.dark    { background: var(--text);         color: #fff;           }
.tag.success { background: var(--success-soft); color: var(--success); }
.tag.warning { background: var(--warning-soft); color: var(--text); }
.tag.error   { background: var(--error-soft);   color: var(--error);   }

/* ============== TOAST ============== */
.toast-static {
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--s-9);
  background: var(--glass-bg-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  color: var(--text);
  padding: var(--s-2) var(--s-2) var(--s-2) var(--s-5);
  border-radius: var(--radius-pill);
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  box-shadow: var(--glass-shadow);
}
.toast-static.success { box-shadow: var(--glass-shadow), 0 0 0 1px var(--success-soft) inset; }
.toast-static.warning { box-shadow: var(--glass-shadow), 0 0 0 1px var(--warning-soft) inset; }
.toast-static.error   { box-shadow: var(--glass-shadow), 0 0 0 1px var(--error-soft)   inset; }
.toast-static .dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--text);
  display: inline-block; flex-shrink: 0;
}
.toast-static.success .dot { background: var(--success); }
.toast-static.warning .dot { background: var(--warning); }
.toast-static.error   .dot { background: var(--error);   }
.toast-static .close {
  width: var(--s-7); height: var(--s-7);
  border-radius: 50%;
  background: rgba(0,0,0,0.06); border: 0; cursor: pointer; color: var(--text);
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  margin-left: auto; /* always pin to right end */
  transition: background 0.12s;
}
.toast-static .close:hover { background: rgba(0,0,0,0.10); }
.toast-static .close svg { width: 14px; height: 14px; stroke: currentColor; stroke-width: 1.6; fill: none; }

.toast-live {
  position: fixed;
  left: 50%;
  top: max(var(--s-6), calc(env(safe-area-inset-top) + var(--s-3)));
  transform: translateX(-50%) translateY(-8px);
  background: var(--glass-bg-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  color: var(--text);
  display: inline-flex; align-items: center; gap: var(--s-3);
  min-height: var(--s-9);
  /* don't blow off-screen on phones — clamp to safe-area-aware viewport width */
  max-width: min(420px, calc(100vw - var(--s-6) - env(safe-area-inset-left) - env(safe-area-inset-right)));
  padding: var(--s-2) var(--s-2) var(--s-2) var(--s-5);
  border-radius: var(--radius-pill);
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  box-shadow: var(--glass-shadow);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s, transform 0.3s;
  z-index: 100;
}
.toast-live.show { opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; }
.toast-live .close {
  width: var(--s-7); height: var(--s-7);
  border-radius: 50%;
  background: rgba(0,0,0,0.06); border: 0; cursor: pointer; color: var(--text);
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  margin-left: var(--s-3); /* breathing room from message text */
  transition: background 0.12s;
}
.toast-live .close:hover { background: rgba(0,0,0,0.10); }
.toast-live .close svg { width: 14px; height: 14px; stroke: currentColor; stroke-width: 1.6; fill: none; }

/* ============== TOOLTIP — glass pill, real element (frosted like toast) ============== */
/* NOTE: backdrop-filter on ::after pseudo-elements is unreliable in Safari, so
   tooltips use a real <span class="tip-bubble"> child instead of [data-tip]::after.
   That guarantees the same frosted glass as toasts. */
.tip {
  position: relative;
  display: inline-block;
}
.tip-bubble {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: rgba(249, 248, 246, 0.82);   /* heavy frosted glass — clearly visible on any backdrop */
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  color: var(--text);
  font-family: inherit;                     /* Futura PT */
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  white-space: normal;
  max-width: min(280px, calc(100vw - 32px));
  text-align: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s, transform 0.18s;
  z-index: 10;
}
.tip:hover .tip-bubble,
.tip:focus-within .tip-bubble {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
@media (hover: none) {
  .tip:hover .tip-bubble { opacity: 0; } /* hover tooltips are dead on touch */
}

/* ============== BRAND ICONS ==============
   Official F. Binsabbar icon set, hosted on Firebase Storage.
   Manifest: brand/icons/manifest.json (38 monochrome 24×24 SVGs).

   Usage:  <span class="icon i-camera"></span>
   Recolor: inherits the parent's `color` (mask-image + currentColor bg).
   Size:    set --icon-size on the host, default 22px.
============================================ */
.icon {
  display: inline-block;
  width: var(--icon-size, 22px);
  height: var(--icon-size, 22px);
  background-color: currentColor;
  mask: var(--fb-icon) center / contain no-repeat;
  -webkit-mask: var(--fb-icon) center / contain no-repeat;
  flex-shrink: 0;
  vertical-align: middle;
}
.i-a4-folded       { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fa4-folded.svg?alt=media'); }
.i-ar-cube         { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Far-cube.svg?alt=media'); }
.i-book            { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fbook.svg?alt=media'); }
.i-camera          { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fcamera.svg?alt=media'); }
.i-chain-length    { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fchain-length.svg?alt=media'); }
.i-chevron-down    { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fchevron-down.svg?alt=media'); }
.i-chevron-left    { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fchevron-left.svg?alt=media'); }
.i-chevron-right   { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fchevron-right.svg?alt=media'); }
.i-chevron-up      { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fchevron-up.svg?alt=media'); }
.i-client          { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fclient.svg?alt=media'); }
.i-close           { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fclose.svg?alt=media'); }
.i-color-wheel     { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fcolor-wheel.svg?alt=media'); }
.i-concierge-bell  { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fconcierge-bell.svg?alt=media'); }
.i-delete          { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fdelete.svg?alt=media'); }
.i-diamond         { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fdiamond.svg?alt=media'); }
.i-envelope-wax    { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fenvelope-wax.svg?alt=media'); }
.i-folder          { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Ffolder.svg?alt=media'); }
.i-fullscreen      { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Ffullscreen.svg?alt=media'); }
.i-globe           { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fglobe.svg?alt=media'); }
.i-loupe           { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Floupe.svg?alt=media'); }
.i-love            { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Flove.svg?alt=media'); }
.i-make-it-unique  { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fmake-it-unique.svg?alt=media'); }
.i-material        { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fmaterial.svg?alt=media'); }
.i-menu            { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fmenu.svg?alt=media'); }
.i-minus           { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fminus.svg?alt=media'); }
.i-model-reference { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fmodel-reference.svg?alt=media'); }
.i-perfume         { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fperfume.svg?alt=media'); }
.i-plus            { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fplus.svg?alt=media'); }
.i-resizable       { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fresizable.svg?alt=media'); }
.i-saudi-emblem    { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fsaudi-emblem.svg?alt=media'); }
.i-search          { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fsearch.svg?alt=media'); }
.i-settings        { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fsettings.svg?alt=media'); }
.i-share           { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fshare.svg?alt=media'); }
.i-shopping-bag    { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fshopping-bag.svg?alt=media'); }
.i-sign-out        { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fsign-out.svg?alt=media'); }
.i-upload          { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fupload.svg?alt=media'); }
.i-zoom-in         { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fzoom-in.svg?alt=media'); }
.i-zoom-out        { --fb-icon: url('https://firebasestorage.googleapis.com/v0/b/f-b-ambassadors-juam8t.firebasestorage.app/o/brand%2Ficons%2Fzoom-out.svg?alt=media'); }

/* When .icon is dropped into existing icon hosts, inherit their sizing/alignment */
.iconbtn .icon, .fab .icon { --icon-size: 22px; }
.popover-item .icon       { --icon-size: 20px; }
.toast-static .close .icon,
.toast-live .close .icon,
.modal-header .close .icon,
.drawer-header .close .icon { --icon-size: 14px; }

/* ============== POPUPS ==============
   Three primitives that all share the kit's glass tokens:
     - .modal      → centered dialog with backdrop (confirm, alert, form)
     - .drawer     → side-anchored slide-in panel (publish, library, settings)
     - .popover    → small attached popup near a trigger (menu, color picker)
   Each has an optional .show class for the visible state; pages drive that
   via JS.  Dismiss-on-Escape and click-outside are application concerns. */

/* --- shared backdrop (used by .modal and full-screen .drawer.has-backdrop) --- */
.popup-backdrop {
  position: fixed; inset: 0;
  background: rgba(14, 14, 14, 0.42);
  backdrop-filter: blur(6px) saturate(120%);
  -webkit-backdrop-filter: blur(6px) saturate(120%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s ease;
  z-index: 9500;
}
.popup-backdrop.show { opacity: 1; pointer-events: auto; }

/* --- MODAL (centered dialog) --- */
.modal {
  position: fixed;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%) scale(0.96);
  width: min(460px, calc(100vw - var(--s-7)));
  max-height: calc(100vh - var(--s-10));
  /* Solid-tinted off-white card (kit-spec for confirm dialogs / form
     modals). Glass-strong (`--glass-bg-strong`) was too transparent
     when stacked over a busy 3D viewport — the form fields read
     muddy. Keep the backdrop-filter for the inner content but paint
     a near-opaque background. */
  background: rgba(253, 252, 250, 0.92);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-glass);
  box-shadow: var(--glass-shadow), 0 30px 60px rgba(0,0,0,0.18);
  display: flex; flex-direction: column;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s ease, transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
  z-index: 9700;        /* above drawers (9600), popovers, and any glass panels */
}
.modal.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  pointer-events: auto;
}

/* Modal header — editorial vertical stack: optional .modal-eyebrow
   (small uppercase tag, "LIBRARY" / "SHARE" / etc.) → h3 title in
   display-size sentence case → optional .modal-subtitle paragraph
   that explains what this modal does. The close X anchors to the
   top-right of the stack. Drawer header keeps the older flat
   horizontal layout (it doesn't need the editorial weight). */
.modal-header {
  position: relative;
  display: flex; flex-direction: column; gap: var(--s-1);
  padding: var(--s-5) var(--s-5) var(--s-4);
  flex-shrink: 0;
  border-bottom: 1px solid rgba(0,0,0,0.06);
}
.modal-eyebrow {
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 500;
}
.modal-header h3 {
  margin: 0;
  font-size: 22px;
  font-weight: 400;
  letter-spacing: 0.005em;
  text-transform: none;
  color: var(--text);
  line-height: 1.18;
}
.modal-subtitle {
  margin: var(--s-1) 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-muted);
  letter-spacing: 0;
  font-weight: 300;
  max-width: 38ch;
}
.modal-footer,
.drawer-header,
.drawer-footer {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  flex-shrink: 0;
}
.drawer-header {
  border-bottom: 1px solid rgba(0,0,0,0.06);
  min-height: var(--s-9);
}
.drawer-header h3 {
  margin: 0;
  flex: 1;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--text);
}
.modal-footer,
.drawer-footer {
  border-top: 1px solid rgba(0,0,0,0.06);
  justify-content: flex-end;
}
.modal-body,
.drawer-body {
  padding: var(--s-5);
  overflow-y: auto;
  flex: 1;
  display: flex; flex-direction: column; gap: var(--s-4);
}
/* Per-field helper line (appears under the input). Sits in the
   .field flex stack so it stays attached to its label/input pair. */
.field-help {
  font-size: 11px;
  line-height: 1.45;
  color: var(--text-faint);
  letter-spacing: 0;
  margin-top: calc(var(--s-1) * -1);   /* tighten to the input above */
}

/* Modal close (top-right X). Modal header is now a vertical stack,
   so the close anchors absolutely to the top-right corner; drawer
   header stays horizontal and uses margin-left:auto. */
.modal-header .close,
.drawer-header .close {
  width: var(--s-7); height: var(--s-7);
  border-radius: 50%;
  background: rgba(0,0,0,0.06); border: 0; cursor: pointer; color: var(--text);
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  transition: background 0.12s;
}
.modal-header .close {
  position: absolute;
  top: var(--s-3); right: var(--s-3);
}
.drawer-header .close { margin-left: auto; }
.modal-header .close:hover,
.drawer-header .close:hover { background: rgba(0,0,0,0.10); }
.modal-header .close svg,
.drawer-header .close svg { width: 14px; height: 14px; stroke: currentColor; stroke-width: 1.6; fill: none; }

/* --- DRAWER (slide-in side panel) --- */
.drawer {
  position: fixed;
  top: var(--s-5); bottom: var(--s-5);
  right: var(--s-5);
  width: min(360px, calc(100vw - var(--s-7)));
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-glass);
  box-shadow: var(--glass-shadow), 0 30px 60px rgba(0,0,0,0.18);
  display: flex; flex-direction: column;
  overflow: hidden;
  transform: translateX(calc(100% + var(--s-7)));
  transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
  z-index: 9600;
}
.drawer.show { transform: translateX(0); }

/* left-anchored variant */
.drawer.from-left {
  right: auto;
  left: var(--s-5);
  transform: translateX(calc(-100% - var(--s-7)));
}
.drawer.from-left.show { transform: translateX(0); }

/* mobile: drawer becomes a bottom sheet */
@media (max-width: 480px) {
  .drawer {
    top: auto;
    right: var(--s-3); left: var(--s-3); bottom: var(--s-3);
    width: auto;
    max-height: 80vh;
    transform: translateY(calc(100% + var(--s-7)));
  }
  .drawer.show { transform: translateY(0); }
  .drawer.from-left {
    left: var(--s-3); transform: translateY(calc(100% + var(--s-7)));
  }
}

/* --- POPOVER (small attached popup) --- */
/* Wrap the trigger + popover in .popover-host so the popover is positioned
   relative to the trigger's bottom-left, regardless of layout context. */
.popover-host {
  position: relative;
  display: inline-block;
  align-self: flex-start;
}
.popover {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  /* Glass like the side panels — translucent + blurred, NOT a near-
     solid sheet. Gives popovers the same surface as the rest of the
     in-app glass chrome (aside.left, drawer, etc.). */
  background: var(--glass-bg-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  /* Outer radius = item-pill cap (s-9 / 2 = 22) + popover padding (s-2 = 8) = 30px,
     so a popover-item's pill curve nests perfectly inside the container. */
  /* Outer radius = item-pill cap (34/2 = 17) + container padding (6)
     so pill items nest with a uniform 6 px gutter from the edge. */
  border-radius: 23px;
  box-shadow: var(--glass-shadow), 0 8px 24px rgba(0,0,0,0.16);
  padding: 6px;
  min-width: 180px;
  max-width: min(280px, calc(100vw - var(--s-5)));
  opacity: 0;
  transform: translateY(-4px);
  pointer-events: none;
  transition: opacity 0.16s ease, transform 0.16s ease;
  z-index: 9000;
}
.popover.show {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
/* Right-anchored variant */
.popover.from-right { left: auto; right: 0; }
.popover-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  min-height: 34px;
  padding: 0 12px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-pill);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: none;            /* render text as authored — Title Case, not UPPERCASE */
  text-decoration: none;           /* anchor items don't underline */
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.popover-item:hover { background: var(--accent); color: #fff; }
.popover-item.danger { color: var(--accent); }
.popover-item.danger:hover { background: var(--accent); color: #fff; }
/* Fixed-width icon slot — every popover-item uses <span class="ico">
   to wrap its icon (brand mask-image OR inline svg). The slot is a
   fixed 22 px column so labels start at the same x regardless of
   which icon (or even no icon) is rendered. */
.popover-item > .ico {
  flex: 0 0 22px;
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  color: inherit;
}
.popover-item > .ico .icon { --icon-size: 16px; }
.popover-item > .ico svg {
  width: 16px; height: 16px;
  stroke: currentColor; fill: none;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
}
/* Backwards-compat — legacy markup that puts the svg / brand icon
   directly inside .popover-item (no .ico wrapper) still gets sized. */
.popover-item > svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  stroke: currentColor;
  stroke-width: 1.4;
  fill: none;
}
/* Label slot — wrap any text in <span class="lbl"> so it takes all
   remaining width and ellipses if too long. Falls back to the legacy
   "any direct span" rule for items that haven't been migrated. */
.popover-item > .lbl,
.popover-item > span:not(.ico):not(.icon) {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.popover-divider {
  height: 1px;
  background: rgba(0,0,0,0.08);
  /* Vertical breathing room + horizontal inset so the line stops short of
     the popover's rounded edges instead of bleeding to them. */
  margin: var(--s-2) var(--s-3);
}

/* ============== RAMP STOPS (env-style) ============== */
.ramp {
  position: relative;
  width: 100%;
  height: 18px;
  border-radius: 9px;
  background: linear-gradient(90deg, #ffd28a 0%, #f74827 50%, #2a1a55 100%);
  margin: 6px 0 28px;
}
.ramp-stop {
  position: absolute; top: 100%;
  transform: translate(-50%, 4px);
  width: 12px; height: 12px;
  border-radius: 50%;
  border: 2px solid var(--text);
  background: #fff;
  cursor: ew-resize;
  box-shadow: 0 1px 3px rgba(0,0,0,0.20);
}
.ramp-stop::after {
  content: "";
  position: absolute;
  inset: -10px;
  border-radius: 50%;
}
.ramp-stop.selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}

/* =================================================================
   COMPATIBILITY ALIASES (visuals only)
   Prefixes selectors with `:root` for an extra specificity bump so
   kit styling wins the cascade against the page's own CSS *without*
   removing or touching the page's rules. Only visuals — layout
   geometry and JS-bound classes are untouched.
================================================================= */

/* ---- Material / object pills, every page.
        materials.html .mat-row, env.html .obj-row, editor.html .mat (drag-drop).
        All three render identically. Editor's `.mat.dragging` and `.mat.applied`
        functional states keep working — they have higher specificity than
        :root .mat. */
:root .mat,
:root .mat-row,
:root .obj-row {
  display: inline-flex; align-items: center;
  height: var(--s-9);
  background: rgba(255,255,255,0.40);
  padding: 0 var(--s-4) 0 var(--s-2);
  font-size: 12px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border: 2px solid transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, box-shadow 0.12s;
}
:root .mat:hover,
:root .mat-row:hover,
:root .obj-row:hover { background: rgba(255,255,255,0.65); }
:root .mat.selected,
:root .mat-row.selected,
:root .obj-row.selected {
  border-color: var(--accent);
  box-shadow: none;
}
:root .mat .swatch,
:root .mat-row .swatch,
:root .obj-row .swatch {
  width: 22px; height: 22px;
  border-radius: 50%;
  display: inline-block;
  box-shadow: 0 0 0 0.5px rgba(0,0,0,0.10);
  margin-right: var(--s-3);
  flex-shrink: 0;
}

/* ---- Editor's publish FAB → kit FAB ---- */
:root .publish-fab {
  width: var(--s-9); height: var(--s-9);
  border-radius: 50%;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  border: 0;
  color: var(--text);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.12s, color 0.12s, transform 0.08s;
}
:root .publish-fab:hover { background: var(--accent); color: #fff; }
:root .publish-fab:active { background: var(--accent); color: #fff; transform: scale(0.94); }

/* ---- Sliders — every variant across pages.
        env / materials use .field. editor uses .sun-dir-row and .panel-row.
        All values pulled from --slider-* tokens in :root. */
:root .field input[type=range],
:root .slider-field input[type=range],
:root .sun-dir-row input[type=range],
:root .panel-row input[type=range] {
  -webkit-appearance: none;
  appearance: none;
  height: 36px;
  background: transparent;
  cursor: pointer;
  flex: 1;
}
:root .field input[type=range]::-webkit-slider-runnable-track,
:root .slider-field input[type=range]::-webkit-slider-runnable-track,
:root .sun-dir-row input[type=range]::-webkit-slider-runnable-track,
:root .panel-row input[type=range]::-webkit-slider-runnable-track {
  height: var(--slider-track-h);
  background: rgba(0,0,0,0.12);
  border-radius: calc(var(--slider-track-h) / 2);
}
:root .field input[type=range]::-webkit-slider-thumb,
:root .slider-field input[type=range]::-webkit-slider-thumb,
:root .sun-dir-row input[type=range]::-webkit-slider-thumb,
:root .panel-row input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: var(--slider-thumb-w);
  height: var(--slider-thumb-h);
  border-radius: var(--control-radius);
  /* Idle = glass — matches FABs, panels, toolbox.
     Solid white reads as a flat sticker on the busy 3D viewport;
     glass tokens tie the thumb visually to the rest of the chrome.
     The accent-pill on :active still wins (rule below). */
  background: var(--glass-bg-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 0;
  box-shadow: var(--glass-shadow);
  margin-top: calc((var(--slider-track-h) - var(--slider-thumb-h)) / 2);
  /* Spring on transform so press / release bounce; keep fill + shadow snappy. */
  transition: transform 0.32s cubic-bezier(0.34, 1.55, 0.64, 1),
              box-shadow 0.12s,
              background 0.12s;
}
:root .field input[type=range]:active::-webkit-slider-thumb,
:root .slider-field input[type=range]:active::-webkit-slider-thumb,
:root .sun-dir-row input[type=range]:active::-webkit-slider-thumb,
:root .panel-row input[type=range]:active::-webkit-slider-thumb {
  background: var(--accent);
  box-shadow: none;
  transform: scale(1.18);
}
:root .field input[type=range]::-moz-range-track,
:root .slider-field input[type=range]::-moz-range-track,
:root .sun-dir-row input[type=range]::-moz-range-track,
:root .panel-row input[type=range]::-moz-range-track {
  height: var(--slider-track-h);
  background: rgba(0,0,0,0.12);
  border-radius: calc(var(--slider-track-h) / 2);
}
:root .field input[type=range]::-moz-range-thumb,
:root .slider-field input[type=range]::-moz-range-thumb,
:root .sun-dir-row input[type=range]::-moz-range-thumb,
:root .panel-row input[type=range]::-moz-range-thumb {
  width: var(--slider-thumb-w);
  height: var(--slider-thumb-h);
  border-radius: var(--control-radius);
  /* Firefox doesn't fully support backdrop-filter on form controls;
     it falls through to the glass-bg tint, which still reads as a
     translucent pill — same direction as Chrome / Safari. */
  background: var(--glass-bg-strong);
  backdrop-filter: var(--glass-blur);
  border: 0;
  box-shadow: var(--glass-shadow);
  transition: transform 0.32s cubic-bezier(0.34, 1.55, 0.64, 1),
              background 0.12s;
}
:root .field input[type=range]:active::-moz-range-thumb,
:root .slider-field input[type=range]:active::-moz-range-thumb,
:root .sun-dir-row input[type=range]:active::-moz-range-thumb,
:root .panel-row input[type=range]:active::-moz-range-thumb {
  background: var(--accent);
  box-shadow: none;
  transform: scale(1.18);
}

/* ---- Compact pill number input — used as slider companion.
        Editor pairs sliders with .sun-val (under .sun-dir-row / .panel-row);
        env/materials use .v-input (under .field).
        Selectors include the parent class so specificity beats editor's
        own `.sun-dir-row .sun-val` (0,2,0). All values via --control-* tokens. */
:root .sun-dir-row .sun-val,
:root .panel-row .sun-val,
:root .field .v-input,
:root .slider-field .v-input,
:root .field .v {
  height: var(--control-h-sm);
  padding: 0 var(--control-px-sm);
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  border-radius: var(--control-radius);
  font-family: inherit;
  font-size: var(--control-fs-sm);
  font-variant-numeric: tabular-nums;
  color: var(--text);
  text-align: center;
  outline: none;
  -moz-appearance: textfield;
  appearance: textfield;
  transition: border-color 0.12s;
}
:root .sun-dir-row .sun-val::-webkit-outer-spin-button,
:root .sun-dir-row .sun-val::-webkit-inner-spin-button,
:root .panel-row .sun-val::-webkit-outer-spin-button,
:root .panel-row .sun-val::-webkit-inner-spin-button,
:root .field .v-input::-webkit-outer-spin-button,
:root .field .v-input::-webkit-inner-spin-button,
:root .slider-field .v-input::-webkit-outer-spin-button,
:root .slider-field .v-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* ---- ALL input fields and selects unified to the SAME compact pill size
        (--control-h-sm = 32px, --control-fs-sm = 12px). One height,
        one font-size, one radius across every form control on every page.
        Buttons are NOT unified — they stay at --control-h (44px) per Apple
        HIG min-tap-target. Exceptions: .pers-input (its 17px engraving
        type is intentional, viewer-only). */
:root select {
  -webkit-appearance: none;
  appearance: none;
  height: var(--control-h-sm);
  padding: 0 var(--s-7) 0 var(--control-px-sm);
  background-color: var(--control-bg);
  background-image: var(--select-chevron);
  background-repeat: no-repeat;
  background-position: right var(--s-3) center;
  background-size: 14px 14px;
  border: 1px solid var(--control-border);
  border-radius: var(--control-radius);
  font-family: inherit;
  font-size: var(--control-fs-sm);
  letter-spacing: var(--control-tracking);
  text-transform: uppercase;
  color: var(--text);
  cursor: pointer;
  outline: none;
  transition: background-color 0.12s, border-color 0.12s;
}
:root select:hover { background-color: var(--control-bg-hover); }

:root input[type=text],
:root input[type=search],
:root input[type=email],
:root input[type=password],
:root input[type=url],
:root input[type=tel] {
  height: var(--control-h-sm);
  padding: 0 var(--control-px-sm);
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  border-radius: var(--control-radius);
  font-family: inherit;
  font-size: var(--control-fs-sm);
  letter-spacing: var(--control-tracking);
  color: var(--text);
  outline: none;
  transition: background 0.12s, border-color 0.12s;
}
:root input[type=text]:focus,
:root input[type=search]:focus,
:root input[type=email]:focus,
:root input[type=password]:focus,
:root input[type=url]:focus,
:root input[type=tel]:focus {
  background: var(--control-bg-hover);
}

/* Personalization input (viewer engraving HUD) — explicitly excluded
   from the unification: keep its 17px engraving size as the customer
   sees it. Force font-weight 400 per spec. */
:root .pers-input {
  font-weight: 400;
}

/* Brand wordmark — switched from 500 → 400 per latest spec, keeping
   the 0.10em tracking that makes "F. BINSABBAR" read correctly. */
:root .brand {
  font-weight: 400;
}

/* ---- All checkboxes and radios → unified kit pill toggle.
        Targets every known page-specific selector explicitly so specificity
        wins the cascade against page rules like `.field input[type=checkbox]`
        (0,1,1) and `.opt-row input[type=checkbox]` (0,1,1). The broad
        `:root input[type=...]` rule covers anything not inside those wrappers. */
:root input[type=checkbox],
:root input[type=radio],
:root .field input[type=checkbox],
:root .field input[type=radio],
:root .opt-row input[type=checkbox],
:root .opt-row input[type=radio],
:root .publish-toggle input[type="checkbox"],
:root .publish-section input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  width: 36px; height: 20px;
  margin: 0;
  border-radius: var(--control-radius);
  border: 2px solid rgba(0,0,0,0.30);
  background: transparent;
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
  transition: border-color 0.15s, background 0.15s;
}
:root input[type=checkbox]:hover,
:root input[type=radio]:hover,
:root .field input[type=checkbox]:hover,
:root .field input[type=radio]:hover,
:root .opt-row input[type=checkbox]:hover,
:root .opt-row input[type=radio]:hover,
:root .publish-toggle input[type="checkbox"]:hover,
:root .publish-section input[type="checkbox"]:hover {
  border-color: rgba(0,0,0,0.55);
}
:root input[type=checkbox]:checked,
:root input[type=radio]:checked,
:root .field input[type=checkbox]:checked,
:root .field input[type=radio]:checked,
:root .opt-row input[type=checkbox]:checked,
:root .opt-row input[type=radio]:checked,
:root .publish-toggle input[type="checkbox"]:checked,
:root .publish-section input[type="checkbox"]:checked {
  border-color: var(--accent);
  background: var(--accent);
}
:root input[type=checkbox]:checked::after,
:root input[type=radio]:checked::after,
:root .field input[type=checkbox]:checked::after,
:root .field input[type=radio]:checked::after,
:root .opt-row input[type=checkbox]:checked::after,
:root .opt-row input[type=radio]:checked::after,
:root .publish-toggle input[type="checkbox"]:checked::after,
:root .publish-section input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: var(--control-radius);
  background: #fff;
}
:root input[type=checkbox]:disabled,
:root input[type=radio]:disabled,
:root .field input[type=checkbox]:disabled,
:root .opt-row input[type=checkbox]:disabled,
:root .publish-toggle input[type="checkbox"]:disabled,
:root .publish-section input[type="checkbox"]:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ---- Editor transform-rotation buttons (.rot-btn): unify with kit pill.
        Page rule `.rot-grid .rot-btn { padding: 6px 4px; font-size:10px;
        font-family: ui-monospace }` was making them tighter and mono.
        Selector specificity (0,3,0) beats page (0,2,0). */
:root .rot-grid .rot-btn,
:root .rot-grid button.secondary {
  height: var(--control-h);
  padding: 0 var(--control-px-sm);
  font-family: inherit;
  font-size: var(--control-fs-sm);
  letter-spacing: var(--control-tracking);
}

/* ---- Editor's Save/Export row: clear my earlier margin-top tweaks so
        side-by-side buttons in .bottom-actions align on the same baseline.
        (button.primary{margin-top:10px} + button.secondary{margin-top:6px}
        was causing a 4px vertical offset between the two.) */
:root .bottom-actions button,
:root #save-file-btn,
:root #export-btn {
  margin-top: 0;
}

/* ---- Page toasts (.toast / .toast.hide on materials, env, editor) → kit
        toast-live look. Visual chrome only — pages keep their own
        position/transition logic. */
:root .toast {
  background: var(--glass-bg-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  color: var(--text);
  min-height: var(--control-h);
  padding: var(--s-2) var(--s-5);
  border-radius: var(--control-radius);
  font-family: inherit;
  font-size: var(--control-fs);
  font-weight: 400;
  letter-spacing: var(--control-tracking);
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
}

/* ---- Page badges → kit .tag style (small uppercase pill).
        materials.html and env.html use .badge inside aside-header.
        materials.html uses .draft-badge for unsaved-draft markers. */
:root .badge,
:root .draft-badge {
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 0 var(--s-3);
  border-radius: var(--control-radius);
  background: rgba(0,0,0,0.06);
  color: var(--text-muted);
  font-family: inherit;
  font-size: 11px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.10em;
}
:root .draft-badge {
  background: var(--accent-soft);
  color: var(--accent);
}

/* ---- Page count pills → kit .count-pill style.
        editor.html uses .publish-count-pill. */
:root .publish-count-pill {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px;
  padding: 0 var(--s-2);
  border-radius: 999px;
  background: rgba(0,0,0,0.08);
  color: var(--text);
  font-family: inherit;
  font-size: 11px;
  font-weight: 400;
}
:root .publish-count-pill.has-items {
  background: var(--accent);
  color: #fff;
}

/* ---- Page icon-only buttons → kit .iconbtn style.
        materials/env: .aside-collapse, .fullscreen-btn.
        editor: .aside-collapse, .mob-fab-burger (FAB-shaped, see below). */
:root .aside-collapse,
:root .fullscreen-btn {
  width: var(--control-h);
  height: var(--control-h);
  border-radius: 50%;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--text);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.12s, color 0.12s, transform 0.08s;
}
:root .aside-collapse:hover,
:root .fullscreen-btn:hover {
  background: var(--accent);
  color: #fff;
}
:root .aside-collapse:active,
:root .fullscreen-btn:active {
  background: var(--accent);
  color: #fff;
  transform: scale(0.94);
}

/* ---- Editor's mobile FAB burger → kit .fab. */
:root .mob-fab-burger {
  width: var(--control-h);
  height: var(--control-h);
  border-radius: 50%;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  border: 0;
  color: var(--text);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.12s, color 0.12s, transform 0.08s;
}
:root .mob-fab-burger:hover {
  background: var(--accent);
  color: #fff;
}

/* ---- Editor login card → kit modal glass treatment.
        Visual chrome only, layout left to page. */
:root .fb-login-card {
  background: var(--glass-bg-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-glass);
  box-shadow: var(--glass-shadow), 0 30px 60px rgba(0,0,0,0.18);
}
/* Login fields container → kit pill text-input look. */
:root .fb-login-fields input {
  height: var(--control-h);
  padding: 0 var(--control-px);
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  border-radius: var(--control-radius);
  font-family: inherit;
  font-size: var(--control-fs);
  letter-spacing: var(--control-tracking);
  color: var(--text);
  outline: none;
}

/* ---- Editor publish-confirm dialog → kit modal glass.
   Removed: the glass surface read as a stacked card on top of the
   panel chrome instead of an inline footer step. The confirm row
   should sit flush in the footer; the panel's own glass provides the
   surface. */
:root .publish-confirm {
  /* background: var(--glass-bg-strong); */
  /* backdrop-filter: var(--glass-blur); */
  /* -webkit-backdrop-filter: var(--glass-blur); */
  /* border-radius: var(--radius-glass); */
  /* box-shadow: var(--glass-shadow), 0 30px 60px rgba(0,0,0,0.18); */
}

/* ---- Editor share-dialog buttons → kit primary/secondary. */
:root .fbj-share-btn,
:root .fbj-share-native,
:root .fbj-share-copy,
:root .publish-action-btn,
:root .publish-submit,
:root .publish-confirm-yes,
:root .publish-confirm-no,
:root .empty-import-cta {
  height: var(--control-h);
  padding: 0 var(--s-5);
  border-radius: var(--control-radius);
  border: 0;
  cursor: pointer;
  font-family: inherit;
  font-weight: 500;
  font-size: var(--control-fs);
  letter-spacing: var(--control-tracking);
  text-transform: uppercase;
  transition: transform 0.08s, opacity 0.12s, filter 0.12s, background 0.12s;
}
/* Primary variants: solid black (or accent for confirm-yes / submit) */
:root .fbj-share-native,
:root .publish-submit,
:root .publish-confirm-yes,
:root .publish-action-btn {
  background: var(--text);
  color: #fff;
}
:root .publish-confirm-yes,
:root .publish-action-btn.is-dirty {
  background: var(--accent);
}
:root .fbj-share-native:hover,
:root .publish-submit:hover,
:root .publish-confirm-yes:hover,
:root .publish-action-btn:hover { opacity: 0.85; filter: brightness(1.10); }

/* Secondary variants: pale glass */
:root .fbj-share-copy,
:root .publish-confirm-no,
:root .empty-import-cta {
  background: var(--control-bg);
  color: var(--text);
  border: 1px solid var(--control-border);
}
:root .fbj-share-copy:hover,
:root .publish-confirm-no:hover,
:root .empty-import-cta:hover { background: var(--control-bg-hover); }

/* ---- Editor share-dialog close X + publish-panel close X → kit close */
:root .fbj-share-close,
:root .publish-panel-close {
  width: var(--s-7);
  height: var(--s-7);
  border-radius: 50%;
  background: rgba(0,0,0,0.06);
  border: 0;
  cursor: pointer;
  color: var(--text);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0;
  line-height: 0;
  padding: 0;
  transition: background 0.12s;
}
:root .fbj-share-close:hover,
:root .publish-panel-close:hover { background: rgba(0,0,0,0.10); }

/* ---- Editor's .panel-row .val displays (Anchor, Surface, Pers. camera
        status, color hex) → kit compact pill. NO overflow hidden so
        color hex like #caaf89 isn't clipped. */
:root .panel-row .val {
  height: var(--control-h-sm);
  min-width: 64px;            /* fits 7-char hex like #caaf89 */
  padding: 0 var(--control-px-sm);
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  border-radius: var(--control-radius);
  font-family: inherit;
  font-size: var(--control-fs-sm);
  font-variant-numeric: tabular-nums;
  color: var(--text);
  text-align: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  overflow: visible;          /* don't clip — let it grow if needed */
}

/* ---- Editor's .rename-input (preview text, filename rename) → kit pill text input. */
:root .rename-input {
  height: var(--control-h-sm);
  padding: 0 var(--control-px-sm);
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  border-radius: var(--control-radius);
  font-family: inherit;
  font-size: var(--control-fs-sm);
  color: var(--text);
  text-align: center;
}

/* ---- Editor's text-alignment 3-button group → kit segmented (.quality-pick).
        JS sets inline background per button to indicate active state, so
        we !important the structural properties (flex, padding, border,
        radius) but leave `background` alone — JS's active dark bg keeps
        showing as the segmented "selected pill" indicator. */
:root .pers-align-group {
  display: flex !important;
  gap: 4px !important;
  padding: 2px !important;
  background: rgba(0,0,0,0.05);
  border-radius: var(--control-radius);
  height: var(--control-h-sm);
  align-items: stretch;
}
:root .pers-align-btn {
  flex: 1 !important;
  padding: 0 !important;
  border: 0 !important;
  border-color: transparent !important;
  border-radius: var(--control-radius) !important;
  height: 100% !important;
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
}

/* ---- Materials texture buttons → kit iconbtn style. */
:root .tex-btn {
  width: var(--control-h);
  height: var(--control-h);
  border-radius: 50%;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--text);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.12s, color 0.12s, transform 0.08s;
}
:root .tex-btn:hover {
  background: var(--accent);
  color: #fff;
}
:root .tex-btn:active {
  background: var(--accent);
  color: #fff;
  transform: scale(0.94);
}
:root .tex-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

/* ---- Slider row layout: input on top, slider below (full width).
        Pages currently render label | range | input on a single row.
        We re-grid the wrapper so the range slips under the label+input. */
:root .field:has(> input[type=range]),
:root .sun-dir-row:has(> input[type=range]),
:root .panel-row:has(> input[type=range]) {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto auto;
  gap: var(--s-2) var(--s-3);
  align-items: center;
}
:root .field:has(> input[type=range]) > label,
:root .sun-dir-row:has(> input[type=range]) > label,
:root .panel-row:has(> input[type=range]) > label {
  grid-column: 1; grid-row: 1;
}
:root .field:has(> input[type=range]) > .v-input,
:root .field:has(> input[type=range]) > .v,
:root .sun-dir-row:has(> input[type=range]) > .sun-val,
:root .panel-row:has(> input[type=range]) > .sun-val {
  grid-column: 2; grid-row: 1;
}
:root .field:has(> input[type=range]) > input[type=range],
:root .sun-dir-row:has(> input[type=range]) > input[type=range],
:root .panel-row:has(> input[type=range]) > input[type=range] {
  grid-column: 1 / -1; grid-row: 2;
  margin: 0;        /* clear editor's `margin: 0 8px` inline padding */
  width: 100%;
}

/* ---- Modal aliases (page-specific glass dialogs) ----
   These ONLY restyle the dialog box itself — they do NOT change
   the backdrop's positioning logic (page may flex-center the modal,
   kit's fixed/transform centering would conflict). Width, padding,
   shadow, radius, and glass background only. */
:root .add-mat-modal,
:root .fbj-share-card {
  background: var(--glass-bg-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-glass);
  box-shadow: var(--glass-shadow), 0 30px 60px rgba(0,0,0,0.18);
}

/* ---- Drawer alias (editor publish panel) — same visual treatment ---- */
:root .publish-panel {
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-glass);
  box-shadow: var(--glass-shadow), 0 30px 60px rgba(0,0,0,0.18);
}
