/* ============================================================
   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);

  /* ---- official brand colors ----
     The four named brand colors. --signature-red and --premium-yellow
     are aliases of the existing --accent / --accent-yellow tokens so
     legacy code keeps working; --royal-lavender and --vert are new. */
  --signature-red:  #f74827;   /* alias of --accent */
  --premium-yellow: #fee5a5;   /* alias of --accent-yellow */
  --royal-lavender: #a77fb2;
  --vert:           #b5af8d;

  /* ---- 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 ---- */
  --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:   32px;
  --slider-thumb-h:   24px;
  --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 {
  border: 1px solid var(--accent) !important;
  box-shadow: inset 0 0 0 1px var(--accent) !important; /* 2nd px via shadow = 2px ring, no reflow */
}
/* 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: none;
  outline-offset: 0;
}
.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; }
  /* iOS Safari sticky-hover trap: a hover style on touch devices makes
     the first tap "hover" instead of "click", so the second tap is
     needed to fire. Neutralize hover on the page-nav pills so tap = click. */
  .search-btn:hover      { background: var(--glass-bg); color: var(--text); box-shadow: var(--glass-shadow); }
  .search-btn:hover svg  { stroke: currentColor; }
  .back:hover            { background: var(--glass-bg); color: var(--text); box-shadow: var(--glass-shadow); }
  .back:hover svg        { stroke: currentColor; }
}

/* 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);
}

/* ============== PAGE NAV (iOS-style large-title) ==============
   Sticky bar at the top of every content page. No background - the
   back pill and tabs pill carry their own glass chrome. Pre-scroll:
   back shows full label, in-nav tabs (if present) are collapsed.
   Post-scroll: back collapses to chevron-only, tabs expand to fill
   the row from after-back to right edge, horizontally scrolling
   their items inside the pill when overflowing.

   Markup:
     <nav class="page-nav">
       <div class="page-nav-inner">
         <a class="back" href="...">
           <svg>chevron</svg>
           <span><span class="brand-name">F. BINSABBAR</span> ... </span>
         </a>
         <nav class="tabs">...optional in-nav tabs...</nav>
       </div>
     </nav>
*/
:root { --page-nav-h: 64px; }

.page-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  /* deliberately no background - the pill chrome (.back, .tabs, .search-btn)
     carries its own glass. Content scrolls under the gap between pills. */
}
.page-nav-inner {
  max-width: 1100px;
  margin: 0 auto;
  min-height: var(--page-nav-h);
  padding:
    var(--s-2)
    max(var(--s-6), env(safe-area-inset-right))
    var(--s-2)
    max(var(--s-6), env(safe-area-inset-left));
  display: flex;
  align-items: center;
  gap: var(--s-3);
  /* Force single row - no wrapping when content overflows the viewport.
     Children handle their own shrink/scroll behavior. */
  flex-wrap: nowrap;
  min-width: 0;
}
/* Homepage doesn't show the back chevron (no parent). Pages should
   add `.page-nav.is-home` to suppress it, or simply omit the .back. */
.page-nav.is-home .back { display: none; }

/* ============== BACK LINK (canonical) ==============
   Single source of truth for the page-level "back to parent" pill.
   Lives inside .page-nav on every non-home page. */
.back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  height: 44px;
  padding: 0 20px 0 16px;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-pill);
  box-shadow: var(--glass-shadow);
  text-decoration: none;
  color: var(--text);
  font-size: 13px;
  letter-spacing: 0.04em;
  margin: 0;
  flex-shrink: 0;
  transition:
    transform 0.15s ease,
    box-shadow 0.15s ease,
    background 0.18s ease,
    color 0.18s ease,
    padding 0.25s ease,
    width 0.25s ease,
    gap 0.25s ease;
}
.back > span {
  display: inline-block;
  max-width: 480px;
  overflow: hidden;
  white-space: nowrap;
  opacity: 1;
  transition: max-width 0.28s ease, opacity 0.18s ease;
}
.back svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  transition: stroke 0.15s ease;
}
.back:hover {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 6px 18px rgba(247,72,39,0.30), var(--glass-shadow);
}
.back:hover svg { stroke: #fff; }

/* Scrolled state - back collapses to chevron-only circle */
.page-nav.scrolled .back {
  width: 44px;
  padding: 0;
  gap: 0;
}
.page-nav.scrolled .back > span {
  max-width: 0;
  opacity: 0;
}

/* In-nav tabs - right-aligned, fit-content, hidden until scrolled.
   Pre-scroll: collapsed to zero width so the back pill owns the row.
   Post-scroll: expands; flex: 0 1 auto + min-width: 0 allow shrinking
   at narrow viewports; horizontal scroll inside the pill kicks in. */
.page-nav .tabs {
  height: 44px;
  margin: 0;
  padding: 0;
  flex: 0 1 auto;
  min-width: 0;
  width: 0;
  max-width: 0;
  justify-content: flex-start;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  overflow-x: hidden;
  overflow-y: hidden;
  transition:
    opacity 0.22s ease,
    max-width 0.28s ease,
    padding 0.22s ease;
}
.page-nav.scrolled .tabs {
  padding: 3px;
  width: auto;
  max-width: 100%;
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.page-nav .tabs a,
.page-nav .tabs button {
  height: 36px;
  padding: 0 14px;
  font-size: 12px;
  letter-spacing: 0.06em;
  display: inline-flex;
  align-items: center;
}

/* Anchor jumps land below the sticky nav */
html { scroll-padding-top: calc(var(--page-nav-h) + var(--s-3)); }

@media (max-width: 540px) {
  :root { --page-nav-h: 56px; }
  /* Expanded state: let back shrink + truncate so search-btn fits same row. */
  .back { height: 40px; padding: 0 16px 0 12px; font-size: 12px; gap: 10px;
          flex: 0 1 auto; min-width: 0; overflow: hidden; }
  .back > span {
    min-width: 0;
    max-width: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Scrolled state: lock back to fixed 40 px circle - must beat the
     expanded-state flex: 0 1 auto + min-width: 0 above, otherwise tabs
     squeeze it to a sliver. */
  .page-nav.scrolled .back {
    width: 40px; padding: 0; gap: 0;
    flex: 0 0 40px; min-width: 40px;
  }
  .page-nav .tabs { height: 40px; min-width: 0; }
  .page-nav .tabs a,
  .page-nav .tabs button { height: 32px; padding: 0 12px; font-size: 11px; }
  /* Search button never shrinks - same lock as the scrolled back. */
  .search-btn { flex: 0 0 40px; }
}

/* ============================================================
   PAGE-LEVEL CHROME (canonical)
   ============================================================
   These rules are shared by every brand-assets page. Importing
   fb-ui.css applies them automatically. Pages still own their own
   `.page` max-width + padding, page-specific hero h1 sizes, and
   any custom layouts; everything else comes from here.
   ============================================================ */

html, body {
  margin: 0;
  background: var(--canvas-bg);
  color: var(--text);
  font-family: futura-pt, "Futura PT", "Trebuchet MS", "Helvetica Neue", Arial, sans-serif;
  font-size: 15px;
  font-weight: 400;
  line-height: 1.55;
  /* Disable iOS double-tap-to-zoom while keeping two-finger pinch zoom. */
  touch-action: manipulation;
}
* { box-sizing: border-box; }

/* The 3-radial warm wash that anchors every brand-assets page. */
body::before {
  content: "";
  position: fixed; inset: 0;
  background:
    radial-gradient(60% 60% at 20% 10%, rgba(247,72,39,0.10), transparent 60%),
    radial-gradient(50% 50% at 90% 20%, rgba(254,229,165,0.30), transparent 70%),
    radial-gradient(80% 80% at 60% 110%, rgba(177,155,125,0.18), transparent 70%);
  z-index: -1;
  pointer-events: none;
}

/* Brand wordmark span. The auto-walker on every page wraps
   F. BINSABBAR text in <span class="brand-name">. Slightly tighter
   tracking than .brand so it works as inline body copy without
   breaking line height. */
.brand-name {
  font-family: futura-pt, "Futura PT", sans-serif;
  font-weight: 500;
  letter-spacing: 0.05em;
  white-space: nowrap;
}

/* ============== HERO ============== */
/* Default: stacked block (eyebrow → h1 → p flow vertically).
   Auto-promoted to a 2-column grid when the hero contains an ornament
   (.hero-mark monogram or .app-icon-img product icon). Pages don't need
   a modifier class - the layout adapts to whatever children they emit. */
.hero {
  display: block;
  margin-bottom: var(--s-9);
}
.hero:has(> .hero-mark, > .app-icon-img) {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s-7);
  align-items: center;
}
@media (max-width: 720px) {
  .hero:has(> .hero-mark, > .app-icon-img) {
    grid-template-columns: 1fr;
    gap: var(--s-5);
  }
}
.hero .eyebrow {
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 var(--s-2);
}
.hero h1 {
  font-family: futura-pt, sans-serif;
  font-weight: 300;
  font-size: clamp(36px, 6vw, 60px);
  letter-spacing: 0.02em;
  margin: 0 0 var(--s-3);
  line-height: 1;
}
.hero h1 sup {
  font-size: 0.22em;
  letter-spacing: 0;
  margin-left: 0.10em;
  vertical-align: 0.85em;
  color: var(--text-faint);
  font-weight: 400;
}
.hero p { color: var(--text-muted); margin: 0; max-width: 640px; font-size: 16px; }

/* Compact the top header on phones. The hero (eyebrow + h1 + p) and
   the back link can easily eat half a phone viewport before content
   starts; shrink everything proportionally below 540 px. */
@media (max-width: 540px) {
  .back {
    padding: 10px 18px 10px 14px;
    font-size: 13px;
    gap: 8px;
  }
  .back svg { width: 16px; height: 16px; }

  .hero { margin-bottom: var(--s-6); }
  .hero .eyebrow {
    font-size: 10px;
    letter-spacing: 0.18em;
    margin: 0 0 6px;
  }
  .hero h1 {
    font-size: clamp(26px, 7.5vw, 36px);
    letter-spacing: 0.02em;
    line-height: 1.05;
  }
  .hero p {
    font-size: 14px;
    line-height: 1.55;
  }
}

/* ============== SECTION ============== */
.section { margin-top: var(--s-9); }
.section > h2 {
  font-weight: 400;
  font-size: 14px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 var(--s-5);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid rgba(0,0,0,0.08);
}

/* ============== TILE - clickable glass card ============== */
a.tile {
  display: block;
  text-decoration: none;
  color: inherit;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-glass);
  padding: var(--s-6);
  box-shadow: var(--glass-shadow);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
a.tile:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 28px rgba(0,0,0,0.10), var(--glass-shadow);
}
a.tile .tile-icon { width: 28px; height: 28px; color: var(--text); margin-bottom: var(--s-3); }
a.tile h3 { font-weight: 500; font-size: 16px; letter-spacing: 0.01em; margin: 0 0 6px; }
a.tile p { color: var(--text-muted); font-size: 13.5px; margin: 0 0 var(--s-3); line-height: 1.55; }
a.tile .meta {
  font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* === Link-direction indicator ===
   Every clickable tile gets a small glass circle in the upper-right
   corner with an arrow inside, signalling whether the link is
   internal (within the brand system) or external (off-site). Driven
   by the link's `target` attribute - no HTML changes needed beyond
   target="_blank" on external links.

   Resting:  frosted-glass circle, --text icon.
   Hover:    circle fills with --accent (Signature Red), icon flips to white.

   ::before  → the 32 px chip (glass at rest, red on hover)
   ::after   → the icon mask on top (--text at rest, white on hover)

     - Internal: right arrow, slides 4 px right on hover.
     - External: arrow-out-of-box, slides up-right on hover.
   To suppress on a specific tile, add the `.no-indicator` class. */
a.tile { position: relative; }

/* Glass circle backdrop. */
a.tile::before {
  content: "";
  position: absolute;
  top: var(--s-4);
  right: var(--s-4);
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  pointer-events: none;
  transition: background 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
  z-index: 1;
}

/* Icon on top of the glass. */
a.tile::after {
  content: "";
  position: absolute;
  top: var(--s-4);
  right: var(--s-4);
  width: 32px; height: 32px;
  background-color: var(--text);
  -webkit-mask-size: 14px 14px; mask-size: 14px 14px;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3Cpolyline points='12 5 19 12 12 19'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3Cpolyline points='12 5 19 12 12 19'/%3E%3C/svg%3E");
  pointer-events: none;
  transition: background-color 0.18s ease, transform 0.18s ease;
  z-index: 2;
}
a.tile[target="_blank"]::after {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 4h6v6'/%3E%3Cline x1='20' y1='4' x2='10' y2='14'/%3E%3Cpath d='M20 14v6H4V4h6'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 4h6v6'/%3E%3Cline x1='20' y1='4' x2='10' y2='14'/%3E%3Cpath d='M20 14v6H4V4h6'/%3E%3C/svg%3E");
}
a.tile:hover::before {
  background: var(--accent);
  box-shadow: 0 6px 18px rgba(247,72,39,0.30), 0 0 0 1px rgba(247,72,39,0.45), inset 0 1px 0 rgba(255,255,255,0.30);
  transform: translateX(4px);
}
a.tile:hover::after {
  background-color: #fff;
  transform: translateX(4px);
}
a.tile[target="_blank"]:hover::before,
a.tile[target="_blank"]:hover::after {
  transform: translate(4px, -4px);
}
a.tile.no-indicator::before,
a.tile.no-indicator::after { display: none; }

/* ============== CARD - content block ============== */
.card {
  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);
  padding: var(--s-6);
}
.card h3 {
  font-weight: 500;
  font-size: 17px;
  letter-spacing: 0.01em;
  margin: 0 0 var(--s-3);
  color: var(--text);
}
/* When an h3 follows a chip/pill/badge inside a card, give it breathing room.
   Removes the need for inline `style="margin-top:var(--s-3);"` on the heading. */
.card h3:not(:first-child) { margin-top: var(--s-3); }
.card p, .card li {
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1.6;
}
.card p { margin: 0 0 var(--s-3); }
.card p:last-child { margin-bottom: 0; }
.card ul { margin: 0; padding-left: var(--s-5); }
.card ul li { margin-bottom: 4px; }
.card strong { color: var(--text); font-weight: 500; }

/* ============== STAT - numeric tile ============== */
.stat {
  display: grid;
  gap: 6px;
  padding: var(--s-5);
  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);
}
.stat .num {
  font-family: futura-pt, sans-serif;
  font-weight: 300;
  font-size: 36px;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--text);
}
.stat .label {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ============== GRIDS ============== */
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--s-4); }
.grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--s-4); }
.grid-4 { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--s-4); }

/* ============== BTN - link-as-button pill ==============
   For hyperlink-style CTAs in content (cards, briefs, "back to top").
   For form actions use button.primary / button.secondary above.
   Height + horizontal padding matched to button.primary so
   side-by-side rows of mixed button kinds line up. */
.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; }
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: var(--s-9);
  padding: 0 var(--s-5);
  background: var(--text);
  color: var(--canvas-bg);
  border: 0;
  border-radius: var(--radius-pill);
  text-decoration: none;
  font-family: inherit;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  transition: transform 0.15s ease, opacity 0.15s ease;
}
.btn:hover { transform: translateY(-1px); opacity: 0.92; }
.btn.outline {
  background: transparent;
  color: var(--text);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.18);
}
.btn.disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}
.btn svg { width: 16px; height: 16px; }

/* ============== TABS - pill jump-nav ==============
   Canonical glass pill containing inline `<a>` or `<button>` items.
   ONE ROW always - never wraps. If items exceed available width
   the row scrolls horizontally inside the pill (scrollbar hidden).
   Each item is a pill that fills with a soft glass tint on hover.
   Add the `sticky` modifier to pin under the viewport top.
*/
.tabs {
  display: flex;
  flex-wrap: nowrap;
  gap: 6px;
  margin: 0 auto var(--s-9);
  padding: 6px;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-pill);
  box-shadow: var(--glass-shadow);
  width: fit-content;
  max-width: 100%;
  justify-content: flex-start;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

/* Sticky-bar wrapper. position: sticky and overflow-x cannot coexist
   on the same element in iOS Safari - sticky stops triggering. So:
   wrap the .tabs in <div class="tabs-bar sticky">, which carries the
   sticky position, while the inner .tabs handles its own scroll. */
.tabs-bar {
  margin: var(--s-5) 0 var(--s-7);
  display: flex;
  justify-content: center;
}
.tabs-bar.sticky {
  position: -webkit-sticky;
  position: sticky;
  top: var(--page-nav-h);
  z-index: 10;
}
.tabs-bar > .tabs { margin: 0; }

/* Back-compat: .tabs.sticky still works on its own (best-effort).
   New pages should prefer the .tabs-bar wrapper for reliable sticky. */
.tabs.sticky {
  position: -webkit-sticky;
  position: sticky;
  top: var(--page-nav-h);
  z-index: 10;
  margin: var(--s-5) auto var(--s-7);
}
.tabs a, .tabs button {
  flex: 0 0 auto;
  text-decoration: none;
  color: var(--text-muted);
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: capitalize;
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  border: 0;
  background: transparent;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, transform 0.18s ease;
  text-align: center;
  white-space: nowrap;
}
.tabs a:hover, .tabs button:hover {
  background: var(--accent);
  color: #fff;
}
.tabs button.active,
.tabs a.active { background: var(--accent); color: #fff; }
@media (max-width: 540px) {
  /* On phones: keep the desktop look - centered fit-content glass
     pill with horizontal-scroll inside. Just tighten the pill items.
     Un-sticky so it scrolls away with the rest of the page. */
  .tabs a, .tabs button {
    padding: 10px 14px;
    font-size: 12px;
    letter-spacing: 0.02em;
  }
  .tabs.sticky,
  .tabs-bar.sticky {
    position: static;
    top: auto;
  }
  .tabs-bar { margin: var(--s-3) 0 var(--s-5); }
}

/* ============== FOOTER (canonical) - editorial double-column ==============
   Used on every brand-system page (login + redirect splashes excepted).
   Markup:
     <footer>
       <hr class="foot-rule">
       <div class="foot-grid">
         <div class="foot-id">
           <span class="foot-logo"><img src="...house-logo-fbj.svg" alt=""></span>
           <div class="foot-id-text">
             <p class="foot-brand">F. BINSABBAR<sup>®</sup></p>
             <p class="foot-tagline">The Fine Jewellery House of Riyadh</p>
           </div>
         </div>
         <div class="foot-meta">
           <p>TM-01-00-44436-25</p>
           <p>UNN 7025824553</p>
           <p>Faisal Binsabbar Design LLC</p>
         </div>
       </div>
     </footer>
   Pages must NOT redefine these styles inline.

   Brand-book deluxe variant adds <footer class="foot foot-deluxe">
   and an extra <p class="foot-discipline"> stanza between the rule
   and the grid. */
footer {
  margin-top: var(--s-10);
  padding-top: 0;
  border-top: 0;
  color: var(--text-muted);
}
.foot-rule {
  border: 0;
  border-top: 1px solid rgba(0,0,0,0.08);
  margin: 0 0 var(--s-7);
}
.foot-grid {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--s-6);
}
.foot-id {
  display: flex;
  align-items: center;
  gap: var(--s-4);
}
.foot-logo {
  width: 32px;
  height: 40px;
  flex-shrink: 0;
  color: var(--text);
}
.foot-logo img,
.foot-logo svg { width: 100%; height: 100%; object-fit: contain; }
.foot-id-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.foot-brand {
  font-family: futura-pt, "Futura PT", sans-serif;
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.10em;
  color: var(--text);
  text-transform: uppercase;
  margin: 0;
}
.foot-brand sup {
  font-size: 0.55em;
  vertical-align: 0.7em;
  margin-left: 0.20em;
  color: var(--text-faint);
}
.foot-tagline {
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin: 0;
}
.foot-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  text-align: right;
}
.foot-meta p { margin: 0; }

/* Brand-book deluxe variant - discipline stanza between rule and grid */
.foot-deluxe .foot-discipline {
  text-align: center;
  font-size: 12px;
  color: var(--text-muted);
  letter-spacing: 0.04em;
  line-height: 1.7;
  margin: 0 auto var(--s-7);
  max-width: 560px;
}

@media (max-width: 540px) {
  .foot-grid {
    grid-template-columns: 1fr;
    gap: var(--s-4);
    text-align: left;
  }
  .foot-id { align-items: center; text-align: left; }
  .foot-meta { align-items: flex-start; text-align: left; }
}

/* ============== APP ICON IMAGE (canonical) ==============
   For pages using a real PNG app icon (the studio-illustrated set in
   app-icons/), drop an <img class="app-icon-img"> wherever you'd
   otherwise use a gradient .app-icon block. Default size is 96 px
   (homepage app-tile); add `.hero` for the 128 px hero ornament. */
img.app-icon-img {
  width: 96px;
  height: 96px;
  border-radius: 22px;
  display: block;
  flex-shrink: 0;
  box-shadow: 0 12px 32px rgba(0,0,0,0.18);
}
img.app-icon-img.hero {
  width: 128px;
  height: 128px;
  border-radius: 28px;
  box-shadow: 0 18px 48px rgba(0,0,0,0.22), 0 8px 24px rgba(0,0,0,0.08);
}

/* ============== PAGE LOADER (canonical) ==============
   Full-page overlay used during initial load on every studio surface
   (editor, materials, env, viewer) and the login gate. Inset matches
   the canvas frame (10 px window margin, 30 px corner radius) so the
   loader visually IS the canvas during a load.

   Markup (drop at the top of <body>):
     <div class="page-loader" aria-hidden="true">
       <div class="ring"></div>
       <div class="star">
         <svg viewBox="0 0 197.02 197.02"><polygon fill="currentColor" points="..."/></svg>
       </div>
     </div>

   Dismiss: add `fb-page-ready` to <body> and the loader fades out.
   Honors prefers-reduced-motion (animations stop, loader still visible).
   Edge-to-edge on phone (≤600 px). */
.page-loader {
  position: fixed; inset: 10px; z-index: 1000;
  background: var(--canvas-bg);
  border-radius: var(--radius-glass);
  display: flex; align-items: center; justify-content: center;
  transition: opacity 0.2s ease;
}
.page-loader .ring {
  position: absolute; top: 50%; left: 50%;
  width: 70px; height: 70px; margin: -35px 0 0 -35px;
  border: 2px solid rgba(0,0,0,0.15);
  border-top: 2px solid var(--text);
  border-radius: 50%; box-sizing: border-box;
  animation: page-loader-spin 1.2s linear infinite;
}
.page-loader .star {
  width: 40px; height: 40px;
  color: var(--text);
  animation: page-loader-pulse 1.6s ease-in-out infinite;
  display: flex; align-items: center; justify-content: center;
}
.page-loader .star svg {
  width: 100%; height: 100%;
  display: block;
  fill: currentColor;
}
@keyframes page-loader-spin { to { transform: rotate(360deg); } }
@keyframes page-loader-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.12); }
}
body.fb-page-ready .page-loader {
  opacity: 0;
  pointer-events: none;
}
@media (max-width: 600px) {
  .page-loader { inset: 0; border-radius: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .page-loader .ring,
  .page-loader .star { animation: none; }
}

/* ============== 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-5);
  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-weight: 500;
  font-size: 13px;
  letter-spacing: 0.06em;
  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 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;
  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);
}
/* Native date/time inputs render TALLER than the set box on WebKit, so they look
   mismatched next to text inputs/selects/buttons. Pin any that use .text-input to the
   exact var(--s-9) control box so EVERY input matches the button height. (Lifted from
   the social planner's page-local fix so it's a kit-wide default.) */
input[type="date"].text-input, input[type="datetime-local"].text-input, input[type="month"].text-input, input[type="week"].text-input {
  -webkit-appearance: none; appearance: none;
  height: var(--s-9); min-height: var(--s-9); max-height: var(--s-9);
  line-height: calc(var(--s-9) - 2px); box-sizing: border-box;
}
input[type="date"].text-input::-webkit-datetime-edit, input[type="datetime-local"].text-input::-webkit-datetime-edit { padding: 0; }
input[type="date"].text-input::-webkit-date-and-time-value, input[type="datetime-local"].text-input::-webkit-date-and-time-value { margin: 0; padding: 0; text-align: left; line-height: calc(var(--s-9) - 2px); }
input[type="date"].text-input::-webkit-calendar-picker-indicator, input[type="datetime-local"].text-input::-webkit-calendar-picker-indicator { margin: 0; opacity: 0.5; cursor: pointer; }
/* Native TIME input: size it WITHOUT -webkit-appearance:none (stripping it on WebKit
   disables inline editing so the value never commits). */
input[type="time"].text-input {
  height: var(--s-9); min-height: var(--s-9); max-height: var(--s-9);
  box-sizing: border-box; padding-top: 0; padding-bottom: 0;
}
input[type="time"].text-input::-webkit-date-and-time-value { text-align: left; }
input[type="time"].text-input::-webkit-calendar-picker-indicator { opacity: 0.5; cursor: pointer; }
/* ---- Default control height (no class required) --------------------------
   Any text-shaped <input> + non-multiple <select> defaults to the button
   control height var(--s-9), so even a bare <input> lines up with .text-input,
   .fb-select and buttons. :where() gives this ZERO specificity, so every
   component class (.text-input, .fb-select, .v-input, .pers-input, …) still
   overrides it. Textarea keeps its content height; checkbox/radio/range/file/
   color/switch are excluded. Date-family inputs also get the appearance reset
   so the height sticks - NOT time (resetting it on WebKit breaks value entry). */
input:where([type="text"], [type="number"], [type="search"], [type="email"], [type="password"], [type="tel"], [type="url"], [type="date"], [type="time"], [type="datetime-local"], [type="month"], [type="week"]),
select:where(:not([multiple])) {
  height: var(--s-9);
  box-sizing: border-box;
}
input:where([type="date"], [type="datetime-local"], [type="month"], [type="week"]) {
  -webkit-appearance: none; appearance: none;
}
input:where([type="date"], [type="datetime-local"])::-webkit-date-and-time-value { text-align: left; }
input:where([type="date"], [type="time"], [type="datetime-local"])::-webkit-calendar-picker-indicator { opacity: 0.5; cursor: pointer; }

.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);
  text-transform: uppercase;
  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 .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: 32px; height: 24px;       /* pill, slightly more balanced */
  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);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 0;
  box-shadow: var(--glass-shadow);
  margin-top: -10px;               /* center 24pt thumb on 4pt track inside 36pt input */
  transition: transform 0.12s, 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: 32px; height: 24px;
  border-radius: var(--radius-pill);
  background: var(--glass-bg);
  /* 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.12s, background 0.12s;
}
/* Held / dragging - solid red pill, no halo, no border */
.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;
}
.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;
}
.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 (60×31 track, 32×23 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);
  width: fit-content;  /* hug content + padding (min footprint) */
  max-width: 80vw;     /* never wider than 80% of the screen */
  overflow: auto;      /* content can't fit the cap -> scroll instead of overflowing */
  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: 12px;
  letter-spacing: 0.04em;
  text-transform: capitalize;
  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);
  max-width: 80vw;     /* never wider than 80% of the screen */
  overflow: auto;      /* content can't fit the cap -> scroll instead of overflowing */
  padding: var(--s-2) var(--s-2) var(--s-2) var(--s-5);
  border-radius: var(--radius-pill);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: capitalize;
  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; }

/* Toasts pad 20px left / 8px right - the tight right assumes a trailing .close
   button sits there. When a toast auto-dismisses with no close button, the
   message <span> is the last child; give it matching right breathing room so
   the text doesn't crowd the edge (12px margin + 8px padding ≈ the 20px left). */
.toast-static > span:last-child,
.toast-live > span:last-child {
  margin-right: var(--s-3);
}

/* ============== 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: 10px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  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);
  transition: transform 0.12s, 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;
}
: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: 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;
}

/* ---- 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) for
        the min-tap-target floor. Exceptions: .pers-input (its 17px
        engraving type is intentional, viewer-only). */
:root select {
  -webkit-appearance: none;
  appearance: none;
  height: var(--s-9);
  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(--s-9);
  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);
  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);
  border: 1px solid var(--accent) !important;
  box-shadow: inset 0 0 0 1px var(--accent) !important; /* 2nd px via shadow = 2px ring, no reflow */
}

/* 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-sm);
  font-weight: 400;
  letter-spacing: var(--control-tracking);
  text-transform: capitalize;
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  max-width: 80vw;     /* never wider than 80% of the screen */
  overflow: auto;      /* content can't fit the cap -> scroll instead of overflowing */
}

/* ---- 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. */
: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);
}

/* ============================================================
   v2 ADDITIONS - lifted from brand-assets duplication + console
   ============================================================ */

/* ============== AUTH-PENDING GATE ==============
   Pages add <script>document.documentElement.classList.add('auth-pending');</script>
   in <head> to hide content until auth.js resolves. */
html.auth-pending,
html.auth-pending body { background: var(--canvas-bg); }
html.auth-pending body { visibility: hidden; }
html.auth-pending::after {
  content: "";
  position: fixed;
  top: 50%; left: 50%;
  width: 44px; height: 44px;
  margin: -22px 0 0 -22px;
  border: 2px solid rgba(14,14,14,0.18);
  border-top-color: var(--text);
  border-radius: 50%;
  animation: fb-auth-spin 1.1s linear infinite;
  z-index: 9999;
}
@keyframes fb-auth-spin { to { transform: rotate(360deg); } }

/* ============== HERO COMPACT MODIFIER ==============
   For editorial sub-pages (glossaries, how-to, icons) that need a
   smaller masthead than the canonical hero. Opt in via class. */
.hero.compact h1 {
  font-size: clamp(32px, 5vw, 52px);
}
.hero.compact p { max-width: 720px; }

/* ============== CONTROLS - sticky glass filter bar ==============
   Used at the top of icon library, glossaries, future search pages.
   Sticks just under the page nav. Phone: un-sticky (scrolls away). */
.controls {
  position: sticky;
  top: var(--page-nav-h);
  z-index: 10;
  display: flex;
  gap: var(--s-3);
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: var(--s-6);
  padding: var(--s-4);
  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);
}
.controls input[type=search] {
  flex: 1;
  min-width: 240px;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-pill);
  background: var(--control-bg);
  font: inherit;
  font-size: 14px;
  color: var(--text);
  outline: none;
  transition: background 0.15s;
}
.controls input[type=search]:focus { background: var(--control-bg-hover); }
@media (max-width: 540px) {
  .controls {
    position: static;
    padding: var(--s-3);
    margin-bottom: var(--s-4);
  }
}

/* ============== TOPIC - large glass section card ==============
   Wraps a glossary chapter / how-to chapter / any editorial topic.
   Bigger sibling to .section. */
.topic {
  margin-top: var(--s-6);
  padding: var(--s-7) var(--s-6);
  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);
}
.topic > h2 {
  font-weight: 400;
  font-size: 22px;
  letter-spacing: 0.02em;
  margin: 0 0 var(--s-2);
  scroll-margin-top: calc(var(--page-nav-h) + var(--s-3));
}
.topic > .lede {
  color: var(--text-muted);
  margin: 0 0 var(--s-7);
  font-size: 14px;
}

/* ============== GROUP - sub-section heading with anchor reveal ==============
   Use inside .topic. The .anchor link appears on hover for deep-linking. */
.group { margin-top: var(--s-6); scroll-margin-top: calc(var(--page-nav-h) + var(--s-3)); }
.group h3 {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 var(--s-3);
  padding-bottom: var(--s-2);
  border-bottom: 1px solid rgba(0,0,0,0.08);
  scroll-margin-top: calc(var(--page-nav-h) + var(--s-3));
}
.group h3 .anchor {
  opacity: 0;
  margin-left: 8px;
  font-size: 12px;
  text-decoration: none;
  color: var(--text-faint);
  transition: opacity 0.15s ease;
}
.group h3:hover .anchor { opacity: 1; }

/* ============== APP-TOC - chip TOC inside a .topic ============== */
.app-toc {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 var(--s-6);
}
.app-toc a {
  display: inline-block;
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  background: rgba(0,0,0,0.04);
  color: var(--text);
  font-size: 12px;
  letter-spacing: 0.02em;
  text-decoration: none;
  transition: background 0.15s ease, color 0.15s ease;
}
.app-toc a:hover { background: var(--accent); color: #fff; }
@media (max-width: 720px) {
  .app-toc {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: 2px;
  }
  .app-toc::-webkit-scrollbar { display: none; }
  .app-toc a { flex: 0 0 auto; white-space: nowrap; }
}

/* ============== HOWTO - procedural content inside .group ============== */
.howto { margin: 0 0 var(--s-4); }
.howto h4 {
  font-weight: 500;
  font-size: 16px;
  letter-spacing: 0.01em;
  margin: var(--s-4) 0 var(--s-2);
  color: var(--text);
  scroll-margin-top: calc(var(--page-nav-h) + var(--s-3));
}
.howto h4 .anchor {
  opacity: 0;
  margin-left: 8px;
  font-size: 12px;
  text-decoration: none;
  color: var(--text-faint);
  transition: opacity 0.15s ease;
}
.howto h4:hover .anchor { opacity: 1; }
.howto p {
  color: var(--text-muted);
  font-size: 13.5px;
  line-height: 1.6;
  margin: 0 0 var(--s-2);
}
.howto ol, .howto ul {
  margin: 0 0 var(--s-3);
  padding-left: var(--s-5);
  color: var(--text-muted);
  font-size: 13.5px;
  line-height: 1.65;
}
.howto ol li, .howto ul li { margin-bottom: 4px; }
.howto strong { color: var(--text); font-weight: 500; }

/* ============== NOTE - accent-tinted callout ============== */
.note {
  margin: var(--s-3) 0;
  padding: var(--s-3) var(--s-4);
  background: rgba(247,72,39,0.06);
  border-left: 2px solid rgba(247,72,39,0.45);
  border-radius: 8px;
  font-size: 13px;
  color: var(--text-muted);
}
.note strong { color: var(--text); }

/* ============== NOTICE - centered redirect splash ==============
   Used by design-system.html and ui-kit.html redirect pages. */
.notice {
  max-width: 480px;
  text-align: center;
  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);
  padding: var(--s-9) var(--s-7);
}
.notice .eyebrow {
  font-size: 11px;
  letter-spacing: 0.20em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 var(--s-4);
}
.notice h1 {
  font-family: futura-pt, sans-serif;
  font-weight: 300;
  font-size: clamp(28px, 4vw, 36px);
  letter-spacing: 0.02em;
  margin: 0 0 var(--s-4);
  line-height: 1;
}
.notice p {
  color: var(--text-muted);
  font-size: 14.5px;
  line-height: 1.65;
  margin: 0 0 var(--s-6);
}

/* ============== GLOSSARY - definition lists baseline ============== */
.topic dl { margin: 0; }
.topic dt {
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.01em;
  margin-top: var(--s-3);
  color: var(--text);
}
.topic dd {
  margin: 2px 0 0;
  color: var(--text-muted);
  font-size: 13.5px;
  line-height: 1.55;
}

/* ============== INLINE CODE - baseline ==============
   Pages stop redefining. Glossary, how-to, brand-book all share this. */
code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  background: rgba(0,0,0,0.05);
  padding: 1px 5px;
  border-radius: 4px;
  color: var(--text);
}

/* ============== PILL - status badge (lifted from system console) ==============
   Sibling to .tag. Use .pill with .ok/.warn/.err/.muted + optional .dot. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 22px;
  padding: 0 var(--s-3);
  border-radius: var(--radius-pill);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-weight: 500;
}
.pill .dot { width: 6px; height: 6px; border-radius: 50%; }
.pill.ok    { background: var(--success-soft); color: var(--success); }
.pill.ok    .dot { background: var(--success); }
.pill.warn  { background: var(--warning-soft); color: #8a6a00; }
.pill.warn  .dot { background: var(--warning); }
.pill.err   { background: var(--accent-soft);  color: var(--accent); }
.pill.err   .dot { background: var(--accent); animation: pulse 1.6s ease-in-out infinite; }
.pill.muted { background: rgba(0,0,0,0.06);    color: var(--text-muted); }
.pill.muted .dot { background: var(--text-faint); }
.pill.champagne { background: rgba(177,155,125,0.18); color: #8a7558; }
.pill.champagne .dot { background: var(--champagne); }
@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.55; transform: scale(0.85); }
}

/* ============== KPI - live-data tile (lifted from console) ============== */
.kpi {
  padding: var(--s-5);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  border-radius: var(--radius-card);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  min-height: 120px;
  position: relative;
  overflow: hidden;
}
.kpi .label {
  font-size: var(--param-title-fs);
  text-transform: var(--param-title-tt);
  letter-spacing: var(--param-title-ls);
  color: var(--text-faint);
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
}
.kpi .value {
  font-size: 32px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
  line-height: 1;
}
.kpi .value .small { font-size: 16px; color: var(--text-faint); margin-left: 2px; }
.kpi .delta { font-size: 11.5px; color: var(--text-muted); margin-top: auto; line-height: 1.4; }
.kpi.ok   .value { color: var(--success); }
.kpi.warn .value { color: var(--warning); }
.kpi.err  .value { color: var(--accent); }

/* ============== ALERT-HERO - system banner (lifted from console) ============== */
.alert-hero {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-5);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  border-radius: var(--radius-card);
  transition: transform 0.12s ease;
}
.alert-hero:active { transform: scale(0.995); }
.alert-hero.healthy { box-shadow: var(--glass-shadow), 0 0 0 1px var(--success-soft) inset; }
.alert-hero.warn    { box-shadow: var(--glass-shadow), 0 0 0 1px var(--warning-soft) inset; }
.alert-hero.err     { box-shadow: var(--glass-shadow), 0 0 0 1px var(--accent-soft)  inset; }
.alert-hero .ah-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.alert-hero.healthy .ah-icon { color: var(--success); }
.alert-hero.warn    .ah-icon { color: #8a6a00; }
.alert-hero.err     .ah-icon { color: var(--accent); }
.alert-hero .ah-text { flex: 1; min-width: 0; }
.alert-hero .ah-title { font-size: 14px; font-weight: 500; color: var(--text); line-height: 1.2; }
.alert-hero .ah-sub   { font-size: 11px; color: var(--text-muted); margin-top: 2px; }

/* ============== FEED - activity log (lifted from console) ============== */
.feed {
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  border-radius: var(--radius-card);
  padding: var(--s-2);
  max-height: 480px;
  overflow-y: auto;
}
.log-entry {
  display: grid;
  grid-template-columns: 90px auto 1fr;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--radius-sm);
  align-items: flex-start;
}
.log-entry + .log-entry { border-top: 1px solid rgba(0,0,0,0.05); }
.log-entry .ts {
  font-size: 11px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
.log-entry .lvl {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-weight: 500;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  align-self: flex-start;
}
.log-entry .lvl.ok   { background: var(--success-soft); color: var(--success); }
.log-entry .lvl.warn { background: var(--warning-soft); color: #8a6a00; }
.log-entry .lvl.err  { background: var(--accent-soft);  color: var(--accent); }
.log-entry .lvl.info { background: rgba(0,0,0,0.06);    color: var(--text-muted); }
.log-entry .msg { font-size: 12.5px; color: var(--text); line-height: 1.5; }

/* ============== SKELETON - loading placeholders (lifted from console) ============== */
.skel-shimmer {
  display: inline-block;
  background: linear-gradient(90deg, rgba(0,0,0,0.06), rgba(0,0,0,0.10), rgba(0,0,0,0.06));
  background-size: 200% 100%;
  animation: shim 1.4s linear infinite;
  color: transparent;
  border-radius: 4px;
}
.skel-row { display: grid; gap: var(--s-3); padding: var(--s-3) var(--s-4); }
@keyframes shim {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}

/* ============== DISCLAIMER - compact legal copy block ============== */
.disclaimer {
  max-width: 720px;
  text-align: center;
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--text-faint);
  letter-spacing: 0.01em;
  padding: 0 var(--s-3);
}
.disclaimer .em { color: var(--text-muted); }
.disclaimer a {
  color: var(--text-muted);
  text-decoration: none;
  border-bottom: 1px solid rgba(0,0,0,0.15);
  padding-bottom: 1px;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.disclaimer a:hover { color: var(--text); border-bottom-color: var(--text); }

/* ============== BTN.DANGER - red prominent action ============== */
.btn.danger {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 6px 18px rgba(247,72,39,0.32);
}
.btn.danger:hover {
  filter: brightness(1.10);
  box-shadow: 0 8px 24px rgba(247,72,39,0.38);
  opacity: 1;
}

/* ============== LOGO-PLATE - display chrome for brand marks ============== */
.logo-plate {
  display: grid;
  place-items: center;
  min-height: 180px;
  padding: 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);
  color: var(--text);
  position: relative;
}
.logo-plate img,
.logo-plate svg {
  max-width: 100%;
  max-height: 120px;
  height: auto;
  display: block;
}
.logo-plate.square { aspect-ratio: 1 / 1; min-height: 0; }
.logo-plate.square img,
.logo-plate.square svg { max-height: 70%; }
.logo-plate.inverse { background: var(--text); color: var(--canvas-bg); }
/* SVG loaded via <img> is isolated from parent CSS - currentColor inside
   won't inherit. Use filter:invert to flip the dark-fill mark to light. */
.logo-plate.inverse img,
.logo-plate.inverse svg { filter: invert(1) brightness(1.05); }
.logo-plate .lp-cap {
  position: absolute;
  bottom: var(--s-4);
  left: var(--s-5);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.logo-plate.inverse .lp-cap { color: rgba(255,255,255,0.55); }
.logo-plate .lp-tier {
  position: absolute;
  top: var(--s-4);
  right: var(--s-4);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 500;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: rgba(0,0,0,0.06);
  color: var(--text-muted);
}
.logo-plate .lp-tier.fbj { background: var(--accent-soft); color: var(--accent); }
.logo-plate.inverse .lp-tier {
  background: rgba(255,255,255,0.18);
  color: #fff;
}

/* ============== BUTTON HOVER - red bg + white text (canonical rule) ==============
   .iconbtn and .fab already do this; extend to .btn for consistency. */
.btn:not(.disabled):hover {
  background: var(--accent);
  color: #fff;
  opacity: 1;
  box-shadow: 0 6px 18px rgba(247,72,39,0.30);
}
.btn.outline:not(.disabled):hover {
  box-shadow: 0 6px 18px rgba(247,72,39,0.30), inset 0 0 0 1px var(--accent);
}

/* ============================================================
   SITE SEARCH - overlay + button (in .page-nav)
   ============================================================ */
.search-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  margin-left: auto;
  padding: 0;
  border: 0;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-pill);
  box-shadow: var(--glass-shadow);
  color: var(--text);
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
}
.search-btn svg {
  width: 20px;
  height: 20px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke 0.15s ease;
}
.search-btn:hover {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 6px 18px rgba(247,72,39,0.30), var(--glass-shadow);
}
.search-btn:hover svg { stroke: #fff; }
@media (max-width: 540px) {
  .search-btn { width: 40px; height: 40px; }
  .search-btn svg { width: 18px; height: 18px; }
}

/* ============== SEARCH MODAL - standard .modal pattern ==============
   Uses the kit's canonical .popup-backdrop + .modal primitives (centered
   glass dialog with editorial header), plus .modal--search adds the
   search-specific bits: input bar, results list, footer hint strip. */
.modal--search {
  width: min(560px, calc(100vw - var(--s-7)));
  max-height: min(640px, calc(100vh - var(--s-9)));
}
.modal--search .modal-header {
  padding: var(--s-5) var(--s-5) var(--s-4);
}
.modal--search .modal-header .modal-close {
  position: absolute;
  top: var(--s-4);
  right: var(--s-4);
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text);
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}
.modal--search .modal-header .modal-close svg {
  width: 16px;
  height: 16px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.modal--search .modal-header .modal-close:hover {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 6px 18px rgba(247,72,39,0.30), var(--glass-shadow);
}
.modal--search .modal-header .modal-close:active {
  transform: scale(0.94);
}
@media (hover: none) {
  /* Touch: neutralize hover so the first tap fires close, not :hover. */
  .modal--search .modal-header .modal-close:hover {
    background: var(--glass-bg);
    color: var(--text);
    box-shadow: var(--glass-shadow);
  }
}
.modal--search .search-input-wrap {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid rgba(0,0,0,0.06);
  flex-shrink: 0;
}
.modal--search .search-input-wrap svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--text-faint);
  stroke: currentColor;
  fill: none;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.modal--search .search-input {
  flex: 1;
  border: 0;
  background: transparent;
  font-family: futura-pt, "Futura PT", sans-serif;
  font-weight: 300;
  font-size: 20px;
  letter-spacing: 0.01em;
  line-height: 1.2;
  color: var(--text);
  outline: none;
  padding: 0;
  height: 28px;
}
.modal--search .search-input::placeholder {
  color: var(--text-faint);
  font-weight: 300;
}
.modal--search .search-input:hover { outline: none; border: 0; }

.search-results {
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  flex: 1;
}
.search-results li { margin: 0; }
.search-group-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: var(--s-4) var(--s-5) var(--s-2);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.20em;
  text-transform: uppercase;
  color: var(--text-faint);
  border-top: 1px solid rgba(0,0,0,0.05);
}
.search-group-head:first-child { border-top: 0; }
.search-group-head .count {
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.search-result {
  display: block;
  padding: var(--s-3) var(--s-5);
  text-decoration: none;
  color: var(--text);
  transition: background 0.12s ease, color 0.12s ease;
}
.search-result:hover,
.search-result.active {
  background: var(--accent);
  color: #fff;
}
.search-result:hover .search-result-meta,
.search-result.active .search-result-meta { color: rgba(255,255,255,0.78); }
.search-result-title {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.3;
  margin: 0 0 3px;
}
.search-result-meta {
  font-size: 10.5px;
  font-weight: 500;
  color: var(--text-faint);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin: 0;
}
.search-empty {
  padding: var(--s-8) var(--s-5);
  text-align: center;
  font-size: 13px;
  color: var(--text-muted);
}
.search-hint {
  padding: var(--s-3) var(--s-5);
  font-size: 10px;
  color: var(--text-faint);
  letter-spacing: 0.10em;
  text-transform: uppercase;
  border-top: 1px solid rgba(0,0,0,0.06);
  display: flex;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
  flex-shrink: 0;
}
.search-hint kbd {
  display: inline-block;
  padding: 1px 6px;
  background: rgba(0,0,0,0.08);
  border-radius: 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px;
  letter-spacing: 0;
  color: var(--text);
  margin: 0 2px;
}

@media (max-width: 540px) {
  .modal--search {
    width: 100%;
    max-height: 100%;
    height: 100%;
    border-radius: 0;
    left: 0; top: 0;
    transform: none;
  }
  .modal--search.show { transform: none; }
  .modal--search .search-input { font-size: 18px; }
}
