/* ============================================================
   SiteBuilder — module styles ("Aurora SaaS").
   Scope: the website-builder dashboard (pages list, NEXT-GEN block
   builder, theme editor, domains) and the public page renderer
   (/p/{slug}). Everything styles to the shared design tokens so
   light + dark and RTL + LTR are handled automatically (logical
   properties). The v2 builder chrome lives under `.sb2` (see
   docs/dev/next-gen-builder.md + editor.php's hook contract).
   ============================================================ */

/* ---------- Shared: back link + page sub-header ---------- */
.sb-back{display:inline-flex;align-items:center;gap:6px;font-size:var(--fz-sm);font-weight:600;
  color:var(--text-2);margin-bottom:var(--s-3)}
.sb-back:hover{color:var(--brand-fg)}
.sb-back .icon{width:16px;height:16px}
/* the arrow points "back" — flip it per writing direction */
[dir="rtl"] .sb-back .icon{transform:scaleX(1)}
[dir="ltr"] .sb-back .icon{transform:scaleX(-1)}

/* ---------- Sub-navigation between builder sections ---------- */
.sb-subnav{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-bottom:var(--s-6)}
.sb-subnav__link{display:inline-flex;align-items:center;gap:8px;padding:9px 16px;border-radius:var(--r-full);
  font-size:var(--fz-sm);font-weight:600;color:var(--text-2);border:1px solid var(--line);background:var(--bg-2);
  transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease),background var(--dur) var(--ease)}
.sb-subnav__link:hover{color:var(--text);border-color:var(--line-brand);background:var(--tint)}
.sb-subnav__link.active{color:var(--brand-fg);border-color:var(--line-brand);background:var(--tint)}
.sb-subnav__link .icon{width:18px;height:18px}

/* Grouped variant: labelled BUILD / DESIGN / CONNECT clusters with hairline
   dividers between them. Logical properties keep it correct in RTL + LTR. */
.sb-subnav--grouped{align-items:center;row-gap:var(--s-3)}
.sb-subnav__group{display:inline-flex;align-items:center;gap:var(--s-2)}
.sb-subnav__group-label{font-size:var(--fz-xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--text-3,var(--text-2));opacity:.85;padding-inline-end:2px;white-space:nowrap}
.sb-subnav__divider{align-self:stretch;width:1px;min-height:24px;background:var(--line);
  margin-inline:var(--s-2);border-radius:1px}
/* on narrow screens the row wraps — hide the now-orphaned vertical rules */
@media (max-width:640px){
  .sb-subnav__divider{display:none}
  .sb-subnav--grouped{flex-direction:column;align-items:stretch;gap:var(--s-4)}
  .sb-subnav__group{flex-wrap:wrap}
}

/* address chip (public URL of the site) */
.sb-address{display:inline-flex;align-items:center;gap:8px;padding:7px 14px;border-radius:var(--r-full);
  background:var(--brand-grad-soft);border:1px solid var(--line-brand);color:var(--text-2);font-size:var(--fz-sm)}
.sb-address .icon{width:16px;height:16px;color:var(--brand-fg)}
.sb-address strong{color:var(--text);font-variant-numeric:tabular-nums}
.sb-address a{color:var(--brand-fg);font-weight:600}

/* ---------- Pages list ---------- */
.sb-pages__title{display:flex;flex-direction:column;gap:4px}
.sb-pages__title a{color:var(--text);font-weight:600}
.sb-pages__title a:hover{color:var(--brand-fg)}
/* The page title is a heading link, not an inline body link — kill the global
   table-cell wipe-underline (tables.css). Row hover + colour shift is enough. */
.table td .sb-pages__title a:not(.btn):not(.chip):not(.badge),
.table td .sb-pages__title a:not(.btn):not(.chip):not(.badge):hover,
.table td .sb-pages__title a:not(.btn):not(.chip):not(.badge):focus-visible{background-image:none}
.sb-pages__slug{font-size:var(--fz-xs);color:var(--muted);font-variant-numeric:tabular-nums}
.sb-handle{user-select:none;color:var(--muted);cursor:grab;display:inline-flex;align-items:center}
.sb-handle:active{cursor:grabbing}
.sb-handle .icon{width:18px;height:18px}
tr.dragging{opacity:.45}
tr.dragging td{background:var(--bg-3)!important}

/* ============================================================
   NEXT-GEN PAGE BUILDER  (root .sb2)
   A canvas-native, glassy, full-bleed editor shell:
     TOP BAR · LEFT rail (layers + palette) · CENTER canvas
     (premium device frame + overlay) · RIGHT rail (properties)
     + template gallery + builder modals/popovers.
   State lives on #sb-builder as data-* / .is-* (JS toggles them).
   Tokens: --sb2-* below layer over the global design tokens so the
   chrome stays consistent in light+dark and honours reduced-motion.
   ============================================================ */
.sb2{
  --sb2-topbar-h:60px;
  --sb2-rail-w:288px;
  --sb2-rail-w-right:320px;
  --sb2-gap:var(--s-4);
  --sb2-radius:var(--r-lg);
  --sb2-glass:var(--glass-bg);
  --sb2-glass-strong:var(--glass-strong);
  --sb2-outline:var(--focus-line);           /* selection outline */
  --sb2-outline-soft:var(--line-brand);   /* hover outline */
  --sb2-ambient:radial-gradient(1200px 600px at 50% -10%,rgba(30,80,200,.10),transparent 60%),
                radial-gradient(900px 500px at 100% 110%,rgba(22,184,245,.08),transparent 55%);
  /* The builder OWNS the viewport (like Webflow/Framer): a full-screen fixed
     shell above the dashboard sidebar/topbar, with its own complete top bar +
     Back button. This sidesteps all offset math against the (conditional)
     impersonation/scope bars and the non-height-constrained .main column. */
  position:fixed;
  inset:0;
  display:flex;
  flex-direction:column;
  background:var(--bg);
  background-image:var(--sb2-ambient);
  z-index:1150;                 /* above sidebar(1000) & scrim(1090), below modals(1300+) */
  isolation:isolate;
}
/* Cancel the ambient page scroll + padded column while the builder is mounted.
   Scoped via :has() to a body that actually contains the builder, so navigating
   away (Turbo swaps .content) instantly restores normal dashboard scrolling. */
body:has(#sb-builder) .content{padding:0;max-width:none}
body:has(#sb-builder){overflow:hidden}

/* Native fullscreen request (JS calls requestFullscreen on the shell): just make
   sure nothing sits above it. The fixed inset:0 already fills the screen. */
.sb2.is-fullscreen{z-index:2147483000}

/* ---------------- TOP BAR ---------------- */
.sb2-topbar{
  flex-shrink:0;height:var(--sb2-topbar-h);
  display:flex;align-items:center;gap:var(--s-3);
  padding-inline:var(--s-4);
  background:var(--sb2-glass-strong);
  -webkit-backdrop-filter:saturate(160%) blur(var(--glass-blur));
  backdrop-filter:saturate(160%) blur(var(--glass-blur));
  border-block-end:1px solid var(--line);
  box-shadow:var(--shadow-xs);
}
.sb2-topbar__group{display:flex;align-items:center;gap:var(--s-2);min-width:0}
.sb2-topbar__group--start{flex:1;min-width:0}
.sb2-topbar__group--center{flex-shrink:0}
.sb2-topbar__group--end{flex:1;justify-content:flex-end;min-width:0}

.sb2-back{width:38px;height:38px;flex-shrink:0;border-radius:var(--r-sm);display:grid;place-items:center;
  color:var(--text-2);border:1px solid transparent;
  transition:color var(--dur) var(--ease),background var(--dur) var(--ease),border-color var(--dur) var(--ease)}
.sb2-back:hover{color:var(--brand-fg);background:var(--tint);border-color:var(--line-brand)}
.sb2-back .icon{width:18px;height:18px}
/* The back glyph is now icon('chevron-left') — it already points inline-start in LTR
   and the global .pmu-flip-rtl rule mirrors it in RTL. The old arrow-right + physical
   scaleX overrides (which lost to base.css's higher-specificity flip in RTL) are gone. */

.sb2-title{display:flex;flex-direction:column;gap:1px;min-width:0}
.sb2-title__input{font-size:var(--fz-md);font-weight:700;color:var(--text);background:none;border:1px solid transparent;
  border-radius:var(--r-xs);padding:3px 8px;margin-inline-start:-8px;max-width:min(46ch,42vw);
  text-overflow:ellipsis;transition:background var(--dur) var(--ease),border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.sb2-title__input:hover{background:var(--bg-3)}
.sb2-title__input:focus{outline:none;background:var(--surface);border-color:var(--line-brand);box-shadow:var(--focus-ring)}
.sb2-title__meta{display:flex;align-items:center;gap:8px;padding-inline-start:0;min-width:0}
.sb2-slug{font-size:var(--fz-xs);color:var(--muted);font-variant-numeric:tabular-nums;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:26ch}

/* draft / published badge */
.sb2-status-badge{display:inline-flex;align-items:center;gap:6px;padding:2px 9px;border-radius:var(--r-full);
  font-size:11px;font-weight:700;letter-spacing:.2px;border:1px solid var(--line);background:var(--bg-3);color:var(--muted)}
.sb2-status-badge__dot{width:7px;height:7px;border-radius:50%;background:currentColor;flex-shrink:0}
.sb2-status-badge__on{display:none}
.sb2-status-badge[data-status="published"]{color:var(--success-fg);background:rgba(14,159,110,.10);border-color:rgba(14,159,110,.30)}
.sb2-status-badge[data-status="published"] .sb2-status-badge__on{display:inline}
.sb2-status-badge[data-status="published"] .sb2-status-badge__off{display:none}
.sb2-status-badge[data-status="draft"]{color:var(--warn-fg);background:rgba(217,130,11,.10);border-color:rgba(217,130,11,.28)}

/* top-bar icon buttons */
.sb2-iconbtn{width:38px;height:38px;flex-shrink:0;border-radius:var(--r-sm);display:grid;place-items:center;
  color:var(--text-2);border:1px solid transparent;background:none;cursor:pointer;
  transition:color var(--dur) var(--ease),background var(--dur) var(--ease),border-color var(--dur) var(--ease)}
.sb2-iconbtn:hover{color:var(--text);background:var(--bg-3)}
.sb2-iconbtn:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.sb2-iconbtn[aria-pressed="true"]{color:var(--brand-fg);background:var(--tint);border-color:var(--line-brand)}
.sb2-iconbtn:disabled{opacity:.4;cursor:not-allowed}
.sb2-iconbtn .icon{width:18px;height:18px}

/* undo/redo cluster (one arrow glyph, mirrored between the two — see the note below) */
.sb2-undo{display:inline-flex;gap:2px;padding-inline-end:var(--s-2);margin-inline-end:var(--s-1);
  border-inline-end:1px solid var(--line)}
/* UNDO AND REDO MUST NOT BE THE SAME GLYPH. Both buttons render icon('arrow-right'),
   and icon() appends .pmu-flip-rtl to it (it is in the helper's $directional list), so
   the global mirror reaches it. Spelled out, because the three sheets do NOT carry the
   same selector and the weakest reading of "all three" is the wrong one:
       components.css:783 and base.css:45
           [dir="rtl"] .pmu-flip-rtl:not(.sidebar__collapse .icon){transform:scaleX(-1)}
       site.css:152 (no :not(), so only (0,2,0))
           [dir="rtl"] .pmu-flip-rtl{transform:scaleX(-1)}
   The heaviest of them is the first, whose specificity is (0,4,0) — one attribute, one class, and :not() contributing its
   most specific argument `.sidebar__collapse .icon` (0,2,0). The rules that used to sit
   here were `[dir="rtl"] .sb2 .sb2-ico-undo`, specificity (0,3,0). They LOST.
   MEASURED in the editor on page 1690, signed in as the owner of demo-fitness:
       en / ltr   undo matrix(0, -1, -1, 0, 0, 0)   redo matrix(0, 1, -1, 0, 0, 0)
       he / rtl   undo matrix(-1, 0, 0, 1, 0, 0)    redo matrix(-1, 0, 0, 1, 0, 0)
   i.e. in Hebrew the global mirror overrode BOTH and the two controls became pixel
   identical — aria-label "ביטול" and "ביצוע מחדש" on two buttons a sighted Hebrew user
   cannot tell apart. Issue #67.

   WHAT THE TRANSFORMS NOW MEAN. Undo points BACK along the reading direction and Redo
   points FORWARD, which is the one thing a bare straight arrow can say honestly:
       ltr   undo <- (scaleX(-1))      redo -> (none)
       rtl   undo -> (none)            redo <- (scaleX(-1))
   This is a DIRECTION-MIRRORED pair, so each rule states its own dir explicitly and
   overrides .pmu-flip-rtl outright rather than composing with it (transform replaces,
   it does not accumulate). The previous rotate(90deg) pair pointed the two arrows up
   and down, which differs but reads as neither undo nor redo.

   SPECIFICITY. Naming the button class as well takes these to (0,5,0), so they beat
   the (0,4,0) global rule on weight alone — not on source order, which depends on the
   order the layout happens to link the stylesheets in and is not ours to rely on.

   STILL OUTSTANDING, and NOT fixable from CSS: the glyph has no curl. Both buttons ask
   for icon('arrow-right') — path "M5 12h14M13 6l6 6-6 6", a straight shaft — and
   resources/views/partials/icons.php ships no curved/rotational arrow at all. Giving
   these the conventional curled undo/redo glyph needs a new entry in that icon map plus
   an edit to SiteBuilder/Views/editor.php; both are outside this file. */
[dir] .sb2 .sb2-undo .sb2-iconbtn .sb2-ico-undo{transform:scaleX(-1)}
[dir] .sb2 .sb2-undo .sb2-iconbtn .sb2-ico-redo{transform:none}
[dir="rtl"] .sb2 .sb2-undo .sb2-iconbtn .sb2-ico-undo{transform:none}
[dir="rtl"] .sb2 .sb2-undo .sb2-iconbtn .sb2-ico-redo{transform:scaleX(-1)}

/* device segmented control (icon-only, reuses .segmented base) */
.sb2-devices button{padding:7px 12px;display:grid;place-items:center}
.sb2-devices .icon{width:17px;height:17px}

/* autosave pill */
.sb2-savepill{display:inline-flex;align-items:center;gap:7px;padding:6px 12px;border-radius:var(--r-full);
  font-size:var(--fz-xs);font-weight:600;color:var(--text-2);background:var(--bg-3);border:1px solid var(--line);
  white-space:nowrap}
.sb2-savepill__dot{width:8px;height:8px;border-radius:50%;background:var(--muted);flex-shrink:0}
.sb2-savepill[data-state="dirty"]{color:var(--warn-fg);background:rgba(217,130,11,.10);border-color:rgba(217,130,11,.28)}
.sb2-savepill[data-state="dirty"] .sb2-savepill__dot{background:var(--accent-amber)}
.sb2-savepill[data-state="saving"]{color:var(--brand-fg);background:var(--tint);border-color:var(--line-brand)}
.sb2-savepill[data-state="saving"] .sb2-savepill__dot{background:var(--brand-2);animation:pmuPulse 1s ease-in-out infinite}
.sb2-savepill[data-state="saved"]{color:var(--success-fg);background:rgba(14,159,110,.10);border-color:rgba(14,159,110,.28)}
.sb2-savepill[data-state="saved"] .sb2-savepill__dot{background:var(--accent-green)}
.sb2-savepill[data-state="error"]{color:var(--danger-fg);background:rgba(225,29,72,.10);border-color:rgba(225,29,72,.28)}
.sb2-savepill[data-state="error"] .sb2-savepill__dot{background:var(--accent-red)}
.sb2-save{flex-shrink:0}

/* ---------------- BODY (3-pane grid) ---------------- */
.sb2-body{flex:1;min-height:0;display:flex;gap:0;overflow:hidden}

/* ---------------- RAILS ---------------- */
.sb2-rail{position:relative;flex-shrink:0;display:flex;flex-direction:column;min-height:0;
  width:var(--sb2-rail-w);
  background:var(--sb2-glass);
  -webkit-backdrop-filter:saturate(140%) blur(var(--glass-blur));
  backdrop-filter:saturate(140%) blur(var(--glass-blur));
  overflow-y:auto;overflow-x:hidden;
  transition:width var(--dur) var(--ease),opacity var(--dur) var(--ease)}
/* The rail is a SCROLLING flex column, so with the default flex-shrink:1 an overflowing
   rail hands its negative free space to every section instead of scrolling — the lower
   sections (PAGE SETTINGS / HEADER & FOOTER) got crushed and collided with the block
   above them. Keep natural heights and let the rail scroll; flex-shrink only, so any
   section that intentionally GROWS still can. */
.sb2-rail>*{flex-shrink:0}
/* ...but flex-shrink:0 alone left ONE shrinkable section (--grow, i.e. the palette)
   to absorb every overflow, and `.sb2-rail__section` overflows VISIBLY. On a page
   with many blocks the (uncapped) layers tree took 731px of an 890px rail, the
   palette collapsed to its 33px header, and its search box + template CTA spilled
   OUT of the section and painted straight over the PAGE SETTINGS / HEADER & FOOTER
   header rows below — so clicking those labels hit the palette's search input and
   the folds looked dead. The floor + containment live on `.sb2-rail__section--grow`
   below (same specificity as this block, so they MUST be declared there to win). */
.sb2-rail--left{border-inline-end:1px solid var(--line)}
.sb2-rail--right{width:var(--sb2-rail-w-right);border-inline-start:1px solid var(--line)}

/* ---- collapsed rails slide out, but KEEP A HANDLE ----------------------------
   The rail used to collapse to `width:0` while `>*{opacity:0;pointer-events:none}`
   killed EVERY child — including .sb2-rail__collapse, the only control that can
   bring the rail back. Measured in the collapsed state: the button's rect was
   x -38..-10 (off-viewport: `inset-inline-end:10px` inside a 0-width box), opacity 0,
   pointer-events none, and clipped anyway by the rail's overflow-x:hidden — 25/25
   sampled points inside its own rect hit nothing. So one click on "Collapse panel"
   destroyed the layers tree, the ADD BLOCK palette and BOTH bottom folds with no way
   back short of a full reload (the state is not persisted, but nothing says so).
   That single dead-end reproduces three of the reported symptoms at once: no blocks
   can be dragged in, and the folds "do nothing" because they are not there.
   Fix: collapse to a slim STUB that still carries the restore button. */
.sb2{--sb2-rail-stub:40px}
.sb2.is-collapse-left .sb2-rail--left,
.sb2.is-collapse-right .sb2-rail--right{width:var(--sb2-rail-stub)}
.sb2.is-collapse-left .sb2-rail--left>*,
.sb2.is-collapse-right .sb2-rail--right>*{opacity:0;pointer-events:none}
/* ...except the handle: centred in the stub (28px button in a 40px rail) and fully
   interactive. Logical insets, so RTL and the right rail need no extra rules. */
.sb2.is-collapse-left .sb2-rail--left>.sb2-rail__collapse,
.sb2.is-collapse-right .sb2-rail--right>.sb2-rail__collapse{
  opacity:1;pointer-events:auto;inset-inline:6px}
/* The chevron now means "bring it back", so it must point INTO the canvas — which is
   the OPPOSITE of what the markup ships (chevron-left on the left rail, chevron-right
   on the right one). Direction matters twice over: icon() tags both glyphs
   .pmu-flip-rtl, so components.css already mirrors them under RTL (except the two
   combos pinned to `transform:none` on the .sb2-rail__collapse .icon line below).
   Hence one rule per resolved state instead of a blanket rotate(180deg):
     LTR, either rail ....... base points outward  -> mirror it
     RTL, left rail ......... base is auto-mirrored -> un-mirror it
     RTL, right rail ........ base is pinned `none`, which already points inward. */
[dir="ltr"] .sb2.is-collapse-left .sb2-rail--left>.sb2-rail__collapse .icon,
[dir="ltr"] .sb2.is-collapse-right .sb2-rail--right>.sb2-rail__collapse .icon{transform:scaleX(-1)}
[dir="rtl"] .sb2.is-collapse-left .sb2-rail--left>.sb2-rail__collapse .icon{transform:none}

.sb2-rail__collapse{position:absolute;inset-block-start:10px;inset-inline-end:10px;z-index:2;
  width:28px;height:28px;border-radius:var(--r-full);display:grid;place-items:center;
  color:var(--muted);border:1px solid var(--line);background:var(--surface);
  transition:color var(--dur) var(--ease),background var(--dur) var(--ease)}
.sb2-rail--right .sb2-rail__collapse{inset-inline-end:auto;inset-inline-start:10px}
.sb2-rail__collapse:hover{color:var(--brand-fg);background:var(--tint)}
.sb2-rail__collapse .icon{width:15px;height:15px}
[dir="ltr"] .sb2-rail--left .sb2-rail__collapse .icon,[dir="rtl"] .sb2-rail--right .sb2-rail__collapse .icon{transform:none}
/* ...and RESERVE ITS LANE in the first section's header row. The handle is
   position:absolute at the rail's top INSIDE corner with z-index:2, and it landed
   straight on top of that row. MEASURED at 1500px LTR: handle 249..277 x 70..98,
   the LAYERS fold chevron 247..271 x 76..100 — a 22x22 overlap of a 24x24 button,
   so document.elementFromPoint at the chevron's own centre returned the HANDLE's
   icon, not the chevron: that control was not clickable at any point of itself.
   It went unnoticed because .sb2-rail__head delegates the click for the whole row
   (measured: clicking the label still flipped aria-expanded true->false), so the
   fold worked and only its affordance was dead. Independent of viewport — the
   overlap is the same at 1500px as at 420px. Padded on the side the handle
   actually occupies (end on the left rail, start on the right, per
   `.sb2-rail--right .sb2-rail__collapse` above); logical properties, so RTL
   mirrors with no extra rule. */
.sb2-rail--left .sb2-rail__section:first-of-type>.sb2-rail__head{padding-inline-end:26px}
.sb2-rail--right .sb2-rail__section:first-of-type>.sb2-rail__head{padding-inline-start:26px}

.sb2-rail__section{display:flex;flex-direction:column;padding:var(--s-4);border-block-end:1px solid var(--line)}
.sb2-rail__section--grow{flex:1 1 auto;min-height:0}
/* The two elastic sections want OPPOSITE things, so they are split by panel.
   PALETTE (left rail): it shares the rail with the layers tree and the two folds,
   so it must shrink — but `min-height:0` let it shrink to its bare 33px header,
   which hid every block tile AND spilled its search box / template CTA out over the
   PAGE SETTINGS + HEADER & FOOTER rows below, eating their clicks. Floor it and
   CONTAIN it; .sb2-palette's own scroller takes the squeeze. */
.sb2-rail__section--grow[data-sb-panel="palette"]{min-height:220px;overflow:hidden}
/* PROPERTIES (right rail): the only section in its rail and arbitrarily tall (a
   block can have dozens of fields), with no inner scroller of its own. It must NOT
   shrink — shrinking would clip fields with no way to reach them. Keep its natural
   height and let the RAIL scroll (overflow-y:auto above). */
.sb2-rail__section--grow[data-sb-panel="props"]{flex:1 0 auto}
.sb2-rail__section:last-child{border-block-end:0}
.sb2-rail__head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-block-end:var(--s-3)}
.sb2-rail__title{display:inline-flex;align-items:center;gap:8px;font-size:var(--fz-xs);font-weight:800;
  letter-spacing:.6px;text-transform:uppercase;color:var(--muted);margin:0;transition:color var(--dur) var(--ease)}
.sb2-rail__title .icon{width:15px;height:15px;color:var(--brand-fg)}
.sb2-rail__toggle{width:24px;height:24px;border-radius:var(--r-xs);display:grid;place-items:center;color:var(--muted);
  transition:color var(--dur) var(--ease)}
.sb2-rail__toggle:hover{color:var(--text)}
.sb2-rail__toggle .icon{width:14px;height:14px;transition:transform var(--dur) var(--ease)}
.sb2-rail__toggle[aria-expanded="false"] .icon{transform:rotate(-90deg)}
[dir="rtl"] .sb2-rail__toggle[aria-expanded="false"] .icon{transform:rotate(90deg)}
.sb2-rail__body[hidden]{display:none}
/* animated fold open/close: JS sets an explicit max-height for the transition,
   then clears it to auto once open so nested content can grow freely. */
.sb2-rail__body{overflow:hidden;transition:max-height var(--dur) var(--ease)}
/* ...plus the missing CONTENT fade, so the fold does not just get shorter — it
   fades out as it collapses and in as it grows.
   Keyed off the TOGGLE's aria-expanded because sitebuilder.js flips that
   SYNCHRONOUSLY at gesture time, whereas [hidden] is only added when the
   max-height tween ENDS and is therefore invisible during a close.
   Written as the NEGATIVE case on purpose: `.sb2-rail__body>*{opacity:0}` as a
   base would permanently hide the fold-LESS bodies (the palette and properties
   sections have no toggle at all). */
.sb2-rail__body>*{transition:opacity var(--dur) var(--ease),
                             transform var(--dur) var(--ease-out-expo)}
.sb2-rail__section:has(>.sb2-rail__head>.sb2-rail__toggle[aria-expanded="false"])>.sb2-rail__body>*{
  /* NO pointer-events:none. The fold's real hidden state is [hidden] (display:none)
     on the body, which already blocks interaction. Keying interactivity off
     aria-expanded as well meant ANY desync between the attribute and [hidden] left a
     visible-but-DEAD panel — a control that looks fine and silently ignores clicks.
     Worst case now is a faded-but-usable panel instead of an inert one. */
  opacity:0;transform:translateY(-4px);
}
/* Reduced motion => INSTANT, without stranding the fold.
   `transition:none` is NOT usable here: sitebuilder.js commits `hidden` from the
   max-height transitionend and has no fallback timer, so with no transition the
   event never fires, the fold stays un-`hidden` forever and the NEXT click
   computes the wrong direction (it reads `hasAttribute('hidden')`).
   1ms is the way out — a non-zero duration still fires transitionend (a 0s one
   does not run at all), so the commit keeps happening while the motion is
   imperceptible. The content fade is dropped outright: it has no JS listener. */
@media (prefers-reduced-motion:reduce){
  .sb2-rail__body{transition-duration:1ms}
  .sb2-rail__body>*{transition:none}
}
.sb2-rail__head{cursor:pointer;user-select:none}
.sb2-rail__head:hover .sb2-rail__title{color:var(--text)}

/* ---------------- SITE CHROME (header/footer) EDITOR ---------------- */
.sb2-chrome-intro{margin-block-end:var(--s-3)}
.sb2-chrome-group{margin-block-end:var(--s-4);padding-block-end:var(--s-4);border-block-end:1px solid var(--line)}
.sb2-chrome-group:last-of-type{border-block-end:0}
.sb2-chrome-group__title{margin:0 0 var(--s-3);font-size:var(--fz-sm);font-weight:800;color:var(--text)}
.sb2-chrome-sublabel{margin:var(--s-3) 0 6px;font-size:var(--fz-xs);font-weight:700;letter-spacing:.4px;
  text-transform:uppercase;color:var(--muted)}
.sb2-chrome-list,.sb2-chrome-cols{display:flex;flex-direction:column;gap:8px}
.sb2-chrome-row{display:grid;grid-template-columns:1fr 1fr auto;gap:6px;align-items:end}
.sb2-chrome-row .field{margin:0}
.sb2-chrome-col{border:1px solid var(--line);border-radius:var(--r-sm);padding:10px;background:var(--bg-3)}
.sb2-chrome-col__head{display:grid;grid-template-columns:1fr auto;gap:6px;align-items:end;margin-block-end:8px}
.sb2-chrome-del{align-self:end;min-width:32px}
.sb2-chrome-add{margin-block-start:8px}
.btn--xs{padding:4px 8px;font-size:var(--fz-xs);border-radius:var(--r-xs)}
.sb2-chrome-actions{margin-block-start:var(--s-3);display:flex;justify-content:flex-end}

/* ---------------- SITE CHROME (public nav/footer render) ---------------- */
.sb-chrome-brand{display:inline-flex;align-items:center;gap:10px;text-decoration:none;color:inherit;font-weight:800}
.sb-chrome-brand__logo{height:34px;width:auto;display:block;object-fit:contain}
.sb-chrome-brand__word{font-size:var(--fz-lg);font-weight:800;color:var(--text)}
.sb-chrome-cta{white-space:nowrap}
.sb-chrome-footer__about{margin-block:10px 14px}
.sb-chrome-social{display:flex;flex-wrap:wrap;gap:10px;margin-block-start:4px}
.sb-chrome-social__link{display:grid;place-items:center;width:36px;height:36px;border-radius:var(--r-full);
  color:var(--muted);border:1px solid var(--line);background:var(--surface);
  transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease),background var(--dur) var(--ease)}
.sb-chrome-social__link:hover{color:var(--brand-fg);border-color:var(--line-brand);background:var(--tint)}
.sb-chrome-social__link svg{width:18px;height:18px}
/* edit-mode affordance: highlight the inline-editable chrome nodes in the builder */
[data-chrome-edit]{outline:1px dashed transparent;outline-offset:2px;transition:outline-color var(--dur) var(--ease)}
[data-chrome] [data-chrome-edit]:hover{outline-color:var(--line-brand);cursor:text}

/* ---------------- BUILDER LANGUAGE TOGGLE + i18n field hints ---------------- */
/* Topbar segmented control that switches the active editing locale. */
.sb2-langtoggle{margin-inline-start:6px}
.sb2-langtoggle button{min-width:36px;font-weight:800;font-size:var(--fz-xs);letter-spacing:.5px}
/* A translatable property/chrome field that is UNTRANSLATED in the active
   (non-primary) locale — it will fall back to the primary text. Subtle dashed
   accent + the primary text shown as the placeholder (set in JS). */
.sb2-untranslated{border-style:dashed !important;border-color:var(--line-brand) !important}
.sb2-untranslated::placeholder{color:var(--muted);font-style:italic;opacity:.85}
[data-i18n-field]{background-image:linear-gradient(90deg,transparent,color-mix(in srgb,var(--brand-2) 5%,transparent))}
/* When editing a non-primary RTL locale in an otherwise-LTR editor (or vice
   versa), the field factories set dir per-input, so no global flip is needed —
   only the canvas iframe document dir is flipped (see setActiveLocale in JS). */

/* ---------------- PUBLIC LANGUAGE SWITCHER (per-site) ---------------- */
/* Floating pill; logical inset so it mirrors LTR<->RTL. Sits above the page. */
.sb-langswitch{position:fixed;inset-block-start:14px;inset-inline-end:16px;z-index:60;
  display:inline-flex;align-items:center;gap:2px;padding:3px;border-radius:var(--r-full);
  background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow)}
.sb-langswitch__opt{display:grid;place-items:center;min-width:34px;height:28px;padding-inline:9px;
  border-radius:var(--r-full);font-size:var(--fz-xs);font-weight:800;letter-spacing:.5px;
  color:var(--text-2);text-decoration:none;
  transition:color var(--dur) var(--ease),background var(--dur) var(--ease)}
.sb-langswitch__opt:hover{color:var(--text);background:var(--tint)}
.sb-langswitch__opt.is-active{color:#fff;background:var(--brand-grad,var(--brand-2))}
@media (max-width:640px){.sb-langswitch{inset-block-start:auto;inset-block-end:14px}}

/* ---------------- LAYERS TREE ---------------- */
/* The tree grows ONE ROW PER BLOCK and its section is flex-shrink:0, so on a long
   page it silently ate the entire rail and starved everything under it (see the
   `.sb2-rail>*` note). Cap it and let the TREE scroll instead of its siblings
   getting crushed. Viewport-relative so short laptops keep the folds in reach. */
.sb2-layers{display:flex;flex-direction:column;gap:4px;
  max-height:min(30vh,340px);overflow-y:auto;overscroll-behavior:contain}
.sb2-layers:empty::before{content:attr(data-empty);display:block;padding:16px 4px;color:var(--muted);
  font-size:var(--fz-xs);text-align:center}
.sb2-layer{display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:var(--r-sm);cursor:pointer;
  color:var(--text-2);border:1px solid transparent;
  transition:color var(--dur) var(--ease),background var(--dur) var(--ease),border-color var(--dur) var(--ease)}
.sb2-layer:hover{color:var(--text);background:var(--bg-3)}
.sb2-layer.is-selected{color:var(--brand-fg);background:var(--tint);border-color:var(--line-brand)}
.sb2-layer.dragging{opacity:.5}
.sb2-layer.drop-before{box-shadow:inset 0 2px 0 var(--brand-2)}
.sb2-layer.drop-after{box-shadow:inset 0 -2px 0 var(--brand-2)}
.sb2-layer__grip{color:var(--muted);cursor:grab;display:inline-flex;flex-shrink:0}
.sb2-layer__grip .icon{width:15px;height:15px}
.sb2-layer__ico{width:26px;height:26px;border-radius:var(--r-xs);display:grid;place-items:center;flex-shrink:0;
  background:var(--brand-grad-soft);color:var(--brand-fg);border:1px solid var(--line)}
.sb2-layer__ico .icon{width:15px;height:15px}
.sb2-layer__label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;
  font-size:var(--fz-sm)}
.sb2-layer__actions{display:inline-flex;gap:2px;opacity:0;transition:opacity var(--dur) var(--ease)}
.sb2-layer:hover .sb2-layer__actions,.sb2-layer.is-selected .sb2-layer__actions{opacity:1}
.sb2-layer__act{width:26px;height:26px;border-radius:var(--r-full);display:grid;place-items:center;color:var(--muted);flex-shrink:0;transition:color var(--dur) var(--ease),background var(--dur) var(--ease)}
.sb2-layer__act:hover{color:var(--text);background:var(--bg-2)}
.sb2-layer__act--del:hover{color:var(--danger-fg);background:rgba(225,29,72,.12)}
.sb2-layer__act .icon{width:14px;height:14px}

/* ---------------- SEARCH INPUT (palette / templates / insert) ---------------- */
.sb2-search{position:relative;display:flex;align-items:center;margin-block-end:var(--s-3)}
.sb2-search .icon{position:absolute;inset-inline-start:11px;width:15px;height:15px;color:var(--muted);pointer-events:none}
.sb2-search__input{width:100%;padding:9px 12px 9px 34px;border-radius:var(--r-full);
  background:var(--bg-3);border:1px solid var(--line);color:var(--text);font-size:var(--fz-sm);
  transition:border-color var(--dur) var(--ease),background var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
[dir="rtl"] .sb2-search__input{padding:9px 34px 9px 12px}
.sb2-search__input:focus{outline:none;border-color:var(--line-brand);background:var(--surface);box-shadow:var(--focus-ring)}
.sb2-search--sm{margin-block-end:var(--s-2)}
.sb2-search--sm .sb2-search__input{padding-block:7px}

/* ---------------- ADD-BLOCK PALETTE ---------------- */
/* `min-height:0` here (plus flex:1) meant the tile grid yielded ALL of its space to
   its fixed-size siblings inside the section — the search field and the "start from a
   template" CTA — and collapsed to ~32px: a scroller with 1300px of blocks inside and
   nothing grabbable on screen. Floor it at roughly two rows of tiles; if the rail
   cannot afford that, the RAIL scrolls (it is a designed scroller) instead of the
   palette silently vanishing. */
.sb2-palette{flex:1 1 auto;min-height:150px;overflow-y:auto;display:flex;flex-direction:column;gap:var(--s-4);
  margin-inline:calc(var(--s-4) * -1);padding-inline:var(--s-4)}
.sb2-palette__group[hidden]{display:none}
.sb2-palette__grouplabel{display:inline-flex;align-items:center;gap:7px;font-size:11px;font-weight:800;
  letter-spacing:.5px;text-transform:uppercase;color:var(--muted);margin-block-end:8px}
.sb2-palette__grouplabel .icon{width:13px;height:13px;color:var(--brand-fg)}
.sb2-palette__items{display:grid;grid-template-columns:1fr 1fr;gap:6px}
.sb2-add{display:flex;flex-direction:column;align-items:center;gap:7px;padding:12px 8px;border-radius:var(--r-sm);
  border:1px solid var(--line);background:var(--surface);color:var(--text-2);cursor:grab;text-align:center;
  font-size:var(--fz-xs);font-weight:600;
  transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease),background var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.sb2-add:hover{color:var(--text);border-color:var(--line-brand);background:var(--tint);box-shadow:var(--shadow-sm)}
.sb2-add:active{cursor:grabbing}
.sb2-add:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.sb2-add.dragging{opacity:.5}
.sb2-add[hidden]{display:none}
.sb2-add__ico{width:34px;height:34px;border-radius:var(--r-xs);display:grid;place-items:center;flex-shrink:0;
  background:var(--brand-grad-soft);color:var(--brand-fg);border:1px solid var(--line)}
.sb2-add__ico .icon{width:18px;height:18px}
.sb2-add__label{line-height:1.25;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.sb2-palette__empty{padding:20px 4px;text-align:center;font-size:var(--fz-xs)}

/* template CTA (left rail) */
.sb2-tpl-cta{display:flex;align-items:center;gap:10px;margin-block-start:var(--s-4);padding:12px;text-align:start;
  width:100%;border-radius:var(--r-sm);border:1px solid var(--line-brand);background:var(--brand-grad-soft);cursor:pointer;
  box-shadow:0 0 0 0 rgba(0,0,0,0);
  transition:box-shadow var(--dur) var(--ease),border-color var(--dur) var(--ease)}
.sb2-tpl-cta:hover{box-shadow:var(--shadow-sm);border-color:var(--brand-line)}
.sb2-tpl-cta__ico{width:34px;height:34px;border-radius:var(--r-xs);display:grid;place-items:center;flex-shrink:0;
  background:var(--surface);color:var(--brand-fg);border:1px solid var(--line-brand)}
.sb2-tpl-cta__ico .icon{width:17px;height:17px}
.sb2-tpl-cta__body{display:flex;flex-direction:column;gap:1px;min-width:0}
.sb2-tpl-cta__title{font-weight:700;color:var(--text);font-size:var(--fz-sm)}
.sb2-tpl-cta__hint{font-size:var(--fz-xs);color:var(--muted);line-height:1.4}

/* ---------------- CANVAS — the STAR ---------------- */
.sb2-canvas{position:relative;flex:1;min-width:0;display:flex;flex-direction:column;overflow:hidden}
.sb2-canvas__scroll{flex:1;min-height:0;overflow:auto;display:flex;align-items:flex-start;justify-content:center;
  padding:var(--s-8) var(--s-6) var(--s-16)}
@media (max-width:900px){.sb2-canvas__scroll{padding:var(--s-4)}}

/* the device frame + glassy browser chrome (echoes the landing "preview-card") */
.sb2-stage{position:relative;width:100%;max-width:1180px;display:flex;flex-direction:column;
  transition:max-width var(--dur-slow) var(--ease-out-expo)}
.sb2-stage[data-device="tablet"]{max-width:840px}
.sb2-stage[data-device="mobile"]{max-width:412px}

/* NO ambient float. This frame holds the live preview the user aims a cursor at:
   an infinite translateY made the whole preview drift up and down for ever, so a
   block slid out from under a STILL pointer, the hover outline re-targeted on its
   own and the canvas read as "it keeps moving". Ambient decoration does not belong
   on a measuring surface (and matches the app-wide removal of decorative motion). */
.sb2-device{position:relative;border-radius:var(--r-xl);overflow:hidden;background:var(--surface);
  border:1px solid var(--line);box-shadow:var(--shadow-lg)}
.sb2-stage[data-device="mobile"] .sb2-device,.sb2-stage[data-device="tablet"] .sb2-device{border-radius:var(--r-2xl)}

.sb2-chrome{display:flex;align-items:center;gap:10px;padding:11px 16px;
  background:var(--surface-2);border-block-end:1px solid var(--line)}
.sb2-chrome__dots{display:inline-flex;gap:6px;flex-shrink:0}
.sb2-chrome__dots i{width:11px;height:11px;border-radius:50%;background:var(--line-strong);display:block}
.sb2-chrome__dots i:nth-child(1){background:#FF5F57}
.sb2-chrome__dots i:nth-child(2){background:#FEBC2E}
.sb2-chrome__dots i:nth-child(3){background:#28C840}
.sb2-chrome__url{flex:0 1 auto;min-width:0;max-width:60%;padding:5px 14px;border-radius:var(--r-full);
  background:var(--bg-3);border:1px solid var(--line);color:var(--muted);font-size:var(--fz-xs);
  font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center}
.sb2-chrome__spacer{flex:1}
.sb2-chrome__live{display:inline-flex;align-items:center;gap:6px;flex-shrink:0;font-size:11px;font-weight:700;
  color:var(--success-fg)}
.sb2-chrome__livedot{width:7px;height:7px;border-radius:50%;background:var(--accent-green);
  box-shadow:0 0 0 0 rgba(14,159,110,.5);animation:sb2Live 2s var(--ease) infinite}
@media (max-width:560px){.sb2-chrome__live span,.sb2-chrome__live{font-size:0}.sb2-chrome__livedot{font-size:0}}

.sb2-viewport{position:relative;background:var(--bg)}
.sb2-preview{display:block;width:100%;height:min(72vh,860px);border:0;background:var(--bg)}
.sb2-stage[data-device="mobile"] .sb2-preview,.sb2-stage[data-device="tablet"] .sb2-preview{height:min(76vh,900px)}

/* overlay host — hover/selection outlines, floating toolbar, insert "+" & drops */
.sb2-overlay{position:absolute;inset:0;z-index:3;pointer-events:none;overflow:hidden}
/* hover + selection outlines (JS positions .sb2-out via inline top/left/width/height) */
.sb2-out{position:absolute;border-radius:var(--r-xs);pointer-events:none;
  transition:transform 90ms var(--ease-out),opacity 90ms var(--ease-out)}
.sb2-out--hover{border:1.5px dashed var(--sb2-outline-soft);background:rgba(30,80,200,.04)}
.sb2-out--select{border:2px solid var(--sb2-outline);box-shadow:0 0 0 4px rgba(30,80,200,.12)}
.sb2-out__chip{position:absolute;inset-block-start:0;inset-inline-start:0;transform:translateY(-100%);
  display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:var(--r-xs) var(--r-xs) 0 0;
  font-size:11px;font-weight:700;color:#fff;background:var(--brand-2);white-space:nowrap}
.sb2-out--hover .sb2-out__chip{background:var(--brand-2);opacity:.85}
.sb2-out__chip .icon{width:12px;height:12px}

/* floating contextual toolbar (anchored to the selection's top edge) */
.sb2-fab{position:absolute;z-index:5;pointer-events:auto;display:inline-flex;align-items:center;gap:2px;padding:4px;
  border-radius:var(--r-full);background:var(--sb2-glass-strong);border:1px solid var(--line-brand);
  box-shadow:var(--shadow);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  transition:transform 120ms var(--ease-out),opacity 120ms var(--ease-out)}
.sb2-fab__btn{width:32px;height:32px;border-radius:var(--r-full);display:grid;place-items:center;color:var(--text-2);
  cursor:pointer;border:0;background:none;transition:color var(--dur) var(--ease),background var(--dur) var(--ease)}
.sb2-fab__btn:hover{color:var(--brand-fg);background:var(--tint)}
.sb2-fab__btn--danger:hover{color:var(--danger-fg);background:rgba(225,29,72,.12)}
.sb2-fab__btn--grip{cursor:grab}
.sb2-fab__btn .icon{width:16px;height:16px}
.sb2-fab__sep{width:1px;height:20px;background:var(--line);margin-inline:2px}
.sb2-fab__label{display:inline-flex;align-items:center;padding-inline:8px;font-size:11px;font-weight:700;color:var(--muted)}
/* the engine toggles .hidden on the JS-created overlay nodes */
.sb2-out[hidden],.sb2-fab[hidden],.sb2-dropline[hidden]{display:none}

/* ---- INSIDE the builder canvas iframe. The app shell paints a 560px
   cursor-following .bg-glow (partials/background.php, driven by core/motion.js)
   behind every page. In the editor that meant a soft blob swelling around the
   pointer on EVERY mousemove inside the canvas — read by the user as the preview
   "expanding the more I move the cursor". A canvas is a measuring surface: no
   pointer-reactive ambient decoration in it.

   THESE RULES ARE COSMETIC, NOT THE CONTROL. The control is that app.js never binds
   the mousemove listener in this document (core/shell-context.js → bootPreviewCanvas),
   so .bg-glow never receives left/top nor the `.on` that fades it in (background.css
   keeps it at opacity:0 without it). If you see the glow MOVE in the canvas, the bug
   is in that gate, not here — a mask cannot un-bind a listener.

   WHY TWO SELECTORS, AND WHY NEITHER IS THE GATE'S SIGNAL. The previous version keyed
   on .sb-site--edit, i.e. on the SAME ?edit=1 flag the boot gate used — so when the
   canvas browsed another page (no ?edit=1) BOTH layers went absent at once and the
   blob came back: two layers of defence sharing one signal is one layer. Now each leg
   has its own producer, and none of them is the boot gate's primary signal:
     :root.sb-canvas        stamped by the PARENT editor on whatever document lands in
                            #sb-preview (sitebuilder.js bindNavHandlers) — needs no
                            marker in the URL and no cooperation from the document;
     :has(.sb-site--canvas) the SERVER's own marker for a ?frame=1 / ?edit=1 render
                            (SiteBuilder::public), so it holds even if the parent's
                            stamp never ran.
   :has() is needed for the second one because .bg-glow is a SIBLING of .sb-site.
   The live site matches neither and is untouched. */
:root.sb-canvas .bg-glow,
:root:has(.sb-site--canvas) .bg-glow{display:none}

/* inline-edit state on a [data-edit] node inside the (same-origin) preview.
   The preview document loads sitebuilder.css too (public layout), so this rule
   styles the contentEditable node while the parent editor drives it. */
[contenteditable="true"].sb2-editing,.sb2-editing[contenteditable="true"]{
  outline:2px solid var(--focus-line);outline-offset:2px;border-radius:3px;
  background:rgba(30,80,200,.05);cursor:text;min-width:1ch}

/* rich-text format tooltip (bold/italic/link/clear) — floats over the overlay */
.sb2-fmt{position:absolute;z-index:6;pointer-events:auto;display:inline-flex;gap:2px;padding:4px;
  border-radius:var(--r-full);background:var(--sb2-glass-strong);border:1px solid var(--line-brand);
  box-shadow:var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}
.sb2-fmt[hidden]{display:none}
.sb2-fmt__btn{width:30px;height:30px;border-radius:var(--r-full);display:grid;place-items:center;color:var(--text-2);
  cursor:pointer;border:0;background:none;transition:color var(--dur) var(--ease),background var(--dur) var(--ease)}
.sb2-fmt__btn:hover{color:var(--brand-fg);background:var(--tint)}
.sb2-fmt__btn .icon{width:15px;height:15px}

/* insert "+" affordance between blocks (JS positions a line + button) */
.sb2-insert{position:absolute;z-index:4;pointer-events:auto;inset-inline:0;display:flex;align-items:center;
  justify-content:center;height:0;opacity:0;transition:opacity var(--dur) var(--ease)}
.sb2-insert::before{content:"";position:absolute;inset-inline:8%;height:2px;border-radius:2px;
  background:linear-gradient(90deg,transparent,var(--brand-2),transparent)}
.sb2-insert:hover,.sb2-insert.is-active{opacity:1}
.sb2-insert__btn{position:relative;z-index:1;width:26px;height:26px;border-radius:var(--r-full);display:grid;place-items:center;
  color:#fff;background:var(--brand-2);border:2px solid var(--surface);box-shadow:var(--shadow-sm);cursor:pointer;
  transition:background var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.sb2-insert__btn .icon{width:14px;height:14px}

/* drop indicator line (drag from palette / reorder) */
.sb2-dropline{position:absolute;z-index:6;inset-inline:0;height:3px;border-radius:3px;pointer-events:none;
  background:var(--brand-2);box-shadow:0 0 0 4px rgba(30,80,200,.16)}
.sb2-dropline::before{content:"";position:absolute;inset-inline-start:0;inset-block-start:50%;
  width:9px;height:9px;border-radius:50%;background:var(--brand-2);transform:translateY(-50%)}
/* The insertion boundary is often OUTSIDE the visible slice of a scrolled preview
   (measured: -28px and +853px in a 684px overlay that clips its children), so the line
   used to vanish and a working drop looked like a no-op. sitebuilder.js clamps it to the
   edge and marks the direction of the real boundary with a static caret — no movement,
   logical properties only, so it mirrors under RTL. */
.sb2-dropline.is-beyond-start::after,.sb2-dropline.is-beyond-end::after{content:"";position:absolute;
  inset-inline-start:14px;width:0;height:0;border-inline:6px solid transparent}
.sb2-dropline.is-beyond-start::after{inset-block-start:4px;border-block-end:7px solid var(--brand-2)}
.sb2-dropline.is-beyond-end::after{inset-block-end:4px;border-block-start:7px solid var(--brand-2)}

/* loading veil while the iframe reloads on structural change */
.sb2-canvas__loader{position:absolute;inset:0;z-index:7;pointer-events:none;opacity:0;
  background:color-mix(in srgb,var(--bg) 55%,transparent);backdrop-filter:blur(1px);
  transition:opacity var(--dur) var(--ease)}
.sb2.is-loading .sb2-canvas__loader{opacity:1;pointer-events:auto}
.sb2.is-loading .sb2-canvas__loader::after{content:"";position:absolute;inset-block-start:50%;inset-inline-start:50%;
  width:28px;height:28px;transform:translate(-50%,-50%);border-radius:50%;
  border:2px solid var(--line-strong);border-top-color:var(--brand-line);animation:pmuSpin .7s linear infinite}

/* empty-canvas prompt.
   The prompt is a CHILD of .sb2-stage, so an empty page must hide the DEVICE FRAME
   and nothing else — sitebuilder.js used to set stage{display:none}, which hid the
   prompt with it and left a brand-new page as a featureless void with nothing to
   drop a block onto. .is-empty is the stage's "no blocks yet" state. */
.sb2-stage.is-empty .sb2-device{display:none}
.sb2-canvas__empty{margin:auto;max-width:420px;text-align:center;display:flex;flex-direction:column;align-items:center;
  gap:var(--s-3);padding:var(--s-12) var(--s-6);border-radius:var(--r-xl);
  border:1.5px dashed transparent;transition:background var(--dur) var(--ease),border-color var(--dur) var(--ease)}
.sb2-canvas__empty[hidden]{display:none}

/* DROP TARGET FEEDBACK — the canvas is a valid drop surface for a palette tile even
   where the drop indicator cannot be drawn (outside the iframe, or on an empty page
   where there is no block to draw a line between). Paint only: an inset ring + tint,
   never a size, margin or transform change, so a dragged cursor can never make the
   preview move or grow. */
.sb2-canvas.is-drop-active{box-shadow:inset 0 0 0 2px var(--line-brand)}
.sb2-canvas.is-drop-active .sb2-canvas__empty{border-color:var(--line-brand);background:var(--tint)}
.sb2-canvas__emptyico{width:56px;height:56px;border-radius:var(--r-lg);display:grid;place-items:center;
  background:var(--brand-grad-soft);color:var(--brand-fg);border:1px solid var(--line-brand);box-shadow:var(--glow)}
.sb2-canvas__emptyico .icon{width:26px;height:26px}
.sb2-canvas__empty h3{font-size:var(--fz-lg);font-weight:700;color:var(--text)}

/* -------- PREVIEW / DEMO MODE (full-bleed, rails hidden) -------- */
.sb2.is-preview .sb2-rail{width:0;border:0}
.sb2.is-preview .sb2-rail>*{opacity:0;pointer-events:none}
.sb2.is-preview .sb2-canvas__scroll{padding:0}
.sb2.is-preview .sb2-stage{max-width:100%}
.sb2.is-preview .sb2-device{border-radius:0;border:0;box-shadow:none;animation:none}
.sb2.is-preview .sb2-chrome{display:none}
/* ...EXCEPT while the preview is browsing another page: the URL bar is then the only
   thing telling the user where they are (sitebuilder.js toggles .is-offpage). (0,4,0)
   beats the (0,3,0) hide above. */
.sb2.is-preview.is-offpage .sb2-chrome{display:flex}
.sb2.is-preview .sb2-preview{height:100%}
.sb2.is-preview .sb2-viewport{height:100%}
.sb2.is-preview .sb2-canvas__scroll{align-items:stretch}
.sb2.is-preview .sb2-overlay{display:none}
.sb2.is-preview .sb2-stage[data-device="tablet"]{max-width:840px;margin-inline:auto}
.sb2.is-preview .sb2-stage[data-device="mobile"]{max-width:412px;margin-inline:auto}
/* floating demo exit bar */
.sb2-demobar{position:absolute;inset-block-end:20px;inset-inline-start:50%;transform:translateX(-50%);z-index:8;
  display:none;align-items:center;gap:var(--s-3);padding:8px;border-radius:var(--r-full);
  background:var(--sb2-glass-strong);border:1px solid var(--line-brand);box-shadow:var(--shadow-lg);
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}
[dir="rtl"] .sb2-demobar{transform:translateX(50%)}
.sb2.is-preview .sb2-demobar{display:flex}
.sb2-demobar[hidden]{display:none}
.sb2.is-preview .sb2-demobar[hidden]{display:flex}

/* ---------------- PROPERTIES PANEL ---------------- */
.sb2-props{display:flex;flex-direction:column;gap:var(--s-4)}
/* empty state (no selection) */
.sb2-props__empty{text-align:center;padding:var(--s-10) var(--s-4);display:flex;flex-direction:column;
  align-items:center;gap:var(--s-2)}
.sb2-props__emptyico{width:48px;height:48px;border-radius:var(--r-lg);display:grid;place-items:center;
  background:var(--brand-grad-soft);color:var(--brand-fg);border:1px solid var(--line)}
.sb2-props__emptyico .icon{width:22px;height:22px}
.sb2-props__empty h3{font-size:var(--fz-base);font-weight:700;color:var(--text)}
.sb2-props__empty p{font-size:var(--fz-xs);color:var(--muted);line-height:1.5;max-width:28ch}
/* selected-block header */
.sb2-props__head{display:flex;align-items:center;gap:10px;padding-block-end:var(--s-4);
  border-block-end:1px solid var(--line)}
.sb2-props__ico{width:36px;height:36px;border-radius:var(--r-sm);display:grid;place-items:center;flex-shrink:0;
  background:var(--brand-grad-soft);color:var(--brand-fg);border:1px solid var(--line)}
.sb2-props__ico .icon{width:19px;height:19px}
.sb2-props__title{font-size:var(--fz-md);font-weight:700;color:var(--text)}
.sb2-props__hint{font-size:var(--fz-xs);color:var(--muted);line-height:1.4}
/* collapsible property group */
.sb2-group{border:1px solid var(--line);border-radius:var(--r-sm);overflow:hidden;background:var(--surface-2)}
.sb2-group__head{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;
  padding:11px 14px;background:none;border:0;cursor:pointer;font-weight:700;color:var(--text);font-size:var(--fz-sm);
  transition:color var(--dur) var(--ease)}
.sb2-group__head:hover{color:var(--brand-fg)}
.sb2-group__head .icon{width:14px;height:14px;color:var(--muted);transition:transform var(--dur) var(--ease)}
.sb2-group__head[aria-expanded="false"] .icon{transform:rotate(-90deg)}
[dir="rtl"] .sb2-group__head[aria-expanded="false"] .icon{transform:rotate(90deg)}
.sb2-group__body{padding:0 14px 14px}
.sb2-group__body[hidden]{display:none}

/* -------- form primitives shared by properties + list items -------- */
/* media picker inside a field */
.sb-media{display:flex;flex-direction:column;gap:8px}
.sb-media__row{display:flex;gap:8px}
.sb-media__row .input{flex:1}
.sb-media__thumb{width:100%;max-height:160px;object-fit:cover;border-radius:var(--r-sm);
  border:1px solid var(--line);display:block}
/* repeatable list item card (features / gallery / plans …) */
.sb-item{border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--s-4);background:var(--surface-2)}
.sb-item + .sb-item{margin-top:var(--s-3)}
.sb-item__head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:var(--s-3)}
.sb-item__num{display:inline-flex;align-items:center;gap:7px;font-size:var(--fz-xs);font-weight:700;color:var(--muted)}
.sb-item__num b{width:22px;height:22px;border-radius:var(--r-xs);display:grid;place-items:center;
  background:var(--brand-grad-soft);color:var(--brand-fg);border:1px solid var(--line);font-size:11px}
.sb-item .field:last-child{margin-bottom:0}
/* small round delete button reused by list items */
.sb-block-row__del{width:28px;height:28px;border-radius:var(--r-full);display:grid;place-items:center;
  color:var(--muted);flex-shrink:0}
.sb-block-row__del:hover{color:var(--danger-fg);background:rgba(225,29,72,.12)}
.sb-block-row__del .icon{width:15px;height:15px}
.sb-block-row__del:disabled{opacity:.35;pointer-events:none}

/* ------------------- FORM-BLOCK BUILDER (props panel) ------------------- */
/* template picker + fields list live in the standard .sb2-props flow. */
.sb-form-tpl .field__hint,.sb-form-fields .field__hint{margin-top:6px}
.sb-form-fieldlist{display:flex;flex-direction:column;gap:var(--s-3);margin-top:6px}
.sb-form-empty{font-size:var(--fz-xs);padding:10px 2px;margin:0}
.sb-form-addfield{margin-top:10px;width:100%;justify-content:center}
/* one field card (extends .sb-item). draggable to reorder. */
.sb-form-field{cursor:default}
.sb-form-field.dragging{opacity:.5}
.sb-form-field.drop-before{box-shadow:0 -2px 0 0 var(--brand-2)}
.sb-form-field.drop-after{box-shadow:0 2px 0 0 var(--brand-2)}
.sb-form-field__lead{display:inline-flex;align-items:center;gap:8px;min-width:0}
.sb-form-field__grip{display:inline-flex;color:var(--muted);cursor:grab}
.sb-form-field__grip .icon{width:15px;height:15px}
.sb-form-field:active .sb-form-field__grip{cursor:grabbing}
.sb-form-field__summary{font-size:var(--fz-xs);font-weight:600;color:var(--text-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:14ch}
.sb-form-field__acts{display:inline-flex;align-items:center;gap:2px}
.sb-form-field__move .icon{width:14px;height:14px}
.sb-form-field__req{margin-top:4px}
/* options editor (select / radio) */
.sb-form-opts{margin-top:10px;padding-top:10px;border-top:1px dashed var(--line)}
.sb-form-opts__label{font-size:var(--fz-xs);font-weight:700;color:var(--muted);margin-bottom:8px}
.sb-form-opts__list{display:flex;flex-direction:column;gap:6px}
.sb-form-opt{display:grid;grid-template-columns:1fr 1fr auto;gap:6px;align-items:center}
.sb-form-opt .input{min-width:0}
.sb-form-opts__add{margin-top:8px}
/* block-level settings (lead toggle + internal name) */
.sb-form-settings{margin-top:var(--s-4);padding-top:var(--s-4);border-top:1px solid var(--line);
  display:flex;flex-direction:column;gap:var(--s-3)}
.sb-form-lead{align-items:flex-start}

/* ============================================================
   TEMPLATE GALLERY  (#sb-tpl-modal)
   ============================================================ */
.sb2-tpl__panel{max-width:960px;width:min(960px,95vw);max-height:88vh;display:flex;flex-direction:column;padding:0}
.sb2-tpl__head{padding:var(--s-6) var(--s-6) var(--s-4)}
.sb2-tpl__head .modal__title{margin-bottom:4px}
.sb2-tpl__desc{font-size:var(--fz-sm);margin:0}
.sb2-tpl__toolbar{display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);flex-wrap:wrap;
  padding:0 var(--s-6) var(--s-4);border-block-end:1px solid var(--line)}
.sb2-tpl__filters{display:flex;flex-wrap:wrap;gap:6px}
.sb2-chip{display:inline-flex;align-items:center;padding:6px 14px;border-radius:var(--r-full);font-size:var(--fz-xs);
  font-weight:700;color:var(--text-2);border:1px solid var(--line);background:var(--bg-2);cursor:pointer;
  transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease),background var(--dur) var(--ease)}
.sb2-chip:hover{color:var(--text);border-color:var(--line-brand);background:var(--tint)}
.sb2-chip.active{color:var(--brand-fg);border-color:var(--line-brand);background:var(--tint)}
.sb2-tpl__search{margin:0;min-width:200px;flex:0 1 260px}
.sb2-tpl__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s-4);padding:var(--s-6);overflow-y:auto}
@media (max-width:760px){.sb2-tpl__grid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:520px){.sb2-tpl__grid{grid-template-columns:1fr}}
.sb2-tpl__grid:empty::after{content:attr(data-empty);grid-column:1/-1;padding:32px;text-align:center;
  color:var(--muted);font-size:var(--fz-sm)}
.sb2-tpl-card{display:flex;flex-direction:column;text-align:start;padding:0;overflow:hidden;
  border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);cursor:pointer;
  transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.sb2-tpl-card:hover{border-color:var(--line-brand);box-shadow:var(--shadow)}
.sb2-tpl-card:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.sb2-tpl-card[hidden]{display:none}
.sb2-tpl-card__thumb{display:flex;flex-direction:column;gap:5px;padding:16px;min-height:120px;
  background:var(--bg-3);border-block-end:1px solid var(--line)}
.sb2-tpl-bar{display:block;height:8px;border-radius:var(--r-full);background:var(--line-strong);opacity:.65}
.sb2-tpl-bar--hero{height:22px;background:var(--brand-grad);opacity:1}
.sb2-tpl-bar--cta-banner,.sb2-tpl-bar--cta{background:var(--brand-2);opacity:.5}
.sb2-tpl-bar--pricing,.sb2-tpl-bar--features,.sb2-tpl-bar--stats{width:72%}
.sb2-tpl-bar--contact-form{width:46%}
.sb2-tpl-card__body{display:flex;flex-direction:column;gap:4px;padding:12px 16px 16px}
.sb2-tpl-card__name{font-weight:700;color:var(--text);font-size:var(--fz-sm)}
.sb2-tpl-card__desc{font-size:var(--fz-xs);color:var(--muted);line-height:1.5}
.sb2-tpl-card__cat{align-self:flex-start;margin-block-start:6px;padding:2px 9px;border-radius:var(--r-full);
  font-size:10px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;color:var(--brand-fg);
  background:var(--tint);border:1px solid var(--line-brand)}

/* ---- New-page modal (pages list): blank option + "or" divider ----
   Reuses the .sb2-tpl gallery (chips/cards/grid). The whole body is a <form>
   that owns the scroll area so its POST submit follows the 302 to the editor. */
.sb2-newpage__form{display:flex;flex-direction:column;min-height:0;flex:1 1 auto}
.sb2-newpage__blankrow{padding:var(--s-4) var(--s-6) 0}
.sb2-newpage__blank{display:flex;align-items:center;gap:12px;inline-size:100%;text-align:start;
  padding:14px 16px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);cursor:pointer;
  transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.sb2-newpage__blank:hover{border-color:var(--line-brand);box-shadow:var(--shadow-sm)}
.sb2-newpage__blank:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.sb2-newpage__blankico{width:38px;height:38px;border-radius:var(--r-sm);display:grid;place-items:center;flex-shrink:0;
  color:var(--brand-fg);background:var(--tint);border:1px solid var(--line-brand)}
.sb2-newpage__blankbody{display:flex;flex-direction:column;gap:2px;min-width:0}
.sb2-newpage__blanktitle{font-weight:700;color:var(--text);font-size:var(--fz-sm)}
.sb2-newpage__blankhint{font-size:var(--fz-xs);color:var(--muted);line-height:1.4}
.sb2-newpage__or{display:flex;align-items:center;gap:12px;margin:0;padding:var(--s-4) var(--s-6) 0;
  color:var(--muted);font-size:var(--fz-xs);font-weight:700;text-transform:uppercase;letter-spacing:.4px}
.sb2-newpage__or::before,.sb2-newpage__or::after{content:"";flex:1;height:1px;background:var(--line)}
.sb2-newpage .sb2-tpl__grid{min-height:0}

/* ============================================================
   BUILDER POPOVERS / MODALS  (#sb-modals — link/image/color/spacing/insert/help)
   Glassy, anchored popovers the engine positions + fills.
   ============================================================ */
.sb2-modals{position:fixed;inset:0;z-index:1400;pointer-events:none}
.sb2-pop{position:absolute;z-index:1401;pointer-events:auto;width:min(320px,92vw);
  background:var(--sb2-glass-strong);border:1px solid var(--line-brand);border-radius:var(--r-lg);
  box-shadow:var(--shadow-xl);-webkit-backdrop-filter:saturate(160%) blur(16px);backdrop-filter:saturate(160%) blur(16px);
  opacity:0;transform:translateY(4px) scale(.98);transition:opacity 140ms var(--ease-out),transform 140ms var(--ease-out)}
.sb2-pop[hidden]{display:none}
.sb2-pop.is-open{opacity:1;transform:none}
.sb2-pop--wide{width:min(380px,94vw)}
.sb2-pop--insert{width:min(300px,92vw)}
.sb2-pop--help{width:min(300px,92vw)}
.sb2-pop__arrow{position:absolute;width:12px;height:12px;background:inherit;border-inline-start:1px solid var(--line-brand);
  border-block-start:1px solid var(--line-brand);transform:rotate(45deg)}
.sb2-pop__head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 14px;
  border-block-end:1px solid var(--line)}
.sb2-pop__title{display:inline-flex;align-items:center;gap:7px;font-weight:700;color:var(--text);font-size:var(--fz-sm)}
.sb2-pop__title .icon{width:15px;height:15px;color:var(--brand-fg)}
.sb2-pop__close{width:26px;height:26px;border-radius:var(--r-full);display:grid;place-items:center;color:var(--muted)}
.sb2-pop__close:hover{color:var(--text);background:var(--bg-3)}
.sb2-pop__close .icon{width:14px;height:14px}
.sb2-pop__body{padding:14px;display:flex;flex-direction:column;gap:var(--s-3)}
.sb2-pop__field{display:flex;flex-direction:column;gap:6px}
.sb2-pop__field[hidden]{display:none}
.sb2-pop__foot{display:flex;justify-content:flex-end;gap:8px;padding:12px 14px;border-block-start:1px solid var(--line)}

/* link picker tab row (small segmented) */
.sb2-seg-tabs{display:inline-flex;padding:3px;background:var(--bg-3);border:1px solid var(--line);border-radius:var(--r-full);gap:2px;width:100%}
.sb2-seg-tabs button{flex:1;padding:7px 10px;border-radius:var(--r-full);font-size:var(--fz-xs);font-weight:700;
  color:var(--text-2);border:0;background:none;cursor:pointer;transition:color var(--dur) var(--ease),background var(--dur) var(--ease)}
.sb2-seg-tabs button:hover{color:var(--text)}
.sb2-seg-tabs button.active{background:var(--surface);color:var(--brand-fg);box-shadow:var(--shadow-xs)}

/* image upload zone */
.sb2-uploadzone{display:flex;flex-direction:column;align-items:center;gap:8px;padding:22px 16px;text-align:center;
  border:1.5px dashed var(--line-strong);border-radius:var(--r-sm);color:var(--muted);cursor:pointer;
  transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease),background var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.sb2-uploadzone:hover,.sb2-uploadzone.is-drag{color:var(--brand-fg);border-color:var(--line-brand);background:var(--tint)}
.sb2-uploadzone:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.sb2-uploadzone .icon{width:24px;height:24px}
.sb2-uploadzone__label{font-size:var(--fz-xs);line-height:1.4;max-width:26ch}
.sb2-media-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;max-height:180px;overflow-y:auto}
.sb2-media-grid:empty{display:none}
.sb2-media-grid img{width:100%;aspect-ratio:1;object-fit:cover;border-radius:var(--r-xs);border:1px solid var(--line);
  cursor:pointer;transition:box-shadow var(--dur) var(--ease)}
.sb2-media-grid img:hover,.sb2-media-grid img.is-selected{box-shadow:var(--focus-ring)}

/* color swatches */
.sb2-swatches{display:flex;flex-wrap:wrap;gap:8px}
.sb2-swatch{width:30px;height:30px;border-radius:var(--r-xs);background:var(--sw,var(--brand-2));cursor:pointer;
  border:1px solid var(--line-strong);box-shadow:var(--shadow-xs);
  transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.sb2-swatch:hover{border-color:var(--brand-line);box-shadow:var(--shadow-sm)}
.sb2-swatch.is-selected{box-shadow:var(--focus-ring)}
.sb2-color-custom{flex-direction:row;align-items:center;gap:8px}
.sb2-color-custom__swatch{position:relative;width:38px;height:38px;border-radius:var(--r-xs);overflow:hidden;flex-shrink:0;
  border:1px solid var(--line-strong)}
.sb2-color-custom__swatch input[type="color"]{position:absolute;inset:-6px;width:calc(100% + 12px);height:calc(100% + 12px);
  border:0;padding:0;background:none;cursor:pointer}
.sb2-color-custom .input{flex:1}

/* spacing segmented */
.sb2-spacing-seg{width:100%}
.sb2-spacing-seg button{flex:1;padding:7px 6px;font-size:var(--fz-xs)}

/* insert-block popover mirrored palette list */
.sb2-insert-list{display:flex;flex-direction:column;gap:2px;max-height:280px;overflow-y:auto;padding:10px}
.sb2-insert-item{display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:var(--r-sm);cursor:pointer;
  color:var(--text-2);border:0;background:none;text-align:start;font-size:var(--fz-sm);font-weight:600;width:100%;
  transition:color var(--dur) var(--ease),background var(--dur) var(--ease)}
.sb2-insert-item:hover,.sb2-insert-item.is-active{color:var(--brand-fg);background:var(--tint)}
.sb2-insert-item[hidden]{display:none}
.sb2-insert-item__ico{width:26px;height:26px;border-radius:var(--r-xs);display:grid;place-items:center;flex-shrink:0;
  background:var(--brand-grad-soft);color:var(--brand-fg);border:1px solid var(--line)}
.sb2-insert-item__ico .icon{width:15px;height:15px}

/* help shortcuts */
.sb2-shortcuts{display:flex;flex-direction:column;gap:8px;margin:0}
.sb2-shortcuts>div{display:flex;align-items:center;justify-content:space-between;gap:12px}
.sb2-shortcuts dt{font-size:var(--fz-sm);color:var(--text-2)}
.sb2-shortcuts dd{margin:0;display:inline-flex;gap:4px}
.sb2-shortcuts kbd{display:inline-grid;place-items:center;min-width:22px;height:22px;padding-inline:6px;
  border-radius:var(--r-xs);font-size:11px;font-weight:700;font-family:inherit;color:var(--text);
  background:var(--bg-3);border:1px solid var(--line-strong);box-shadow:0 1px 0 var(--line-strong)}

/* ---------------- ANIMATIONS ---------------- */
/* (sb2Float removed — see .sb2-device: the preview frame must not drift.) */
@keyframes sb2Live{0%{box-shadow:0 0 0 0 rgba(14,159,110,.5)}70%{box-shadow:0 0 0 6px rgba(14,159,110,0)}100%{box-shadow:0 0 0 0 rgba(14,159,110,0)}}

/* ---------------- RESPONSIVE ---------------- */
@media (max-width:1200px){
  .sb2{--sb2-rail-w:256px;--sb2-rail-w-right:288px}
}
/* ---- UNDER 1024px: NARROWER RAILS + A SCROLLING BODY, NOT OFF-CANVAS DRAWERS --
   WHAT WAS HERE AND WHY IT COULD NOT WORK. This block used to turn both rails
   into slide-over drawers — `position:absolute` + `translateX(±102%)`, correctly
   mirrored for RTL — and the ONLY rules that brought either back were
   `.sb2.is-open-left` / `.sb2.is-open-right`. Nothing in the tree has ever set
   those classes: 1 occurrence of each literal in the whole repo and it was that
   rule itself, no `'is-open-' + side` concatenation anywhere, and the builder's
   own toggle writes is-collapse-left / is-collapse-right instead (sitebuilder.js
   [data-sb-rail-toggle] + focusProps()). So below 1024px — a small laptop, a
   tablet, a split window — the layers tree, the ADD BLOCK palette and the
   properties rail were ALL parked off-screen with no producer for the only
   selector that could return them, and the collapse handle that would have
   brought them back rode off-screen inside the rail. The editor was not cramped,
   it was inoperable.

   WHY IN-FLOW + SCROLL RATHER THAN A DRAWER. A drawer needs a state producer, and
   the two classes that exist mean the OPPOSITE thing: `is-collapse-*` present =
   collapsed. Re-using them as "open" would invert the button's aria-expanded, and
   focusProps() (which REMOVES is-collapse-right to reveal the properties of a
   just-selected block) would start CLOSING the rail it is meant to open. A
   drawer therefore needs new markup + JS, which this workflow may not write —
   and adding a class nothing sets is exactly the bug above. So: keep the rails
   where the desktop keeps them, shrink them, and let the BODY scroll sideways
   when all three panes no longer fit. One behaviour at every viewport.

   WHAT THIS PRESERVES. The desktop 40px-stub collapse is untouched and now
   applies unchanged at every width: `.sb2.is-collapse-* .sb2-rail--*` still wins
   over the narrowed --sb2-rail-w (it sets `width`, these set a variable), the
   rail is in flow so the stub occupies real space, and .sb2-rail__collapse stays
   visible and hit-testable inside it. There is always a visible affordance to
   bring a rail back, at every viewport — which is the invariant the earlier
   one-way-collapse fix established.

   The canvas keeps a usable floor (min-width) instead of being crushed to 0 by
   `flex:1;min-width:0`; that floor is what pushes the body into overflow and
   makes the third pane reachable by scrolling, with its clipped edge and the
   scrollbar as the affordance. Both values are logical/direction-free, so RTL
   needs no extra rule: the flex row mirrors itself. */
@media (max-width:1024px){
  .sb2{--sb2-rail-w:236px;--sb2-rail-w-right:252px}
  .sb2-body{overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain}
  .sb2-canvas{min-width:min(360px,46vw)}
}
/* ---- PHONE (<=640px): THE THREE PANES STACK. --------------------------------
   WHAT WAS HERE AND WHY IT WAS NOT ENOUGH (GH #62). This block narrowed the rails
   to 220/236px and floored the canvas at `min(300px,40vw)`, inheriting the
   sideways-scrolling body from the 1024px block above. MEASURED at 390x844 in
   headless Chromium 1148, signed in as the owner of demo-fitness, on TWO page ids
   (1690 home, 2424 'about') and in BOTH locales (en/ltr and he/rtl — identical
   numbers, only mirrored):
     .sb2-body            flex-direction row, scrollWidth 612 vs clientWidth 390
     left rail            220px, fully on screen
     .sb2-canvas          156px  (40vw won the min(); its iframe was 123px wide)
     right rail           236px wide with FOURTEEN pixels of it on screen (rect
                          x 376..612 in ltr, -222..14 in rtl), and
                          document.elementFromPoint at the centre of its own
                          "Properties" heading ("מאפיינים" in he) returned NULL —
                          the point is outside the viewport, so there is nothing
                          there to hit at all. AFTER: it returns the heading's own
                          SPAN.
   A 156px canvas showing a 123px-wide render of the page being edited is not a
   cramped editor, it is a keyhole — and 40vw guarantees it stays a keyhole at every
   phone width, because it is a FRACTION of the viewport, not a floor. Shaving the
   rails cannot fix that: 220+236 is already 117% of a 390px screen before the
   canvas gets anything.

   WHAT IT DOES INSTEAD. One column: CANVAS, then PROPERTIES, then STRUCTURE + ADD
   BLOCK, each the full width of the screen, with the BODY as the single vertical
   scroller. Every pane gets 390px instead of one pane getting 156px and one getting
   14px. Nothing is off-screen and nothing depends on a class no producer sets — the
   whole reason the pre-1024px drawers documented above were inoperable.

   WHY `order` RATHER THAN NEW MARKUP. The DOM is left rail -> canvas -> right rail
   (editor.php), and this file cannot change it. Landing on the editor and seeing the
   layers tree + a 24-tile palette with the page itself ~500px below the fold is the
   same failure in a different axis, so the canvas is ordered first. Tab/AT order is
   unaffected (it stays the DOM order, i.e. the desktop order) and all three panes
   are labelled regions, so the two sequences are each coherent; that is the trade
   this makes, stated rather than hidden.

   WHY NO NESTED SCROLLERS. On touch, a scroller inside a scroller steals the
   gesture, so the page scrolls and the panes are as tall as their content. Said
   exactly: the rails' own `overflow-y:auto` is released to `visible` below, and the
   layers tree and the palette really do end up visible/visible — but a rail keeps
   `overflow-x:hidden` from its base rule, and CSS promotes a one-axis `visible` back
   to `auto` when the other axis is not visible, so a rail still COMPUTES
   `overflow-y:auto` here (driven in Chromium at 390x844). That is harmless and it is
   not what does the work: what removes the nested scroll is that the rails are no
   longer height-constrained. Measured on the same page — left rail scrollHeight 2369
   vs clientHeight 2369, right rail 257 vs 257: neither has anything to scroll.

   The preview iframe drops to 58vh (~490px at 390x844) for one reason beyond size:
   it leaves the "Properties" heading below it ON SCREEN, so the fact that there is
   more editor under the canvas is visible rather than implied by a scrollbar.

   DIRECTION. A column stack has no inline axis, so RTL needs no mirroring; the two
   collapse-chevron rules at the end are the exception and they are written for both
   dirs at once. VERIFIED in he/rtl at 390px — same numbers as en/ltr (every pane
   x 0..390, nothing clipped, document scrollWidth == clientWidth == 390, in the
   default state and in all three collapse combinations and in preview mode).

   WHERE THIS STOPS, MEASURED RATHER THAN GUESSED. The breakpoint is 640px, so 641px
   and up keep the 1024px block's row-with-sideways-scroll — DRIVEN at 641x900: body
   scrollWidth 783 vs clientWidth 641, rails 236 + 252, canvas 295, and 110px of the
   right rail on screen with the rest reachable by scrolling. That is the earlier,
   separately-documented decision for the 641-1023px band (a tablet or a split
   window), it was not what GH #62 filed, and it is left alone rather than
   re-litigated here. The boundary itself is driven: 640px stacks, 641px does not. */
@media (max-width:640px){
  .sb2{--sb2-topbar-h:auto;--sb2-rail-w:auto;--sb2-rail-w-right:auto}
  .sb2-topbar{flex-wrap:wrap;padding-block:8px;gap:8px}
  .sb2-topbar__group--center{order:3;flex-basis:100%;justify-content:center}
  .sb2-savepill__label{display:none}

  /* ONE COLUMN, ONE SCROLLER. overflow-x:hidden cancels the 1024px block's
     sideways scroll — with the panes stacked there is nothing to the side. */
  .sb2-body{flex-direction:column;overflow-x:hidden;overflow-y:auto;
    overscroll-behavior-y:contain;-webkit-overflow-scrolling:touch}

  /* Full-width panes at their natural height. The rails' own overflow-y:auto is
     released so the body is the only scroller; their inline border becomes the
     horizontal rule between stacked panes. */
  .sb2-rail,.sb2-rail--right{width:auto;overflow-y:visible}
  .sb2-rail--left{border-inline-end:0;border-block-start:1px solid var(--line)}
  .sb2-rail--right{border-inline-start:0;border-block-start:1px solid var(--line)}

  /* The canvas is the subject of the screen: first, full width, no 40vw keyhole. */
  .sb2-canvas{order:1;min-width:0;flex:0 0 auto;overflow:visible}
  .sb2-rail--right{order:2}
  .sb2-rail--left{order:3}
  .sb2-canvas__scroll{padding:var(--s-3);overflow:visible}
  .sb2-preview,
  .sb2-stage[data-device="mobile"] .sb2-preview,
  .sb2-stage[data-device="tablet"] .sb2-preview{height:min(58vh,520px)}

  /* No scroller inside the scroller (see the note above). */
  .sb2-layers{max-height:none;overflow-y:visible}
  .sb2-rail__section--grow[data-sb-panel="palette"]{min-height:0;overflow:visible}
  .sb2-palette{min-height:0;overflow-y:visible}

  /* COLLAPSE STILL WORKS, ON THE AXIS THE PANES NOW USE. The desktop rule shrinks
     WIDTH to a 40px stub; stacked, that would leave a 40px-wide column with the
     rest of the row empty. Fold the HEIGHT to the same stub instead — the rail
     keeps its full width and its .sb2-rail__collapse handle stays inside the stub
     (top 10px + 28px tall = 38px of 40px), so the "there is always a visible way
     to bring a rail back" invariant holds here too. */
  .sb2.is-collapse-left .sb2-rail--left,
  .sb2.is-collapse-right .sb2-rail--right{
    width:auto;height:var(--sb2-rail-stub);min-height:var(--sb2-rail-stub);overflow:hidden}
  /* ...and the chevron must mean "bring it back" on the VERTICAL axis. The base
     glyphs point inline (chevron-left on the left rail, chevron-right on the right),
     and the desktop collapsed-state rules mirror them horizontally; rotate each so
     it points DOWN, into the pane that will reappear. [dir] carries the same
     specificity as those dir-scoped rules and comes later, so it wins in ltr AND
     rtl without naming either. */
  [dir] .sb2.is-collapse-left .sb2-rail--left>.sb2-rail__collapse .icon{transform:rotate(-90deg)}
  [dir] .sb2.is-collapse-right .sb2-rail--right>.sb2-rail__collapse .icon{transform:rotate(90deg)}

  /* ---- AND PREVIEW / DEMO MODE, WHICH THE STACK WOULD OTHERWISE DESTROY -------
     THE STATE THAT ALMOST GOT MISSED, so it is written down. Full-bleed preview
     hides the rails by ZEROING THEIR WIDTH (`.sb2.is-preview .sb2-rail{width:0}`
     at the top of this file) — an INLINE-AXIS trick. A column has no inline axis
     to collapse, and that rule outranks the `width:auto` above it (0,2,0 vs
     0,1,0), so stacking alone left both rails 0px WIDE and still their full
     CONTENT HEIGHT in the column. MEASURED at 390x844 on page 1690, className
     forced to `sb2 is-preview`, before this rule existed:
         .sb2-rail--left    w 0    h 2730   (invisible, still in flow)
         .sb2-rail--right   w 0    h  491
         .sb2-canvas        w 390  h  150   <- the "full-bleed preview"
         .sb2-preview       w 390  h  150
         .sb2-body          scrollHeight 3371 vs clientHeight 713
     i.e. pressing Preview on a phone put the site above 2.6 SCREENS of empty
     scroll, and shrank the canvas that is supposed to BE the preview from the whole
     body to 150px — because `.sb2.is-preview .sb2-preview{height:100%}` resolves
     against a canvas the stack had made `flex:0 0 auto` (content-sized), so the
     canvas took its height FROM the iframe instead of giving height TO it. That is
     a worse failure than the 156px canvas this whole block exists to remove, and it
     is invisible to any probe that only loads the editor in its default state.
     So: take the rails OUT OF FLOW (display:none — they are already opacity 0 and
     pointer-events none here, and out of the a11y tree is more honest for a
     full-bleed preview than an unreachable rail), and give the canvas the body's
     free space back. AFTER, same drive: rails 0x0, canvas 390x713 (the entire
     body), .sb2-body scrollHeight == clientHeight == 713 — no dead scroll.
     Re-driven in he/rtl: identical. Direction-free, so RTL needs nothing extra.

     ONE THING THIS DOES NOT FIX, AND IT IS NOT A PHONE PROBLEM. The iframe itself
     still measures 150px tall in preview mode — but SO DOES IT ON DESKTOP: driven
     at 1440x900 with the same forced className, `.sb2-preview` is 1180x150 there
     too, i.e. the iframe falls back to its own CSS default height because nothing
     between .sb2-canvas__scroll and .sb2-viewport gives `height:100%` a resolved
     containing block. That is a pre-existing bug in the full-bleed preview at EVERY
     width, it is not what GH #62 filed, and it is recorded here rather than fixed
     silently at one breakpoint — which would have left the two widths disagreeing
     about a chain neither of them resolves. */
  .sb2.is-preview .sb2-rail{display:none}
  .sb2.is-preview .sb2-canvas{flex:1 1 auto;min-height:0;overflow:hidden}
  .sb2.is-preview .sb2-canvas__scroll{overflow:hidden}
  .sb2.is-preview .sb2-body{overflow-y:hidden}
}

/* ============================================================
   LIVE-CANVAS SWAP ANIMATIONS
   Applied to block roots INSIDE the same-origin preview (which loads this same
   stylesheet). The engine adds these classes after an in-place AJAX render:
   changed/new blocks fade+rise in, leaving blocks fade out before removal, and
   surviving blocks that moved get a JS FLIP (inline transform + transition).
   ~150–220ms, var(--ease). Reduced-motion path is handled in the block below
   (the engine also skips these entirely under prefers-reduced-motion).
   ============================================================ */
.sb2-swap-in{animation:sb2SwapIn 200ms var(--ease,ease) both}
.sb2-swap-out{animation:sb2SwapOut 150ms var(--ease,ease) both}
@keyframes sb2SwapIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@keyframes sb2SwapOut{from{opacity:1}to{opacity:0;transform:translateY(-4px)}}

/* Selection outline + toolbar: a touch more presence when a block is selected,
   and clear hover/active/focus affordances on every interactive control. */
.sb2-out--select{animation:sb2SelectPop 180ms var(--ease-out,ease)}
@keyframes sb2SelectPop{from{box-shadow:0 0 0 8px rgba(30,80,200,0)}to{box-shadow:0 0 0 4px rgba(30,80,200,.12)}}
.sb2-fab{animation:sb2FabIn 160ms var(--ease-out,ease)}
@keyframes sb2FabIn{from{opacity:0;transform:translateY(6px) scale(.96)}to{opacity:1;transform:none}}
.sb2-fab__btn:focus-visible,.sb2-fmt__btn:focus-visible{outline:2px solid var(--focus-line);outline-offset:2px}
.sb2-insert__btn:hover{background:color-mix(in srgb,var(--brand-2) 82%,#fff);box-shadow:var(--shadow)}
.sb2-insert__btn:focus-visible{outline:2px solid var(--focus-line);outline-offset:2px}

/* ============================================================
   CUSTOM CONFIRM / ALERT / PROMPT DIALOG
   Reusable, glassy, focus-trapped modal that replaces native
   window.confirm()/alert()/prompt() across the builder. Lives on document.body
   (dashboard document), so it sits above the whole builder shell.
   ============================================================ */
.sb2-dialog{position:fixed;inset:0;z-index:1000;display:grid;place-items:center;padding:var(--s-4)}
.sb2-dialog[hidden]{display:none}
/* This overlay is currently orphaned (sbConfirm delegates to pmuConfirm; nothing
   renders .sb2-dialog markup). It is kept only for the reduced-motion rules below.
   Back-port the SAME pointer-events guard its promoted sibling .pmu-dialog carries so
   that IF the markup is ever reintroduced, a full-viewport layer left hidden=false but
   not .is-open (mid-open throw / detached listeners) stays inert instead of silently
   eating every click and wedging the tab. */
.sb2-dialog:not(.is-open){pointer-events:none}
.sb2-dialog.is-open{pointer-events:auto}
.sb2-dialog__backdrop{position:absolute;inset:0;background:rgba(12,18,32,.42);opacity:0;
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);transition:opacity 160ms var(--ease,ease)}
.sb2-dialog.is-open .sb2-dialog__backdrop{opacity:1}
.sb2-dialog__panel{position:relative;width:min(420px,100%);border-radius:var(--r-lg);padding:var(--s-6);
  text-align:center;background:var(--sb2-glass-strong,var(--surface));border:1px solid var(--line-strong);
  box-shadow:var(--shadow-lg,var(--shadow));-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);
  opacity:0;transform:translateY(10px) scale(.97);transition:opacity 180ms var(--ease-out,ease),transform 180ms var(--ease-out,ease)}
.sb2-dialog.is-open .sb2-dialog__panel{opacity:1;transform:none}
.sb2-dialog__icon{width:46px;height:46px;margin:0 auto var(--s-3);border-radius:var(--r-full);display:grid;place-items:center;
  color:var(--brand-fg);background:var(--brand-grad-soft,var(--tint));border:1px solid var(--line-brand)}
.sb2-dialog__icon .icon,.sb2-dialog__icon svg{width:22px;height:22px}
.sb2-dialog.is-danger .sb2-dialog__icon{color:var(--danger-fg);background:rgba(225,29,72,.1);border-color:rgba(225,29,72,.25)}
.sb2-dialog__title{font-size:var(--fz-lg);font-weight:700;color:var(--text);margin:0 0 var(--s-2)}
.sb2-dialog__msg{font-size:var(--fz-sm);color:var(--text-2);margin:0 0 var(--s-4);line-height:1.5}
.sb2-dialog__msg[hidden]{display:none}
.sb2-dialog__input{margin:0 0 var(--s-4)}
.sb2-dialog__input[hidden]{display:none}
.sb2-dialog__foot{display:flex;gap:var(--s-2);justify-content:center}
.sb2-dialog__foot .btn{min-width:112px}
.sb2-dialog__cancel[hidden]{display:none}
.sb2-dialog.is-danger .sb2-dialog__ok{--btn-bg:var(--accent-red);background:var(--accent-red);border-color:var(--accent-red)}

/* ---------------- REDUCED MOTION ---------------- */
@media (prefers-reduced-motion:reduce){
  .sb2-device{animation:none}
  .sb2-chrome__livedot{animation:none}
  .sb2 *,.sb2-pop,.sb2-out,.sb2-fab,.sb2-rail,.sb2-stage{transition-duration:.01ms!important;animation-duration:.01ms!important}
  .sb2-swap-in,.sb2-swap-out,.sb2-out--select,.sb2-fab{animation:none!important}
  .sb2-dialog__backdrop,.sb2-dialog__panel{transition-duration:.01ms!important}
  .sb2-dialog__panel{transform:none}
}

/* ============================================================
   PUBLIC PAGE RENDERER — the tenant's live site (/p/{slug}).
   BlockRenderer emits: .sb-hero .sb-richtext .sb-image
   .sb-features .sb-cta .sb-gallery, each inside .sb-site.
   ============================================================ */
.sb-site{overflow-x:hidden}
.sb-site .section{padding-block:var(--s-16)}
/* First block clears the FIXED top nav. Based on var(--nav-h) (the nav height the
   layout owns) instead of a brittle hardcoded 80px, so it tracks the nav. NOTE:
   nav-offset.css zeroes #main's own offset whenever it holds a .sb-site, so THIS is
   the public site's sole nav clearance. */
.sb-site .section:first-child{padding-block-start:calc(var(--nav-h) + var(--s-6))}
/* Robustness: when the first block is NOT a .section (e.g. a spacer <div>, so the
   :first-child rule above never matches) it must still clear the nav — otherwise the
   top of the site renders under it. Direct-child selector = mutually exclusive with
   the rule above, so the two never double-offset. */
.sb-site > :first-child:not(.section){padding-block-start:calc(var(--nav-h) + var(--s-6))}
.sb-site .section > *{max-width:var(--container);margin-inline:auto;padding-inline:var(--s-6)}

/* edit-mode-only placeholder for a media block with no source yet (image/video/
   embed). Emitted by BlockRenderer ONLY in edit mode (self::$edit), so it never
   appears on the public site — it just gives a freshly added, still-empty media
   block a visible, selectable canvas node. RTL-safe (logical props only). */
.sb-block-empty > .sb-block-empty__inner{max-width:920px;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:var(--s-3);min-height:200px;text-align:center;
  border:1px dashed var(--line-brand,var(--line));border-radius:var(--r-xl);
  background:var(--brand-grad-soft,var(--tint,var(--bg-3)));color:var(--muted);padding:var(--s-8)}
.sb-block-empty__ico{display:inline-flex}
/* A 40px glyph is a large UI element (3:1 floor). --brand-2 left it at 2.44:1 on
   the dark card; --brand-fg is 8.35:1. */
.sb-block-empty__ico svg{width:40px;height:40px;color:var(--brand-fg,var(--muted))}
.sb-block-empty__label{font-size:var(--fz-sm);font-weight:600;color:var(--text-2,var(--muted))}

/* hero block */
.sb-hero{position:relative;isolation:isolate}
.sb-hero .hero__inner{max-width:820px;margin-inline:auto;padding-inline:var(--s-6);
  display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--s-4)}
.sb-hero .page-title{max-width:16ch}
.sb-hero .hero__desc{max-width:52ch;color:var(--text-2)}
.sb-hero__bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:.16;z-index:-1;
  -webkit-mask-image:linear-gradient(#000,transparent);mask-image:linear-gradient(#000,transparent)}

/* rich text block */
.sb-richtext .sb-prose{max-width:70ch;margin-inline:auto;line-height:1.9;font-size:var(--fz-md);color:var(--text-2)}
.sb-prose p{margin-bottom:var(--s-4)}
.sb-prose p:last-child{margin-bottom:0}

/* image + gallery */
.sb-image .sb-figure{max-width:920px}
.sb-figure{margin:0}
.sb-figure img{display:block;width:100%;height:auto;border-radius:var(--r-xl);border:1px solid var(--line);
  box-shadow:var(--shadow-sm)}
.sb-figure figcaption{margin-top:var(--s-3);font-size:var(--fz-sm);text-align:center;color:var(--muted)}
.sb-gallery .grid img{aspect-ratio:4 / 3;object-fit:cover}

/* features + cta blocks reuse the card / grid library */
.sb-features .section__head{margin-bottom:var(--s-8)}
.sb-features .feature__icon{font-size:26px;line-height:1}
.sb-cta .card{max-width:760px;margin-inline:auto;background:var(--brand-grad-soft);
  border-color:var(--line-brand);padding-block:var(--s-10)}

/* ---- testimonials ---- */
.sb-quote{display:flex;flex-direction:column;gap:var(--s-4);text-align:start}
.sb-quote__text{margin:0;font-size:var(--fz-md);line-height:1.7;color:var(--text)}
.sb-quote__text::before{content:"“";font-size:2.4em;line-height:0;color:var(--brand-fg);
  vertical-align:-.35em;margin-inline-end:.08em}
.sb-quote__by{display:flex;align-items:center;gap:12px;margin-top:auto}
.sb-quote__avatar{width:44px;height:44px;border-radius:50%;object-fit:cover;flex-shrink:0;border:1px solid var(--line)}
.sb-quote__meta{display:flex;flex-direction:column;line-height:1.3}
.sb-quote__meta b{color:var(--text);font-weight:700}
.sb-quote__meta .muted{font-size:var(--fz-xs)}

/* ---- faq (accordion via .collapsible) ---- */
.sb-faq__list{max-width:760px;margin-inline:auto;display:flex;flex-direction:column;gap:var(--s-3)}
.sb-faq__item{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);overflow:hidden}
.sb-faq__q{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;
  padding:16px 20px;text-align:start;font-weight:600;color:var(--text);font-size:var(--fz-md);
  background:none;border:0;cursor:pointer;transition:color var(--dur) var(--ease)}
.sb-faq__q:hover{color:var(--brand-fg)}
.sb-faq__chev{width:12px;height:12px;flex-shrink:0;border-inline-end:2px solid currentColor;
  border-block-end:2px solid currentColor;transform:rotate(45deg);transition:transform var(--dur) var(--ease)}
.sb-faq__q[aria-expanded="true"] .sb-faq__chev{transform:rotate(-135deg)}
.sb-faq__a{padding:0 20px 18px;color:var(--text-2);line-height:1.8}

/* ---- pricing table ---- */
.sb-pricing__grid{align-items:stretch}
.sb-plan{position:relative;display:flex;flex-direction:column;gap:var(--s-3);text-align:center}
.sb-plan--featured{border-color:var(--line-brand);box-shadow:var(--shadow);
  background:var(--brand-grad-soft)}
.sb-plan__flag{position:absolute;inset-block-start:-12px;inset-inline-start:50%;transform:translateX(-50%)}
[dir="rtl"] .sb-plan__flag{transform:translateX(50%)}
.sb-plan__name{font-size:var(--fz-lg);font-weight:700;color:var(--text)}
.sb-plan__price{display:flex;align-items:baseline;justify-content:center;gap:6px}
.sb-plan__amount{font-size:var(--fz-2xl);font-weight:800;color:var(--text)}
.sb-plan__period{font-size:var(--fz-sm)}
.sb-plan__list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:8px;
  text-align:start;color:var(--text-2);font-size:var(--fz-sm)}
.sb-plan__list li{position:relative;padding-inline-start:24px}
.sb-plan__list li::before{content:"";position:absolute;inset-inline-start:0;inset-block-start:.35em;
  width:14px;height:8px;border-inline-start:2px solid var(--accent-green);border-block-end:2px solid var(--accent-green);
  transform:rotate(-45deg)}
.sb-plan__cta{margin-top:auto}

/* ---- video + embed ---- */
.sb-video__wrap,.sb-embed__wrap{max-width:920px;margin-inline:auto}
.sb-embed__frame{position:relative;width:100%;aspect-ratio:16 / 9;border-radius:var(--r-xl);overflow:hidden;
  border:1px solid var(--line);box-shadow:var(--shadow-sm);background:#000}
.sb-embed__frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}
.sb-video__player{display:block;width:100%;border-radius:var(--r-xl);overflow:hidden;border:1px solid var(--line)}
.sb-video figcaption,.sb-embed figcaption{margin-top:var(--s-3);font-size:var(--fz-sm);text-align:center;color:var(--muted)}

/* ---- contact form ---- */
.sb-contact__card{max-width:720px;margin-inline:auto}
.sb-contact__head{text-align:center}
.sb-contact__intro{text-align:center;margin-bottom:var(--s-6)}
.sb-contact__form{display:flex;flex-direction:column;gap:var(--s-4)}
.sb-contact__form .grid--2{gap:var(--s-4)}
.sb-contact__form .btn{align-self:flex-start}
/* honeypot — visually + AT hidden, still submittable by bots */
.sb-hp{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}
.sb-contact__note{padding:11px 14px;border-radius:var(--r-sm);font-size:var(--fz-sm);font-weight:600}
.sb-contact__note--ok{color:var(--success-fg);background:rgba(14,159,110,.10);border:1px solid rgba(14,159,110,.28)}
.sb-contact__note--err{color:var(--danger-fg);background:rgba(225,29,72,.10);border:1px solid rgba(225,29,72,.28)}

/* ---- cta banner ---- */
.sb-cta-banner > *{max-width:var(--container)}
.sb-banner{display:flex;align-items:center;justify-content:space-between;gap:var(--s-6);flex-wrap:wrap;
  padding:var(--s-10) var(--s-8);border-radius:var(--r-2xl);color:#fff;background:var(--brand-grad);
  box-shadow:var(--shadow)}
.sb-banner__body{flex:1;min-width:260px}
.sb-banner__title{font-size:var(--fz-2xl);font-weight:800;line-height:1.15;margin-block:6px 0}
.sb-banner__text{margin-top:10px;opacity:.92;line-height:1.6}
.badge--on-brand{background:rgba(255,255,255,.2);color:#fff;border-color:rgba(255,255,255,.35)}
.sb-banner__btn{background:#fff;color:var(--brand-fg);border-color:#fff}
.sb-banner__btn:hover{background:rgba(255,255,255,.9)}

/* ---- logos strip ---- */
.sb-logos{text-align:center}
.sb-logos__title{text-transform:uppercase;letter-spacing:1px;font-size:var(--fz-xs);font-weight:700;
  margin-bottom:var(--s-6)}
.sb-logos__strip{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--s-8)}
.sb-logos__item{display:inline-flex;align-items:center;justify-content:center}
.sb-logos__img{max-height:44px;width:auto;object-fit:contain;filter:grayscale(1);opacity:.7;
  transition:filter var(--dur) var(--ease),opacity var(--dur) var(--ease)}
.sb-logos__item:hover .sb-logos__img{filter:grayscale(0);opacity:1}

/* ---- stats / metrics row ---- */
.sb-stats__row{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:var(--s-6);
  text-align:center}
.sb-stat{display:flex;flex-direction:column;gap:4px;padding:var(--s-4)}
.sb-stat__value{font-size:var(--fz-3xl);font-weight:800;line-height:1;
  background:var(--brand-grad-fg);-webkit-background-clip:text;background-clip:text;color:transparent}
.sb-stat__label{font-weight:600;color:var(--text)}
.sb-stat__sub{font-size:var(--fz-sm)}

/* ---- two-column feature ---- */
.sb-cols__grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-10);align-items:center}
.sb-cols__media img{display:block;width:100%;height:auto;border-radius:var(--r-xl);
  border:1px solid var(--line);box-shadow:var(--shadow-sm)}
.sb-cols__body{display:flex;flex-direction:column;gap:var(--s-4);align-items:flex-start}
.sb-cols__title{max-width:18ch}
.sb-cols__text{color:var(--text-2);line-height:1.8}
.sb-cols__text p{margin-bottom:var(--s-4)}
.sb-cols__text p:last-child{margin-bottom:0}
/* image_side=left flips the visual order (source stays media-first for a11y) */
.sb-cols__grid--flip .sb-cols__media{order:-1}

/* ---- markdown / rich-text prose ---- */
.sb-md h2,.sb-md h3,.sb-md h4{color:var(--text);font-weight:700;line-height:1.25;
  margin-block:var(--s-6) var(--s-3)}
.sb-md h2{font-size:var(--fz-xl)}
.sb-md h3{font-size:var(--fz-lg)}
.sb-md h4{font-size:var(--fz-md)}
.sb-md h2:first-child,.sb-md h3:first-child,.sb-md h4:first-child{margin-block-start:0}
.sb-md ul,.sb-md ol{margin-block:var(--s-3) var(--s-4);padding-inline-start:1.4em;display:flex;
  flex-direction:column;gap:6px}
.sb-md li{line-height:1.7}
.sb-md a{color:var(--brand-fg);font-weight:600;text-decoration:underline}
.sb-md blockquote{margin-block:var(--s-4);padding-inline-start:var(--s-4);
  border-inline-start:3px solid var(--line-brand);color:var(--text);font-style:italic}
.sb-md hr{border:0;border-block-start:1px solid var(--line);margin-block:var(--s-6)}
.sb-md code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em;
  background:var(--bg-3);border:1px solid var(--line);border-radius:var(--r-xs);padding:1px 6px}

/* ---- spacer / divider ---- */
.sb-spacer{display:flex;align-items:center;justify-content:center}
.sb-spacer--sm{height:var(--s-6)}
.sb-spacer--md{height:var(--s-10)}
.sb-spacer--lg{height:var(--s-16)}
.sb-spacer--xl{height:var(--s-20)}
.sb-spacer--rule{padding-inline:var(--s-6)}
.sb-spacer__rule{display:block;width:100%;max-width:var(--container);margin-inline:auto;height:1px;
  background:linear-gradient(90deg,transparent,var(--line-strong),transparent)}

/* ---- steps / how-it-works ---- */
.sb-steps__list{max-width:760px;margin-inline:auto;list-style:none;padding:0;
  display:flex;flex-direction:column;gap:var(--s-6)}
.sb-step{display:flex;gap:var(--s-4);align-items:flex-start}
.sb-step__num{width:38px;height:38px;flex-shrink:0;border-radius:50%;display:grid;place-items:center;
  font-weight:800;color:#fff;background:var(--brand-grad);box-shadow:var(--shadow-xs);
  font-variant-numeric:tabular-nums}
.sb-step__body{padding-block-start:4px}
.sb-step__title{font-size:var(--fz-md);font-weight:700;color:var(--text);margin-bottom:4px}
.sb-step__body p{line-height:1.7}

/* responsive collapse of block grids on the public site */
@media (max-width:820px){
  .sb-site .grid--3,.sb-site .grid--2{grid-template-columns:1fr}
  .sb-site .section{padding-block:var(--s-12)}
  .sb-banner{padding:var(--s-8) var(--s-6)}
  .sb-cols__grid{grid-template-columns:1fr;gap:var(--s-6)}
  .sb-cols__grid--flip .sb-cols__media{order:0}
}

/* ---- contact submissions inbox (dashboard) ---- */
.sb-subs-toolbar{max-width:420px}
.sb-subs-contact{display:flex;flex-direction:column;gap:2px}
.sb-subs-contact a{text-decoration:none}
.sb-subs-contact a:hover{text-decoration:underline}
.sb-subs-msg{display:inline-block;max-width:52ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}
.sb-hidden{display:none !important}

/* ============================================================
   THEME editor  (kept from v1 — used by the theme mode of this module)
   ============================================================ */
.sb-theme-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-6);align-items:start}
@media (max-width:900px){.sb-theme-grid{grid-template-columns:1fr}}
.sb-color{display:flex;align-items:center;gap:12px}
.sb-color__swatch{position:relative;width:52px;height:52px;border-radius:var(--r-sm);flex-shrink:0;
  border:1px solid var(--line-strong);overflow:hidden;box-shadow:var(--shadow-xs)}
.sb-color__swatch input[type="color"]{position:absolute;inset:-6px;width:calc(100% + 12px);height:calc(100% + 12px);
  border:0;padding:0;background:none;cursor:pointer}
.sb-color__meta{flex:1;min-width:0}
.sb-color__val{font-size:var(--fz-xs);color:var(--muted);font-variant-numeric:tabular-nums;text-transform:uppercase}
.sb-range{width:100%;-webkit-appearance:none;appearance:none;height:6px;border-radius:var(--r-full);
  background:var(--bg-3);border:1px solid var(--line);cursor:pointer}
.sb-range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;border-radius:50%;
  background:var(--brand-grad);border:2px solid var(--surface);box-shadow:var(--shadow-sm);cursor:pointer}
.sb-range::-moz-range-thumb{width:18px;height:18px;border-radius:50%;background:var(--brand-2);
  border:2px solid var(--surface);box-shadow:var(--shadow-sm);cursor:pointer}
.sb-radius-val{font-weight:700;color:var(--brand-fg);font-variant-numeric:tabular-nums}
/* The preview mock owns its OWN surface/line/text/font tokens (NOT the dashboard
   theme) so the Theme + Font selects give live visual feedback before saving. The
   data-theme attr set by initTheme's applySwatch() flips the light/dark context. */
#th-swatch{--sw-primary:#1E50C8;--sw-accent:#16B8F5;--sw-radius:14px;
  --sw-font:'Rubik','Heebo',system-ui,sans-serif;
  --sw-surface:#ffffff;--sw-surface-2:#f6f8fc;--sw-line:#e6eaf3;--sw-line-strong:#cfd7e8;
  --sw-text:#0b1b3a;--sw-muted:#5a6b8c}
#th-swatch[data-theme="light"]{--sw-surface:#ffffff;--sw-surface-2:#f6f8fc;--sw-line:#e6eaf3;
  --sw-line-strong:#cfd7e8;--sw-text:#0b1b3a;--sw-muted:#5a6b8c}
#th-swatch[data-theme="dark"]{--sw-surface:#161c2e;--sw-surface-2:#1e2540;--sw-line:#2e3654;
  --sw-line-strong:#3c4668;--sw-text:#eef2fb;--sw-muted:#9fb0d0}
.sb-mock{border:1px solid var(--sw-line);border-radius:calc(var(--sw-radius) + 6px);overflow:hidden;
  background:var(--sw-surface-2);color:var(--sw-text);font-family:var(--sw-font);box-shadow:var(--shadow-sm)}
.sb-mock__hero{padding:var(--s-6);color:#fff;text-align:center;
  background:linear-gradient(135deg,var(--sw-primary),var(--sw-accent))}
.sb-mock__eyebrow{display:inline-block;font-size:11px;font-weight:700;letter-spacing:1px;text-transform:uppercase;
  padding:4px 12px;border-radius:var(--sw-radius);background:rgba(255,255,255,.18);margin-bottom:10px}
.sb-mock__title{font-size:var(--fz-xl);font-weight:800;line-height:1.15;margin-bottom:8px}
.sb-mock__sub{font-size:var(--fz-sm);opacity:.9}
.sb-mock__btn{display:inline-block;margin-top:14px;padding:9px 20px;border-radius:var(--sw-radius);
  background:#fff;color:var(--sw-primary);font-weight:700;font-size:var(--fz-sm)}
.sb-mock__body{padding:var(--s-5);display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s-3)}
.sb-mock__tile{background:var(--sw-surface);border:1px solid var(--sw-line);border-radius:var(--sw-radius);padding:var(--s-3)}
.sb-mock__tile i{display:block;width:26px;height:26px;border-radius:calc(var(--sw-radius) - 4px);margin-bottom:8px;
  background:linear-gradient(135deg,var(--sw-primary),var(--sw-accent))}
.sb-mock__tile b{display:block;height:8px;border-radius:var(--r-full);background:var(--sw-line-strong);margin-bottom:6px}
.sb-mock__tile span{display:block;height:6px;width:70%;border-radius:var(--r-full);background:var(--sw-line)}
.sb-swatch{padding:22px 18px;border-radius:var(--sw-radius);color:#fff;font-weight:700;border:1px solid var(--line)}
.sb-swatch--primary{background:var(--sw-primary)}
.sb-swatch--accent{background:var(--sw-accent)}
.sb-swatch--card{background:linear-gradient(135deg,var(--sw-primary),var(--sw-accent))}

/* ============================================================
   DOMAINS  (kept from v1)
   ============================================================ */
.sb-dns{background:var(--bg-3);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:12px 14px;display:flex;align-items:center;gap:10px}
.sb-dns code{flex:1;min-width:0;font-size:var(--fz-xs);color:var(--text);word-break:break-all;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.sb-copy{width:32px;height:32px;border-radius:var(--r-sm);display:grid;place-items:center;flex-shrink:0;
  color:var(--muted);border:1px solid var(--line);background:var(--bg-2)}
.sb-copy:hover{color:var(--brand-fg);border-color:var(--line-brand);background:var(--tint)}
.sb-copy .icon{width:15px;height:15px}
.sb-dns-guide{display:grid;gap:var(--s-3);margin-top:var(--s-4)}
.sb-dns-step{display:flex;gap:12px;align-items:flex-start}
.sb-dns-step__num{width:26px;height:26px;border-radius:var(--r-full);display:grid;place-items:center;flex-shrink:0;
  background:var(--brand-grad);color:#fff;font-weight:700;font-size:var(--fz-xs)}
.sb-dns-step__body{font-size:var(--fz-sm);color:var(--text-2)}
.sb-dns-step__body code{background:var(--bg-3);border:1px solid var(--line);border-radius:var(--r-xs);
  padding:1px 7px;font-size:var(--fz-xs);color:var(--text)}

/* ============================================================
   DYNAMIC BLOCKS — courses / workout-plans / nutrition-plans / booking
   Public + editor. Cards are filled from live tenant rows at render time;
   these styles cover the section head, the card shells and the empty state.
   ============================================================ */
.sb-dyn__head{display:flex;flex-direction:column;gap:6px;margin-bottom:var(--s-5)}
.sb-dyn__head .section__sub{margin:0;max-width:60ch}
.sb-dyn__list{display:flex;flex-direction:column;gap:var(--s-3)}

/* empty state (edit-mode placeholder) */
.sb-dyn--empty .sb-dyn__empty{border:1px dashed var(--line);border-radius:var(--r-md);
  padding:28px 20px;text-align:center;background:var(--bg-2)}
.sb-dyn__empty-note{color:var(--muted);font-size:var(--fz-sm)}

/* course cards */
.sb-course-card{display:flex;flex-direction:column;overflow:hidden;text-decoration:none;color:inherit}
.sb-course-card__media{aspect-ratio:16/9;background:var(--bg-3);overflow:hidden}
.sb-course-card__media img{width:100%;height:100%;object-fit:cover;display:block}
.sb-course-card__body{display:flex;flex-direction:column;gap:8px;padding:var(--s-3) 0 0;flex:1}
.sb-course-card__sub{margin:0;font-size:var(--fz-sm)}
.sb-course-card__meta{font-size:var(--fz-xs)}
.sb-course-card__foot{margin-top:auto;display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:var(--s-2)}
.sb-course-card__price{font-weight:800;color:var(--text)}
.sb-course-card__price--free{color:var(--brand-fg)}

/* product plan cards (workout / nutrition) */
.sb-plan-card{display:flex;flex-direction:column}
.sb-plan-card__body{display:flex;flex-direction:column;gap:10px;flex:1}
.sb-plan-card__desc{margin:0;font-size:var(--fz-sm)}
.sb-plan-card__price{display:flex;align-items:baseline;gap:6px;margin-top:auto}
.sb-plan-card__amount{font-size:var(--fz-2xl);font-weight:800;color:var(--text)}
.sb-plan-card__amount--free{color:var(--brand-fg)}
.sb-plan-card__cycle{font-size:var(--fz-sm)}
.sb-plan-card__cta{margin-top:var(--s-2)}

/* booking */
.sb-booking__types{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:var(--s-4)}
.sb-service-card{display:flex;flex-direction:column}
.sb-service-card__body{display:flex;flex-direction:column;gap:10px;flex:1}
.sb-service-card__desc{margin:0;font-size:var(--fz-sm)}
.sb-service-card__meta{font-size:var(--fz-sm);font-weight:600;color:var(--text)}
.sb-service-card__cta{margin-top:auto}
.sb-booking__foot{margin-top:var(--s-5);text-align:center}

/* ============================================================
   BLOCK STORE (browse / install / my-shared) + SHARE modal.
   Community marketplace of shared page blocks. Cards show a SAFE,
   scaled-down preview (server-escaped BlockRenderer html) plus the
   title, author ("by {brand}"), usage count and category. RTL-safe
   (logical props), light+dark via tokens.
   ============================================================ */

/* ---- Store shell + tabs ---- */
.sb-store{display:flex;flex-direction:column;gap:var(--s-5)}
.sb-store__tabs{align-self:flex-start}
.sb-store__tabs button{display:inline-flex;align-items:center;gap:8px}
.sb-store__tabs .icon{width:16px;height:16px}

/* ---- Toolbar: search + filters ---- */
.sb-store__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-3);padding:var(--s-3)}
.sb-store__search{flex:1 1 240px;min-width:0}
.sb-store__filters{display:flex;flex-wrap:wrap;gap:var(--s-2)}
.sb-store__filters .input{min-width:150px}

/* ---- Grid of cards ---- */
.sb-store__grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:var(--s-4)}
.sb-store-card{display:flex;flex-direction:column;overflow:hidden;padding:0;gap:0}
.sb-store-card__preview{position:relative;height:180px;overflow:hidden;background:var(--bg-3);
  border-bottom:1px solid var(--line);cursor:pointer}
/* The preview is real rendered block HTML; scale it down so a full-width block
   reads as a thumbnail. pointer-events:none keeps clicks on the card, not links. */
.sb-store-card__scale{position:absolute;inset:0;width:200%;transform:scale(.5);transform-origin:top left;
  pointer-events:none}
[dir="rtl"] .sb-store-card__scale{transform-origin:top right}
.sb-store-card__preview::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,transparent 60%,var(--bg-2))}
.sb-store-card__body{display:flex;flex-direction:column;gap:8px;padding:var(--s-4)}
.sb-store-card__title{margin:0;font-size:var(--fz-base);font-weight:700;color:var(--text);line-height:1.3}
.sb-store-card__meta{display:flex;flex-wrap:wrap;gap:4px 10px;font-size:var(--fz-xs);color:var(--text-2)}
.sb-store-card__author{font-weight:600}
.sb-store-card__usage{font-variant-numeric:tabular-nums}
.sb-store-card__chips{display:flex;flex-wrap:wrap;gap:6px}
.sb-store-card__chip{display:inline-flex;align-items:center;padding:2px 10px;border-radius:var(--r-full);
  font-size:var(--fz-xs);font-weight:600;background:var(--tint);color:var(--brand-fg);border:1px solid var(--line-brand)}
.sb-store-card__chip--type{background:var(--bg-3);color:var(--text-2);border-color:var(--line)}
.sb-store-card__chip--unlisted{background:var(--bg-3);color:var(--text-2);border-color:var(--line)}
.sb-store-card__chip--removed{background:var(--bg-3);color:var(--text-3,var(--text-2));border-color:var(--line)}
.sb-store-card__desc{margin:0;font-size:var(--fz-sm);line-height:1.45;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.sb-store-card__foot{display:flex;align-items:center;justify-content:flex-end;gap:8px;
  margin-top:auto;padding:var(--s-3) var(--s-4);border-top:1px solid var(--line);background:var(--bg-2)}
.sb-store-card__footusage{margin-inline-end:auto;font-size:var(--fz-xs);font-variant-numeric:tabular-nums}

/* ---- Empty / loading / pager ---- */
/* count pill on the "My shared blocks" tab + the empty state's title/how lines */
.sb-store__tabcount{display:inline-flex;align-items:center;justify-content:center;min-width:18px;
  padding:0 5px;margin-inline-start:6px;border-radius:var(--r-full);background:var(--tint-strong,var(--tint));
  color:var(--brand-fg);font-size:11px;font-weight:700;font-variant-numeric:tabular-nums}
.sb-store__emptytitle{margin:0;font-size:var(--fz-base);font-weight:700;color:var(--text)}
.sb-store__emptyhow{max-width:52ch}
.sb-store__empty{display:flex;flex-direction:column;align-items:center;gap:var(--s-2);
  padding:var(--s-8) var(--s-4);text-align:center}
.sb-store__emptyico{display:inline-flex;color:var(--text-3,var(--text-2));opacity:.6}
.sb-store__emptyico .icon{width:40px;height:40px}
.sb-store__loading{display:flex;justify-content:center;padding:var(--s-6)}
.sb-store__spinner{width:28px;height:28px;border-radius:50%;border:3px solid var(--line);
  border-top-color:var(--brand-line);animation:sb-store-spin .7s linear infinite}
@keyframes sb-store-spin{to{transform:rotate(360deg)}}
.sb-store__pager{display:flex;align-items:center;justify-content:center;gap:var(--s-4);padding-top:var(--s-2)}
.sb-store__pager .icon{width:16px;height:16px}
.sb-store__pageinfo{font-variant-numeric:tabular-nums;font-size:var(--fz-sm)}
.sb-store__minehint{margin-bottom:var(--s-2)}
@media (prefers-reduced-motion:reduce){.sb-store__spinner{animation-duration:1.6s}}

/* ---- Store preview + install modals ---- */
.sb-store-modal__panel{max-width:720px;width:min(720px,94vw)}
.sb-store-modal__head{margin-bottom:var(--s-4)}
.sb-store-modal__meta{margin:6px 0 0;font-size:var(--fz-sm)}
.sb-store-modal__preview{border:1px solid var(--line);border-radius:var(--r-lg,12px);overflow:hidden;
  background:var(--bg-2);max-height:52vh;overflow-y:auto}
.sb-store-modal__foot{display:flex;justify-content:flex-end;gap:var(--s-2);margin-top:var(--s-5)}
.sb-store-install__panel{max-width:480px;width:min(480px,94vw)}
.sb-store-install__body{display:flex;flex-direction:column;gap:var(--s-3)}

/* ---- Editor: SHARE modal (from the block toolbar) ---- */
.sb-share-modal__panel{max-width:520px;width:min(520px,94vw)}
.sb-share-modal__head{margin-bottom:var(--s-4)}
.sb-share-modal__head .modal__title{margin:0 0 6px}
.sb-share-modal__type{margin:10px 0 0;font-size:var(--fz-sm)}
.sb-share-modal__body{display:flex;flex-direction:column;gap:var(--s-3)}
.sb-share-modal__foot{display:flex;justify-content:flex-end;gap:var(--s-2);margin-top:var(--s-5)}
.sb-share-vis{display:flex;flex-direction:column;gap:var(--s-2)}
.sb-share-vis__opt{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border:1px solid var(--line);
  border-radius:var(--r-lg,12px);cursor:pointer;transition:border-color var(--dur) var(--ease),background var(--dur) var(--ease)}
.sb-share-vis__opt:hover{border-color:var(--line-brand);background:var(--tint)}
.sb-share-vis__opt input{margin-top:3px}
.sb-share-vis__txt{display:flex;flex-direction:column;gap:2px}
.sb-share-vis__name{font-weight:600;color:var(--text);font-size:var(--fz-sm)}
.sb-share-vis__hint{font-size:var(--fz-xs)}

/* Toolbar share button — subtle accent so it reads as a "publish" affordance. */
.sb2-fab__btn[data-fab="share"]:hover{color:var(--brand-fg)}

/* ============================ WhatsApp quick-chat ============================
   The optional green "Chat on WhatsApp" button on the configurable form block
   (public render) + its editor config fields. Brand green, own hover, works in
   light/dark and RTL. */
.sb-form__whatsapp{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  margin-block-start:12px;padding:11px 20px;border-radius:var(--r-full,999px);
  background:#25D366;color:#0b3d1e;font-weight:600;border:1px solid #1eb457;
  text-decoration:none;line-height:1;transition:background .15s ease,transform .15s ease,box-shadow .15s ease;
}
.sb-form__whatsapp:hover{background:#1ebe5d;color:#062a14;box-shadow:0 6px 18px rgba(37,211,102,.35)}
.sb-form__whatsapp:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(37,211,102,.45)}
.sb-form__whatsapp svg{flex:0 0 20px}
/* editor: WhatsApp settings block indent + reveal */
.sb-form-wa{margin-block-start:6px}
.sb-form-wa-fields{margin-block-start:10px;padding-inline-start:12px;border-inline-start:2px solid var(--line)}
.sb-form-wa-fields[hidden]{display:none}

/* ============================ Domains (setup + live tester) ============================ */
.sb-domains{display:flex;flex-direction:column;gap:14px}
.sb-domain{padding:18px 20px}
.sb-domain__top{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
.sb-domain__id{display:flex;align-items:center;gap:12px;min-width:0}
.sb-domain__ic{display:grid;place-items:center;width:40px;height:40px;flex:0 0 40px;border-radius:var(--r-md);background:var(--tint);color:var(--brand-fg)}
.sb-domain__name{display:block;font-size:var(--fz-md);word-break:break-all}
.sb-domain__status{margin-top:4px}
.sb-domain__acts{display:flex;align-items:center;gap:8px}
/* live tester result rows */
.sb-domain__test{margin-top:14px;padding:12px 14px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2,var(--bg-3));display:flex;flex-direction:column;gap:8px}
.sb-domain__check{display:flex;align-items:center;gap:10px;font-size:var(--fz-sm);color:var(--text-2)}
.sb-domain__dot{flex:0 0 10px;width:10px;height:10px;border-radius:50%;background:var(--line-strong,var(--line));transition:background .2s ease,box-shadow .2s ease}
.sb-domain__check[data-state="pending"] .sb-domain__dot{background:var(--accent-amber);animation:sbPulse 1s ease-in-out infinite}
.sb-domain__check[data-state="ok"] .sb-domain__dot{background:var(--accent-green);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent-green) 20%,transparent)}
.sb-domain__check[data-state="ok"]{color:var(--text)}
.sb-domain__check[data-state="fail"] .sb-domain__dot{background:var(--accent-red)}
.sb-domain__checked{margin:2px 0 0;font-size:var(--fz-xs)}
@keyframes sbPulse{0%,100%{opacity:1}50%{opacity:.35}}
/* setup accordion */
.sb-domain__setup{margin-top:14px;border-top:1px solid var(--line);padding-top:12px}
.sb-domain__setup>summary{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:600;color:var(--text);list-style:none;font-size:var(--fz-sm)}
.sb-domain__setup>summary::-webkit-details-marker{display:none}
.sb-domain__chev{margin-inline-start:auto;transition:transform .2s ease}
.sb-domain__setup[open] .sb-domain__chev{transform:rotate(180deg)}
/* Animated BOTH ways via motion.css section (4b) — ::details-content is the
   0fr<->1fr grid and this body is the ROW ITEM, so its block padding must
   collapse with the row (a 0fr row zeroes the content box only). */
.sb-domain__setupbody{padding-block-start:12px;
  transition:padding-block-start var(--dur-slow) var(--ease)}
.sb-domain__setup:not([open]) .sb-domain__setupbody{padding-block-start:0}
/* Inside the skipped subtree there is no previous computed style on open, so this
   padding would jump 12px in one frame before the height eases. @starting-style
   supplies the missing "before" value. */
@starting-style{
  .sb-domain__setup[open] .sb-domain__setupbody{padding-block-start:0}
}
@media (prefers-reduced-motion:reduce){
  .sb-domain__setupbody{transition:none}
}
.sb-domain__setuplead{font-size:var(--fz-sm);margin:0 0 12px}
/* provider tabs */
.sb-prov{margin-bottom:14px}
.sb-prov__seg{flex-wrap:wrap;max-width:100%}
.sb-prov__hint{margin:10px 2px 0;font-size:var(--fz-sm);color:var(--text-2);line-height:1.6}
/* DNS record rows */
.sb-rec{display:flex;flex-direction:column;gap:10px}
.sb-rec__row{border:1px solid var(--line);border-radius:var(--r-md);padding:12px 14px;background:var(--surface)}
.sb-rec__tag{display:inline-block;font-size:11px;font-weight:700;letter-spacing:.04em;padding:3px 8px;border-radius:var(--r-sm);background:var(--tint);color:var(--brand-fg);margin-bottom:8px}
.sb-rec__kv{display:flex;flex-wrap:wrap;gap:10px 20px}
.sb-rec__field{display:flex;align-items:center;gap:8px;min-width:0}
.sb-rec__k{font-size:var(--fz-xs);color:var(--text-3);text-transform:uppercase;letter-spacing:.03em}
.sb-rec__v{font-family:var(--font-mono,monospace);font-size:var(--fz-sm);background:var(--bg-3);padding:3px 8px;border-radius:var(--r-sm);color:var(--text);word-break:break-all;max-width:100%}
.sb-rec__note{margin:8px 0 0;font-size:var(--fz-xs)}
.sb-copy{display:inline-grid;place-items:center;width:28px;height:28px;flex:0 0 28px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);color:var(--text-2);cursor:pointer;transition:color .15s,border-color .15s}
.sb-copy:hover{color:var(--brand-fg);border-color:var(--brand-line)}
@media (max-width:560px){ .sb-domain__top{flex-direction:column;align-items:flex-start} .sb-domain__acts{width:100%} }
