:root {
	--msb-bg: #fafafa;
	--msb-fg: #111;
	--msb-muted: #6b6b6b;
	--msb-border: #e2e2e2;
	--msb-accent: #111;
	--msb-radius: 14px;
	/* Platform-wide theme (Settings > Branding & Login > Platform
	   Appearance) -- these get overridden per-request by an inline
	   :root block from MSB_Settings::platform_theme_css(); the
	   fallbacks below match the plugin's original built-in look, and
	   are what's used until an admin customizes them. Scoped to this
	   stylesheet only, which is never loaded on a customer's own
	   built site -- see that class's docblock for why. */
	--msb-font-heading: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
	--msb-font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
	--msb-btn-radius: 10px;
	--msb-btn-pad-y: 11px;
	--msb-btn-pad-x: 16px;
	--msb-page-width-wide: 1160px;
	--msb-header-width: none;
}

* { box-sizing: border-box; }

body.msb-frontend {
	margin: 0;
	font-family: var(--msb-font-body);
	background: var(--msb-bg);
	color: var(--msb-fg);
	-webkit-font-smoothing: antialiased;
}
body.msb-frontend h1, body.msb-frontend h2, body.msb-frontend h3 { font-family: var(--msb-font-heading); }

.msb-topbar { position: sticky; top: 0; z-index: 20; background: #fff; border-bottom: 1px solid var(--msb-border); }
.msb-topbar-inner {
	position: relative;
	display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
	padding: 14px 16px; width: 100%; max-width: var(--msb-header-width, none); margin: 0 auto; box-sizing: border-box;
}
.msb-topbar h1 { font-size: 17px; margin: 0; }
.msb-topbar-logo { display: flex; align-items: center; text-decoration: none; }
.msb-topbar-textlogo { font-size: 17px; font-weight: 700; color: var(--msb-fg); }
.msb-topbar-links { display: flex; align-items: center; gap: 16px; }
.msb-topbar-links a { color: var(--msb-muted); text-decoration: none; font-size: 14px; }
.msb-topbar-links a.msb-nav-active { color: var(--msb-fg); font-weight: 600; }
.msb-topbar-user { color: var(--msb-fg); font-size: 13px; font-weight: 600; }
.msb-topbar-cta { background: var(--msb-accent); color: #fff !important; padding: 6px 12px; border-radius: 8px; }
/* v7.14.0 (Screenshot_9): each desktop nav item is now an icon + label
   pair, so the item itself has to be a flex box with its own internal
   gap -- the 16px below is the space BETWEEN items, and the 7px inside
   .msb-nav-common a is the space between an item's icon and its text.
   Without the padding, 16px of tracking measured from the icon edge
   reads visually tighter than 16px between two plain words did. */
.msb-nav-common { display: flex; align-items: center; gap: 16px; }
/* v7.17.0 (Screenshot 18): 7px between an item's icon and its own label
   read as too loose once every desktop nav item became an icon+label
   pair -- this wrapper is already desktop-only (see msb-nav-desktop-only
   above), so tightening it here doesn't touch the mobile menu at all. */
.msb-nav-common a { display: inline-flex; align-items: center; gap: 4px; padding: 6px 2px; border-radius: 6px; white-space: nowrap; transition: color .15s ease; }
.msb-nav-common a .msb-user-dropdown-icon { margin-right: 0; opacity: .6; flex: 0 0 auto; }
.msb-nav-common a:hover { color: var(--msb-fg); }
.msb-nav-common a:hover .msb-user-dropdown-icon { opacity: .9; }
.msb-nav-common a.msb-nav-active .msb-user-dropdown-icon { opacity: 1; }

.msb-mobile-menu-trigger { background: none; border: none; cursor: pointer; padding: 6px; color: var(--msb-fg); display: none; }
.msb-mobile-menu-panel {
	display: none; position: absolute; right: 16px; top: calc(100% + 4px);
	background: #fff; border: 1px solid var(--msb-border); border-radius: 10px;
	box-shadow: 0 8px 24px rgba(0,0,0,.12); min-width: 200px; overflow: hidden; z-index: 40;
}
.msb-mobile-menu-panel.msb-mobile-menu-open { display: block; }
.msb-mobile-menu-panel a {
	display: block; padding: 12px 16px; font-size: 14px; text-decoration: none;
	color: var(--msb-fg); border-bottom: 1px solid var(--msb-border);
}
.msb-mobile-menu-panel a:last-child { border-bottom: none; }
.msb-mobile-menu-panel a:hover { background: var(--msb-bg-alt, #f7f4f2); }

@media (max-width: 780px) {
	.msb-nav-desktop-only { display: none !important; }
	.msb-mobile-menu-trigger { display: inline-flex; align-items: center; justify-content: center; }
	/* Only the avatar link and the hamburger button are visible in
	   .msb-topbar-links at this width -- set the gap between them
	   precisely to 10px, and drop the avatar's own extra right-margin so
	   it doesn't stack on top of that gap. Both stay right-aligned
	   exactly as before; only the spacing between the two changes. */
	.msb-topbar-links { gap: 10px; }
	.msb-mobile-avatar { margin-right: 0; }
}
@media (min-width: 781px) {
	.msb-nav-mobile-only { display: none !important; }
}

.msb-user-menu { position: relative; }
.msb-user-trigger {
	background: none; border: none; font-size: 14px; font-weight: 600; color: var(--msb-fg);
	cursor: pointer; display: flex; align-items: center; gap: 8px; padding: 4px 0;
}
.msb-user-trigger-name { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msb-user-caret { font-size: 10px; opacity: .6; }
.msb-avatar-img { border-radius: 50%; object-fit: cover; display: block; flex-shrink: 0; }
.msb-avatar-fallback {
	display: inline-flex; align-items: center; justify-content: center; border-radius: 50%;
	background: var(--msb-fg); color: #fff; font-weight: 700; flex-shrink: 0;
}
.msb-mobile-avatar { display: inline-flex; align-items: center; margin-right: 4px; }
.msb-user-dropdown {
	display: none; position: absolute; right: 0; top: calc(100% + 8px);
	background: #fff; border: 1px solid var(--msb-border); border-radius: 10px;
	box-shadow: 0 8px 24px rgba(0,0,0,.12); min-width: 210px; overflow: hidden; z-index: 40;
}
.msb-user-menu-open .msb-user-dropdown { display: block; }
.msb-user-dropdown a {
	display: block; padding: 10px 14px; font-size: 14px; text-decoration: none;
	color: var(--msb-fg); border-bottom: 1px solid var(--msb-border);
}
.msb-user-dropdown a:last-child { border-bottom: none; }
.msb-user-dropdown a:hover { background: var(--msb-bg-alt, #f7f4f2); }
.msb-user-dropdown-icon { display: inline-block; vertical-align: -2px; margin-right: 8px; opacity: .75; }
.msb-user-plan-badge {
	display: flex; align-items: center; justify-content: space-between; gap: 8px;
	padding: 10px 14px; border-bottom: 1px solid var(--msb-border); background: var(--msb-bg-alt, #f7f4f2);
}
/* v7.9.6 (Screenshot_10): nowrap + a wider dropdown (170px -> 210px)
   so "Free Plan" never breaks onto two lines just because the Upgrade
   pill on the same row was squeezing it. */
.msb-user-plan-label { font-size: 13px; font-weight: 600; color: var(--msb-fg); white-space: nowrap; }
/* v7.9.4 (Screenshot_6): the label + crown are wrapped in their own
   link straight to My Profile's Plan tab -- qualified with the parent
   class (not just "a") so it doesn't inherit .msb-user-dropdown a's
   block/border-bottom/hover-background styling meant for the full-width
   menu rows below it; this is an inline label sitting inside the badge
   row, not one of those rows itself. */
.msb-user-plan-badge a.msb-user-plan-link {
	display: inline-flex; align-items: center; gap: 5px; border-bottom: none;
	padding: 0; color: var(--msb-fg); text-decoration: none;
}
.msb-user-plan-badge a.msb-user-plan-link:hover { text-decoration: underline; background: none; }
.msb-user-plan-crown { display: inline-block; flex-shrink: 0; }
/* v7.9.1 fix: .msb-user-dropdown a's own color rule above (higher
   specificity: one class + tag vs. this single class) was winning over
   this pill's white text, making it read as black-on-black until
   :hover's lighter background accidentally revealed it. Qualifying the
   selector with .msb-user-dropdown a.msb-user-plan-upgrade matches that
   specificity so white always wins, in both states. */
.msb-user-dropdown a.msb-user-plan-upgrade {
	font-size: 12px; font-weight: 700; color: #fff !important; background: var(--msb-primary, #141414);
	padding: 4px 10px; border-radius: 999px; text-decoration: none; flex-shrink: 0; border-bottom: none; display: inline-block;
}
.msb-user-dropdown a.msb-user-plan-upgrade:hover { opacity: .85; background: var(--msb-primary, #141414); color: #fff !important; }

.msb-container { padding: 16px; max-width: 640px; margin: 0 auto; padding-bottom: 96px; }
.msb-container-wide { max-width: var(--msb-page-width-wide); }

/* v7.56.6-v7.57.0: Manage Site page (#msb-manage-app) -- Leads + Domain
   tabs, reached via the Manage icon on each My Sites card. */
.msb-manage-back-link { display: inline-block; margin-bottom: 12px; font-size: 13px; font-weight: 600; color: inherit; text-decoration: none; opacity: .7; }
.msb-manage-back-link:hover { opacity: 1; }
.msb-manage-title { font-size: 22px; font-weight: 700; margin: 0 0 18px; }
.msb-manage-tabs { display: flex; gap: 22px; border-bottom: 1px solid var(--msb-border); margin-bottom: 24px; }
.msb-manage-tab-btn { background: none; border: none; padding: 10px 2px; font-size: 14px; font-weight: 600; color: #888; cursor: pointer; border-bottom: 2px solid transparent; }
.msb-manage-tab-btn-active { color: #141414; border-bottom-color: #141414; }
.msb-manage-tab-body { max-width: 680px; }

/* v7.57.0: Leads tab toolbar + list -- was inline-styled (see
   Screenshot_23), rebuilt as real classes with consistent spacing so
   the filter/search/export row and each submission card have proper
   breathing room instead of everything jammed edge-to-edge. */
.msb-leads-toolbar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 18px; }
.msb-leads-toolbar select,
.msb-leads-toolbar input[type="search"] { padding: 9px 10px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 13px; background: #fff; }
/* v7.59.18 (Screenshot_58, direct request): the native dropdown arrow
   was sitting flush against the box's own right edge/border with no
   breathing room -- extra right padding just for the select (the
   search input doesn't have this arrow, so it keeps the plain 10px). */
.msb-leads-toolbar select { padding-right: 28px; }
.msb-leads-toolbar select { flex: 1; min-width: 140px; }
.msb-leads-toolbar input[type="search"] { flex: 2; min-width: 180px; }
.msb-leads-toolbar .msb-btn { flex-shrink: 0; }
.msb-lead-card { border: 1px solid var(--msb-border); border-radius: 12px; padding: 16px; margin-bottom: 12px; }
.msb-lead-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.msb-lead-card-name { font-size: 15px; font-weight: 700; }
.msb-lead-card-meta { font-size: 12px; color: #888; margin-top: 3px; }
.msb-lead-source-badge { font-weight: 600; }
.msb-lead-details { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--msb-border); font-size: 13px; line-height: 1.7; }
.msb-lead-field strong { font-weight: 600; margin-right: 4px; }
.msb-leads-footer { display: flex; justify-content: space-between; align-items: center; margin-top: 14px; font-size: 12px; color: #888; }
.msb-leads-footer-actions { display: flex; gap: 8px; }

/* v7.57.0: Domain tab's own sub-navigation (My Domain / Search & Buy /
   Manage DNS) -- a smaller, secondary pill row nested under the
   Leads/Domain top-level tabs above, per direct request for a clean
   dedicated place for domain search, purchase and DNS management. */
.msb-domain-subtabs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 20px; }
.msb-domain-subtab-btn { background: #f4f2ef; border: 1px solid transparent; border-radius: 999px; padding: 7px 14px; font-size: 12.5px; font-weight: 600; color: #666; cursor: pointer; }
.msb-domain-subtab-btn-active { background: #141414; color: #fff; }
.msb-domain-subtab-btn:disabled { opacity: .45; cursor: not-allowed; }

.msb-domain-section-title { font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: .02em; color: #888; margin: 0 0 14px; }
.msb-domain-field-row { margin-bottom: 14px; }
.msb-domain-field-row label { display: block; font-size: 12.5px; font-weight: 600; margin-bottom: 6px; }
.msb-domain-field-row input,
.msb-domain-field-row select { width: 100%; padding: 9px 10px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 14px; box-sizing: border-box; }
.msb-domain-field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
@media (max-width: 480px) { .msb-domain-field-grid { grid-template-columns: 1fr; } }

.msb-domain-search-bar { display: flex; gap: 8px; margin-bottom: 18px; }
.msb-domain-search-bar input { flex: 1; padding: 10px 12px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 14px; }
.msb-domain-search-results { display: flex; flex-direction: column; gap: 10px; }
.msb-domain-search-row {
	display: flex; justify-content: space-between; align-items: center; gap: 12px;
	padding: 14px 16px; border: 1px solid var(--msb-border); border-radius: 10px; flex-wrap: wrap;
}
.msb-domain-search-row-name { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 14px; }
/* v7.59.3 (live-reported, Screenshot_17/18): name.com's own reason a
   domain isn't available (e.g. "Domain not available", registry-
   specific restrictions) surfaced right under the Taken pill, instead
   of a bare status with no explanation -- see resultRowHtml()'s own
   comment for why this matters (an "Environment: Test/OTE Sandbox"
   mismatch is the single most common cause of this looking wrong). */
.msb-domain-search-row-error { flex-basis: 100%; font-size: 12px; color: #a35a00; }
.msb-domain-search-row-right { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.msb-domain-search-price { font-weight: 700; font-size: 14px; }
.msb-domain-suggestions-title { font-size: 12.5px; font-weight: 700; color: #888; margin: 22px 0 10px; text-transform: uppercase; letter-spacing: .02em; }

.msb-domain-purchase-status { border: 1px solid var(--msb-border); border-radius: 10px; padding: 14px 16px; margin-bottom: 18px; }
.msb-domain-purchase-status-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }

.msb-dns-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.msb-dns-table th { text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .02em; color: #888; padding: 0 8px 8px 0; }
.msb-dns-table td { padding: 10px 8px 10px 0; border-top: 1px solid var(--msb-border); vertical-align: middle; }
.msb-dns-table code { background: #f4f2ef; padding: 2px 6px; border-radius: 4px; }
.msb-dns-add-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; align-items: flex-end; }
.msb-dns-add-row > div { display: flex; flex-direction: column; gap: 4px; }
.msb-dns-add-row label { font-size: 11.5px; font-weight: 600; color: #888; }
.msb-dns-add-row input, .msb-dns-add-row select { padding: 8px 9px; border: 1px solid var(--msb-border); border-radius: 7px; font-size: 13px; }

/* v7.59.0: My Profile > Domains -- reuses most Domain-tab classes
   above as-is; this just styles the inline site-picker <select> used
   in the "assign an unassigned purchase to a site" row. */
.msb-profile-assign-site-select { padding: 8px 10px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 13px; }


/* Screenshot_28: 60px top/bottom on desktop (40px/40px on mobile) for
   every page that shares the platform topbar (Dashboard, My Sites,
   Browse Templates, Pricing, Home, My Profile) -- scoped off the
   editor's own body class since the editor's .msb-container still
   needs its 96px bottom padding above for the floating save bar, not
   this spacing. */
body:not(.msb-editor-page) .msb-container { padding-top: 60px; padding-bottom: 60px; }
@media (max-width: 780px) {
	body:not(.msb-editor-page) .msb-container { padding-top: 40px; padding-bottom: 40px; }
	.msb-home-hero-boxed { padding: 40px 16px; }
}

/* ---- Home hero (logged-out and logged-in variants) ---- */
/* v7.17.3 (Screenshot_31): "header + hero fills exactly 100vh" is done
   with flexbox instead of a hardcoded calc(100vh - Npx) -- the topbar's
   real rendered height changes with content/branding/breakpoint, and a
   flex column always fills whatever's left over exactly, with no magic
   number to keep in sync. Scoped to body.msb-home-page only (set by
   render_home()'s extra body class) so no other page's layout changes. */
body.msb-home-page { min-height: 100vh; display: flex; flex-direction: column; }
body.msb-home-page .msb-topbar { flex: 0 0 auto; }
.msb-home-hero {
	position: relative; width: 100%; flex: 1 0 auto;
	display: flex; background-color: var(--msb-bg);
	background-size: cover; background-position: center center; background-repeat: no-repeat;
}
/* v7.17.3: no dark overlay by request -- the background image shows at
   its own natural brightness/colors, fully transparent on top. Pick a
   background image with enough contrast for the (still white, on a
   has-bg hero) text to stay readable. */
.msb-home-hero-boxed {
	/* Mirrors the topbar's own width setting (Settings > Branding &
	   Login > Header Width) so the hero's content column always lines
	   up with the header above it. */
	width: 100%; max-width: var(--msb-header-width, none); margin: 0 auto; padding: 60px 16px;
	flex: 1; display: flex; align-items: center; justify-content: center;
}
.msb-home-hero-inner {
	/* max-width percentage set inline per-request from Settings >
	   Homepage > Content Width (%). */
	width: 100%; margin: 0 auto; text-align: center;
}
.msb-home-hero-title {
	font-size: 30px; line-height: 1.2; font-weight: 800; margin: 0 0 14px; letter-spacing: -.01em;
}
.msb-home-hero-subtitle {
	font-size: 16px; line-height: 1.5; font-weight: 600; color: var(--msb-fg); margin: 0 0 12px;
}
.msb-home-hero-description {
	font-size: 15px; line-height: 1.6; color: var(--msb-muted); margin: 0 auto 28px; max-width: 520px;
}
.msb-home-hero-has-bg .msb-home-hero-title,
.msb-home-hero-has-bg .msb-home-hero-subtitle { color: #fff; }
.msb-home-hero-has-bg .msb-home-hero-description { color: rgba(255,255,255,.85); }
.msb-home-hero-cta {
	font-size: 15px; padding: 14px 30px; border-radius: var(--msb-btn-radius);
}
@media (min-width: 781px) {
	.msb-home-hero-title { font-size: 46px; }
	.msb-home-hero-subtitle { font-size: 19px; }
	.msb-home-hero-description { font-size: 16.5px; }
}


/* v7.59.15 (direct request, correction to v7.59.14): v7.59.14 made
   the category filter always inline/visible at every width -- turns
   out that wasn't wanted after all; the mobile slide-in popup is
   restored below. What v7.59.14 DID get right and is kept as-is: the
   plain radio-style label (small circle + text, no bordered pill)
   with its smaller/lighter font, and desktop's own always-visible
   horizontal scrolling row (.msb-filter-bar-row, see below) is
   untouched. The actual list of categories lives in
   .msb-filter-bar-row now, a separate element from .msb-filter-bar
   itself, so the popup's own Close button (a sibling of that row, see
   initGallery()/initDashboard()) never gets dragged into the row's
   own horizontal-on-desktop/vertical-on-mobile scrolling. */
.msb-filter-bar-row { display: flex; gap: 18px; }
/* Plain radio-style label -- no border/pill background, just a small
   circular indicator before the text (empty ring = not selected,
   filled accent dot = selected), smaller and lighter than the old
   bordered-pill look. Shared by both the desktop row and the mobile
   popup list below. */
.msb-filter-btn {
	display: inline-flex; align-items: center; gap: 7px; flex-shrink: 0; white-space: nowrap;
	background: none; border: none; border-radius: 0;
	padding: 6px 2px; font-size: 13px; font-weight: 400; color: var(--msb-muted); cursor: pointer;
}
.msb-filter-btn::before {
	content: ''; display: block; box-sizing: border-box; flex-shrink: 0;
	width: 13px; height: 13px; border-radius: 50%; border: 1.5px solid var(--msb-border);
	transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}
.msb-filter-btn-active {
	background: none; border-color: transparent; color: var(--msb-fg, #141414);
}
.msb-filter-btn-active::before {
	border-color: var(--msb-accent); background: var(--msb-accent);
	box-shadow: inset 0 0 0 3px #fff; /* leaves a white ring around the filled center -- the classic radio "dot" look. */
}
/* v7.59.18 (Screenshots 54/55, direct request): "Get a New Domain"
   empty-state CTA on the My Domains tab -- sizes the plus-circle icon
   (ICON_ADD_SECTION) and gives it a small gap from the label text. */
.msb-profile-domains-cta { display: inline-flex; align-items: center; gap: 6px; margin-top: 4px; }
.msb-profile-domains-cta svg { width: 16px; height: 16px; flex-shrink: 0; }
.msb-filter-close, .msb-filter-backdrop { display: none; }
button.msb-filter-toggle { display: none; }
/* v7.59.16 (Screenshot_49, direct request): the funnel icon added in
   v7.59.15 had no sizing of its own -- an inline <svg> with neither a
   CSS width/height nor an explicit width/height attribute doesn't
   reliably size itself to a sane small icon size across browsers, so
   it was rendering effectively invisible/degenerate next to the
   "Filters" label instead of showing as a small glyph. Sized
   explicitly here, matching the small-icon treatment used elsewhere
   in this file (e.g. .msb-scratch-icon). */
button.msb-filter-toggle svg { width: 14px; height: 14px; flex-shrink: 0; }
/* v7.59.17 (Screenshot_51, direct request): small breathing room
   between the funnel icon and the "Filters" text (were flush against
   each other with no gap), plus a bigger/bolder label than the
   generic .msb-btn base (14px/600) this button would otherwise
   inherit unchanged. */
button.msb-filter-toggle { gap: 2px; font-size: 16px; font-weight: 600; }
/* v7.59.17 (Screenshot_51, direct request): locks background page
   scroll while the mobile filter popup is open (see openFilters()/
   openDashFilters() in frontend.js, which toggle this on
   document.body) -- without it, a finger-drag on the page behind the
   popup could still scroll the page underneath it. */
.msb-body-scroll-locked { overflow: hidden; }
.msb-filter-toggle-row { display: flex; align-items: center; }
/* v7.59.12 (Screenshots 46/47, direct request), restored in v7.59.15:
   mobile-only "Reset Filter" label next to the Filters button --
   relevant again now that the mobile popup (restored below) hides the
   active category once closed, same reasoning it originally shipped
   for. Desktop shows its selected item in the always-visible row at
   all times, so it stays hidden there. */
.msb-filter-reset { display: none; }
@media (min-width: 640px) {
	/* Plain horizontal row of labels, no popup -- scrolls sideways
	   instead of wrapping if enough categories are added. */
	.msb-filter-bar-row {
		flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px;
		-webkit-overflow-scrolling: touch; cursor: grab; user-select: none;
		/* v7.59.16 (Screenshot_50, direct request): the browser's own
		   default scrollbar for this row rendered full track width
		   with the OS's normal (often quite chunky, arrow-button-
		   carrying) scrollbar style -- replaced with a slim, minimal
		   bar consistent with every other horizontally-scrolling row
		   elsewhere in this file. Firefox via scrollbar-width, every
		   Chromium/WebKit browser via the ::-webkit-scrollbar rules
		   just below. */
		scrollbar-width: thin;
	}
	.msb-filter-bar-row::-webkit-scrollbar { height: 5px; }
	.msb-filter-bar-row::-webkit-scrollbar-track { background: transparent; }
	.msb-filter-bar-row::-webkit-scrollbar-thumb { background: var(--msb-border); border-radius: 999px; }
	.msb-filter-bar-row::-webkit-scrollbar-thumb:hover { background: var(--msb-muted); }
	.msb-filter-bar-row:active { cursor: grabbing; }
	.msb-filter-bar { margin-bottom: 18px; }
}

@media (max-width: 639px) {
	.msb-filter-toggle-row { margin-bottom: 14px; gap: 10px; }
	button.msb-filter-toggle { display: inline-flex; margin-bottom: 0; }
	.msb-filter-reset.msb-filter-reset-visible {
		display: inline-flex; align-items: center; gap: 4px;
		background: none; border: none; padding: 0; margin: 0;
		font-size: 12.5px; font-weight: 600; color: var(--msb-accent);
		text-decoration: underline; cursor: pointer;
	}
	.msb-filter-reset svg { width: 11px; height: 11px; flex-shrink: 0; }
	/* v7.59.15 (direct request): restores the mobile slide-in popup
	   removed in v7.59.14 -- a real popup (dark backdrop, its own
	   Close button, slides in from the left) rather than an
	   always-visible inline row. The one real change from how this
	   looked before v7.59.14: the category list inside it is a single
	   scrollable column (.msb-filter-bar-row, flex-direction:column)
	   sized to however many categories actually exist -- rather than
	   whatever the old flat structure was doing that read as "wired
	   extra rows" -- with the Close button pinned above it as its own
	   separate element, never part of that scrolling list. */
	.msb-filter-bar {
		position: fixed; top: 0; left: 0; bottom: 0; width: 78vw; max-width: 300px;
		background: #fff; display: flex; flex-direction: column;
		padding: 24px 16px; margin-bottom: 0; overflow-y: auto; z-index: 120;
		transform: translateX(-100%); transition: transform .25s ease;
		box-shadow: 4px 0 24px rgba(0,0,0,.15);
	}
	.msb-filter-bar.msb-filter-bar-open { transform: translateX(0); }
	.msb-filter-bar-row { flex-direction: column; align-items: stretch; gap: 10px; margin-top: 4px; }
	.msb-filter-btn { width: 100%; text-align: left; padding: 8px 2px; }
	.msb-filter-close { display: block; font-weight: 700; margin-bottom: 6px; text-align: left; background: none; border: none; font-size: 15px; cursor: pointer; }
	.msb-filter-backdrop {
		display: none; position: fixed; inset: 0; background: rgba(0,0,0,.4); z-index: 110;
	}
	.msb-filter-backdrop.msb-filter-bar-open { display: block; }
}

/* ---- Gallery ---- */
/* v7.50.0 BUGFIX: confirmed real, live-reported issues -- (1) "Build
   with AI" was missing entirely from the Dashboard's own Browse
   Templates section (only Start from Scratch appeared there -- see
   initDashboard()'s own new msb-start-ai-build card), and (2) on
   both the Gallery page and the Dashboard, these two cards stacked
   one below the other even on a wide desktop screen, wasting most of
   the available width. .msb-scratch-row lays its two (or more) direct
   .msb-scratch-card children out side by side once there's genuinely
   room for it, collapsing back to the original full-width stack below
   that -- same breakpoint the cards' own inline-subtext behavior
   already switches at, so the two changes always agree with each
   other.
*/
.msb-scratch-row { display: flex; flex-direction: column; margin-bottom: 18px; }
@media (min-width: 700px) {
	.msb-scratch-row { flex-direction: row; gap: 14px; align-items: stretch; margin-bottom: 18px; }
	.msb-scratch-row .msb-scratch-card { flex: 1; margin-bottom: 0; }
}
.msb-scratch-card {
	display: flex; align-items: center; gap: 14px; width: 100%; box-sizing: border-box;
	background: #fff; border: 2px dashed var(--msb-border); border-radius: 12px;
	padding: 18px 20px; margin-bottom: 18px; cursor: pointer; text-align: left;
	transition: border-color .15s ease, background .15s ease;
	position: relative;
}
.msb-scratch-card:hover { border-color: var(--msb-accent); background: #fafafa; }
/* v7.47.0: the AI Build card sits first, with a solid (not dashed)
   accent border and a soft tinted background -- visually signals "the
   fast, guided path" next to Start from Scratch's plain, blank-canvas
   framing immediately after it, without introducing a whole new visual
   language for one card. */
.msb-scratch-card-ai {
	border-style: solid; border-color: var(--msb-accent);
	background: linear-gradient(135deg, rgba(79,70,229,.06), rgba(219,39,119,.06));
	/* v7.59.0 (Screenshot_34): this card is an <a>, which carries the
	   browser's default underline on its text content -- "Build with
	   AI" and its description rendered underlined even though nothing
	   else on this card (or its Start from Scratch sibling, a <div>)
	   is styled that way. Strip it here rather than on .msb-scratch-
	   title/-sub individually so any future text added inside this
	   specific card is never underlined either. */
	text-decoration: none;
}
.msb-scratch-card-ai:hover { background: linear-gradient(135deg, rgba(79,70,229,.1), rgba(219,39,119,.1)); }
.msb-scratch-card-ai .msb-scratch-icon { color: #7C3AED; }
.msb-scratch-icon { flex-shrink: 0; width: 34px; height: 34px; color: var(--msb-accent); }
.msb-scratch-icon svg { width: 100%; height: 100%; }
.msb-scratch-title { display: block; font-weight: 700; font-size: 15px; }
.msb-scratch-sub { display: block; font-size: 12px; color: var(--msb-muted); margin-top: 2px; }
@media (min-width: 480px) {
	.msb-scratch-card { flex-wrap: wrap; }
	.msb-scratch-title, .msb-scratch-sub { display: inline; }
	.msb-scratch-sub { margin-left: 6px; }
}

/* Mobile only: the sub-text becomes an info icon + tooltip instead
   (not enough room next to the title on a phone); desktop keeps the
   plain inline text exactly as before. */
.msb-scratch-info-btn { display: none; }
.msb-scratch-tooltip { display: none; }
@media (max-width: 780px) {
	.msb-scratch-sub { display: none; }
	.msb-scratch-info-btn {
		display: inline-flex; align-items: center; justify-content: center;
		margin-left: auto; flex-shrink: 0; background: none; border: none;
		cursor: pointer; color: var(--msb-muted); padding: 4px;
	}
	.msb-scratch-info-btn svg { width: 18px; height: 18px; display: block; }
	.msb-scratch-info-btn:hover, .msb-scratch-info-btn:focus { color: var(--msb-fg); }
	.msb-scratch-tooltip.msb-scratch-tooltip-open {
		display: block; position: absolute; right: 16px; left: 16px; top: calc(100% + 6px); z-index: 30;
		background: #1c1c1e; color: #fff; font-size: 12.5px; line-height: 1.5; padding: 10px 12px;
		border-radius: 8px; box-shadow: 0 8px 20px rgba(0,0,0,.2);
	}
}

.msb-template-grid {
	display: grid; grid-template-columns: 1fr; gap: 14px;
}
@media (min-width: 640px) {
	.msb-template-grid { grid-template-columns: 1fr 1fr; }
}
@media (min-width: 980px) {
	.msb-template-grid { grid-template-columns: repeat(3, 1fr); }
}
/* v7.58.0 (direct request): clean/minimal search bar above the
   template grid (Browse Templates + Dashboard's copy of it), plus the
   invisible scroll-trigger element renderTemplateGridBatched() uses
   for its load-on-scroll (batch size in frontend.js, v7.75.5: 3) --
   purely a layout hook, never actually seen. */
/* v7.58.1/v7.58.2 (direct request, Screenshot_44/45): sticky search
   bar for the template grid, full width of the PAGE's own boxed
   container (.msb-container-wide) -- NOT the browser viewport (an
   earlier pass used the 100vw-breakout trick, which visibly extended
   past the page's normal content edges; corrected to just fill its
   parent's width like every other element on the page). `top` is set
   at runtime (see setTemplateSearchStickyOffset() in frontend.js) to
   the platform header's own actual rendered height, since that varies
   slightly by breakpoint/logo size -- a hardcoded pixel value would
   drift out of sync on some installs. */
.msb-template-search-bar {
	position: sticky; top: 64px; z-index: 15;
	/* v7.59.0 (Screenshot_1): this used to paint a full-width solid
	   white bar behind the search pill -- visually a second, unwanted
	   "card" floating above the template grid instead of just the
	   search input itself. Made transparent (still sticky, so it
	   still stays put below the header while scrolling) and the
	   vertical padding trimmed to match, so only the pill-shaped
	   input itself is visible, same as every other bare-input control
	   on this page. */
	background: transparent; padding: 4px 0; margin-bottom: 16px;
}
.msb-template-search-bar input[type="search"] {
	width: 100%; max-width: none; padding: 10px 14px; font-size: 14px;
	border: 1px solid var(--msb-border); border-radius: 999px; background: #fff;
}
.msb-template-search-bar input[type="search"]:focus { outline: none; border-color: var(--msb-accent, #ff6a2b); }
.msb-template-grid-sentinel { grid-column: 1 / -1; height: 1px; }
.msb-template-card {
	background: #fff; border: 1px solid var(--msb-border); border-radius: var(--msb-radius);
	overflow: hidden; display: flex; flex-direction: column;
}
.msb-template-card .thumb {
	/* v7.59.0 (Screenshot_1): was 4/3 (a 0.75 height/width ratio), but
	   scaleThumb() (frontend.js) always scales the preview iframe from
	   a fixed 1440x900 virtual canvas -- a 0.625 ratio. Those two
	   numbers disagreeing left a visible strip of this box's own #eee
	   background exposed below the scaled-down page content on every
	   card. Matching this box's ratio to the SAME 1440:900 (8:5) the
	   iframe is actually scaled to closes that gap exactly, instead of
	   the iframe merely getting close to filling an unrelated 4:3 box.
	*/
	aspect-ratio: 8/5; background: #eee; position: relative; overflow: hidden;
}
.msb-template-card .thumb iframe {
	position: absolute; top: 0; left: 0; border: 0; pointer-events: none; display: block;
}
.msb-template-card .body { padding: 16px 14px 14px; }
.msb-template-card h3 { margin: 0; font-size: 15px; }
.msb-template-card .cat { color: var(--msb-muted); font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; margin-bottom: 2px; }

/* ---- v7.69.0 (direct follow-up feedback): the plan-lock pill moved
   OFF the thumbnail entirely -- it now sits at the right end of the
   SAME row as the template's own category/name text, so the preview
   underneath is 100% unobstructed rather than merely unblurred. ---- */
.msb-template-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.msb-template-card-titles { min-width: 0; }

/* ---- v7.67.0: shared "this needs a plan you don't have" indicator --
   a small lock icon (platform's own branded primary color, see
   --msb-accent, admin-configurable in Settings > Branding) plus a
   tier-name label, used on both the Templates gallery
   (templateCardHtml(), frontend.js -- planLockPillHtml(), a solid
   pill) and a Section Block's "Choose a Layout" panel
   (openLayoutVariationSheet(), frontend.js -- planLockLinkHtml(), an
   underlined link matching "Apply Layout"'s own style) -- see either
   function's own doc comment in frontend.js for the full history:
   started as a full-thumbnail overlay (v7.67.0), dropped its blur/
   white-wash for being too heavy-handed (v7.68.0), then moved off the
   thumbnail entirely onto the card's own title/toprow (v7.69.0). ---- */
.msb-plan-lock-pill {
	display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
	background: #f5f5f5; color: #1d1d1f; font-size: 11px; font-weight: 700; letter-spacing: .02em;
	padding: 4px 12px 4px 4px; border-radius: 999px;
}
.msb-plan-lock-pill-icon {
	width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0;
	background: var(--msb-accent, #111); color: #fff;
	display: flex; align-items: center; justify-content: center;
}
.msb-plan-lock-pill-icon svg { width: 11px; height: 11px; }
.msb-template-card .card-actions, .msb-card-actions { display: flex; gap: 8px; margin-top: 10px; }
.msb-card-actions .msb-btn { flex: 1; }
/* v7.69.0 (direct feedback: "make the Preview & Use this Template
   text to regular from bold, bold text for this not looking right")
   -- .msb-btn's own base font-weight is 600 (bold enough to matter
   here); Templates' own gallery card buttons alone are dialed back to
   a regular weight, without touching every OTHER button elsewhere
   that still wants the bolder default (Publish, Save, etc). */
.msb-card-actions .msb-btn { font-weight: 400; }

/* ---- Buttons ---- */
.msb-btn {
	display: inline-flex; align-items: center; justify-content: center;
	padding: var(--msb-btn-pad-y) var(--msb-btn-pad-x); border-radius: var(--msb-btn-radius); border: 1px solid var(--msb-border);
	background: #fff; color: var(--msb-fg); font-size: 14px; font-weight: 600;
	cursor: pointer; text-decoration: none;
}
.msb-btn-primary { background: var(--msb-accent); color: #fff; border-color: var(--msb-accent); }
/* v7.71.0 (direct request: "change the background of this Upgrade to
   use button to black so there is a differentiation for Paid
   Templates... it's only for Upgrade to use button not for Use this
   template"): deliberately NOT --msb-accent (that's the platform's
   own branded color, already used elsewhere on this exact card for
   the lock icon/pill and for "Use this template") -- black is its own
   distinct, unmistakable signal that a card needs an upgrade, never
   confusable with the platform's ordinary primary-action color. */
.msb-btn-upgrade { background: #111; color: #fff; border-color: #111; }
.msb-btn-upgrade:hover { filter: brightness(1.3); border-color: #111; }
.msb-btn-block { width: 100%; }
.msb-btn-sm { padding: 6px 12px; font-size: 13px; }

/* ---- My Sites ---- */
.msb-site-row {
	display: flex; align-items: center; justify-content: space-between;
	background: #fff; border: 1px solid var(--msb-border); border-radius: var(--msb-radius);
	padding: 14px; margin-bottom: 10px;
}
.msb-toolbar { margin: 0 0 16px; display: flex; gap: 8px; }
.msb-site-grid { display: grid; grid-template-columns: 1fr; gap: 18px; }
@media (min-width: 640px) { .msb-site-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 980px) { .msb-site-grid { grid-template-columns: repeat(3, 1fr); } }

/* ---- v7.19.15: Recently Deleted (customer's own trash + restore) ----
   Sits below the live grid, visually quieter than it on purpose: this
   is a recovery area, not a second set of sites. Muted background, no
   thumbnails, a single button per row. */
.msb-deleted-section { margin-top: 40px; padding-top: 24px; border-top: 1px solid var(--msb-border); }
.msb-deleted-head h2 { font-size: 17px; margin: 0 0 4px; }
.msb-deleted-head p { font-size: 13px; color: var(--msb-muted); margin: 0 0 16px; max-width: 620px; }
.msb-deleted-blocked {
	font-size: 13px; color: #8a5a00; background: #fff6e5; border: 1px solid #f2d9a8;
	border-radius: 8px; padding: 10px 12px; margin: 0 0 16px;
}
.msb-deleted-blocked a { color: #8a5a00; font-weight: 600; }
/* v7.19.16: shown when the trash list itself could not be fetched --
   distinct from the "at your limit" notice above, which is a normal
   state rather than a fault. */
.msb-deleted-error { font-size: 13px; color: var(--msb-muted); margin: 0 0 8px; max-width: 640px; line-height: 1.55; }
.msb-deleted-error code { background: #ececec; border-radius: 4px; padding: 1px 5px; font-size: 12px; }
.msb-deleted-grid { display: grid; grid-template-columns: 1fr; gap: 10px; }
@media (min-width: 640px) { .msb-deleted-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 980px) { .msb-deleted-grid { grid-template-columns: repeat(3, 1fr); } }
.msb-deleted-card {
	display: flex; align-items: center; justify-content: space-between; gap: 12px;
	background: var(--msb-bg-alt, #f7f7f6); border: 1px solid var(--msb-border);
	border-radius: var(--msb-radius); padding: 14px 16px;
}
.msb-deleted-card-main { min-width: 0; }
.msb-deleted-card-slug { font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msb-deleted-card-meta { font-size: 12.5px; color: var(--msb-muted); margin-top: 2px; }
/* Seven days or fewer left -- the one state where waiting actually costs something. */
.msb-deleted-card-meta-urgent { color: #a12b2b; font-weight: 600; }
.msb-deleted-card .msb-btn[disabled] { opacity: .5; cursor: not-allowed; }
.msb-site-card {
	background: #fff; border: 1px solid var(--msb-border); border-radius: var(--msb-radius);
	overflow: hidden; margin-bottom: 0; position: relative;
}
.msb-site-thumb {
	display: block; position: relative; width: 100%; height: 190px; overflow: hidden;
	background: var(--msb-bg-alt, #f0eeec); border-bottom: 1px solid var(--msb-border);
}
/* v7.49.0 BUGFIX: confirmed real, live-reported regression from the
   v7.48.0 attempt at this same card (Screenshot_91) -- switching to
   scaleAllThumbsDynamic() scales the ENTIRE page (however many
   sections it has) down to fit this card's own short box, which for
   any real multi-section site shrinks the scale factor so far that
   the Header+Hero at the top render as a tiny, illegible sliver
   rather than a clean preview -- "stretched too much so it's not
   even in visibility," exactly as reported. Reverted to
   scaleAllThumbs() (a fixed 1440x900 virtual "window" onto the page,
   scaled by WIDTH only -- 900 as of v7.50.7, see scaleThumb()'s own
   doc comment in assets/js/frontend.js for why): this card is
   explicitly meant to show "the
   desktop version of Header and Hero at the top, as it is" -- a
   cropped snapshot of the top of the page, not a shrink-to-fit of the
   whole thing. The 190px height above (with overflow:hidden) is what
   actually performs that crop; scaleThumb() only ever controls the
   scale factor, same as it always did before v7.48.0's change.
*/
.msb-site-thumb iframe {
	position: absolute; top: 0; left: 0; border: 0; pointer-events: none; display: block;
}
.msb-site-card-head, .msb-site-card-actions { padding-left: 14px; padding-right: 14px; }
.msb-site-card-head { display: flex; align-items: center; justify-content: space-between; margin: 12px 0 10px; gap: 8px; }
/* v7.9.9 (Screenshot_30): moved off the thumbnail overlay and back into
   the head row, right after the slug, at regular weight (was 700) --
   colors (orange/green, matching the Plans page's Current Plan badge
   and savings pill respectively) are unchanged from v7.9.8. */
.msb-site-card-head-left { display: flex; align-items: center; gap: 8px; min-width: 0; overflow: hidden; }
.msb-site-card-head-left .slug { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msb-domain-status-pill {
	display: inline-block; font-size: 11px; font-weight: 400; padding: 3px 10px;
	border-radius: 999px; letter-spacing: .01em; flex-shrink: 0;
}
/* v7.10.19 (Screenshot_13): "Custom Domain Enabled" (a capability, no
   domain connected yet) is now black/white instead of the same green
   as an actually-connected domain, so the two states are impossible
   to mistake for each other at a glance. */
.msb-domain-status-enabled { background: #000; color: #fff; }
.msb-domain-status-platform { background: #f97316; color: #fff; }
/* An actually verified, connected domain -- darker green than the old
   shared color, with white text for contrast (was black text). */
.msb-domain-status-connected { background: #1a7a3a; color: #fff; text-decoration: none; }
.msb-domain-status-connected:hover { background: #156130; color: #fff; text-decoration: underline; }
/* v7.45.0 (Screenshot_9): View Live now sits right after Edit Site (its
   own left-hand group) instead of trailing after Leads, and Leads keeps
   the row's far-right slot on its own via space-between -- so the two
   "work on this site" actions stay together and the one "see who
   contacted you" action stays visually separate from them. */
.msb-site-card-actions { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; padding-bottom: 14px; }
.msb-site-card-actions-left { display: flex; align-items: center; gap: 8px; min-width: 0; }
/* v7.45.2 (Screenshot_19): View Live and Leads drop the oval outline
   entirely -- no border, no background fill, just icon + label sitting
   directly on the card, so they read as plain text-level actions
   rather than competing with Edit Site's actual button chrome. Hover
   gets a quiet color shift instead of a filled pill so there's still
   feedback without reintroducing a shape around them. */
.msb-site-card-actions .msb-icon-btn-label {
	width: auto; height: auto; padding: 4px 2px; gap: 3px; margin-left: 0;
	border: none; background: transparent; border-radius: 6px;
}
.msb-icon-btn-label-text { font-size: 12.5px; font-weight: 500; white-space: nowrap; }
/* v7.45.1: neutral hover for the labeled site-card actions -- the
   plain .msb-icon-btn:hover rule further down (Manage Sections) turns
   red on hover, which is right for a destructive action but wrong for
   View Live/Leads; scoped the same way as the sizing fix above so it
   wins regardless of file order.
   v7.45.2: no border/background left to restyle now that the oval is
   gone -- just a color shift on the icon+text together. */
.msb-site-card-actions .msb-icon-btn-label:hover { color: var(--msb-accent); border-color: transparent; background: transparent; }
/* Edit Site: icon + label on the existing .msb-btn-sm pill, trimmed a
   touch tighter than the default small-button padding so it sits
   close to the text/icon rather than looking oversized next to the
   now-borderless View Live/Leads beside it. */
.msb-btn-icon-label { gap: 5px; padding: 5px 10px; font-weight: 400; }
.msb-btn-icon-label svg { width: 14px; height: 14px; flex-shrink: 0; }
.msb-icon-btn {
	display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px;
	border-radius: 10px; border: 1px solid var(--msb-border); background: #fff; color: var(--msb-muted);
	cursor: pointer; text-decoration: none; margin-left: auto; flex-shrink: 0;
}
.msb-icon-btn svg { width: 16px; height: 16px; }
.msb-icon-btn:hover { background: var(--msb-bg-alt, #f7f4f2); color: var(--msb-fg); }
.msb-icon-btn + .msb-icon-btn { margin-left: 6px; }
.msb-icon-btn-danger { color: #c0392b; border-color: #f0d0cc; }
.msb-icon-btn-danger:hover { background: #fdecea; border-color: #e8a29a; color: #a3271d; }
/* v7.40.0 (Screenshot_36): Delete moved off the actions row and onto
   the card's own top-right corner, overlaying the thumbnail -- solid
   white background since it sits over a live iframe preview of
   unpredictable colors underneath it. */
.msb-site-delete-corner {
	position: absolute; top: 8px; right: 8px; z-index: 3; margin-left: 0;
	background: #fff; box-shadow: 0 1px 4px rgba(0,0,0,.15);
}
.msb-site-row .slug { font-weight: 600; }
.msb-status-pill {
	font-size: 11px; padding: 2px 9px; border-radius: 10px; text-transform: capitalize;
	background: #eee;
}
.msb-status-live { background: #d1f2df; color: #0a6b3d; }
.msb-status-draft { background: #fff2cc; color: #8a6d00; }
/* v7.59.19 (direct request): Publish/Draft/Private status pill --
   a distinct color from both Live (green) and Draft (yellow) so a
   password-gated site reads as its own third state at a glance. */
.msb-status-private { background: #e3d9fb; color: #4b2e9c; }
.msb-status-suspended { background: #fbdcdc; color: #9c1c1c; }
/* v7.59.19 (direct request): Publish split-button + its Publish/Draft/
   Private dropdown -- matches the reference image's own "main button +
   small caret" shape, restyled to this plugin's existing button
   language (msb-btn-primary) rather than that reference's dark theme. */
.msb-publish-split { position: relative; display: inline-flex; flex: 1 1 auto; }
.msb-publish-split .msb-publish-btn-grow { border-radius: 8px 0 0 8px; }
/* v7.59.25 (Screenshots 73/74, direct request): the v7.59.23 permanent
   flat-grey override for Draft/Private is removed -- it fought the
   existing dirty/clean mechanism (msb-btn-dirty/msb-btn-clean,
   updatePublishBtnLook() in frontend.js) that Public's own button
   already used, forcing Draft/Private to always look the same grey
   regardless of whether there was actually anything unsaved to push.
   All three states now share that one mechanism uniformly -- see
   frontend.js's own comment on updatePublishBtnLook() for exactly how
   "dirty" is now decided per state.
   flex:1 1 auto added to the split wrapper itself (not just the inner
   .msb-publish-btn-grow) -- the wrapper is the actual flex ITEM inside
   whichever bar it sits in, so growth had to be declared here for the
   button to actually claim the row's free space, rather than the
   inner button growing only relative to its own sibling (the caret)
   inside a wrapper that itself stayed at its intrinsic width. */
.msb-publish-caret {
	padding: 0 8px; border-radius: 0 8px 8px 0; border-left: 1px solid rgba(255,255,255,.35);
	flex-shrink: 0;
}
.msb-publish-caret svg { width: 14px; height: 14px; display: block; }
.msb-publish-menu {
	display: none; position: fixed; z-index: 40;
	background: #fff; border: 1px solid var(--msb-border); border-radius: 10px;
	box-shadow: 0 8px 24px rgba(0,0,0,.15); padding: 6px; min-width: 150px;
}
/* v7.59.21 (Screenshot_65, direct request): confirmed live on the
   DESKTOP panel too, not just mobile (v7.59.20's own fix) -- that
   button also sits at the very bottom of its own panel, so opening
   downward pushed the menu below the visible viewport there as well.
   Both menus now share one identical approach: position:fixed (so
   neither can ever be clipped by an ancestor's overflow, whatever
   that ancestor happens to be) with the exact position computed in
   JS from each caret's own bounding rect at the moment it opens,
   always placed ABOVE its trigger -- see the caret's shared onclick
   handler in frontend.js, which no longer branches by which bar this
   is at all. */
.msb-publish-menu.msb-publish-menu-open { display: block; }
.msb-publish-menu-item {
	display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
	background: none; border: none; padding: 8px 10px; border-radius: 6px;
	font-size: 13.5px; font-weight: 500; color: var(--msb-fg); cursor: pointer;
}
.msb-publish-menu-item:hover { background: #f4f4f5; }
.msb-publish-menu-item svg { width: 15px; height: 15px; flex-shrink: 0; color: var(--msb-muted); }
/* v7.59.22 (Screenshot_66, direct request): highlights whichever of
   Publish/Draft/Private matches the site's actual current status --
   the three options otherwise looked completely identical with no
   way to tell which one was already active before opening the menu. */
.msb-publish-menu-item-active { background: #f4f4f5; font-weight: 600; }
.msb-publish-menu-item-check { margin-left: auto; display: flex; color: var(--msb-accent); }
.msb-publish-menu-item-check svg { width: 14px; height: 14px; color: var(--msb-accent); }
/* v7.59.20 (Screenshot_62, direct request): clean field layout for
   the Access Control tab -- consistent label spacing + a proper
   show/hide toggle sitting inside the password input itself, instead
   of the plain stacked <label><br><input> and bare, unstyled inputs
   the tab first shipped with.
   v7.59.23 (direct request): compacted -- this form's own
   .msb-settings-form flex gap (10px) PLUS every child's own margin
   (a 14px-top section title, 14px-bottom fields, 18px hint) was
   compounding into enough total height that Save Access Settings
   needed a scroll to reach. Tightened every one of those individually
   rather than fighting the base .msb-settings-form gap globally
   (which other, unrelated tabs still rely on as-is), and the days
   field now sits on ONE row with its own label instead of stacked on
   two, since a 3-character number input never needed a full-width
   line to itself.
   v7.59.26 (Screenshot_72, direct request): compacted further -- the
   form's own 6px flex gap was STACKING on top of each field's own
   margin-bottom (8px), so the actual visible gap between a field and
   the next line was their sum (14px), not just the smaller of the
   two. Cut each field's own margin-bottom down to 2px so the flex
   gap does most of the work and the two no longer double up. */
.msb-access-control-form { gap: 6px; }
.msb-access-control-form .msb-section-title { margin: 8px 0 2px; }
.msb-access-control-form .msb-access-field { margin-bottom: 2px; }
.msb-access-control-form .msb-access-field label {
	display: block; font-size: 13px; font-weight: 600; color: var(--msb-fg); margin-bottom: 5px;
}
.msb-access-control-form .msb-access-field-row {
	display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 2px;
}
.msb-access-control-form .msb-access-field-row label { font-size: 13px; font-weight: 600; color: var(--msb-fg); }
.msb-access-password-wrap { position: relative; }
.msb-access-password-wrap input {
	width: 100%; box-sizing: border-box; padding: 10px 40px 10px 12px;
	border: 1px solid var(--msb-border); border-radius: 8px; font-size: 14px;
}
.msb-access-password-toggle {
	position: absolute; top: 50%; right: 4px; transform: translateY(-50%);
	width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
	background: none; border: none; border-radius: 6px; cursor: pointer; color: var(--msb-muted); padding: 0;
}
.msb-access-password-toggle:hover { background: #f4f4f5; color: var(--msb-fg); }
.msb-access-password-toggle svg { width: 18px; height: 18px; }
.msb-access-days-input {
	width: 80px; flex-shrink: 0; box-sizing: border-box; padding: 8px 10px;
	border: 1px solid var(--msb-border); border-radius: 8px; font-size: 14px; text-align: center;
}
.msb-access-control-form .msb-access-subtext { margin: 4px 0 0; }
.msb-access-control-form .msb-settings-divider { margin: 6px 0; }
/* v7.59.26 (Screenshot_76, direct request): the status note ("This
   site is currently Public/Private...") was inheriting the same 12px
   as every other small hint in this tab -- bumped a touch so it reads
   comfortably across two lines instead of being cramped small text. */
.msb-access-control-form .msb-access-status-note { font-size: 13px; line-height: 1.5; }
/* Overrides the generic ".msb-sheet select" rule below (width:100%,
   bigger padding) which has higher specificity than a single class and
   was silently winning over every size tweak here -- that's the actual
   reason this dropdown kept overflowing no matter how its own padding
   was adjusted. Two classes beats one class + one element, so this
   wins reliably regardless of where in the sheet it's rendered. */
.msb-sheet .msb-page-status-select {
	width: auto; font-size: 12px; padding: 12px 6px; border-radius: 6px; border: 1px solid var(--msb-border);
	background: #fff; color: #1a1a1a; flex-shrink: 0; cursor: pointer; box-sizing: border-box;
}

/* ---- Editor: full-width live preview is the entire interface now --
   there's no Content panel at all. Every edit happens by tapping
   directly on a section in the preview, which opens a popup covering
   everything in that section. Settings (fonts/colors/backgrounds/
   width/widgets) is reached via a small floating button, on every
   screen size. ---- */
.msb-editor-full { max-width: 1300px; margin: 0 auto; padding: 20px 16px 100px; }
.msb-preview-wrap iframe { height: 80vh; }

@media (max-width: 899px) {
	.msb-preview-wrap iframe { height: calc(100vh - 220px); }
}

/* ---- Editor screen locked to 100vh (Screenshot_19): the whole page never
   scrolls -- only the preview area does, internally. Topbar and the fixed
   bottom bar keep their natural heights; everything between them is one
   flex column that fills exactly the space left over, so there is never
   a page-scroll gap below the bottom bar or an over-tall iframe pushing
   the page past one screen. Scoped to the editor only. ---- */
body.msb-editor-page {
	height: 100vh; height: 100dvh; overflow: hidden;
	display: flex; flex-direction: column;
}
body.msb-editor-page .msb-topbar { flex: 0 0 auto; }
body.msb-editor-page #msb-editor-app { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
body.msb-editor-page .msb-editor-full {
	/* width:100% is required here -- without it, a flex item with
	   auto left/right margins (used below to center it under the old
	   max-width cap) shrinks to its CONTENT's intrinsic width instead
	   of stretching to fill the row, which is what made the live
	   preview render as a narrow ~300px column with huge empty gutters
	   on either side, on both mobile and desktop. The max-width cap
	   itself is also dropped per request -- the preview now fills the
	   entire available area on every screen size instead of capping
	   at 1300px on wide desktops. */
	flex: 1 1 auto; min-height: 0; width: 100%; display: flex; flex-direction: column;
	padding: 5px; overflow: hidden;
}
@media (min-width: 900px) {
	body.msb-editor-page .msb-editor-full { padding: 10px; }
}
body.msb-editor-page .msb-preview-wrap {
	flex: 1 1 auto; min-height: 0; overflow-y: auto;
	-webkit-overflow-scrolling: touch; margin-bottom: 0;
}
body.msb-editor-page .msb-preview-wrap iframe { height: 100%; min-height: 100%; }
body.msb-editor-page .msb-bottom-bar { position: relative; flex: 0 0 auto; }

/* ---- Page switcher dropdown, centered in the bottom bar (only shown once a site has more than one page) ---- */
.msb-page-switch-wrap {
	position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
	z-index: 5; /* Doesn't take part in the bar's own flex layout -- the bar itself is always a positioned element (fixed by default, relative in the 100vh editor layout) -- so this sits dead-center regardless of how wide the icon groups on either side are. */
}
.msb-page-switch-btn {
	display: inline-flex; align-items: center; gap: 8px;
	padding: 6px 10px; border: 1px solid var(--msb-border); border-radius: 8px;
	background: var(--msb-bg-alt, #f7f4f2); font-size: 13px; font-weight: 600; cursor: pointer; color: inherit;
}
.msb-page-switch-btn svg { width: 16px; height: 16px; flex-shrink: 0; opacity: .6; }
.msb-page-switch-home { font-weight: 400; color: var(--msb-muted); }
.msb-page-switch-menu {
	position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%); z-index: 30;
	min-width: 220px; max-height: 320px; overflow-y: auto;
	background: #fff; border: 1px solid var(--msb-border); border-radius: 10px;
	box-shadow: 0 8px 24px rgba(0,0,0,.12); padding: 6px;
}
.msb-page-switch-item {
	display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
	padding: 8px 10px; border: none; background: none; border-radius: 8px; cursor: pointer;
	font-size: 13px; color: inherit; text-align: left;
}
.msb-page-switch-item:hover { background: var(--msb-bg-alt, #f7f4f2); }
/* On a narrow/mobile screen there's no room to sit inline between the two
   icon groups, so it floats just above the bar instead, still centered. */
@media (max-width: 600px) {
	.msb-page-switch-wrap {
		position: fixed; left: 50%; top: auto; bottom: calc(85px + env(safe-area-inset-bottom, 0px));
		transform: translateX(-50%);
	}
}

/* ---- v7.40.0 (Screenshot_39): reverted back to floating -- the
   v7.39.0 full-width strip this replaced ate a whole extra row of
   vertical space that used to belong to the live preview. This row
   is now zero-height (position:relative + height:0) and the pill
   itself is absolutely positioned to float 10px above wherever
   .msb-bottom-bar (the very next element in the DOM) actually
   starts -- so it takes no layout space of its own at all, exactly
   like the original floating pill did, while still landing
   precisely above the icon row regardless of how tall that row is
   at a given breakpoint (no guessed pixel offset to keep in sync
   with the icon-size media queries below). */
.msb-bar-page-switch-row { display: none; }
body.msb-editor-page .msb-bar-page-switch-row {
	display: block; position: relative; height: 0; flex: 0 0 auto; z-index: 25;
}
@media (min-width: 1280px) {
	body.msb-editor-page .msb-bar-page-switch-row { display: none; }
}
body.msb-editor-page .msb-bar-page-switch-row .msb-page-switch-wrap {
	position: absolute; bottom: 10px; left: 50%; top: auto; transform: translateX(-50%);
	width: auto; max-width: calc(100vw - 32px);
}
body.msb-editor-page .msb-bar-page-switch-row .msb-page-switch-btn { width: auto; }
body.msb-editor-page .msb-bar-page-switch-row .msb-page-switch-menu {
	position: absolute; bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%);
	width: max-content; min-width: 220px; max-width: calc(100vw - 32px);
}

.msb-field-row .preview { display: flex; align-items: center; gap: 6px; }
.msb-edit-icon { font-size: 13px; opacity: 0.6; }

.msb-settings-form { display: flex; flex-direction: column; gap: 10px; }
.msb-settings-form label { font-size: 13px; font-weight: 600; display: block; }
.msb-settings-form input[type="text"], .msb-settings-form input[type="email"], .msb-settings-form select {
	width: 100%; padding: 9px 10px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 14px; margin-top: 4px;
}
.msb-settings-hint { font-size: 12px; color: var(--msb-muted); margin: -4px 0 0; line-height: 1.5; }
.msb-settings-form .msb-section-title { margin: 14px 0 4px; }
.msb-page-name-url-row { display: flex; gap: 10px; }
/* v7.9.8 (Screenshot_28/29): Domain tab clean-up. Own gap + its own
   hint spacing (not the -4px "pull up under a label" rule above, which
   was written for label+input pairs, not hint+button pairs) so notes
   and controls don't crowd each other. */
.msb-settings-divider { border: none; border-top: 1px solid var(--msb-border); margin: 4px 0 2px; }
.msb-domain-panel { display: flex; flex-direction: column; gap: 12px; }
.msb-domain-panel .msb-settings-hint { margin: 0; }
.msb-domain-input-row input[type="text"] { width: 100%; padding: 9px 10px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 14px; }

/* v7.59.1 (direct request, Screenshot_10): "My Domain" (a connected
   domain's status/DNS/verify/remove view) redesigned as one clean
   bordered card with real breathing room, instead of the previous
   tightly-packed name+pills+boxes with no card/wrapper at all --
   pills especially had almost no gap and no wrapping room, causing
   the cramped look in that screenshot. */
.msb-domain-card {
	border: 1px solid var(--msb-border); border-radius: 14px; padding: 20px;
	display: flex; flex-direction: column; gap: 16px;
}
.msb-domain-current-row { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; font-size: 16px; }
.msb-domain-current-row strong { font-size: 17px; }
.msb-domain-current-row .msb-status-pill { margin: 0; }
.msb-domain-dns-group { display: flex; flex-direction: column; gap: 10px; }
.msb-domain-dns-box {
	background: var(--msb-bg-alt, #f6f5f3); border: 1px solid var(--msb-border); border-radius: 10px;
	padding: 14px 16px; font-size: 13.5px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; line-height: 1.6;
}
.msb-domain-dns-box code { background: transparent; padding: 0; }
/* v7.10.19 (Screenshot_14): "currently set to" box sits above the
   "change to" target box -- same shape, color-coded by whether the
   live lookup already matches the required target so the customer
   can tell at a glance whether they still need to act. */
.msb-domain-dns-current { border-style: dashed; }
.msb-domain-dns-tag { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .03em; opacity: .75; }
.msb-domain-dns-current-none { background: transparent; color: var(--msb-muted); }
.msb-domain-dns-current-mismatch { background: #fff8e6; border-color: #f0d38a; color: #8a6100; }
.msb-domain-dns-current-match { background: #eafaf0; border-color: #a6e6c1; color: #14622f; }
.msb-domain-dns-check { font-weight: 700; }
.msb-domain-action-row { display: flex; gap: 10px; margin-top: 4px; }
.msb-domain-action-row .msb-btn { flex: 1; }
/* Remove is destructive but shouldn't shout as loud as Verify -- quiet
   outline/red-text treatment instead of a second identical black bar. */
.msb-btn-outline-danger { background: #fff; color: #c62828; border-color: #f0c3c3; }
.msb-btn-outline-danger:hover { background: #fdf0f0; border-color: #e39b9b; }
.msb-page-name-url-row label { flex: 1; min-width: 0; }
@media (max-width: 600px) {
	.msb-page-name-url-row { flex-direction: column; gap: 10px; }
}

.msb-panel-tabs { display: flex; gap: 24px; border-bottom: 1px solid var(--msb-border); margin-bottom: 18px; overflow-x: auto; }
.msb-panel-tab {
	background: none; border: none; padding: 0 0 10px; font-size: 14px; font-weight: 600;
	color: var(--msb-muted); cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px;
	flex-shrink: 0;
}
.msb-panel-tab-active { color: var(--msb-fg); border-bottom-color: var(--msb-fg); }
/* Screenshot_5 (direct request): the Site Settings tab row (also
   shared by the Home/Pages sheet and the image/icon pickers, all via
   the same .msb-panel-tabs/.msb-panel-tab base) used a flat 24px gap
   at every width -- fine on the 1280px+ desktop panel where there's
   plenty of room, but on a phone-width sheet 4 longer labels
   (Integrations/Domain/Access/Backups) could outrun the available
   width and need a horizontal scroll to reach the last one. Tightened
   progressively below the desktop-panel breakpoint so the row keeps
   fitting without scrolling even on the narrowest phones; unchanged
   at 1280px+. */
@media (max-width: 1279px) {
	.msb-panel-tabs { gap: 18px; }
}
@media (max-width: 480px) {
	.msb-panel-tabs { gap: 13px; }
	.msb-panel-tab { font-size: 13px; }
}
@media (max-width: 360px) {
	.msb-panel-tabs { gap: 10px; }
	.msb-panel-tab { font-size: 12px; }
}
/* v7.19.11 (Screenshots 32/33): was var(--msb-muted), a mid grey that
   sat almost invisibly against the panel -- easy to miss the one signal
   telling you your change was actually saved. Pure black, since this is
   transient text that only appears while something is happening. */
.msb-save-status { margin-left: auto; align-self: center; font-size: 12px; color: #000; font-weight: 600; padding-bottom: 10px; }

/* ---- SEO tab: horizontal page-select row (v7.5.6) -- replaces the old
   dropdown. Wraps to a new line while it fits; once there are more pages
   than comfortably fit (5+), it switches to a single scrollable/swipeable
   row instead of wrapping or shrinking each tab. ---- */
.msb-seo-page-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.msb-seo-page-tabs-swipe { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.msb-seo-page-tabs-swipe::-webkit-scrollbar { display: none; }
.msb-seo-page-tab {
	flex-shrink: 0; background: var(--msb-bg-alt, #f2f0ee); border: 1px solid transparent; border-radius: 20px;
	padding: 7px 14px; font-size: 13px; font-weight: 600; color: var(--msb-muted); cursor: pointer; white-space: nowrap;
}
.msb-seo-page-tab-active { background: var(--msb-fg); color: var(--msb-bg); }

/* ---- Customization sub-tabs (Colors/Typography/Buttons/Layout) ---- */
.msb-subtabs { display: flex; gap: 6px; margin-bottom: 16px; background: var(--msb-bg-alt, #f2f0ee); border-radius: 8px; padding: 3px; }
.msb-subtab { flex: 1; background: none; border: none; padding: 7px 4px; font-size: 12px; font-weight: 600; color: var(--msb-muted); cursor: pointer; border-radius: 6px; }
.msb-subtab-active { background: #fff; color: var(--msb-fg); box-shadow: 0 1px 3px rgba(0,0,0,.1); }

.msb-two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
/* Base Font Size / Desktop Scale / Mobile Scale (v7.5.7) -- one clean
   row on a normal panel width; falls back to two columns only if the
   panel itself is ever squeezed narrower than the fields can read. */
.msb-three-col { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }
@media (max-width: 360px) { .msb-three-col { grid-template-columns: 1fr 1fr; } }

/* ---- HSL color picker -- v7.77.3: Hue/Saturation/Lightness sliders
   removed (direct request), HEX row + drag-square swatch only remains */
.msb-hsl-picker { margin-bottom: 14px; }
.msb-hsl-row { margin-bottom: 10px; }
.msb-hsl-row label { display: flex; justify-content: space-between; font-size: 12px; font-weight: 600; margin-bottom: 4px; }
.msb-hsl-hex-row { display: flex; gap: 8px; align-items: center; }
.msb-hsl-hex { flex: 1; padding: 8px 10px; border: 1px solid var(--msb-border); border-radius: 8px; font-family: monospace; font-size: 13px; }
.msb-color-swatch, .msb-hsl-swatch-btn { width: 38px; height: 38px; border-radius: 8px; border: 1px solid var(--msb-border); flex-shrink: 0; padding: 0; cursor: pointer; background-clip: padding-box; }

/* ---- Custom color picker popover (v6.35.0) -- replaces native <input type="color">
   everywhere so mobile never hands off to the buggy OS/WebView color picker. ---- */
.msb-color-popover {
	z-index: 100000; background: #fff; border: 1px solid var(--msb-border); border-radius: 12px;
	box-shadow: 0 8px 28px rgba(0,0,0,.18); padding: 12px;
}
.msb-cp-square { position: relative; width: 200px; height: 130px; border-radius: 8px; overflow: hidden; touch-action: none; cursor: crosshair; margin-bottom: 10px; }
.msb-cp-square-thumb {
	position: absolute; width: 14px; height: 14px; border-radius: 50%; border: 2px solid #fff;
	box-shadow: 0 0 0 1px rgba(0,0,0,.3); transform: translate(-7px, -7px); pointer-events: none;
}
.msb-cp-hue { position: relative; width: 200px; height: 14px; border-radius: 999px; margin-bottom: 12px; touch-action: none; cursor: pointer;
	background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
}
.msb-cp-hue-thumb {
	position: absolute; top: -2px; width: 18px; height: 18px; border-radius: 50%; border: 2px solid #fff;
	box-shadow: 0 0 0 1px rgba(0,0,0,.3); transform: translateX(-9px); pointer-events: none;
}
.msb-cp-live-hex { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.msb-cp-live-hex-swatch { width: 22px; height: 22px; border-radius: 6px; border: 1px solid var(--msb-border); flex-shrink: 0; }
.msb-cp-live-hex-value { font-family: monospace; font-size: 13px; font-weight: 600; letter-spacing: .02em; }
/* Anchored (desktop) positioning is set inline via JS; this variant
   overrides it to center the picker on screen instead, used on a
   narrow/mobile viewport where anchoring next to the swatch can push
   the picker off-screen or under a thumb. */
.msb-cp-centered {
	position: fixed !important; top: 50% !important; left: 50% !important;
	transform: translate(-50%, -50%); max-height: calc(100vh - 32px); overflow-y: auto;
}
.msb-cp-mode-toggle { display: flex; gap: 4px; margin-bottom: 8px; }
.msb-cp-mode-toggle button { flex: 1; padding: 5px 0; font-size: 11px; font-weight: 700; border: 1px solid var(--msb-border); border-radius: 6px; background: #fff; cursor: pointer; }
.msb-cp-mode-toggle button.msb-cp-mode-active { background: #1a1a1a; color: #fff; border-color: #1a1a1a; }
.msb-cp-mode-body input[type="text"] { width: 100%; padding: 8px 10px; border: 1px solid var(--msb-border); border-radius: 8px; font-family: monospace; font-size: 13px; }
.msb-cp-mode-body { display: flex; gap: 6px; }
.msb-cp-mode-body .msb-cp-hex-input { flex: 1; }
.msb-cp-num { flex: 1; text-align: center; font-size: 10px; color: var(--msb-muted); }
.msb-cp-num span { display: block; margin-bottom: 2px; font-weight: 700; }
.msb-cp-num input { width: 100%; padding: 6px 2px; text-align: center; border: 1px solid var(--msb-border); border-radius: 6px; font-size: 12px; }

/* ---- Harmony presets ---- */
.msb-harmony-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-bottom: 14px; }
.msb-harmony-btn { padding: 9px 6px; font-size: 12px; font-weight: 600; border: 1px solid var(--msb-border); border-radius: 8px; background: #fff; cursor: pointer; }
.msb-harmony-btn-active { background: var(--msb-fg); color: #fff; border-color: var(--msb-fg); }

/* ---- Secondary color roles ---- */
.msb-role-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid var(--msb-border); border-radius: 10px; padding: 10px 12px; margin-bottom: 8px; font-size: 13px; }
.msb-role-toggle { flex-shrink: 0; width: 38px; height: 22px; }

/* ---- Palette swatch cards ---- */
.msb-swatch-rows { margin-top: 4px; }
.msb-swatch-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; margin-bottom: 10px; }
.msb-swatch-card { border: 1px solid var(--msb-border); border-radius: 10px; overflow: hidden; background: #fff; }
.msb-swatch-fill { height: 46px; }
.msb-swatch-meta { padding: 8px 10px; font-size: 11px; line-height: 1.4; }
.msb-swatch-meta code { font-size: 10px; color: var(--msb-muted); }

/* ---- Fluid typography scale table ---- */
.msb-type-scale-table { width: 100%; border-collapse: collapse; margin-top: 12px; font-size: 12px; }
.msb-type-scale-table th { text-align: left; color: var(--msb-muted); font-weight: 600; padding: 6px 4px; border-bottom: 1px solid var(--msb-border); }
.msb-type-scale-table td { padding: 8px 4px; border-bottom: 1px solid var(--msb-border); vertical-align: middle; }

/* ---- Preset row buttons (button size / corner radius) ---- */
/* v7.19.14: .msb-preset-row removed -- the Buttons tab's Size/Corners
   rows were its only users and now use .msb-segmented instead. The
   .msb-preset-btn rules below stay: the Layout tab's preloader
   Text/Logo/GIF row still uses them (inside .msb-preloader-other-row,
   which supplies its own flex container). */
.msb-preset-btn { padding: 8px 14px; font-size: 13px; font-weight: 600; border: 1px solid var(--msb-border); border-radius: 8px; background: #fff; cursor: pointer; }
.msb-preset-btn-active { background: var(--msb-fg); color: #fff; border-color: var(--msb-fg); }

/* ---- Button/tag live preview ---- */
/* v7.19.14: .msb-btn-preview-group / -row removed -- replaced by
   .msb-btn-stage, which frames the preview instead of letting it
   float loose at the top of the tab. */
.msb-btn-preview { font-family: inherit; text-transform: uppercase; letter-spacing: .02em; transition: background .15s ease, border-radius .15s ease, padding .15s ease; }
.msb-btn-outline-preview { display: inline-block; padding: var(--msb-btn-py) var(--msb-btn-px); font-size: var(--msb-btn-fs); border-radius: var(--msb-btn-radius); border: 2px solid; text-decoration: none; font-weight: 600; background: transparent; }
.msb-tag { display: inline-block; padding: 4px 12px; font-size: 12px; font-weight: 700; }
.msb-preview-wrap {
	border: 1px solid var(--msb-border); border-radius: var(--msb-radius);
	overflow: hidden; margin-bottom: 8px; background: #fff;
	/* v7.19.10: anchors the double-buffered preview iframe below. */
	position: relative;
}
.msb-preview-wrap iframe { width: 100%; border: 0; display: block; }
/* v7.19.10 (Screenshot_26): the incoming half of the double-buffered
   preview swap -- see refreshPreviewNow() in frontend.js. Stacked
   exactly over the visible preview at the same size (so it lays out and
   paints identically, and is ready the instant it's promoted) but fully
   invisible and inert until then, so the customer never sees a blank
   frame mid-reload. */
.msb-preview-buffer {
	position: absolute; top: 0; left: 0; width: 100%; height: 100%;
	opacity: 0; pointer-events: none; border: 0;
}

.msb-layout-options { display: flex; flex-direction: column; gap: 10px; }
@media (min-width: 560px) {
	.msb-layout-options { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
}
.msb-layout-option {
	width: 100%; text-align: left; padding: 0; border-radius: 10px; overflow: hidden;
	border: 1px solid var(--msb-border); background: #fff;
}
.msb-layout-option-active { border-color: var(--msb-fg); border-width: 2px; }
/* v7.70.0 (direct feedback: "how can we make this noticeable" -- see
   planLockCtaHtml()'s own doc comment in frontend.js for the full
   before/after): a thin accent-colored left edge, so a locked design
   reads as different from an ordinary one at a glance while scanning
   down the list, before the eye even reaches the CTA button on the
   right. Quiet on purpose -- the button below carries the real
   attention-grabbing weight, this is just a secondary cue. */
.msb-layout-option-locked { border-left: 3px solid var(--msb-accent, #111); }
.msb-layout-option-clickable, .msb-library-card-clickable { cursor: pointer; transition: box-shadow .15s ease, transform .15s ease; }
.msb-layout-option-clickable:hover, .msb-library-card-clickable:hover { box-shadow: 0 0 0 2px var(--msb-accent, #111); }
.msb-layout-option-clickable:active, .msb-library-card-clickable:active { transform: scale(.98); }
.msb-layout-option-clickable.msb-applying, .msb-library-card-clickable.msb-applying { opacity: .5; pointer-events: none; }
/* v7.45.2 (Screenshot_17): min-height dropped from 40px to a 1px
   placeholder -- this only ever matters for the instant before the
   thumbnail's iframe finishes loading and scaleThumbDynamic() sets a
   real, content-hugging height; keeping a 40px floor here meant a
   short section (like a slim header) could still show extra empty
   space if that JS measurement landed anywhere below 40px. */
.msb-layout-option-thumb { display: block; position: relative; width: 100%; min-height: 1px; overflow: hidden; background: var(--msb-bg-alt, #f0eeec); }
.msb-layout-option-thumb iframe { position: absolute; top: 0; left: 0; border: 0; pointer-events: none; }
.msb-layout-option-toprow { display: flex; align-items: center; gap: 8px; padding: 10px 14px; }
.msb-layout-option-name {
	flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 500;
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.msb-layout-option-current {
	flex: 0 0 auto; text-align: right; font-size: 12px; font-weight: 700;
	color: var(--msb-muted); text-transform: uppercase; letter-spacing: .04em;
}

/* v7.77.0 (Motion Style -- Change Layout only, see openLayoutVariationSheet()
   in frontend.js for why Add Section deliberately does NOT get this filter).
   Three tiers, deliberately distinct colors AND shapes (never color alone --
   see the ICON_MOTION_* comment in frontend.js): a calm blue for Subtle
   (the platform's own accent-adjacent blue, not the muted gray a "just
   inactive" affordance would suggest), teal for Interactive, violet for
   Immersive -- close in spirit to how the rest of this plugin uses
   --msb-accent for its own single brand color, but this is a genuinely
   3-way categorical distinction, not a brand/neutral one, so it earns its
   own small palette here rather than reusing --msb-accent three times over.

   v7.77.2 (Screenshot_59/60/61, direct report): three real polish fixes.
   (1) The label and pill row were one inline flex row -- on any category
   name long enough (or on mobile's narrower width) the row wrapped
   mid-sentence, reading as clumsy/broken. Stacked instead: label on its
   own row, pills on a second row below, on every viewport -- no
   breakpoint-specific override needed since a column layout degrades
   identically at any width. (2) The active/selected chip previously used
   a neutral dark border+gray fill (the same "just a toggle" treatment
   every other filter chip in this plugin uses) -- changed to the Subtle
   tier's own blue as a universal "this is selected" indicator regardless
   of WHICH tier is active, a deliberate second meaning for that one color
   (Subtle's identity AND the platform's own selection color), confirmed
   unambiguous since only one chip is ever active at a time. (3) Labels
   and chip text rendered in whichever custom Google Font the SITE ITSELF
   uses (an editor-chrome element with no font-family of its own inherits
   `body`'s, which --msb-font-body overrides per-site) -- confirmed via
   the reported screenshot's own cursive/script rendering matching that
   site's brand headline font exactly. This is admin/editor CHROME, never
   part of the rendered page -- explicitly pinned to the same fixed
   system-UI stack --msb-font-body itself defaults to, so it reads
   identically regardless of which customer's site is open.

   v7.81.3 (Screenshot_1/2/3, direct report): the active/selected chip's
   hardcoded Subtle-blue fill (see v7.77.2 note above) is superseded --
   swapped to var(--msb-accent), the same platform-primary color the
   Publish button itself uses, so the "this is selected" affordance now
   reads as platform-brand rather than a borrowed tier color. Active
   chip text and icon recolored to solid white for contrast against
   that fill (all three tier-specific icon colors -- blue/teal/violet --
   collapse to white only while active; each tier's own color still
   shows on its icon while inactive, unchanged). Label/icon weight
   dropped from 500 (600 active) to 100 (thin) throughout, and
   justify-content:center added alongside the existing align-items so
   icon+label center both vertically and horizontally inside the pill
   at every size, including the 560px mobile compaction below.

   v7.81.4 (Screenshot_5, direct report): the 100 weight above read too
   thin/light in practice -- moved up to 400 (regular) for both the
   inactive and active chip state, everything else from v7.81.3
   (color, centering) left as-is. */
.msb-motion-filter-wrap { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; margin: 4px 0 10px; }
.msb-motion-filter-label {
	display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600;
	text-transform: uppercase; letter-spacing: .04em; color: var(--msb-muted);
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.msb-info-dot {
	display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px;
	border-radius: 50%; border: 1px solid var(--msb-muted); color: var(--msb-muted);
	font-size: 9.5px; font-weight: 700; font-style: italic; cursor: help; flex-shrink: 0;
	font-family: Georgia, serif;
}
.msb-motion-filter { display: flex; gap: 6px; flex-wrap: wrap; width: 100%; }
.msb-motion-chip {
	display: inline-flex; align-items: center; justify-content: center; gap: 5px; padding: 6px 12px; border-radius: 8px;
	border: 1px solid var(--msb-border); background: #fff; color: var(--msb-fg);
	font-size: 12.5px; font-weight: 400; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
	line-height: 1; cursor: pointer; transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.msb-motion-chip svg { width: 13px; height: 13px; flex-shrink: 0; display: block; }
.msb-motion-chip-active {
	background: var(--msb-accent, #ff6a2b); border-color: var(--msb-accent, #ff6a2b);
	color: #fff; font-weight: 400;
}
.msb-motion-chip[data-motion="subtle"] svg { color: #3B6FE0; }
.msb-motion-chip[data-motion="interactive"] svg { color: #0E8F7D; }
.msb-motion-chip[data-motion="immersive"] svg { color: #6D4AFF; }
.msb-motion-chip[data-motion="all"] svg { color: var(--msb-muted); }
.msb-motion-chip-active svg,
.msb-motion-chip-active[data-motion="all"] svg,
.msb-motion-chip-active[data-motion="subtle"] svg,
.msb-motion-chip-active[data-motion="interactive"] svg,
.msb-motion-chip-active[data-motion="immersive"] svg { color: #fff; }
/* v7.77.3 (Screenshot_62, direct report): on a real phone width the 4
   pills wrapped ("Immersive" alone on its own second line) -- desktop
   was already fine, left untouched. Compacted specifically at this
   file's own established 480px editor-chrome mobile breakpoint (§42 of
   the Section Block Rulebook records 390px as the real preview width
   this needs to fit): smaller horizontal padding, smaller icon, smaller
   (still regular-weight) font -- shaves enough width off all 4 pills
   combined that they now sit on one row with room to spare, confirmed
   against the reported "All / Subtle / Interactive / Immersive" set.
   v7.77.4 (Screenshot_64, direct report): still wrapped after the first
   pass -- that round under-shrank for a real device's actual width.
   Recalculated against "Interactive" (the longest label) with real
   margin, and widened the breakpoint from 480px to 560px in case a real
   device's reported CSS width sits between the two. white-space:nowrap
   added so a label can never wrap INSIDE its own pill either, isolating
   "which pill drops to row 2" as the only possible wrap behavior. */
@media (max-width: 560px) {
	.msb-motion-chip { padding: 4px 6px; font-size: 10px; gap: 3px; white-space: nowrap; }
	.msb-motion-chip svg { width: 9px; height: 9px; }
	.msb-motion-filter { gap: 4px; }
}

/* v7.77.2: Change Layout cards only now (Add Section's own copy was
   removed per direct instruction -- see openSectionLibrarySheet() in
   frontend.js). Also doubles as this session's active-filter legend.
   v7.81.4 (Screenshot_5, direct report): weight dropped from 700 to
   100 (thin) to match the filter chips above it, and icon+label now
   explicitly centered on both axes (justify-content added alongside
   the existing align-items, plus line-height:1 and a blocked-out SVG)
   so the thin weight doesn't read as vertically off inside the pill. */
.msb-motion-badge {
	position: absolute; top: 8px; left: 8px; z-index: 2; display: inline-flex; align-items: center; justify-content: center; gap: 4px;
	padding: 3px 8px; border-radius: 999px; background: rgba(255,255,255,.94); font-size: 10.5px; font-weight: 100;
	line-height: 1; box-shadow: 0 1px 3px rgba(0,0,0,.12);
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.msb-motion-badge svg { width: 11px; height: 11px; flex-shrink: 0; display: block; }
/* v7.81.5 (Screenshot_6, direct report): with line-height:1 alone
   (v7.81.4) the raw text node's line box still used the thin-weight
   font's own ascent/descent split -- asymmetric enough at 100 weight
   to leave visibly more room below "Subtle" than above it even though
   the badge's own padding is symmetric (3px top/bottom). Text is now
   wrapped in its own span forced to the exact same box height as the
   icon (11px, same as .msb-motion-badge svg above) so flex
   align-items:center centers two equal-height children instead of an
   icon against a taller, unevenly-leaded text line box -- the
   remaining top/bottom gap around both is then purely the badge's own
   symmetric padding, pixel-even on both sides.
   v7.81.6 (direct follow-up, same report thread): even-centered still
   read 2px shy underneath in practice -- added margin-bottom:2px to
   the label span only (icon and the filter-chip row above both
   untouched, per direct instruction). */
.msb-motion-badge-label { display: block; height: 11px; line-height: 11px; margin-bottom: 2px; }
.msb-motion-badge-subtle { color: #3B6FE0; }
.msb-motion-badge-interactive { color: #0E8F7D; }
.msb-motion-badge-immersive { color: #6D4AFF; }


.msb-section-title {
	font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
	color: var(--msb-muted); margin: 20px 0 8px;
}
.msb-section-title-row {
	display: flex; align-items: center; justify-content: space-between; gap: 12px;
	margin: 20px 0 8px;
}
.msb-section-title-row .msb-section-title { margin: 0; }
.msb-mode-toggle {
	display: inline-flex; align-items: center; gap: 2px;
	background: #f0f0f0; border-radius: 10px; padding: 3px; flex-shrink: 0;
}
.msb-mode-btn {
	width: 34px; height: 30px; display: flex; align-items: center; justify-content: center;
	border: none; background: transparent; border-radius: 8px; cursor: pointer;
	color: #8a8a8a; padding: 0;
}
.msb-mode-btn svg { width: 16px; height: 16px; }
.msb-mode-btn-active { background: #fff; color: #4b3fd6; box-shadow: 0 1px 3px rgba(0,0,0,.15); }

/* ---- Manage Sections ---- */
.msb-manage-sections-list { display: flex; flex-direction: column; gap: 6px; }
.msb-manage-section-actions { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.msb-icon-btn {
	width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
	border: 1px solid var(--msb-border); background: #fff; border-radius: 50%;
	font-size: 15px; line-height: 1; color: var(--msb-muted); cursor: pointer; padding: 0;
}
.msb-icon-btn:hover { color: #c0392b; border-color: #c0392b; }

/* v7.45.0 (Screenshot_7/8): a lighter-weight variant of .msb-icon-btn --
   no circle outline/fill, no red hover -- for actions that sit inside an
   already-bordered row (Manage Sections) and don't need their own visual
   weight competing with it. Each specific action below tunes color/size
   on top of this shared reset. */
.msb-icon-btn-ghost {
	width: auto; height: auto; border: none; background: transparent; border-radius: 8px;
	padding: 6px; color: var(--msb-muted);
}
.msb-icon-btn-ghost svg { width: 16px; height: 16px; display: block; }
.msb-icon-btn-ghost:hover { background: var(--msb-bg-alt, #f2efec); border-color: transparent; color: var(--msb-fg, #141414); }

/* Rename: same ghost treatment as Visibility/Remove, sitting first in
   the actions group (Screenshot_13) rather than inline next to the
   name -- keeps all three per-section actions scannable as one row
   group instead of splitting rename off on its own. */
.msb-rename-section-btn { opacity: .7; }
.msb-rename-section-btn:hover { opacity: 1; }

/* Visibility: an eye that fills with the editor's own brand accent while
   the section is live, and greys out to a slashed eye once hidden -- the
   same show/hide visual language as a password field's reveal toggle,
   so "on" vs "off" reads at a glance without needing a text label. */
.msb-section-visibility-btn { color: #b9b3ac; }
.msb-section-visibility-btn-active { color: var(--msb-accent); }
.msb-section-visibility-btn:hover { background: var(--msb-bg-alt, #f2efec); color: var(--msb-accent); }
.msb-section-visibility-btn-active:hover { color: var(--msb-accent); filter: brightness(.85); }

/* Remove stays the one destructive action in this row -- red only on
   hover, so it doesn't visually compete with Hide/Show at rest. */
.msb-remove-section-btn:hover { background: #fdecea; color: #c0392b; }

/* v7.45.4 (Screenshot_22): Save/Cancel pair that briefly replaces the
   Rename/Visibility/Remove trio while a name is being edited in place.
   Save is filled solid with the brand accent so it's unmistakably the
   affirmative action; Cancel stays a plain ghost icon like its usual
   neighbours. */
.msb-rename-save-btn-active {
	color: #fff; background: var(--msb-accent); border-radius: 50%;
}
.msb-rename-save-btn-active:hover { background: var(--msb-accent); filter: brightness(1.08); color: #fff; }
.msb-rename-cancel-btn:hover { background: #fdecea; color: #c0392b; }

/* ---- Section-popup action list (Change Layout / Add Section Below / Hide) ---- */
.msb-action-group {
	border: 1px solid var(--msb-border); border-radius: 12px; overflow: hidden; background: #fff;
}
.msb-action-row {
	display: flex; align-items: center; gap: 12px; width: 100%; box-sizing: border-box;
	padding: 13px 14px; background: #fff; border: none; cursor: pointer;
	font-size: 14px; font-weight: 600; color: var(--msb-fg, #141414); text-align: left;
	font-family: inherit; -webkit-appearance: none; appearance: none;
}
.msb-action-group .msb-action-row + .msb-action-row { border-top: 1px solid var(--msb-border); }
.msb-action-row:active { background: #f7f7f7; }
.msb-action-icon {
	flex-shrink: 0; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center;
	color: var(--msb-muted);
}
.msb-action-icon svg { width: 18px; height: 18px; }
.msb-action-label { flex: 1; min-width: 0; }
.msb-action-chevron { flex-shrink: 0; color: var(--msb-muted); display: flex; }
.msb-action-chevron svg { width: 16px; height: 16px; }
.msb-action-row-danger {
	border: 1px solid #f3d4d0; border-radius: 12px; color: #c0392b;
}
.msb-action-row-danger .msb-action-icon { color: #c0392b; }
.msb-action-row-danger:active { background: #fdf3f2; }

/* v7.16.0 (Screenshot 17): Hide This Section + Remove This Section side
   by side on one row, instead of Remove stacking as a whole separate
   row underneath. Only used when both buttons are present -- a lone
   Hide button (Header/Footer, which never gets a Remove option) still
   renders full-width via the plain .msb-action-group rules above. */
.msb-action-group-row { display: flex; gap: 10px; border: none; background: transparent; overflow: visible; }
.msb-action-group-row .msb-action-row { width: auto; flex: 1 1 0; min-width: 0; justify-content: center; text-align: center; }
/* v7.17.0 (Screenshot 19): a `border-top:none` on the adjacent-sibling
   button used to sit here, copied from the vertically-STACKED group
   pattern above (where it kills a doubled divider line between two
   full-width rows sharing one outer box). It doesn't apply to this
   side-by-side row at all -- each button here draws its own complete,
   independent border via .msb-action-row-danger below -- so it was
   silently stripping just the Remove button's top edge for no reason.
   Removed; both buttons keep their full border on all four sides. */
.msb-action-row-half .msb-action-label { flex: none; }
/* v7.19.6 (Screenshot_21): these two were squeezed into half a phone
   width each, with the icon crowding a wrapping label and a 13px tap
   target. Tighter internal gap, a guaranteed 44px touch height, no
   wrapping, and a slightly softer fill so they read as a related pair
   of secondary actions rather than two alarming outlined boxes. */
.msb-action-group-row .msb-action-row-danger {
	gap: 7px; padding: 11px 10px; min-height: 44px; background: #fdf7f6;
	font-size: 13.5px; white-space: nowrap;
}
.msb-action-group-row .msb-action-icon { width: 18px; height: 18px; }
.msb-action-group-row .msb-action-icon svg { width: 16px; height: 16px; }
/* v7.45.5 (Screenshot_23): Hide Section + Remove Section now stay side
   by side at every mobile width instead of stacking into two full rows
   below 420px -- shrinking the icon, text, and padding here keeps both
   comfortably tappable and non-overlapping without needing the extra
   vertical space stacking used to buy. */
@media (max-width: 420px) {
	.msb-action-group-row .msb-action-row-danger {
		gap: 5px; padding: 9px 6px; min-height: 40px; font-size: 11.5px;
	}
	.msb-action-group-row .msb-action-icon { width: 15px; height: 15px; }
	.msb-action-group-row .msb-action-icon svg { width: 14px; height: 14px; }
}

.msb-manage-section-row {
	display: flex; align-items: center; gap: 10px; cursor: default;
	-webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
.msb-manage-section-label {
	flex: 1; min-width: 0; display: flex; align-items: center;
}
/* v7.45.3 (Screenshot_21): the name area is a real click target now --
   tapping it jumps the live preview straight to that section (see
   scrollPreviewToSection() in frontend.js). Cursor + a subtle hover/
   focus color shift on the text itself is the only visual change so
   it still reads as part of the row, not a separate button. */
.msb-manage-section-label-clickable { cursor: pointer; border-radius: 6px; margin: -4px -6px; padding: 4px 6px; }
.msb-manage-section-label-clickable:hover strong,
.msb-manage-section-label-clickable:focus-visible strong { color: var(--msb-accent); }
.msb-manage-section-label-clickable:hover,
.msb-manage-section-label-clickable:focus-visible { background: var(--msb-bg-alt, #f2efec); outline: none; }
/* v7.45.4 (Screenshot_22): while a name is being edited in place, the
   label no longer acts as a nav/click target -- no pointer cursor, no
   hover tint -- since it now contains a real text input instead of the
   clickable name text. */
.msb-manage-section-label-editing { cursor: default; background: transparent; margin: -4px -6px; padding: 4px 6px; }
.msb-manage-section-label-editing:hover,
.msb-manage-section-label-editing:focus-visible { background: transparent; }
.msb-manage-section-rename-input {
	width: 100%; box-sizing: border-box; padding: 3px 6px; margin: -3px -6px;
	border: 1px solid var(--msb-accent); border-radius: 6px; background: #fff;
	font-family: inherit; font-size: 14px; font-weight: 500; color: var(--msb-fg, #141414);
	outline: none;
}
/* v7.41.1 (Screenshot_43/44): a long section-block name used to wrap
   onto 2-3 lines, making every row a different height and the whole
   list feel cluttered. Single line + ellipsis, and a lighter weight
   (500, not <strong>'s default 700) for a cleaner, more minimal look
   -- the row is still a single flex line at a predictable height
   regardless of name length. */
.msb-manage-section-label strong {
	display: block; font-weight: 500; font-size: 14px; line-height: 1.3;
	white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 1 1 auto;
}
.msb-drag-handle {
	cursor: grab; color: var(--msb-muted); flex-shrink: 0;
	display: flex; align-items: center; justify-content: center; width: 22px; height: 22px;
	touch-action: none; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
.msb-drag-handle svg { width: 20px; height: 20px; pointer-events: none; }
.msb-drag-handle-locked { cursor: default; opacity: .55; }
@media (max-width: 600px) {
	.msb-manage-section-row .msb-manage-section-label strong { font-size: 13px; }
}
.msb-row-dragging { position: relative; cursor: grabbing; box-shadow: 0 4px 14px rgba(0,0,0,.15); background: #fff; }
.msb-rename-section-btn { color: var(--msb-muted); }

/* ---- Pages tab (Task 21) ---- */
.msb-page-list { display: flex; flex-direction: column; gap: 6px; }
.msb-page-home-badge {
	display: inline-flex; align-items: center; font-size: 11px; font-weight: 600;
	padding: 2px 8px; border-radius: 10px; background: #eef2ff; color: #3355dd;
	margin-left: 6px; vertical-align: middle;
}
.msb-page-row {
	display: flex; align-items: center; justify-content: space-between; gap: 10px;
	padding: 10px 12px; border: 1px solid var(--msb-border); border-radius: 10px; background: #fff;
}
.msb-page-row-active { border-color: var(--msb-primary, #0B5FFF); background: var(--msb-bg-alt, #f7f9ff); }
.msb-page-row-main { flex: 1; min-width: 0; cursor: pointer; display: flex; align-items: center; flex-wrap: wrap; }
.msb-page-row-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }

/* ---- Menu item editor rows (Task 21) ---- */
.msb-menu-item-row {
	border: 1px solid var(--msb-border); border-radius: 10px; padding: 10px; margin-bottom: 8px;
	display: flex; flex-direction: column; gap: 8px;
}
.msb-menu-item-row-top { display: flex; align-items: center; gap: 8px; }
.msb-menu-item-label { flex: 1 1 45%; min-width: 0; padding: 8px 10px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 13px; box-sizing: border-box; }
.msb-menu-item-link { flex: 1 1 45%; min-width: 0; padding: 8px 10px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 13px; box-sizing: border-box; }
.msb-menu-item-url { width: 100%; padding: 8px 10px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 13px; box-sizing: border-box; }
.msb-menu-item-remove { flex-shrink: 0; margin-left: 0; }
.msb-hidden { display: none !important; }

/* v7.12.0: a top-level item's group -- its own row, an indented list of
   Sub-item rows (if any), and the "+ Add Sub-item" control, all inside
   one bordered card so a dropdown's items visually belong to their
   parent instead of reading as separate top-level entries. */
.msb-menu-item-group { border: 1px solid var(--msb-border); border-radius: 12px; padding: 10px; margin-bottom: 10px; background: var(--msb-bg-alt, #fafafa); }
.msb-menu-item-group .msb-menu-item-row { margin-bottom: 0; background: #fff; }
.msb-menu-children { display: flex; flex-direction: column; gap: 8px; margin: 8px 0 0 20px; padding-left: 12px; border-left: 2px solid var(--msb-border); }
.msb-menu-item-row-child { background: #fff; }
.msb-add-submenu-item { margin-top: 8px; font-size: 12px; padding: 6px 10px; }

.msb-mi-label, .msb-mi-url { width: 100%; padding: 8px 10px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 13px; box-sizing: border-box; }
.msb-mi-type, .msb-mi-page { padding: 8px 6px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 13px; box-sizing: border-box; }
.msb-rename-section-btn:hover { color: var(--msb-fg, #141414); border-color: var(--msb-fg, #141414); }

/* Belt-and-braces: while a row is being dragged, suppress text selection
   site-wide. This is what actually stops Android's long-press "Copy /
   Share / Select all / Web search" bar from hijacking the gesture --
   touch-action and user-select on the handle alone weren't reliably
   enough on every device. */
body.msb-dragging-no-select { -webkit-user-select: none; user-select: none; }

/* ---- Section Library picker ---- */
.msb-library-controls { display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; }
.msb-library-controls input[type="text"] {
	width: 100%; box-sizing: border-box; padding: 10px 12px; border: 1px solid var(--msb-border);
	border-radius: 8px; font-size: 14px;
}
.msb-library-cats {
	display: flex; flex-wrap: nowrap; gap: 6px; overflow-x: auto; padding-bottom: 4px;
	-webkit-overflow-scrolling: touch; scrollbar-width: thin; cursor: grab; user-select: none;
}
.msb-library-cats:active { cursor: grabbing; }
.msb-library-cats .msb-filter-chip { flex-shrink: 0; }
.msb-filter-chip {
	background: #fff; border: 1px solid var(--msb-border); border-radius: 999px;
	padding: 5px 12px; font-size: 12px; font-weight: 600; color: var(--msb-muted); cursor: pointer;
	text-transform: capitalize;
}
.msb-filter-chip-active { background: var(--msb-fg, #141414); color: #fff; border-color: var(--msb-fg, #141414); }
.msb-library-grid { column-count: 1; column-gap: 12px; }
@media (min-width: 480px) {
	.msb-library-grid { column-count: 2; }
}
.msb-library-card { border: 1px solid var(--msb-border); border-radius: 10px; overflow: hidden; background: #fff; break-inside: avoid; margin-bottom: 12px; }
/* v7.45.2 (Screenshot_17): same 1px placeholder-only floor as
   .msb-layout-option-thumb above, for the same reason -- the Add New
   Section library grid uses the same scaleThumbDynamic() sizing. */
.msb-library-card-thumb { display: block; position: relative; width: 100%; min-height: 1px; overflow: hidden; background: var(--msb-bg-alt, #f0eeec); }
.msb-library-card-thumb iframe { position: absolute; top: 0; left: 0; border: 0; pointer-events: none; }
.msb-library-card-body { padding: 10px; display: flex; flex-direction: column; gap: 4px; }
.msb-library-card-toprow { display: flex; align-items: center; gap: 8px; }
.msb-library-card-title {
	flex: 1 1 auto; min-width: 0; font-size: 12px; font-weight: 500;
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.msb-insert-link {
	display: inline-flex; align-items: center; justify-content: flex-end; gap: 4px;
	flex: 0 0 auto; white-space: nowrap; box-sizing: border-box;
	background: none; border: none; cursor: pointer; font-family: inherit; padding: 0;
	font-size: 11.5px; font-weight: 500; text-decoration: underline; text-underline-offset: 2px;
	color: #2563eb;
}
.msb-insert-link:hover { color: #1d4fd1; }
.msb-insert-link:disabled { opacity: .4; cursor: default; }
.msb-insert-icon { display: flex; flex-shrink: 0; }
.msb-insert-icon svg { width: 14px; height: 14px; }

/* v7.76.0 (Screenshot_11, direct request): the editor's own right-
   click/long-press context menu, floated over the whole editor at a
   fixed screen position (see showFloatingPreviewMenu() in
   frontend.js) -- also doubles as the hover "+" insert zone's small
   Add/Paste chooser. */
.msb-context-menu {
	position: fixed; z-index: 9999; min-width: 190px;
	background: #fff; border: 1px solid var(--msb-border); border-radius: 10px;
	box-shadow: 0 8px 28px rgba(0,0,0,0.18); padding: 6px; font-family: inherit;
}
.msb-context-menu-item {
	display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
	background: none; border: none; border-radius: 6px; cursor: pointer;
	padding: 8px 10px; font-size: 13px; font-weight: 500; color: #141414;
}
.msb-context-menu-item:hover { background: var(--msb-bg-alt, #f0eeec); }
.msb-context-menu-item:disabled { opacity: .4; cursor: default; }
.msb-context-menu-item:disabled:hover { background: none; }
.msb-context-menu-icon { display: flex; flex-shrink: 0; color: #666; }
.msb-context-menu-icon svg { width: 16px; height: 16px; }
/* v7.76.4 (Screenshot_26, direct request): a destructive item (Remove
   Section) in red icon + red text, same #b32d2e danger red already
   used elsewhere in the admin/editor (e.g. .msb-set-default-block
   family), with its own small top divider so it reads as separated
   from the safe actions above it rather than just another row. */
.msb-context-menu-item-danger { color: #b32d2e; margin-top: 4px; padding-top: 9px; border-top: 1px solid var(--msb-border, #eee); }
.msb-context-menu-item-danger .msb-context-menu-icon { color: #b32d2e; }
.msb-context-menu-item-danger:hover { background: rgba(179,45,46,0.08); }

/* v7.70.0: planLockCtaHtml()'s own button (Section Blocks' "Choose a
   Layout" panel) -- a solid pill BUTTON rather than a plain underlined
   link, so the one row in the list that's actually an upgrade
   opportunity doesn't blend in at the same visual weight as every
   ordinary "Apply Layout" text link around it.
   v7.72.0 (direct follow-up, matching the same request already
   applied to Templates' own "Upgrade to use" in v7.71.0): background
   fixed to black rather than var(--msb-accent) -- the platform's own
   branded color is already used elsewhere on this exact panel (every
   OTHER thumbnail's own content, plus the accent left-edge on this
   same locked row), so a fixed black keeps "this needs an upgrade" a
   single, consistent, unmistakable color across the whole product,
   never confusable with the platform's ordinary accent color. */
.msb-plan-lock-cta {
	display: inline-flex; align-items: center; justify-content: center; gap: 5px;
	flex: 0 0 auto; white-space: nowrap; box-sizing: border-box;
	background: #111; color: #fff;
	font-size: 11.5px; font-weight: 700; letter-spacing: .01em;
	text-decoration: none; border-radius: 999px; padding: 6px 14px 6px 10px;
	box-shadow: 0 2px 6px rgba(0, 0, 0, 0.16);
	transition: filter .12s ease, box-shadow .12s ease, transform .1s ease;
}
.msb-plan-lock-cta:hover { filter: brightness(1.3); box-shadow: 0 3px 10px rgba(0, 0, 0, 0.22); }
.msb-plan-lock-cta:active { transform: scale(.97); }
.msb-plan-lock-cta-icon { display: flex; flex-shrink: 0; }
.msb-plan-lock-cta-icon svg { width: 12px; height: 12px; }

/* ---- Floating "Use This Template" CTA on the standalone template-preview page ---- */
#msb-template-use-cta { position: fixed; z-index: 9999; right: 20px; bottom: 20px; }
.msb-template-cta-btn {
	display: inline-flex; align-items: center; gap: 8px;
	background: #141414; color: #fff; border: none; border-radius: 999px;
	padding: 15px 26px; font-size: 15px; font-weight: 700; cursor: pointer;
	box-shadow: 0 8px 24px rgba(0,0,0,.28); transition: transform .15s ease, box-shadow .15s ease;
}
.msb-template-cta-btn:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(0,0,0,.32); }
@media (max-width: 640px) {
	#msb-template-use-cta { left: 16px; right: 16px; bottom: 16px; }
	.msb-template-cta-btn { width: 100%; justify-content: center; }
}
.msb-field-row {
	display: flex; align-items: center; justify-content: space-between;
	background: #fff; border: 1px solid var(--msb-border); border-radius: 10px;
	padding: 12px 14px; margin-bottom: 8px;
}
.msb-field-row .label { font-size: 14px; font-weight: 500; }
.msb-field-row .preview { font-size: 12px; color: var(--msb-muted); max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.msb-inline-field { margin-bottom: 14px; }
/* v7.59.39: 2-column row for adjacent Image/Logo/Icon fields (see
   groupAdjacentPhotoFieldsHtml() in frontend.js) -- a photo thumbnail
   is capped at 140px (.msb-photo-box below) regardless of the column
   width it's given, so two side by side wastes far less space than
   stacking them full-width one per row. Each child .msb-inline-field
   keeps its own margin-bottom for the row-to-row gap; min-width:0
   keeps a long label from forcing the row wider than its parent. */
.msb-inline-field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.msb-inline-field-row .msb-inline-field { min-width: 0; margin-bottom: 0; }
.msb-link-label-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.msb-link-label-row label { margin-bottom: 0; }
.msb-link-field .msb-link-type-select {
	padding: 6px 8px; font-size: 10.4px; font-weight: 600;
	border: 1px solid var(--msb-border); border-radius: 8px; background: #fff; color: var(--msb-fg, #141414);
	cursor: pointer; width: auto; max-width: 160px; flex-shrink: 0;
}
.msb-link-raw-input {
	width: 100%; box-sizing: border-box; padding: 10px 12px; border: 1px solid var(--msb-border);
	border-radius: 8px; font-size: 14px; font-family: inherit;
}
.msb-link-page-select {
	width: 100%; box-sizing: border-box; padding: 10px 12px; border: 1px solid var(--msb-border);
	border-radius: 8px; font-size: 14px; font-family: inherit; background: #fff; cursor: pointer;
}
/* v7.17.11: video field's URL/Upload source select -- same visual
   treatment as the link field's type select right above, since it sits
   in the same label-row position. */
.msb-video-field .msb-video-source-select {
	padding: 6px 8px; font-size: 10.4px; font-weight: 600;
	border: 1px solid var(--msb-border); border-radius: 8px; background: #fff; color: var(--msb-fg, #141414);
	cursor: pointer; width: auto; max-width: 180px; flex-shrink: 0;
}
.msb-video-uploaded-row {
	display: flex; align-items: center; justify-content: space-between; gap: 10px;
	padding: 10px 12px; border: 1px solid var(--msb-border); border-radius: 8px; background: #fafafa;
}
.msb-video-uploaded-url {
	font-size: 13px; color: var(--msb-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.msb-video-reupload-btn { flex-shrink: 0; padding: 6px 12px; font-size: 12.5px; }
.msb-inline-field label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 4px; }
.msb-inline-field .msb-inline-input,
.msb-inline-field input[type="text"], .msb-inline-field input[type="url"] {
	width: 100%; padding: 10px 12px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 14px;
}
.msb-inline-toggle label { display: flex; align-items: center; gap: 8px; font-weight: 500; }

/* v7.15.1: unified "photo box" used for EVERY image field across the
   editor (single section image fields, SEO meta image, repeater item
   photos) -- one square box, image filling it edge-to-edge, a single
   round upload-icon button overlaid directly ON the image's own
   bottom-right corner. No separate "beside" button, and no remove/
   clear control anywhere: an image field can only ever be swapped for
   another image, never emptied, because an emptied image broke the
   section's layout. */
.msb-photo-box {
	position: relative; width: 100%; max-width: 140px; aspect-ratio: 1 / 1;
	border-radius: 10px; border: 1px solid var(--msb-border); overflow: hidden;
	background: var(--msb-bg-alt, #f7f4f2);
}
.msb-photo-box img { width: 100%; height: 100%; object-fit: cover; display: block; }
.msb-photo-upload-btn {
	position: absolute; right: 6px; bottom: 6px; width: 32px; height: 32px; border-radius: 50%;
	border: 1px solid var(--msb-border); background: #fff; color: var(--msb-fg);
	display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0;
	box-shadow: 0 1px 3px rgba(0,0,0,.2); line-height: 0;
}
/* svg display:block + no line-height keeps the icon glyph itself
   dead-center in the circle -- an inline svg has baseline spacing
   that throws flex centering off by a couple px otherwise. */
.msb-photo-upload-btn svg { width: 15px; height: 15px; display: block; }
.msb-photo-upload-btn:hover { background: var(--msb-bg-alt, #f7f4f2); }

.msb-repeater-item-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.msb-repeater-item-index { font-size: 12px; font-weight: 600; color: #888; text-transform: uppercase; letter-spacing: .03em; }

/* v7.15.1: repeater item 2-column layout (photo column ~30% / content
   fields ~70%) with Move Up / Move Down / Duplicate / Remove actions.
   Move Up/Down replaced v7.15.0's drag handle, which didn't reliably
   detect a drop target once items had multiple stacked fields of
   uneven height. */
.msb-repeater-item { position: relative; }
.msb-repeater-item-actions { display: flex; align-items: center; gap: 4px; margin-left: auto; }
.msb-repeater-item-body { display: flex; gap: 12px; align-items: flex-start; }
.msb-repeater-item-photo-col { flex: 0 0 30%; max-width: 130px; }
.msb-repeater-item-fields-col { flex: 1 1 auto; min-width: 0; }
.msb-repeater-photo-field { margin-bottom: 6px; }
.msb-repeater-photo-field label { display: block; font-size: 11px; color: #888; margin-bottom: 4px; }
.msb-repeater-photo-field .msb-photo-box { max-width: none; }
/* v7.51.0: an icon field's own preview box -- object-fit:contain
   instead of the photo box's cover (a Lucide/custom icon is a small
   stroke shape with a lot of transparent padding around it; cover
   would crop straight into the glyph itself) plus a little internal
   padding so the icon doesn't touch the box's own edges. */
.msb-icon-box img { object-fit: contain; padding: 18%; box-sizing: border-box; }
.msb-icon-btn[disabled] { opacity: .3; cursor: default; pointer-events: none; }

.msb-bottom-bar {
	position: fixed; bottom: 0; left: 0; right: 0; z-index: 30;
	display: flex; align-items: center; gap: 8px; padding: 12px 16px;
	background: #fff; border-top: 1px solid var(--msb-border);
	/* v7.39.0: safety net for the narrowest devices (see the tiered
	   shrink rules below) -- scrolls instead of clipping if a real
	   viewport somehow still can't fit everything. */
	overflow-x: auto;
}
/* v7.41.2 (Screenshot_46): every icon (Branding, Navigation, Settings,
   Sections, Responsive, View Live) now lives in ONE group with ONE
   gap value -- previously split across two separate flex groups with
   their own, different gaps (a tight 2-4px one for the left 4 icons,
   the bar's own wider 8px for the right 2), which is exactly what
   read as uneven spacing. Update/Publish sits in the same row and
   flow, growing to fill the remaining width at narrow sizes (see the
   max-width:600px rule below) rather than being a second, separately-
   justified group. */
.msb-bottom-bar-group { display: flex; align-items: center; justify-content: center; gap: 8px; flex-shrink: 0; width: 100%; }
.msb-bar-icon-btn {
	flex: 0 0 auto !important; width: 44px; height: 44px; padding: 0 !important;
	display: flex; align-items: center; justify-content: center;
}
.msb-bar-icon-btn svg { width: 20px; height: 20px; }
/* v7.59.26 (Screenshot_75, direct request): Branding/Navigation/
   Settings/Sections carry a small label under their icon now, same
   idea as the desktop panel's own .msb-panel-icon-btn row -- fixed
   44x44 square swapped for an auto-height column so the label has
   room underneath, without changing the icon's own drawn size. Share/
   Live/Update are untouched (still plain icon-only or, for Update,
   its own full-width split button). */
.msb-bar-icon-btn-labeled {
	width: auto; min-width: 46px; height: auto; flex-direction: column; gap: 3px; padding: 4px 4px 3px !important;
}
.msb-bar-icon-btn-label { font-size: 9.5px; font-weight: 500; line-height: 1; color: var(--msb-muted); white-space: nowrap; }
.msb-bar-icon-btn-labeled:hover .msb-bar-icon-btn-label { color: var(--msb-accent); }
.msb-publish-btn-grow {
	font-size: 14px; padding-left: 18px; padding-right: 18px; height: 44px; flex: 1 1 auto;
	display: flex; align-items: center; justify-content: center; gap: 5px; white-space: nowrap;
}
/* v7.59.24 (Screenshot_70, direct request): the small state label
   after "Update"/"Publish" ("Update Draft", "Update Private", etc) --
   .msb-publish-btn-grow's own flex:1 1 auto already lets the button
   grow past its old fixed content width to fit this extra text
   (rather than needing an explicit min-width bump), so the only CSS
   this itself needs is to read as visually secondary to the main
   word: smaller, a touch translucent, normal weight against the main
   label's bold. */
.msb-publish-btn-sublabel { font-size: 12px; font-weight: 400; opacity: .85; }
/* Update/Publish visibly distinguishes "changes waiting to go live"
   (highlighted, the normal accent color) from "already live, nothing new
   to push" (quiet grey) -- see updatePublishBtnLook() in frontend.js. */
.msb-btn-dirty { background: var(--msb-accent); border-color: var(--msb-accent); color: #fff; }
.msb-btn-clean { background: #eee; border-color: #eee; color: #888; }
@media (max-width: 480px) {
	.msb-bar-icon-btn:not(.msb-bar-icon-btn-labeled) { width: 38px; height: 38px; }
	.msb-bar-icon-btn svg { width: 18px; height: 18px; }
	.msb-bar-icon-btn-label { font-size: 9px; }
	.msb-bottom-bar-group { gap: 6px; }
	/* Screenshot_2 (direct request): below the "Update"/"Draft" sits
	   too wide on a phone-width bottom bar -- the sublabel drops under
	   the main label instead of running after it on the same line, so
	   the split button reads as two short stacked lines instead of one
	   wide one. Height/padding switch to auto so the extra line has
	   room without the button growing wider. */
	.msb-publish-btn-grow {
		flex-direction: column; gap: 1px; height: auto; min-height: 44px;
		padding-top: 6px; padding-bottom: 6px; line-height: 1.15;
	}
	.msb-publish-btn-sublabel { display: block; font-size: 10.5px; }
}
@media (max-width: 380px) {
	/* height stays auto (set in the 480px tier above) so the stacked
	   Update/Draft two-line label still has room at this narrower tier. */
	.msb-publish-btn-grow { font-size: 12.5px; padding-left: 10px; padding-right: 10px; min-height: 40px; }
	.msb-bar-icon-btn:not(.msb-bar-icon-btn-labeled) { width: 34px; height: 34px; }
	.msb-bar-icon-btn svg { width: 17px; height: 17px; }
	.msb-bar-icon-btn-labeled { min-width: 38px; }
	.msb-bar-icon-btn-label { font-size: 8.5px; }
	.msb-bottom-bar { padding-left: 10px; padding-right: 10px; }
	.msb-bottom-bar-group { gap: 4px; }
}
@media (max-width: 340px) {
	.msb-bar-icon-btn:not(.msb-bar-icon-btn-labeled) { width: 30px; height: 30px; }
	.msb-bar-icon-btn svg { width: 15px; height: 15px; }
	.msb-bar-icon-btn-labeled { min-width: 34px; }
	.msb-bar-icon-btn-label { display: none; } /* No room left for labels at all at this width -- icons alone. */
	.msb-publish-btn-grow { padding-left: 6px; padding-right: 6px; font-size: 11px; }
}
/* On mobile, Share now lives as a floating button on the Preview page
   itself (native share sheet -- WhatsApp, Mail, Messages, etc.) instead
   of here, so it's removed from the bottom bar to make room. Desktop
   keeps Share in the bottom bar exactly as before -- this only hides it
   below the same width the breakpoint-preview page treats as mobile. */
@media (max-width: 600px) {
	#msb-share-btn { display: none; }
}

/* Subtle, minimal motion on every plugin button -- a slight lift on
   hover and a slight press-down on active, tinted with the platform's
   own Primary Color (--msb-accent) rather than a generic gray, so it
   reads as an intentional part of this UI rather than a browser
   default. Purely visual -- no layout shift, since transform doesn't
   affect box size. */
.msb-btn { transition: transform .1s ease, box-shadow .12s ease, border-color .12s ease, background .12s ease; }
.msb-btn:hover { border-color: var(--msb-accent); box-shadow: 0 2px 8px rgba(0,0,0,.08); transform: translateY(-1px); }
.msb-btn:active { transform: translateY(0); box-shadow: none; }
.msb-btn-primary:hover, .msb-btn-dirty:hover { filter: brightness(1.08); border-color: var(--msb-accent); }
.msb-btn-clean:hover { border-color: #ddd; filter: none; box-shadow: none; transform: none; }
.msb-bar-icon-btn:hover { border-color: var(--msb-accent); color: var(--msb-accent); }
.msb-bar-icon-btn-active { border-color: var(--msb-accent); color: var(--msb-accent); background: var(--msb-bg-alt, #f7f4f2); }

/* ---- Edit sheet: centered modal on every screen size ---- */
.msb-sheet-backdrop {
	position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 50;
	display: flex; align-items: center; justify-content: center; padding: 16px;
}
.msb-sheet {
	background: #fff; width: 100%; max-width: 420px; border-radius: 16px;
	max-height: 85vh; overflow: hidden; padding: 0;
	display: flex; flex-direction: column;
}
@media (min-width: 900px) {
	.msb-sheet { width: 50vw; max-width: 640px; }
}
.msb-sheet-head {
	display: flex; align-items: center; gap: 8px; margin-bottom: 0; flex-shrink: 0;
	padding: 18px 18px 14px; border-bottom: 1px solid var(--msb-border);
}
.msb-sheet-body { overflow-y: auto; overflow-x: hidden; flex: 1; min-height: 0; padding: 14px 18px; }
.msb-sheet-head-icon-btn {
	display: flex; flex-direction: column; align-items: center; gap: 1px; flex-shrink: 0;
	background: none; border: none; cursor: pointer; color: var(--msb-fg, #141414); padding: 2px 4px;
}
.msb-sheet-head-icon-btn svg { width: 18px; height: 18px; }
.msb-sheet-head-icon-btn span { font-size: 9px; font-weight: 600; text-transform: uppercase; letter-spacing: .02em; }
.msb-sheet-head-icon-btn:active { opacity: .6; }
.msb-sheet-head h2 { flex: 1; min-width: 0; margin: 0; font-size: 14px; font-weight: 600; color: var(--msb-accent); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msb-sheet-back-btn {
	display: flex; align-items: center; gap: 3px; flex-shrink: 0;
	background: none; border: none; cursor: pointer; padding: 4px 2px 4px 0;
	font-size: 14px; font-weight: 600; color: var(--msb-fg, #141414);
}
.msb-sheet-back-btn svg { width: 18px; height: 18px; }
.msb-sheet-close-icon {
	background: none; border: none; font-size: 26px; line-height: 1; cursor: pointer;
	color: var(--msb-muted); padding: 4px 6px; margin: -4px -6px -4px 0;
	border-radius: 8px; flex-shrink: 0;
}
.msb-sheet-close-icon:hover { background: var(--msb-bg-alt, #f7f4f2); color: var(--msb-fg); }
.msb-sheet textarea, .msb-sheet input[type="text"], .msb-sheet input[type="url"], .msb-sheet input[type="email"], .msb-sheet select {
	width: 100%; padding: 10px 12px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 14px;
}
.msb-sheet-actions {
	display: flex; gap: 8px; margin-top: 0; flex-shrink: 0;
	padding: 12px 18px calc(12px + env(safe-area-inset-bottom)); border-top: 1px solid var(--msb-border);
}
.msb-sheet-actions .msb-btn { flex: 1; }

/* "New Site" popup (dashboard hero CTA): wider than the default sheet
   since it holds a template grid, not a single field/form. */
.msb-newsite-sheet { max-width: 480px; }
@media (min-width: 900px) {
	.msb-newsite-sheet { width: 90vw; max-width: 900px; }
}

/* v7.40.0 (Screenshot_36): Leads popup, opened from a site card on My
   Sites -- wide enough for the source-filter/search/export toolbar to
   sit on one line rather than default-sheet width forcing an early wrap. */
.msb-leads-popup-sheet { max-width: 480px; }
@media (min-width: 900px) {
	.msb-leads-popup-sheet { width: 70vw; max-width: 720px; }
}
.msb-newsite-search { margin-bottom: 16px; }
.msb-newsite-grid { margin-top: 4px; }


.msb-richtext-toolbar { display: flex; gap: 6px; margin-bottom: 8px; }
.msb-richtext-toolbar button {
	width: 32px; height: 32px; border-radius: 6px; border: 1px solid var(--msb-border); background: #fff; cursor: pointer;
}
.msb-richtext-area {
	min-height: 90px; border: 1px solid var(--msb-border); border-radius: 8px; padding: 10px 12px; font-size: 14px;
}
.msb-richtext-area a { color: #1a6fd4; text-decoration: underline; }
.msb-richtext-toolbar button u { text-decoration: underline; }

/* Compact color grid used in Site Settings -- several colors per row instead of a full-width bar each */
.msb-color-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 12px; margin-bottom: 16px; }
.msb-color-field label { display: block; font-size: 12px; font-weight: 600; margin-bottom: 4px; }
.msb-color-field-inputs {
	display: flex; align-items: stretch; border: 1px solid var(--msb-border); border-radius: 8px;
	overflow: hidden; background: #fff;
}
.msb-color-field-inputs .msb-color-swatch {
	width: 38px; height: 38px; padding: 0; margin: 0; border: none; border-right: 1px solid var(--msb-border);
	flex-shrink: 0; cursor: pointer; border-radius: 0;
}
.msb-color-hex { border: none !important; padding: 0 8px !important; font-size: 12px !important; width: 100% !important; font-family: monospace; height: 38px; }

/* v7.50.12: confirmed real, live-reported issue -- the "currently
   selected image" preview shown inside the upload dropzone (Your
   Uploads tab) was taking up too much vertical space, pushing the
   rest of the panel (Media Library grid, search) further down before
   any scrolling. Reduced 25% on desktop (200px -> 150px) and, since a
   phone/tablet screen has far less vertical room to spare in the
   first place, a further cut down to 50% of the ORIGINAL height
   (100px) under the same 900px breakpoint already used elsewhere in
   this file for tablet-and-narrower tweaks -- tablet and mobile share
   one value here since both were asked for the same 50% reduction.
   Only the height is capped; max-width/object-fit are unchanged, so
   the image still displays at its full natural width up to 200px,
   just shorter. */
.msb-upload-preview { max-width: 200px; width: 100%; height: auto; max-height: 150px; object-fit: contain; border-radius: 8px; margin-bottom: 10px; display: block; background: var(--msb-bg-alt, #f0eeec); }
@media (max-width: 900px) {
	.msb-upload-preview { max-height: 100px; }
}

/* ---- Site Logo + Favicon, side by side (v7.5.6) ---- */
/* ---- Site Logo / Favicon tiles (v7.19.5, Screenshot_16) ----
   Two identical, equally sized cards rather than the old mismatched
   thumb-plus-stretched-button pair. Deliberately NOT inside any
   desktop-only media query: this is the same on phone, tablet and
   desktop. See brandTileHtml() in frontend.js. ---- */
.msb-brand-icons-row { display: flex; gap: 12px; align-items: stretch; }
.msb-brand-icon-col {
	flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center;
	padding: 12px 10px; border: 1px solid var(--msb-border); border-radius: 10px; background: #fff;
}
.msb-brand-tile-head { display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 10px; }
.msb-brand-tile-label { font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--msb-muted, #6b6b6b); }
.msb-brand-tile {
	width: 64px; height: 64px; border-radius: 10px; overflow: hidden; flex-shrink: 0;
	display: flex; align-items: center; justify-content: center;
	background: repeating-conic-gradient(#f0f0f0 0% 25%, #fafafa 0% 50%) 50% / 10px 10px;
	border: 1px solid var(--msb-border);
}
.msb-brand-tile img { max-width: 78%; max-height: 78%; object-fit: contain; display: block; }
.msb-brand-tile-empty { background: #fafafa; border-style: dashed; color: #c2c2c2; }
.msb-brand-tile-empty svg { width: 24px; height: 24px; }
.msb-brand-tile-empty img { opacity: .45; }
/* v7.45.5 (Screenshot_24): the preview tile is a real click target now,
   same action as the "Upload"/"Replace" text below it -- a subtle hover/
   focus ring makes that discoverable without changing the tile's actual
   size or look at rest. */
.msb-brand-tile-clickable { cursor: pointer; }
.msb-brand-tile-clickable:hover,
.msb-brand-tile-clickable:focus-visible { border-color: var(--msb-accent); outline: none; }
.msb-brand-tile-clickable.msb-brand-tile-empty:hover,
.msb-brand-tile-clickable.msb-brand-tile-empty:focus-visible { background: var(--msb-bg-alt, #f2efec); color: var(--msb-accent); }
.msb-brand-tile-actions { display: flex; align-items: center; gap: 4px; margin-top: 10px; }
.msb-brand-tile-action {
	background: none; border: 0; padding: 4px 6px; cursor: pointer; border-radius: 6px;
	font-size: 12px; font-weight: 600; color: var(--msb-fg, #141414); font-family: inherit;
}
.msb-brand-tile-action:hover { background: var(--msb-bg-alt, #f7f4f2); }
.msb-brand-tile-action-remove { color: #c14b4b; }
.msb-brand-tile-action-remove:hover { background: #fdeeee; }
.msb-brand-tile-actions .msb-brand-tile-action + .msb-brand-tile-action { border-left: 1px solid var(--msb-border); border-radius: 0 6px 6px 0; }
.msb-brand-tile-status { font-size: 11px; color: #888; margin: 4px 0 0; text-align: center; min-height: 0; }
.msb-brand-tile-status:empty { display: none; }
/* v7.19.5: these used to stack on a phone, because the old logo row was
   too wide to sit next to anything. The tiles are compact and equal now,
   so two of them fit comfortably side by side even on a 320px screen --
   which keeps the "two matching cards" reading identical at every width
   instead of turning into a tall stack on mobile only. */
@media (max-width: 480px) {
	.msb-brand-icons-row { gap: 8px; }
	.msb-brand-icon-col { padding: 10px 6px; }
}

.msb-logo-row { display: flex; align-items: center; gap: 10px; }
.msb-logo-thumb { flex-shrink: 0; width: 52px; height: 52px; border-radius: 8px; background: repeating-conic-gradient(#eee 0% 25%, #f7f7f7 0% 50%) 50% / 12px 12px; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.msb-logo-thumb img { max-width: 100%; max-height: 100%; object-fit: contain; }
.msb-logo-thumb-placeholder { opacity: .5; border: 1px dashed #ccc; background: #fafafa; }
/* v7.59.39: the thumbnail is now a real click target in the Edit
   Header popup too (matching Site Settings > Branding > Colors' own
   .msb-brand-tile-clickable, v7.45.5) -- same cursor/hover/focus
   affordance so it visibly reads as clickable, not just decorative. */
.msb-logo-thumb-clickable { cursor: pointer; transition: opacity .15s ease, box-shadow .15s ease; }
.msb-logo-thumb-clickable:hover { opacity: .85; }
.msb-logo-thumb-clickable:focus-visible { outline: 2px solid var(--msb-role-buttons, #0B5FFF); outline-offset: 2px; }
.msb-logo-thumb-placeholder.msb-logo-thumb-clickable:hover { opacity: .8; border-color: #999; }
.msb-logo-row .msb-btn { flex: 1; }
.msb-logo-size-row { display: flex; align-items: center; gap: 8px; margin-top: 10px; font-size: 13px; color: #666; }
.msb-logo-size-row input[type="range"] { flex: 1; }
.msb-logo-size-row span:last-child { flex-shrink: 0; width: 40px; text-align: right; font-variant-numeric: tabular-nums; }

.msb-info-icon {
	display: inline-flex; align-items: center; justify-content: center;
	width: 16px; height: 16px; border-radius: 50%; background: #e2e2e2; color: #666;
	font-size: 11px; font-style: italic; font-family: Georgia, serif; cursor: help; flex-shrink: 0;
}

.msb-spacing-preview { margin-top: 12px; padding: 12px 14px; border-radius: 8px; background: var(--msb-bg-alt, #f7f4f2); }
.msb-spacing-preview-row { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #666; margin-bottom: 6px; }
.msb-spacing-preview-row:last-child { margin-bottom: 0; }
.msb-spacing-preview-label { flex: 0 0 110px; }
.msb-spacing-preview-track { flex: 1; height: 8px; background: #e4e0db; border-radius: 4px; overflow: hidden; }
.msb-spacing-preview-bar { height: 100%; background: var(--msb-primary, #0B5FFF); border-radius: 4px; transition: width .15s ease; }
.msb-spacing-preview-val { flex: 0 0 40px; text-align: right; font-variant-numeric: tabular-nums; }

/* ---- In-app toast (replaces native alert) ---- */
.msb-toast {
	position: fixed; left: 50%; bottom: 90px; transform: translateX(-50%) translateY(20px);
	background: #1a1a1a; color: #fff; padding: 12px 20px; border-radius: 10px;
	font-size: 14px; max-width: 90vw; text-align: center; z-index: 200;
	opacity: 0; transition: opacity .2s ease, transform .2s ease; cursor: pointer;
	box-shadow: 0 6px 20px rgba(0,0,0,.25);
}
.msb-toast-show { opacity: 1; transform: translateX(-50%) translateY(0); }
.msb-toast-error { background: #7a1f1f; }
.msb-toast-success { background: #1f5e3a; }

/* ---- In-app confirm/prompt (replaces native confirm/prompt) ---- */
.msb-confirm-sheet { padding: 28px 24px 24px; text-align: center; }
.msb-confirm-icon { width: 44px; height: 44px; margin: 0 auto 14px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: #eef2ff; color: #4f5bd5; }
.msb-confirm-icon svg { width: 22px; height: 22px; }
.msb-confirm-icon-warn { background: #fdeeee; color: #d64545; }
.msb-confirm-sheet p { margin: 0 0 14px; font-size: 15px; }
.msb-confirm-sheet input[type="text"] { width: 100%; padding: 10px 12px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 14px; margin-bottom: 4px; text-align: left; }
.msb-btn-danger { background: #d64545; border-color: #d64545; color: #fff; }
.msb-btn-danger[disabled] { background: #f0c4c4; border-color: #f0c4c4; color: #fff; cursor: not-allowed; }
.msb-media-grid { display: flex; flex-wrap: wrap; gap: 8px; max-height: 220px; overflow-y: auto; }
.msb-media-thumb-wrap { position: relative; flex-shrink: 0; }
.msb-media-thumb { max-width: 100px; width: auto; height: auto; max-height: 100px; object-fit: contain; border-radius: 6px; cursor: pointer; border: 2px solid transparent; background: var(--msb-bg-alt, #f0eeec); display: block; transition: border-color .12s ease, transform .12s ease; }
.msb-media-thumb:hover { border-color: var(--msb-primary, #111); transform: scale(1.03); }
/* v7.17.12: video item in the shared image+video gallery -- object-fit
   on a <video> works the same as on an <img>, and the play badge marks
   it as video before a click. */
.msb-media-thumb-video video.msb-media-thumb { object-fit: cover; }
.msb-media-video-badge {
	position: absolute; bottom: 4px; left: 4px; width: 20px; height: 20px;
	border-radius: 50%; background: rgba(0,0,0,.7); color: #fff;
	display: flex; align-items: center; justify-content: center; pointer-events: none;
}
.msb-media-video-badge svg { width: 10px; height: 10px; }
.msb-media-delete-btn {
	position: absolute; top: -6px; right: -6px; width: 22px; height: 22px;
	border-radius: 50%; border: 1px solid var(--msb-border); background: #fff; color: #c0392b;
	display: flex; align-items: center; justify-content: center; padding: 0; cursor: pointer;
	box-shadow: 0 1px 3px rgba(0,0,0,.15);
}
.msb-media-delete-btn svg { width: 12px; height: 12px; }
.msb-media-delete-btn:hover { background: #fdecea; border-color: #e8a29a; }
/* v7.53.0 BUGFIX: confirmed real, live-reported issue -- inside the
   masonry grid (.msb-masonry-grid uses CSS column-count), a plain
   block-level element like this empty-state message is treated as
   flowable COLUMN CONTENT by default, same as any other text -- if
   it's tall enough, the browser splits it mid-sentence across two
   columns instead of keeping it as one readable block, which is
   exactly the "breaking in 2 column like mess" reported. column-span:
   all makes it explicitly escape the column flow and span the full
   grid width as a single block, and the flex centering keeps it
   visually centered within that full-width space. */
.msb-media-empty { font-size: 12px; color: var(--msb-muted); padding: 16px 8px; text-align: center; column-span: all; display: flex; align-items: center; justify-content: center; }

/* ---- Image picker: drag-and-drop upload zone + progress bar (Task 17) ---- */
.msb-upload-zone {
	display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
	border: 2px dashed var(--msb-border); border-radius: 12px; padding: 22px 14px; text-align: center;
	cursor: pointer; background: var(--msb-bg-alt, #f7f4f2); transition: border-color .15s ease, background .15s ease;
}
.msb-upload-zone:hover, .msb-upload-zone:focus-visible { border-color: var(--msb-primary, #111); outline: none; }
.msb-upload-zone-drag { border-color: var(--msb-primary, #0B5FFF); background: #eef4ff; }
.msb-upload-zone-busy { opacity: .6; pointer-events: none; }
.msb-upload-zone-icon { width: 28px; height: 28px; color: var(--msb-muted); }
.msb-upload-zone-icon svg { width: 100%; height: 100%; }
.msb-upload-zone-text { font-size: 13px; color: var(--msb-muted); margin: 0; }
.msb-upload-zone .msb-upload-preview { margin: 0 0 8px; }

.msb-upload-progress-wrap { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.msb-upload-progress-bar { flex: 1; height: 8px; border-radius: 4px; background: var(--msb-bg-alt, #eee); overflow: hidden; }
.msb-upload-progress-fill { height: 100%; width: 0%; background: var(--msb-primary, #0B5FFF); border-radius: 4px; transition: width .15s ease; }
.msb-upload-progress-label {
	font-size: 12px; font-variant-numeric: tabular-nums; color: var(--msb-muted); flex-shrink: 0;
	min-width: 34px; width: auto; white-space: nowrap; text-align: right;
}
.msb-upload-status { font-size: 12px; color: #d64545; margin: 6px 0 0; min-height: 14px; }

.msb-media-gallery-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 16px; margin-bottom: 14px; }
.msb-media-gallery-head .msb-section-title { margin: 0; flex-shrink: 0; }
.msb-media-search {
	flex: 1; max-width: 180px; padding: 7px 10px; font-size: 13px;
	border: 1px solid var(--msb-border); border-radius: 8px;
}

.msb-media-skeleton {
	width: 100px; height: 100px; border-radius: 6px; flex-shrink: 0;
	background: linear-gradient(90deg, var(--msb-bg-alt, #eee) 25%, #f6f4f2 37%, var(--msb-bg-alt, #eee) 63%);
	background-size: 400% 100%; animation: msb-skeleton-shimmer 1.4s ease infinite;
}
@keyframes msb-skeleton-shimmer {
	0% { background-position: 100% 50%; }
	100% { background-position: 0 50%; }
}

/* v7.50.8: "Choose Image" panel's Your Uploads / Stock Photos tabs --
   reuses the same .msb-panel-tabs/.msb-panel-tab bottom-border look
   already used by Site Settings/Pages/SEO, just inside this one sheet.
   A little tighter on top margin than those since it sits directly
   under the sheet's own title, with no other content above it. */
.msb-image-picker-tabs { margin-top: 0; margin-bottom: 16px; }
/* v7.60.1, direct correction of v7.60.0: that min-height (460px) was
   meant to stop the "Choose Image" popup resizing/flickering while
   thumbnails load or while switching tabs -- but it was taller than
   either tab's own real content in several common cases (a short
   Stock Photos result set, "Your Uploads" before any real photos
   exist), leaving a large, obviously-unintentional blank gap below
   the content instead. Removed: the actual anti-flicker mechanism
   that matters was already in place before v7.60.0 and needs no
   help from an outer min-height -- both tabs' own image grids
   (`.msb-stock-scroll-wrap`/`.msb-uploads-scroll-wrap`, just below)
   already carry a fixed max-height + overflow-y:auto, so the popup's
   overall height never changes as thumbnails resolve inside that
   fixed-size, scrollable area. */

.msb-stock-search-wrap { position: relative; flex: 1; max-width: 220px; }
.msb-stock-search-wrap svg {
	position: absolute; top: 50%; left: 9px; transform: translateY(-50%);
	width: 14px; height: 14px; color: var(--msb-muted); pointer-events: none;
}
.msb-stock-search-input { max-width: none; width: 100%; padding-left: 28px; }

/* Same base thumb-wrap positioning as the Your Uploads tab
   (.msb-media-thumb-wrap covers position:relative) -- these add what's
   specific to a Pexels result: the photographer credit strip and the
   in-flight import spinner. */
.msb-stock-thumb-wrap { cursor: pointer; }
.msb-stock-thumb-wrap .msb-media-thumb { cursor: pointer; }
.msb-stock-credit {
	position: absolute; left: 0; right: 0; bottom: 0;
	font-size: 10px; line-height: 1.3; color: #fff; background: rgba(0,0,0,.55);
	padding: 3px 6px; border-radius: 0 0 6px 6px; white-space: nowrap; overflow: hidden;
	text-overflow: ellipsis; opacity: 0; transition: opacity .12s ease; pointer-events: none;
}
.msb-stock-thumb-wrap:hover .msb-stock-credit, .msb-stock-thumb-wrap:focus-visible .msb-stock-credit { opacity: 1; }
.msb-stock-thumb-loading { position: relative; pointer-events: none; opacity: .55; }
.msb-stock-thumb-loading::after {
	content: ""; position: absolute; top: 50%; left: 50%; width: 20px; height: 20px;
	margin: -10px 0 0 -10px; border-radius: 50%;
	border: 2px solid rgba(255,255,255,.5); border-top-color: #fff;
	animation: msb-build-spin 0.8s linear infinite;
}
/* v7.50.10 BUGFIX: confirmed real, live-reported regression -- v7.50.9
   put the column-layout AND the fixed-height/overflow-y:auto scroll
   behavior on the SAME element, which made CSS columns treat the
   fixed height as a hard per-column-set limit: once the first set of
   3 columns filled up vertically, it started a SECOND set of 3
   columns to the right instead of growing the existing set downward
   -- a horizontal scrollbar, the opposite of "just want vertical
   scroll." Fixed by splitting the two jobs across two elements:
   .msb-masonry-grid (this rule) has NO height cap of its own -- it
   grows to fit its content as a single set of columns, however tall
   that turns out to be -- while a separate wrapping element
   (.msb-stock-scroll-wrap / .msb-uploads-scroll-wrap, below) carries
   the fixed height + overflow-y:auto that actually clips/scrolls it.
   Shared between both the Stock Photos AND Your Uploads tabs now
   (v7.50.10) -- Your Uploads' own old fixed-box flex layout left
   uneven empty space around differently-shaped photos for the exact
   same underlying reason a plain grid always does with mixed aspect
   ratios; masonry fixes both tabs the same way. Each photo keeps its
   own real aspect ratio at full column width instead of being boxed
   into a fixed square, so a tall portrait shot and a wide landscape
   shot both look intentional side by side. Overrides the base
   .msb-media-grid's flex/max-width/max-height rules (same
   specificity, this rule comes later in the file so it wins) --
   .msb-media-grid still supplies the skeleton shimmer, unchanged. */
.msb-masonry-grid { display: block; column-count: 3; column-gap: 8px; max-height: none; overflow: visible; width: 100%; }
.msb-masonry-grid .msb-media-thumb-wrap { display: inline-block; width: 100%; break-inside: avoid; -webkit-column-break-inside: avoid; margin: 0 0 8px; }
.msb-masonry-grid .msb-media-thumb { width: 100%; max-width: none; height: auto; max-height: none; display: block; border-radius: 6px; }
.msb-masonry-grid .msb-media-skeleton { width: 100%; max-width: none; height: 90px; display: inline-block; margin: 0 0 8px; }
/* v7.59.39, real bug fixed (screenshot, real phone -- "Your Uploads"
   stacking one image per row despite visibly having room for 2): this
   breakpoint was 420px, which SHOULD already cover a typical ~390px
   phone -- but the actual wrapper this grid sits in
   (.msb-media-scroll-wrap/.msb-uploads-scroll-wrap) had no `width`
   assertion of its own, and a `column-count` container that hasn't
   been given an explicit width can end up sized by its own (still
   loading, in-flight) content on first paint rather than by the
   viewport -- exactly the kind of narrow-then-never-recomputed width
   a CSS multicol layout can get stuck at. Added `width:100%` directly
   on `.msb-masonry-grid` itself (belt-and-suspenders with the fix
   below) so it always sizes from its real parent, not its own
   content. Also widened the breakpoint from 420px to 480px --
   comfortably covers every current phone width in portrait (the
   largest, iPhone Pro Max, is 430px), removing any razor-thin margin
   between "should be 2 columns" and "isn't quite." */
@media (max-width: 480px) {
	.msb-masonry-grid { column-count: 2; }
}

/* v7.60.4 BUGFIX, confirmed real (Screenshots 1/2/6/7 -- consistent
   blank white rectangles in the grid): v7.60.3's CSS Grid fixed the
   "stacks in one column" bug, but a plain CSS Grid's default
   align-items:stretch stretches every grid ITEM (each
   .msb-media-thumb-wrap) to match the tallest item sharing its row --
   since the <img> inside stays at its own natural height, that
   stretch is exactly what showed up as dead white space below any
   shorter photo next to a taller one. Fixed by building the layout
   from two separate pieces instead of relying on one CSS mechanism
   for both properties at once: frontend.js's layoutMedia()
   (openImagePicker() -> wireUploadTab()) now creates N sibling
   .msb-uploads-photo-col elements as this grid's only children and
   distributes every photo across them round-robin (item i -> column
   i % N) -- guaranteeing the requested "fill row 1 across every
   column, then row 2" order -- while THIS class only has to lay those
   N columns out side by side, no row-height coordination between them
   at all. Each column then packs its own photos tightly with no
   forced shared height with its neighbors -- the actual Masonry-style
   look (Screenshot 8): columns of different total height, zero dead
   space, just a small consistent gap between photos, matching the
   reference image exactly. Column COUNT (3 desktop / 2 on phones,
   <=480px) is decided in JS (numCols(), same breakpoint as before)
   since the distribution itself needs to know N, not just the CSS
   paint -- this class no longer needs its own breakpoint rule. */
.msb-uploads-photo-grid { display: flex; align-items: flex-start; gap: 8px; max-height: none; overflow: visible; width: 100%; }
/* Direct-child skeletons are the brief initial-loading state (3 flat
   placeholder blocks, before any real columns exist yet) -- sized to
   share the row evenly same as a real column would, so the loading
   state doesn't visibly jump when it's replaced by the first real
   layoutMedia() render. */
.msb-uploads-photo-grid > .msb-media-skeleton { flex: 1 1 0; width: auto; max-width: none; height: 90px; margin: 0; }
.msb-uploads-photo-col { display: flex; flex-direction: column; flex: 1 1 0; min-width: 0; gap: 8px; }
.msb-uploads-photo-col .msb-media-thumb-wrap { width: 100%; margin: 0; }
.msb-uploads-photo-col .msb-media-thumb { width: 100%; max-width: none; height: auto; max-height: none; display: block; border-radius: 6px; }
/* The empty-state message ("No previous uploads yet...") is a plain
   <p> set directly as this grid's only child via lib.innerHTML
   (layoutMedia() in frontend.js), never inside a column -- a flex
   container's default sizing already lets it just take its own
   natural width/height here, no override needed (unlike the CSS Grid
   version this replaced, where it had to be force-spanned across
   every column explicitly). */
/* v7.50.10: the actual scroll boundary -- fixed height, vertical
   overflow only, never horizontal. Two heights: Stock Photos got
   taller (~3 rows) per a separate earlier request; Your Uploads keeps
   its original 220px, unchanged apart from the masonry fix above. */
.msb-media-scroll-wrap { overflow-y: auto; overflow-x: hidden; width: 100%; }
.msb-stock-scroll-wrap { max-height: 340px; }
.msb-uploads-scroll-wrap { max-height: 220px; }
/* v7.50.11: back to scroll-triggered loading (per direct request),
   this time gated in JS (see wireStockTab()'s own checkScroll()) so
   it can't repeat v7.50.9's "loads continuously while scrolling" bug
   -- one batch per genuine additional scroll past the last trigger,
   not a rolling load on every near-bottom scroll event. This small
   spinner is the only loading affordance now; toggled directly via
   JS (display:flex/none), shown only while a batch is actually being
   fetched. */
.msb-stock-infinite-loader { display: none; justify-content: center; align-items: center; padding: 10px 0 2px; }
.msb-stock-spinner {
	width: 18px; height: 18px; border-radius: 50%; display: inline-block;
	border: 2px solid var(--msb-border); border-top-color: var(--msb-primary, #111);
	animation: msb-build-spin 0.8s linear infinite;
}
.msb-stock-attribution { font-size: 11px; color: var(--msb-muted); text-align: center; margin: 10px 0 0; }
.msb-stock-attribution a { color: inherit; text-decoration: underline; }

/* v7.51.0: Icon Library / Upload Custom Icon picker grid -- plain
   uniform small square thumbs (NOT the masonry layout the photo
   pickers use above; every icon shares the same square aspect ratio,
   so a fixed grid is the correct shape here, not a workaround).
   Neutral light box + object-fit:contain + padding so a stroke-based
   glyph never touches its own edges and stays legible at a small
   size regardless of the icon's own proportions. Icons render at
   their natural (often black/dark) stroke color in this browsing
   grid -- deliberately NOT brand-tinted here, same as any other icon
   picker gallery; the live site is what applies the actual chip/
   brand tinting (MSB_Global_Chrome::icon_utility_css()) once picked. */
/* v7.53.1 BUGFIX: confirmed real, live-reported issue -- "enable load
   on scroll here, as user scroll down it should load other icons."
   wireIconGridTab()'s checkScroll() (frontend.js) listens for a
   'scroll' event on THIS grid's own wrapper (.msb-icon-scroll-wrap,
   id="{prefix}-scroll-wrap"), same as Stock Photos/Your Uploads do on
   their own wrappers. But unlike those two -- which each got their
   own dedicated scroll-boundary class below (.msb-stock-scroll-wrap /
   .msb-uploads-scroll-wrap) as part of the v7.50.10 masonry fix --
   .msb-icon-grid was never given the matching override, so it still
   inherits the base .msb-media-grid rule's OWN max-height:220px +
   overflow-y:auto. That makes the INNER grid element the one that
   actually scrolls, while the OUTER wrapper .checkScroll() is
   listening on never scrolls at all (it just shrink-wraps the grid's
   own fixed height) -- so scrolling the icon list never fired the
   'scroll' event, and the next batch of Lucide icons never loaded.
   Same fix pattern as v7.50.10: strip the grid's own height cap so it
   grows to fit all loaded icons, and move the real fixed-height/
   overflow-y:auto scroll boundary onto the wrapper below, where the
   scroll listener actually is. */
.msb-icon-grid { gap: 6px; max-height: none; overflow: visible; }
.msb-icon-scroll-wrap { max-height: 320px; }
.msb-icon-thumb-wrap { cursor: pointer; }
/* v7.55.5 fix (rulebook §64b): the currently-picked icon now gets a
   visible marker in the grid -- reuses the same badges-role
   guaranteed-contrast pair and pill-shaped-option visual language
   already established for .msb-icon-settings-option-active (Icon
   Settings' own Stroke Width/Size buttons), rather than inventing a
   new "this one is selected" treatment. A ring (not a solid fill)
   since the thumb's own background/icon color must stay exactly as
   it renders on the live site -- a fill here would misrepresent it. */
.msb-icon-thumb-wrap-active {
	border-radius: 8px;
	box-shadow: 0 0 0 2px var(--msb-role-badges, var(--msb-primary, #111));
}
.msb-icon-thumb-wrap-active .msb-icon-thumb { background: var(--msb-bg-alt, #f7f4f2); }
.msb-icon-thumb {
	width: 44px; height: 44px; max-width: 44px; max-height: 44px; padding: 8px; box-sizing: border-box;
	object-fit: contain; border-radius: 8px; background: var(--msb-bg-alt, #f7f4f2);
}
.msb-icon-skeleton { width: 44px; height: 44px; }

/* v7.52.0: the picker's own 5-tab bar (Icon Library / Popular /
   Recently Used / Icon Settings / Upload Custom Icon) is wider than
   the 2-tab bar this pattern was designed for -- scrolls horizontally
   instead of wrapping, so every tab stays reachable and the bar's own
   height never grows, at any panel width including mobile. */
.msb-icon-picker-tabs { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.msb-icon-picker-tabs::-webkit-scrollbar { display: none; }
.msb-icon-picker-tabs .msb-panel-tab { flex-shrink: 0; white-space: nowrap; }
/* v7.53.0: Recent/Popular render as a small icon glyph instead of a
   text label -- centers the svg and gives it a fixed, comfortable
   tap target instead of the text tabs' auto-width padding.
   v7.53.1 BUGFIX: confirmed real, live-reported issue -- these icon
   tabs sat visibly out of line with the text tabs beside them ("not
   in the same line as Icon Library and Custom Icons"). Root cause:
   .msb-panel-tab (the shared base rule) uses padding: 0 0 10px --
   ZERO top padding, so a text label's own line-box sits flush against
   the tab bar's top edge, with the 10px gap only below it before the
   border. This icon-only variant instead used padding: 8px 0 (equal
   top AND bottom), a completely different box model that shifted the
   icon down from that shared top edge and shortened the bottom gap --
   different total height, different vertical position, so the two
   tab styles never lined up on the same baseline. Fixed by giving the
   icon tab the SAME box model as the text tabs (zero top padding, the
   same 10px bottom padding) with a fixed content height matching a
   14px/600-weight label's own line-box, so both tab styles now sit
   flush against the same top edge and share the exact same bottom gap
   before the border -- pixel-aligned regardless of which tabs are
   text and which are icon-only. */
.msb-icon-picker-tabs .msb-panel-tab-icon-only { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 17px; padding: 0 0 10px; box-sizing: content-box; }
.msb-icon-picker-tabs .msb-panel-tab-icon-only svg { width: 18px; height: 18px; flex-shrink: 0; }

/* ---- v6.35.0: Typography font-pairing presets ---- */
/* v7.19.14 (img2): was `grid-template-columns: 1fr` -- one tall card
   per row, six of them running well past a screen.
   Three columns are set EXPLICITLY rather than via auto-fill+minmax:
   the desktop settings panel is a fixed 440px (minus 18px padding each
   side = 404px usable), and at that width any minmax floor big enough
   to keep a font name readable resolves to 2 columns, not the 3 this
   redesign is for. Explicit columns also stop a wider sheet from
   silently going to 4, which would shrink the samples past the point
   of being comparable. minmax(0,1fr) rather than plain 1fr so a long
   family name ellipsises instead of forcing the column wider. */
.msb-font-preset-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-bottom: 12px; }
/* Two on a narrow phone sheet -- three 100px cards would leave no room
   for the sample to read as a sample. */
@media (max-width: 430px) {
	.msb-font-preset-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.msb-font-preset-card {
	/* Padding tightened from 14px/16px and the border from 2px: at three
	   cards per row the old values ate most of each card's usable width,
	   which is what forced the samples to be small in the first place. */
	border: 1px solid var(--msb-border); border-radius: 10px; padding: 10px; text-align: left;
	background: #fff; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease;
	display: block; width: 100%; font: inherit; color: inherit;
	appearance: none; -webkit-appearance: none; min-width: 0;
}
.msb-font-preset-card:hover { border-color: #b9b9b9; }
.msb-font-preset-card-active { border-color: var(--msb-fg); box-shadow: inset 0 0 0 1px var(--msb-fg); background: #fafafa; }
/* The sample block is a fixed height so every card in a row lines up
   regardless of how tall its two typefaces happen to render -- without
   it, a display face with tall ascenders makes its whole column taller
   than the ones beside it and the grid stops reading as a comparison. */
.msb-font-preset-sample {
	display: flex; flex-direction: column; align-items: flex-start; justify-content: center;
	gap: 2px; height: 48px; margin-bottom: 8px;
}
.msb-font-preset-heading { font-size: 24px; font-weight: 700; line-height: 1; color: var(--msb-fg); }
.msb-font-preset-body { font-size: 12px; color: var(--msb-muted); line-height: 1.3; }
.msb-font-preset-names { display: block; min-width: 0; }
.msb-font-preset-name {
	display: block; font-size: 12px; font-weight: 600; color: var(--msb-fg);
	white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.msb-font-preset-sub {
	display: block; font-size: 11px; color: var(--msb-muted);
	white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.msb-font-custom-toggle { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; margin: 6px 0 14px; }
.msb-font-custom-body { border-top: 1px solid var(--msb-border); padding-top: 14px; margin-top: 4px; }

/* ---- v6.35.0: Preloader picker (Layout tab) ---- */
.msb-preloader-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(74px, 1fr)); gap: 8px; margin-bottom: 14px; }
.msb-preloader-tile {
	border: 2px solid var(--msb-border); border-radius: 10px; background: #fff; cursor: pointer;
	display: flex; align-items: center; justify-content: center; height: 64px; position: relative; overflow: hidden;
}
.msb-preloader-tile-active { border-color: var(--msb-fg); }
.msb-preloader-tile .msb-pl-spinner { transform: scale(.6); }
.msb-preloader-other-row { display: flex; gap: 8px; margin-bottom: 14px; }
.msb-preloader-other-row .msb-preset-btn { flex: 1; }

/* ---- Image Style live preview (Layout tab) ---- */
.msb-image-style-row { display: flex; align-items: flex-start; gap: 16px; }
.msb-image-style-row > label { flex: 1; min-width: 0; }
.msb-image-style-preview-wrap { flex-shrink: 0; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.msb-image-style-preview { width: 100px; height: 76px; object-fit: cover; border: 1px solid var(--msb-border); transition: border-radius .15s ease; }
.msb-link-btn { border: none; background: none; padding: 0; font-size: 11px; color: var(--msb-muted); text-decoration: underline; cursor: pointer; }
.msb-link-btn:hover { color: inherit; }
@media (max-width: 480px) {
	.msb-image-style-row { flex-wrap: wrap; }
}
#msb-preloader-body { display: flex; flex-direction: column; gap: 12px; }
#msb-preloader-body .msb-preloader-grid, #msb-preloader-body .msb-preloader-other-row { margin-bottom: 0; }
#msb-preloader-body .msb-settings-hint { margin: 0; }
.msb-preloader-bg-mode-row { display: flex; gap: 8px; }
.msb-preloader-bg-mode-btn {
	flex: 1; display: flex; align-items: center; justify-content: center; gap: 8px;
	padding: 8px 12px; border: 1px solid var(--msb-border); border-radius: 8px;
	background: #fff; font-size: 13px; font-weight: 600; cursor: pointer;
}
.msb-preloader-bg-mode-swatch { width: 18px; height: 18px; border-radius: 5px; border: 1px solid var(--msb-border); flex-shrink: 0; }
.msb-preloader-bg-mode-active { border-color: #1a1a1a; box-shadow: inset 0 0 0 1px #1a1a1a; }

/* Spinner shapes for the preview tiles above -- a fixed neutral color
   here, since these are static swatches in the settings UI rather than
   tied to any one site's live accent color (the actual preloader shown
   to visitors picks up the site's real accent color; see
   MSB_Global_Chrome::preloader_css()). */
@keyframes msbPlSpin { to { transform: rotate(360deg); } }
@keyframes msbPlBounce { 0%, 80%, 100% { transform: translateY(0); opacity: .4; } 40% { transform: translateY(-12px); opacity: 1; } }
@keyframes msbPlPulseDot { 0% { transform: scale(0); opacity: 1; } 100% { transform: scale(3.2); opacity: 0; } }
@keyframes msbPlBar { 0%, 100% { transform: scaleY(.3); } 50% { transform: scaleY(1); } }
@keyframes msbPlRipple { 0% { transform: scale(.3); opacity: 1; } 100% { transform: scale(1.3); opacity: 0; } }
@keyframes msbPlFade { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }
@keyframes msbPlOrbit { from { transform: rotate(0) translateY(0); } to { transform: rotate(360deg) translateY(0); } }
.msb-pl-spinner { position: relative; width: 44px; height: 44px; }
.msb-pl-spinner-1 { border: 3px solid rgba(0,0,0,.12); border-top-color: #1a1a1a; border-radius: 50%; animation: msbPlSpin .8s linear infinite; }
.msb-pl-spinner-2 { border: 3px solid transparent; border-top-color: #1a1a1a; border-bottom-color: #1a1a1a; border-radius: 50%; animation: msbPlSpin 1s linear infinite; }
.msb-pl-spinner-3 span { position: absolute; width: 8px; height: 8px; border-radius: 50%; background: #1a1a1a; animation: msbPlBounce 1.2s ease-in-out infinite; }
.msb-pl-spinner-3 span:nth-child(1) { left: 0; animation-delay: 0s; }
.msb-pl-spinner-3 span:nth-child(2) { left: 14px; animation-delay: .15s; }
.msb-pl-spinner-3 span:nth-child(3) { left: 28px; animation-delay: .3s; }
.msb-pl-spinner-3 span:nth-child(4) { display: none; }
.msb-pl-spinner-4 span { position: absolute; width: 10px; height: 10px; border-radius: 50%; background: #1a1a1a; animation: msbPlPulseDot 1.2s ease-in-out infinite; left: 17px; top: 17px; }
.msb-pl-spinner-4 span:nth-child(2) { animation-delay: .2s; }
.msb-pl-spinner-4 span:nth-child(3) { animation-delay: .4s; }
.msb-pl-spinner-4 span:nth-child(4) { display: none; }
.msb-pl-spinner-5 { border-radius: 50%; background: #1a1a1a; animation: msbPlPulseDot 1.1s ease-out infinite; }
.msb-pl-spinner-5 span { display: none; }
.msb-pl-spinner-6 { display: flex; gap: 4px; align-items: flex-end; height: 32px; width: auto; }
.msb-pl-spinner-6 span { width: 6px; height: 100%; background: #1a1a1a; display: block; animation: msbPlBar 1s ease-in-out infinite; border-radius: 2px; }
.msb-pl-spinner-6 span:nth-child(1) { animation-delay: 0s; }
.msb-pl-spinner-6 span:nth-child(2) { animation-delay: .15s; }
.msb-pl-spinner-6 span:nth-child(3) { animation-delay: .3s; }
.msb-pl-spinner-6 span:nth-child(4) { animation-delay: .45s; }
.msb-pl-spinner-7 { border: 3px solid #1a1a1a; border-radius: 50%; animation: msbPlRipple 1.4s ease-out infinite; }
.msb-pl-spinner-7 span { display: none; }
.msb-pl-spinner-8 { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; width: 32px; height: 32px; }
.msb-pl-spinner-8 span { background: #1a1a1a; display: block; animation: msbPlFade 1.2s ease-in-out infinite; }
.msb-pl-spinner-8 span:nth-child(1) { animation-delay: 0s; }
.msb-pl-spinner-8 span:nth-child(2) { animation-delay: .3s; }
.msb-pl-spinner-8 span:nth-child(3) { animation-delay: .6s; }
.msb-pl-spinner-8 span:nth-child(4) { animation-delay: .9s; }
.msb-pl-spinner-9 { position: relative; }
.msb-pl-spinner-9 span { position: absolute; width: 8px; height: 8px; border-radius: 50%; background: #1a1a1a; top: 0; left: 18px; animation: msbPlOrbit 1s linear infinite; transform-origin: 4px 22px; }
.msb-pl-spinner-9 span:nth-child(2) { animation-delay: -.25s; opacity: .7; }
.msb-pl-spinner-9 span:nth-child(3) { animation-delay: -.5s; opacity: .4; }
.msb-pl-spinner-9 span:nth-child(4) { display: none; }
.msb-pl-spinner-10 { border: 3px solid rgba(0,0,0,.12); border-radius: 50%; border-top-color: #1a1a1a; border-right-color: #1a1a1a; animation: msbPlSpin .8s ease-in-out infinite; }
.msb-pl-spinner-10 span { display: none; }

/* ---- v6.35.0: Global Style Presets tab ---- */
/* Kept at 2 columns in the 404px panel: a preset's name ("Coffeeshop",
   "Baby Store") needs more horizontal room than a font sample does, and
   the height win here came from collapsing the card's three stacked
   bands into two, not from adding a column. */
.msb-style-preset-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
@media (min-width: 560px) {
	.msb-style-preset-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.msb-style-preset-card {
	border: 1px solid var(--msb-border); border-radius: 10px; overflow: hidden; background: #fff;
	cursor: pointer; text-align: left; display: block; width: 100%;
	padding: 0; margin: 0; font: inherit; color: inherit; appearance: none; -webkit-appearance: none;
}
.msb-style-preset-card:hover { border-color: #b9b9b9; }
.msb-style-preset-swatch { height: 40px; display: flex; }
.msb-style-preset-swatch span { flex: 1; }
/* v7.19.14 (img5): name and Apply share one row instead of stacking as
   two more full-width bands under the swatch. Apply is a quiet inline
   affordance now, not a solid black bar -- the whole card was always
   the click target, so six filled bars were six false primary actions
   competing with each other and with the sheet's real Update button. */
.msb-style-preset-row {
	display: flex; align-items: center; justify-content: space-between;
	gap: 6px; padding: 7px 9px;
}
.msb-style-preset-name {
	font-size: 12px; font-weight: 600; color: var(--msb-fg); min-width: 0;
	white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.msb-style-preset-apply {
	flex: 0 0 auto; font-size: 11px; font-weight: 700; color: var(--msb-muted);
	text-transform: uppercase; letter-spacing: .04em; transition: color .15s ease;
}
.msb-style-preset-card:hover .msb-style-preset-apply { color: var(--msb-fg); }

/* ---- v7.6.0: Space Settings preset picker (Layout sub-tab) ---- */
.msb-space-preset-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; }
.msb-space-preset-card {
	border: 1px solid var(--msb-border); border-radius: 10px; overflow: hidden; background: #fff;
	cursor: pointer; text-align: left; display: block; width: 100%;
	padding: 0; margin: 0; font: inherit; color: inherit; appearance: none; -webkit-appearance: none;
}
.msb-space-preset-card:hover { border-color: #b9b9b9; }
.msb-space-preset-card-active { border-color: var(--msb-fg); border-width: 2px; }
.msb-space-preset-preview { height: 48px; display: flex; align-items: flex-end; justify-content: center; gap: 6px; background: #f6f6f5; padding: 6px 0; }
.msb-space-preset-bar { width: 8px; background: var(--msb-border); border-radius: 2px; }
.msb-space-preset-card-active .msb-space-preset-bar { background: var(--msb-fg); }
.msb-space-preset-meta { padding: 6px 8px; text-align: center; }
.msb-space-preset-meta strong { font-size: 12px; font-weight: 600; }
@media (max-width: 480px) { .msb-space-preset-grid { grid-template-columns: 1fr 1fr; } }

/* ---- v6.35.0: Custom login/register page (replaces wp-login.php) ---- */
.msb-auth-body { margin: 0; font-family: var(--msb-font-body); background: #eef0fb; }
.msb-auth-body h1, .msb-auth-body h2, .msb-auth-body h3 { font-family: var(--msb-font-heading); }
.msb-auth-shell { display: flex; min-height: 100vh; }
.msb-auth-banner {
	flex: 1; min-height: 100vh; background: #2b1e63 center/cover no-repeat; display: flex; align-items: flex-end;
	padding: 40px; box-sizing: border-box;
}
.msb-auth-quote { color: #fff; max-width: 420px; }
.msb-auth-quote-mark { font-size: 40px; line-height: 1; opacity: .8; }
.msb-auth-quote-author { font-size: 13px; font-weight: 600; opacity: .8; margin: 6px 0 8px; }
.msb-auth-quote-text { font-size: 22px; font-weight: 600; line-height: 1.35; margin: 0; }
.msb-auth-panel { flex: 1; max-width: 460px; background: #fff; display: flex; flex-direction: column; justify-content: center; padding: 48px; box-sizing: border-box; }
/* v7.18.3: now an <a> (was a bare, unlinked <div>) -- inline-block so
   its own margin-bottom still applies the same as it did as a block
   element; anchors don't respect vertical margins in normal inline flow. */
.msb-auth-logo { display: inline-block; margin-bottom: 24px; }
.msb-auth-logo img { display: block; max-width: 100%; }
.msb-auth-tabs { display: flex; gap: 18px; margin-bottom: 24px; border-bottom: 1px solid #eee; }
.msb-auth-tab { padding: 8px 0; font-size: 14px; font-weight: 600; color: #888; text-decoration: none; border-bottom: 2px solid transparent; }
.msb-auth-tab-active { color: #2b1e63; border-bottom-color: #2b1e63; }
.msb-auth-notice { padding: 10px 14px; border-radius: 8px; font-size: 13px; margin-bottom: 16px; }
.msb-auth-notice-error { background: #fdeeee; color: #a12b2b; }
.msb-auth-notice-success { background: #eafaf0; color: #1f7a45; }
.msb-auth-form label { display: block; font-size: 12px; font-weight: 600; margin-bottom: 16px; color: #444; }
.msb-auth-form input[type="text"], .msb-auth-form input[type="email"], .msb-auth-form input[type="password"], .msb-auth-form input[type="tel"],
.msb-auth-otp-block input[type="text"], .msb-auth-otp-block input[type="tel"] {
	display: block; width: 100%; margin-top: 6px; padding: 12px 14px; border: 1px solid #ddd; border-radius: 8px;
	font-size: 14px; box-sizing: border-box;
}
/* v7.19.15 (Screenshot_1): password show/hide eye. The wrapper takes
   over the input's own margin-top so the field sits exactly where it
   did before the <span> was introduced, and the input reserves room on
   the right so a long password never runs underneath the button. */
.msb-auth-pw-wrap { position: relative; display: block; margin-top: 6px; }
.msb-auth-form .msb-auth-pw-wrap input[type="password"],
.msb-auth-form .msb-auth-pw-wrap input[type="text"] { margin-top: 0; padding-right: 46px; }
.msb-auth-pw-toggle {
	position: absolute; top: 0; right: 0; height: 100%; width: 44px; padding: 0;
	display: flex; align-items: center; justify-content: center;
	background: none; border: none; cursor: pointer; color: #8a8a8a; border-radius: 0 8px 8px 0;
}
.msb-auth-pw-toggle:hover { color: #5b5fe0; }
.msb-auth-pw-toggle:focus-visible { outline: 2px solid #5b5fe0; outline-offset: -2px; }
.msb-auth-pw-icon { display: block; }
/* Hidden password (default): show the open eye, meaning "reveal".
   Revealed password: swap to the struck-through eye, meaning "hide". */
.msb-auth-pw-icon-hide { display: none; }
.msb-auth-pw-visible .msb-auth-pw-icon-show { display: none; }
.msb-auth-pw-visible .msb-auth-pw-icon-hide { display: block; }
.msb-auth-otp-block label { display: block; font-size: 12px; font-weight: 600; margin-bottom: 16px; color: #444; }
.msb-auth-row { display: flex; align-items: center; justify-content: space-between; font-size: 13px; margin-bottom: 20px; }
.msb-auth-remember { display: flex; align-items: center; gap: 6px; font-weight: 400; margin: 0; }
.msb-auth-row a { color: #5b5fe0; text-decoration: none; }
.msb-auth-submit { width: 100%; padding: 13px 0; border: none; border-radius: 8px; background: linear-gradient(90deg,#6a4bd6,#5b5fe0); color: #fff; font-size: 15px; font-weight: 700; cursor: pointer; }
.msb-auth-switch { text-align: center; font-size: 13px; color: #666; margin-top: 18px; }
.msb-auth-switch a { color: #5b5fe0; font-weight: 600; text-decoration: none; }
.msb-auth-hint { font-size: 12px; color: #888; margin-top: -8px; margin-bottom: 16px; }
.msb-auth-google-btn {
	width: 100%; display: flex; align-items: center; justify-content: center; gap: 10px; padding: 11px 0;
	border: 1px solid #ddd; border-radius: 8px; background: #fff; font-size: 13px; font-weight: 600; color: #444;
	cursor: not-allowed; opacity: .7; margin-bottom: 16px;
}
/* v7.11.0: real Google Identity Services button mounts here (its own
   iframe/markup, sized to fill the slot) -- unlike the old disabled
   .msb-auth-google-btn placeholder above, which stays in the codebase
   only as a fallback look while Google isn't configured. */
.msb-auth-google-slot { width: 100%; margin-bottom: 16px; display: flex; justify-content: center; }
.msb-auth-google-slot > div { width: 100% !important; }
.msb-auth-method-switch { text-align: center; margin: -4px 0 16px; }
.msb-auth-link-btn {
	background: none; border: none; padding: 0; color: #5b5fe0; font-size: 13px; font-weight: 600;
	cursor: pointer; text-decoration: underline;
}
.msb-auth-otp-block { margin-bottom: 6px; }
.msb-auth-otp-send { width: 100%; margin-bottom: 12px; }
.msb-auth-otp-verify { margin-top: 4px; }
.msb-auth-otp-status { font-size: 12.5px; margin: 6px 0 14px; min-height: 16px; color: #1f7a45; }
.msb-auth-otp-status-error { color: #a12b2b; }
.msb-auth-divider { text-align: center; font-size: 12px; color: #999; margin: 4px 0 18px; position: relative; }
.msb-auth-divider span { background: #fff; padding: 0 10px; }
/* v7.11.0: My Profile > Settings > Login Methods list. */
.msb-login-method-row {
	display: flex; align-items: center; justify-content: space-between; gap: 12px;
	padding: 14px 0; border-bottom: 1px solid var(--msb-border, #eee);
}
.msb-login-method-info { display: flex; align-items: center; gap: 12px; }
.msb-login-method-icon { font-size: 18px; width: 24px; text-align: center; display: inline-flex; align-items: center; justify-content: center; }
.msb-login-method-label { font-size: 14px; font-weight: 600; }
.msb-login-method-detail { font-size: 12.5px; color: #888; }
.msb-login-method-add-block { margin-top: 16px; padding: 14px; background: var(--msb-bg-alt, #f6f5f3); border-radius: 8px; }
.msb-login-method-add-block label { display: block; font-size: 12px; font-weight: 600; margin-bottom: 12px; color: #444; }
.msb-login-method-add-block input { display: block; width: 100%; max-width: 280px; margin-top: 6px; padding: 10px 12px; border: 1px solid #ddd; border-radius: 8px; font-size: 14px; box-sizing: border-box; }
@media (max-width: 720px) {
	.msb-auth-banner { display: none; }
	.msb-auth-panel { max-width: 100%; padding: 32px 24px; }
}

/* ===== Dashboard (v7.4.6) =====
   Hero CTA unchanged in structure; its button is now light (not the
   near-black --msb-accent every other primary button uses) so it reads
   against the dark hero instead of blending into it.
   My Sites reuses .msb-site-grid exactly as the My Sites page renders it
   (3-col desktop) -- on mobile ONLY, scoped to .msb-dashboard-sites so
   the real My Sites page is untouched, it becomes a one-card-per-view
   horizontal swiper with a dot indicator below.
   Browse Templates reuses the exact Browse Templates page experience
   (filter chips, Start from Scratch card, .msb-template-grid) at every
   width -- no horizontal scroll here at all, mobile or desktop. */
/* v7.36.5 (Screenshot_7): redesigned from a solid black bar (white text
   on black, white-on-white button) to a white card with a border and
   dark text -- the solid black slab read heavy/dated next to the rest
   of the dashboard's light, bordered cards, and a white button on a
   black background inverted the usual button hierarchy (the CTA was
   the quietest-looking element on the page). The "+ New Site" button
   is now the one dark, filled element here, which is what a primary
   CTA should look like against a light card. */
/* v7.50.0: "resume where you left off" banner -- sits above the
   dashboard's main hero card, same rounded-card language, distinct
   accent-tinted background so it reads as a prompt/notice rather than
   blending into the ordinary hero. */
.msb-dashboard-resume-banner {
	display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
	background: linear-gradient(135deg, rgba(79,70,229,.06), rgba(219,39,119,.06));
	border: 1px solid var(--msb-accent); border-radius: var(--msb-radius);
	padding: 14px 18px; margin: 4px 0 14px;
}
.msb-dashboard-resume-text { display: flex; flex-direction: column; gap: 2px; }
.msb-dashboard-resume-text strong { font-size: 14px; }
.msb-dashboard-resume-text span { font-size: 12.5px; color: var(--msb-muted); }
.msb-dashboard-resume-actions { display: flex; gap: 10px; flex-shrink: 0; }
.msb-dashboard-hero {
	display: flex; align-items: center; justify-content: space-between; gap: 16px;
	background: #fff; color: var(--msb-fg); border: 1px solid var(--msb-border); border-radius: var(--msb-radius);
	padding: 18px 20px; margin: 4px 0 22px;
}
.msb-dashboard-hero-sub { font-size: 12.5px; color: var(--msb-muted); margin: 0 0 2px; }
.msb-dashboard-hero-text h1 { font-size: 17px; margin: 0; color: var(--msb-fg); display: flex; align-items: center; gap: 8px; }
/* Small, purely decorative arrow next to the heading (see
   ICON_HERO_ARROW in frontend.js) -- sized and weighted to sit quietly
   next to the text rather than compete with it. */
.msb-dashboard-hero-icon { display: inline-flex; }
.msb-dashboard-hero-icon svg { width: 15px; height: 15px; stroke-width: 2.5; }
.msb-dashboard-hero-btn {
	flex-shrink: 0; white-space: nowrap;
	background: var(--msb-fg); color: #fff; border-color: var(--msb-fg);
}
.msb-dashboard-hero-btn:hover { background: #2b2b2b; border-color: #2b2b2b; }

/* v7.59.0 (Screenshot_34): the Build with AI / Start from Scratch row
   now sits directly under the hero instead of down inside Browse
   Templates -- a bit more breathing room below it than the default
   .msb-scratch-row margin, so it reads as its own step between the
   hero and My Sites rather than crowding straight into that section's
   heading. */
.msb-dashboard-scratch-row { margin-bottom: 28px; }

.msb-dashboard-section { margin-bottom: 32px; }
.msb-dashboard-section-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; gap: 12px; }
.msb-dashboard-section-head h2 { font-size: 16px; margin: 0; }
.msb-dashboard-empty { color: var(--msb-muted); font-size: 14px; }

/* My Sites: plain .msb-site-grid at tablet/desktop widths (identical to
   the My Sites page). Mobile-only override below turns it into a swiper,
   scoped to this dashboard container so /my-sites/ itself never changes. */
@media (max-width: 639px) {
	.msb-dashboard-sites {
		display: flex; grid-template-columns: unset; gap: 0;
		overflow-x: auto; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
	}
	.msb-dashboard-sites .msb-site-card { flex: 0 0 100%; scroll-snap-align: center; }
}
.msb-dashboard-swipe-dots { display: none; justify-content: center; gap: 6px; margin-top: 14px; }
@media (max-width: 639px) {
	.msb-dashboard-swipe-dots { display: flex; }
}
.msb-dashboard-swipe-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--msb-border); transition: all .2s; }
.msb-dashboard-swipe-dot-active { width: 18px; border-radius: 4px; background: var(--msb-fg); }

@media (min-width: 720px) {
	.msb-dashboard-hero { padding: 24px 28px; }
	.msb-dashboard-hero-text h1 { font-size: 20px; }
}

/* ===== My Profile ===== */
.msb-profile-tabs { display: flex; gap: 8px; margin: 16px 0 20px; border-bottom: 1px solid var(--msb-border); }
.msb-profile-tab {
	background: none; border: none; padding: 10px 4px; margin-right: 20px; font-size: 14px; font-weight: 600;
	color: var(--msb-muted); cursor: pointer; border-bottom: 2px solid transparent;
}
.msb-profile-tab-active { color: var(--msb-fg); border-bottom-color: var(--msb-fg); }
.msb-profile-form { display: flex; flex-direction: column; gap: 14px; max-width: 420px; }
.msb-profile-form label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; font-weight: 600; color: var(--msb-muted); }
.msb-profile-form input {
	padding: 11px 12px; border: 1px solid var(--msb-border); border-radius: 8px; font-size: 15px; font-family: inherit;
	color: var(--msb-fg); background: #fff;
}
.msb-profile-form input:disabled { background: var(--msb-bg); color: var(--msb-muted); }
.msb-profile-form .msb-btn { align-self: flex-start; margin-top: 6px; }

.msb-profile-account-layout { display: flex; gap: 48px; flex-wrap: wrap; align-items: flex-start; }
.msb-profile-avatar-block { display: flex; flex-direction: column; align-items: center; gap: 10px; width: 140px; }
.msb-profile-avatar-label { font-size: 13px; font-weight: 600; color: var(--msb-muted); }
.msb-profile-avatar-preview {
	width: 110px; height: 110px; border-radius: 50%; overflow: hidden; background: var(--msb-fg);
	display: flex; align-items: center; justify-content: center; color: #fff; font-size: 40px; font-weight: 700;
}
.msb-profile-avatar-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.msb-profile-avatar-note { font-size: 10px; color: var(--msb-muted); text-align: center; }
.msb-btn-text { background: none; border: none; color: var(--msb-muted); text-decoration: underline; padding: 4px; }
.msb-btn-text:hover { color: #c0392b; }

/* ===== 404 ===== */
.msb-404 { text-align: center; padding: 80px 20px 100px; max-width: 520px; }
.msb-404-code { font-size: 88px; font-weight: 800; color: var(--msb-fg); opacity: .1; line-height: 1; }
.msb-404-title { font-size: 26px; margin: 4px 0 10px; color: var(--msb-fg); }
.msb-404-text { color: var(--msb-muted); font-size: 15px; line-height: 1.6; margin: 0 0 28px; }
.msb-404-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
@media (max-width: 480px) {
	.msb-404 { padding: 50px 16px 70px; }
	.msb-404-code { font-size: 64px; }
	.msb-404-actions { flex-direction: column; align-items: stretch; }
}

/* ===== Plans / Pricing (v7.8.0) ===== */
.msb-plans-header { text-align: center; padding: 40px 16px 8px; }
.msb-plans-header h1 { margin: 0 0 8px; font-size: 30px; }
.msb-plans-header p { color: var(--msb-muted); margin: 0; }
.msb-plans-grid {
	display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 20px;
	max-width: 980px; margin: 28px auto; padding: 0 16px;
}
.msb-plan-card {
	border: 1px solid var(--msb-border); border-radius: 14px; padding: 24px 20px;
	display: flex; flex-direction: column; gap: 10px; background: #fff;
}
.msb-plan-card-free { background: var(--msb-bg); }
.msb-plan-card h3 { margin: 0; font-size: 19px; }
.msb-plan-type-badge {
	display: inline-block; font-size: 12px; font-weight: 600; color: var(--msb-muted);
	background: var(--msb-bg); border-radius: 999px; padding: 4px 10px; width: fit-content;
}
.msb-plan-price { font-size: 30px; font-weight: 800; color: var(--msb-fg); margin: 8px 0 0; }
.msb-plan-price span { font-size: 14px; font-weight: 500; color: var(--msb-muted); }
.msb-plan-features { list-style: none; margin: 6px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; font-size: 14px; color: var(--msb-muted); }
.msb-plan-features li::before { content: "\2713"; color: #2e9e5b; font-weight: 700; margin-right: 8px; }
.msb-plan-cta-row { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.msb-plan-card .msb-btn { margin-top: 12px; text-align: center; justify-content: center; }
.msb-plans-addon-note { text-align: center; color: var(--msb-muted); font-size: 14px; margin: 0 0 60px; }

.msb-checkout-backdrop {
	position: fixed; inset: 0; background: rgba(0,0,0,.5); z-index: 10001;
	display: flex; align-items: center; justify-content: center; padding: 20px;
}
.msb-checkout-modal {
	background: #fff; border-radius: 14px; padding: 28px; width: 100%; max-width: 380px;
	position: relative; text-align: center;
}
.msb-checkout-close { position: absolute; top: 12px; right: 14px; background: none; border: none; font-size: 22px; cursor: pointer; color: var(--msb-muted); }
.msb-checkout-amount { font-size: 22px; font-weight: 800; margin: 4px 0 16px; }
.msb-checkout-gateway { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-bottom: 14px; }
/* v7.57.2 (live-reported, Screenshot_35): domain-purchase checkout
   modal specifically wants its QR code + UPI/PayPal details left-
   aligned instead of centered -- scoped to this one modifier class
   rather than changing .msb-checkout-gateway itself, so the existing
   Plans-page checkout (which still wants the centered look) is
   unaffected. */
.msb-checkout-gateway-left { align-items: flex-start; text-align: left; }
.msb-checkout-gateway-left .msb-checkout-upi { text-align: left; }
.msb-checkout-qr { width: 180px; height: 180px; object-fit: contain; border: 1px solid var(--msb-border); border-radius: 10px; }
.msb-checkout-upi { font-size: 14px; color: var(--msb-muted); margin: 0; }
.msb-checkout-instructions { font-size: 13px; color: var(--msb-muted); margin: 0 0 16px; }
.msb-checkout-note { font-size: 12px; color: var(--msb-muted); margin: 14px 0 0; }
@media (max-width: 600px) {
	.msb-plans-grid { grid-template-columns: 1fr; }
}

/* ===== My Profile: Plan tab (v7.8.3) ===== */
.msb-plan-tab-card { border: 1px solid var(--msb-border); border-radius: 12px; padding: 20px; max-width: 420px; }
.msb-plan-tab-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.msb-plan-tab-label { font-size: 17px; font-weight: 700; margin-right: 8px; }
.msb-plan-tab-details { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; font-size: 14px; color: var(--msb-muted); }
/* v7.9.6/7.9.7 (Screenshot_13 + Screenshot_20): "no site yet" nudge and
   the domain-slot assignment area share this one slot below the details
   list. */
.msb-plan-tab-note { font-size: 13px; color: var(--msb-muted); margin: 14px 0 0; }
.msb-plan-tab-note a { color: var(--msb-primary, #141414); font-weight: 600; }
.msb-plan-tab-slots { margin-top: 14px; }
.msb-plan-tab-slots-label { font-size: 13px; font-weight: 600; color: var(--msb-fg); margin-bottom: 8px; }
.msb-plan-tab-slot-select { display: block; width: 100%; margin-top: 6px; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--msb-border); font-size: 14px; font-weight: 400; }
/* v7.19.15: each dropdown now carries its slot number. The slots are
   stored positionally (see MSB_Billing_Manager::set_domain_sites()),
   so "Slot 2" is a real, stable thing a customer can refer to -- and
   without a label, three identical dropdowns stacked on top of each
   other gave no hint that they were separate connections at all. */
/* v7.38.3 (Screenshot_29): each slot is now its own bordered card
   instead of a bare stacked label -- room for the new "Manage Domain"
   link (only shown once a site is actually chosen for that slot)
   without it looking bolted onto the select. */
.msb-plan-tab-slot {
	display: block; margin-top: 10px; padding: 12px 14px;
	border: 1px solid var(--msb-border); border-radius: 10px; background: var(--msb-bg-alt, #f9f9f9);
}
.msb-plan-tab-slot-num { display: block; font-size: 12px; font-weight: 600; color: var(--msb-muted, #888); }
.msb-plan-tab-slot .msb-plan-tab-slot-select { margin-top: 4px; background: #fff; }
.msb-plan-tab-slot-domain-link {
	display: flex; align-items: center; gap: 6px; margin-top: 10px; padding: 8px 10px;
	font-size: 13px; font-weight: 600; color: var(--msb-primary, #141414); text-decoration: none;
	border: 1px solid var(--msb-border); border-radius: 8px; background: #fff; width: fit-content;
}
.msb-plan-tab-slot-domain-link:hover { background: var(--msb-primary, #141414); color: #fff; border-color: var(--msb-primary, #141414); }
.msb-plan-tab-slot-domain-link svg { width: 15px; height: 15px; flex-shrink: 0; }

/* ===== v7.18.3: requested UI overrides ===== */

/*Pages Dropdown Adjustment*/
.msb-sheet .msb-page-status-select {
	padding: 4px;
}
/*Bottom Bar Live Notifications Alignment*/
.msb-bottom-bar {
	--bottom-bar-height: 70px;
}
#msb-toast {
	position: fixed;
	left: 50%;
	bottom: 90px; /* 70px bottom bar + 20px gap */
	transform: translateX(-50%);
	z-index: 99999;
}
/*Free Pricing Text Font Size*/
.msb-plan-price {
	font-size: 30px !important;
}
/*Leads Search Field Padding*/
input#msb-leads-search {
	padding: 10px;
}
/* Leads "All sources" filter -- the native select's own arrow glyph
   doesn't respect the element's right padding the way the left-side
   text does, so it sat visibly closer to the box's right edge than
   "All sources" sits to the left edge (reported directly, with the
   two gaps compared side by side). Replaced the native arrow with a
   custom one, explicitly positioned the same 12px from the edge as
   the left-side text padding, so both gaps now match. */
#msb-leads-source-filter {
	appearance: none;
	-webkit-appearance: none;
	-moz-appearance: none;
	padding-right: 34px !important;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%23666666' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 12px center;
	background-size: 12px 8px;
}

/* ============================================================
 * DESKTOP EDITOR REDESIGN (v7.19.0)
 * Everything below only ever applies to the editor page
 * (body.msb-editor-page) at a real browser viewport of 1280px or
 * wider. Below that -- including tablets -- none of this has any
 * visible effect at all; the classic bottom-bar + sliding-sheet
 * editor (all the CSS above this block) keeps working exactly as it
 * always has. See desktop_editor_topbar_html() in
 * class-msb-editor-frontend.php and the "DESKTOP EDITOR REDESIGN"
 * block inside initEditor() in frontend.js for the matching PHP/JS.
 * ============================================================ */

/* ---- Desktop-only header: hidden by default (and on every other
   page, and on the editor page below 1280px), shown only on the
   editor page at 1280px+, in place of the generic .msb-topbar. ---- */
.msb-editor-desktop-topbar { display: none; }
@media (min-width: 1280px) {
	body.msb-editor-page .msb-topbar { display: none; }
	body.msb-editor-page .msb-editor-desktop-topbar { display: block; }
}

/* ---- v7.39.0/v7.40.0: mobile/tablet-only header (below 1280px), the
   touch counterpart to .msb-editor-desktop-topbar just above -- see
   mobile_editor_topbar_html() in class-msb-editor-frontend.php and
   renderMobileHeaderCenter() in frontend.js. Small square platform
   logo, a center slot filled in client-side with this site's own
   domain label + Live/Draft badge, an avatar link straight to My
   Profile, and (Screenshot_37) its own hamburger trigger to the same
   mobile nav panel the generic platform topbar already used
   everywhere else. ---- */
.msb-editor-mobile-topbar {
	display: flex; align-items: center; gap: 10px; padding: 8px 12px;
	background: #fff; border-bottom: 1px solid var(--msb-border);
	position: sticky; top: 0; z-index: 20; flex: 0 0 auto;
}
@media (min-width: 1280px) {
	body.msb-editor-page .msb-editor-mobile-topbar { display: none; }
}
.msb-editor-mobile-logo { flex: 0 0 auto; display: flex; align-items: center; text-decoration: none; }
/* v7.41.1 (Screenshot_45): no rounding on the platform logo/icon
   itself here -- an uploaded square icon or logo mark should render
   exactly as provided, not have corners clipped by plugin chrome. */
.msb-editor-mobile-logo img { max-height: 26px; width: auto; }
/* v7.41.0: the dedicated square Platform Icon (Settings > Platform),
   distinct from the plain wordmark-logo fallback img rule just above
   -- a fixed 26x26 box (matching that fallback's own max-height) with
   object-fit:contain so a non-square upload doesn't distort. */
.msb-editor-mobile-logo img.msb-platform-icon-img { width: 26px; height: 26px; object-fit: contain; }
.msb-editor-mobile-logo .msb-topbar-textlogo {
	display: flex; align-items: center; justify-content: center;
	width: 28px; height: 28px; border-radius: 7px; background: var(--msb-fg, #141414);
	color: #fff; font-size: 13px; font-weight: 700; font-family: inherit;
}
.msb-editor-mobile-center { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; justify-content: center; }
.msb-editor-mobile-center-label { font-size: 10.5px; color: var(--msb-muted, #8a8a8a); line-height: 1.2; }
.msb-editor-mobile-center-row { display: flex; align-items: center; gap: 7px; min-width: 0; }
.msb-editor-mobile-center-row .msb-ed-domain-url {
	font-size: 13.5px; font-weight: 700; color: var(--msb-fg); max-width: none;
}
.msb-editor-mobile-avatar { flex: 0 0 auto; display: flex; align-items: center; }
.msb-editor-mobile-avatar img, .msb-editor-mobile-avatar .msb-avatar-fallback { border-radius: 50%; display: block; }
/* v7.40.0 (Screenshot_37): hamburger trigger for this page's own
   nav panel (mobile_editor_topbar_html()'s #msb-editor-mobile-menu-
   panel), same look as the generic .msb-mobile-menu-trigger it's
   modeled on -- this page just needs its own copy since the
   generic one is hidden here (see the critical CSS in head()). */
.msb-editor-mobile-menu-trigger {
	flex: 0 0 auto; background: none; border: none; cursor: pointer; padding: 6px;
	color: var(--msb-fg); display: inline-flex; align-items: center; justify-content: center;
}

.msb-editor-desktop-topbar {
	position: sticky; top: 0; z-index: 20;
	background: #fff; border-bottom: 1px solid var(--msb-border);
}
body.msb-editor-page .msb-editor-desktop-topbar { flex: 0 0 auto; }
/* v7.19.8 (Screenshot_20 follow-up): 1800px, matching the shell below
   it. The header and #msb-editor-app must always carry the SAME cap or
   the logo/account row visibly misaligns with the preview and panel
   underneath it. */
.msb-editor-desktop-topbar-inner {
	max-width: 1800px; margin: 0 auto; padding: 12px 20px; box-sizing: border-box;
	display: flex; align-items: center; gap: 20px;
}
.msb-editor-desktop-logo { display: flex; align-items: center; flex: 0 0 auto; text-decoration: none; }
.msb-editor-desktop-center {
	flex: 1 1 auto; min-width: 0;
	display: flex; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap;
}
.msb-editor-desktop-right { display: flex; align-items: center; gap: 20px; flex: 0 0 auto; }
/* v7.19.9 (Screenshot_24): the slug / domain / Live / viewport-switcher
   cluster was centered in the HEADER, but the thing it describes is the
   live preview -- and the preview column is only "everything except the
   440px panel", so the two centers drift further apart the wider the
   screen gets. On a 1920px monitor the cluster sat well left of where
   the preview actually ends, which is what made it look like it was
   sliding away. (On a small screen the header and the preview column
   are nearly the same width, so centering looked correct there --
   which is why this only ever showed up on large displays.)
   Anchoring it to the preview column's right edge instead makes its
   position independent of screen size: it's taken out of the flex flow
   and pinned 440px in from the right of the header's inner box, which
   is exactly where the panel's left border sits, since the header's
   inner row and #msb-editor-app share the same max-width and centering.
   The extra padding-right just keeps it off that border. */
@media (min-width: 1280px) {
	body.msb-editor-page .msb-editor-desktop-topbar-inner { position: relative; }
	body.msb-editor-page .msb-editor-desktop-center {
		position: absolute; top: 0; bottom: 0; left: auto;
		/* Keep in step with the panel width used by
		   .msb-editor-desktop-panel / -actions and the preview column's
		   calc(100% - 440px). */
		right: 440px;
		width: auto; flex: 0 0 auto;
		justify-content: flex-end; flex-wrap: nowrap; padding-right: 16px;
	}
	/* The center slot no longer stretches, so this needs to claim the
	   leftover space itself to stay hard right. */
	body.msb-editor-page .msb-editor-desktop-right { margin-left: auto; }
}
.msb-editor-desktop-dashboard a {
	display: inline-flex; align-items: center; gap: 6px; padding: 6px 2px;
	color: var(--msb-muted); text-decoration: none; font-size: 14px; white-space: nowrap;
}
.msb-editor-desktop-dashboard a .msb-user-dropdown-icon { opacity: .6; }
.msb-editor-desktop-dashboard a:hover { color: var(--msb-fg); }
.msb-editor-desktop-dashboard a:hover .msb-user-dropdown-icon { opacity: .9; }

/* Site slug / live domain / Live-or-Draft badge / viewport switcher --
   filled in client-side by renderDesktopHeaderCenter() in frontend.js
   once the site has actually loaded, since none of it is known at the
   time the PHP shell first renders. */
.msb-ed-slug { font-size: 13px; font-weight: 600; color: var(--msb-fg); white-space: nowrap; }
/* v7.19.5 (Screenshot_15): a status LABEL now ("Fingosite Domain" /
   "Custom Domain Enabled"), not a printed URL -- so it deliberately
   carries no link affordance (no underline, no pointer cursor, default
   text selection). When a custom domain IS connected, the domain
   itself follows in .msb-ed-domain-url, styled as quiet monospace-ish
   text so it reads as information rather than a button. */
.msb-ed-domain-pill {
	font-size: 12px; font-weight: 700; padding: 4px 10px; border-radius: 20px;
	background: #d1f2df; color: #0a6b3d; white-space: nowrap;
}
.msb-ed-domain-pill-custom { background: #e2e9ff; color: #23409a; }
.msb-ed-domain-url {
	font-size: 12px; font-weight: 600; color: var(--msb-muted, #6b6b6b);
	white-space: nowrap; max-width: 220px; overflow: hidden; text-overflow: ellipsis;
}
.msb-ed-viewport-switch {
	display: flex; align-items: center; gap: 2px; background: var(--msb-bg-alt, #f7f4f2);
	border-radius: 10px; padding: 3px; margin-left: 6px;
}
.msb-ed-viewport-btn {
	display: flex; align-items: center; gap: 6px; border: none; background: none; cursor: pointer;
	padding: 6px 12px; border-radius: 8px; font-size: 13px; font-weight: 600; color: var(--msb-muted);
	transition: background .12s ease, color .12s ease;
}
.msb-ed-viewport-btn svg { width: 16px; height: 16px; flex-shrink: 0; }
.msb-ed-viewport-btn:hover { color: var(--msb-fg); }
.msb-ed-viewport-btn-active { background: #fff; color: var(--msb-fg); box-shadow: 0 1px 3px rgba(0,0,0,.14); }
/* Once the header itself is genuinely tight, drop the text labels and
   keep just the three icons so nothing wraps or overflows.
   v7.19.9: upper bound raised from 1439px to 1519px. The cluster is no
   longer centered -- it's pinned 440px in from the right (see above) --
   so its left edge now sits ~440px further left than it used to at any
   given width, and at the low end of this range the full-width version
   would have run into the logo. */
@media (min-width: 1280px) and (max-width: 1519px) {
	.msb-ed-viewport-btn span { display: none; }
	.msb-ed-slug { display: none; }
}

/* ---- Two-column shell: live preview + persistent right panel,
   1280px+ only. .msb-editor-desktop-panel is a sibling of
   .msb-editor-full (not a wrapper around it), so the existing 100vh
   flex-column layout for #msb-editor-app is completely untouched --
   the panel is simply positioned over the right edge of
   #msb-editor-app, which becomes an 1800px-capped, centered,
   position:relative box at this width to anchor it. ---- */
.msb-editor-desktop-panel { display: none; }
.msb-editor-desktop-actions { display: none; }
@media (min-width: 1280px) {
	/* v7.19.8 (Screenshot_20 follow-up): 1800px, centered -- wide
	   enough that the editor uses essentially all of a 1920px monitor
	   while still stopping short of the very large screens where an
	   uncapped preview stops being representative (the iframe would
	   render the customer's site wider than any real visitor's
	   browser, letting their own container max-widths take over).
	   With the 440px panel alongside it, that leaves the live preview
	   roughly 1340px -- close to a real desktop browser viewport.
	   Must stay in lockstep with .msb-editor-desktop-topbar-inner's cap
	   above, and with the copy of this rule in the critical CSS in
	   class-msb-editor-frontend.php. */
	body.msb-editor-page #msb-editor-app {
		max-width: 1800px; margin: 0 auto; width: 100%; position: relative;
	}
	body.msb-editor-page .msb-bottom-bar { display: none; }
	/* v7.19.2: margin-right alone doesn't shrink this box -- it's an
	   ADDITIONAL gap outside a box whose width is still governed by the
	   base (non-editor-page) ".msb-editor-full { max-width:1300px;
	   margin:0 auto }" rule, which this block never fully overrides
	   (only some of its longhands). That combination left a box that
	   was both capped short of the available space AND pushed further
	   right by leftover auto-margins -- the empty gray gap + hard crop
	   reported against Screenshot_4. Computing the width explicitly via
	   calc() and zeroing out every inherited margin/max-width side
	   removes the ambiguity entirely. */
	body.msb-editor-page .msb-editor-full {
		width: calc(100% - 440px); max-width: none; margin: 0;
		/* v7.19.5 (Screenshot_17): the canvas the live preview sits on
		   was inheriting the app's grey page background, which read as
		   dead space rather than a working surface -- and looked
		   particularly rough with a dark site loaded in the iframe.
		   A warm off-white reads as "paper" and, importantly, still
		   distinguishes the canvas from the site itself at simulated
		   tablet/mobile widths, where the iframe is narrower than this
		   column and the canvas shows on both sides of it. Desktop
		   shell only -- below 1280px the preview fills the screen edge
		   to edge, so there's no canvas to colour. */
		background: #FFF5EE;
	}
	/* v7.19.3 (Screenshot_7): .msb-preview-wrap's base rule (further
	   below) carries a border + border-radius meant for the old,
	   inset/padded editor layout -- looked like an unwanted rounded
	   frame around the live preview in this flush, edge-to-edge
	   desktop shell. Desktop mode only; tablet/mobile below 1280px
	   keep that base rule exactly as before. */
	body.msb-editor-page .msb-preview-wrap {
		border: 0; border-radius: 0; margin-bottom: 0;
	}
	body.msb-editor-page .msb-editor-desktop-panel {
		display: flex; flex-direction: column;
		position: absolute; top: 0; right: 0; bottom: 0; width: 440px;
		background: #fff; border-left: 1px solid var(--msb-border);
		/* v7.19.6 (Screenshot_18): reserves the exact height of the
		   docked action bar below, so the panel's scrolling content
		   ends where that bar begins instead of sliding underneath it.
		   Previously the bar floated over the panel with only a 56px
		   padding-bottom on the mount to keep clear of it -- but the
		   bar is 44px of button plus 16px of offset, i.e. 60px, so the
		   last row of any long panel (the Presets grid in the report)
		   was always partly buried. */
		padding-bottom: 65px;
	}
	/* Share / View live / Update. Absolutely positioned because it's a
	   DOM sibling of the panel rather than a child, but styled as what
	   it actually is: a solid, full-width footer bar docked to the
	   bottom of the panel, with its own top border. Nothing can overlap
	   it now -- it's opaque, and the panel's padding-bottom above
	   reserves its space. */
	/* v7.59.25 (Screenshot_74, direct request): flex-end packed every
	   child (the 3 icon buttons + the publish split-button) tightly
	   against the right edge, leaving a large empty gap on the left of
	   the whole group -- .msb-publish-split's own flex:1 1 auto (see
	   frontend.css above) had nothing to grow INTO under flex-end,
	   since there was no "remaining space" left of the group for it to
	   claim. flex-start lets the 3 fixed-width icon buttons settle at
	   the left in their natural DOM order, with the split-button
	   (the only actual flex-growing child here) expanding to fill
	   everything to their right instead. */
	body.msb-editor-page .msb-editor-desktop-actions {
		display: flex; align-items: center; justify-content: flex-start; gap: 8px;
		position: absolute; right: 0; bottom: 0; left: auto;
		width: 440px; box-sizing: border-box;
		padding: 10px 14px; z-index: 6;
		background: #fff; border-top: 1px solid var(--msb-border);
	}
	/* Desktop/Tablet/Mobile preview-width simulator -- see
	   setEditorViewport() in frontend.js. Resizes the SAME live,
	   already-loaded iframe via a plain class swap; never touches its
	   src, so nothing reloads and every edit stays live at any
	   simulated width. */
	body.msb-editor-page .msb-editor-full.msb-viewport-tablet .msb-preview-wrap,
	body.msb-editor-page .msb-editor-full.msb-viewport-mobile .msb-preview-wrap {
		max-width: 100%; margin: 0 auto;
		border: 1px solid var(--msb-border); border-radius: 0; background: #fff;
	}
	body.msb-editor-page .msb-editor-full.msb-viewport-tablet .msb-preview-wrap { width: 834px; }
	body.msb-editor-page .msb-editor-full.msb-viewport-mobile .msb-preview-wrap { width: 390px; }

	/* v7.45.1 (Screenshot_14/15): Choose a Layout and Add New Section's
	   card grids each switch to 2 columns starting at a fairly narrow
	   min-width (560px / 480px) -- fine for the mobile/tablet full-width
	   sheet these were designed for, but the desktop panel itself is
	   only 440px wide, so at this 1280px+ viewport the grid saw "wide
	   enough for 2 columns" and squeezed every card thumbnail down to
	   under 200px, unreadable. Forced back to a single column here,
	   scoped to the desktop panel only -- the mobile/tablet sheet at
	   narrower viewports is untouched. */
	body.msb-editor-page .msb-editor-desktop-panel .msb-layout-options {
		display: flex; flex-direction: column; grid-template-columns: none;
	}
	body.msb-editor-page .msb-editor-desktop-panel .msb-library-grid {
		column-count: 1;
	}
}

.msb-editor-desktop-panel-topbar {
	display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap;
	padding: 12px 14px; border-bottom: 1px solid var(--msb-border); flex: 0 0 auto;
}
.msb-editor-desktop-panel-icons { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
/* v7.45.6 (Screenshot_25): stacked icon + small label, desktop panel
   only -- the mobile/tablet bottom bar uses its own separate
   .msb-bar-icon-btn markup entirely, so this doesn't touch that. Fixed
   34px square swapped for an auto-height column so the label has room
   underneath without changing the icon's own size. */
.msb-panel-icon-btn {
	display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
	width: auto; min-width: 40px; height: auto; padding: 5px 6px 4px; border-radius: 8px; border: 1px solid transparent;
	background: none; cursor: pointer; color: var(--msb-fg); flex-shrink: 0;
}
.msb-panel-icon-btn svg { width: 18px; height: 18px; }
/* v7.45.8 (Screenshot_27): ICON_BRANDING and ICON_COMPASS's actual
   drawn shapes don't fill their 24x24 viewBox the same way ICON_GEAR
   and ICON_LAYERS's do -- the palette's own path sits well short of
   the box's top/bottom edges (visibly shorter and with more empty
   space beneath it than the others), and the compass's circle is a
   touch smaller too -- so at an identical 18x18 render size the two
   read as visibly smaller than their neighbours despite sharing the
   same box. Desktop panel only, per how it was asked for -- the
   mobile/tablet bottom bar's own separate .msb-bar-icon-btn markup is
   untouched. Nudged each up individually, scaled from its own center,
   so all four now read as the same visual weight/height without
   redrawing any of the icons themselves (each icon keeps its exact
   shape) and without changing the button's own layout box or the
   spacing/alignment around it. */
#msb-panel-branding-btn svg { transform: scale(1.22); transform-origin: center; }
#msb-panel-home-btn svg { transform: scale(1.08); transform-origin: center; }
.msb-panel-icon-btn-label { font-size: 10px; font-weight: 500; line-height: 1; color: var(--msb-muted); white-space: nowrap; }
.msb-panel-icon-btn:hover { background: var(--msb-bg-alt, #f7f4f2); }
.msb-panel-icon-btn-active { border-color: var(--msb-border); background: var(--msb-bg-alt, #f7f4f2); }
/* v7.19.6: padding-bottom used to be 56px here, as a guess at how much
   room the floating action buttons needed. The buttons are a docked,
   opaque footer now and the panel itself reserves their exact height
   (see .msb-editor-desktop-panel above), so this no longer needs to
   dodge anything. */
.msb-editor-desktop-panel-mount { flex: 1 1 auto; overflow-y: auto; min-height: 0; }

/* The panel's own copy of the page-switch dropdown (idPrefix "ed-" --
   see pageSwitcherHtml() in frontend.js) sits inline in the panel's
   topbar row instead of being absolutely centered like the bottom
   bar's copy, and its menu drops DOWN below the button instead of
   floating up above it, since this bar is at the top of the screen
   rather than the bottom. */
.msb-editor-desktop-panel-topbar .msb-page-switch-wrap {
	position: relative; left: auto; top: auto; transform: none; flex: 1 1 auto; min-width: 0;
}
.msb-editor-desktop-panel-topbar .msb-page-switch-btn { max-width: 100%; }
.msb-editor-desktop-panel-topbar .msb-page-switch-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msb-editor-desktop-panel-topbar .msb-page-switch-menu {
	position: absolute; top: calc(100% + 8px); bottom: auto; left: 0; transform: none;
}

/* A sheet mounted straight into the panel (no backdrop/overlay -- see
   openSheet()'s panelMount branch in frontend.js) fills the mount
   instead of behaving like the small centered/right-drawer card it is
   below 1280px. */
.msb-sheet-inline {
	width: 100% !important; max-width: none !important; height: 100%;
	border-radius: 0; max-height: none;
}

/* ---- v7.19.5 (Screenshot_11/12/13): every remaining popup in the
   desktop editor -- the confirm dialog, the name-this-page/menu
   prompt, and any nested sheet such as the Header menu editor --
   still needs a real overlay so it can stack over whatever is already
   in the panel. It just shouldn't take over the whole screen to do
   it. msbConfineBackdropToPanel() in frontend.js pins the overlay's
   position:fixed box to the right panel's live on-screen rect (top/
   left/width/height set inline, recomputed on resize); everything
   below is the styling that goes with that. Applied by JS class only,
   so an overlay anywhere else on the site -- and every overlay at all
   on mobile/tablet -- is completely untouched. ---- */
.msb-sheet-backdrop-in-panel {
	/* The base rule's `inset: 0` would otherwise fight the inline
	   top/left/width/height set by JS. */
	inset: auto;
	padding: 12px;
	background: rgba(0, 0, 0, .35);
	/* Above the panel's own content, below the topbar. */
	z-index: 30;
}
.msb-sheet-backdrop-in-panel .msb-sheet {
	width: 100%; max-width: none; max-height: 100%;
	border-radius: 12px;
}
/* The prompt/confirm cards are centered, narrow, and quite padded by
   default -- fine over a full screen, cramped inside a 440px column. */
.msb-sheet-backdrop-in-panel .msb-confirm-sheet { padding: 22px 18px 18px; }

/* ---- v7.19.5 (Screenshot_10): toasts belong to the live preview,
   not the browser window. msbPinToastToPreview() in frontend.js sets
   left/bottom inline from the preview column's rect; this only has to
   neutralise the two id-selector defaults it can't reach and keep the
   toast narrow enough to sit inside that column. ---- */
.msb-toast-over-preview {
	/* Narrow enough to stay inside the preview column it's centered on
	   (the base rule's 90vw would overhang into the right panel). The
	   toast's own position/z-index come from the #msb-toast id rule
	   above plus the inline left/bottom that JS sets. */
	max-width: min(520px, 60vw);
}

/* =========================================================
   INTEGRATIONS tab (v7.19.13) -- rebuilt from four <hr>-separated
   full-height stacks into four compact rows (see the note above
   integrationsTabHtml() in frontend.js for the why).

   Sizing rule for this block: a switched-OFF row is exactly one
   56px line. Four of them plus gaps land near 250px, so the whole
   tab is visible at once on a phone with nothing expanded -- the
   scrolling complaint that prompted the rebuild. Only a row that's
   actually switched on is allowed to add height.
   ========================================================= */
.msb-int-list { display: flex; flex-direction: column; gap: 8px; }

.msb-int-row {
	border: 1px solid var(--msb-border); border-radius: 12px;
	background: #fff; overflow: hidden;
	transition: border-color .15s ease, box-shadow .15s ease;
}
.msb-int-row-on { border-color: var(--msb-fg); box-shadow: 0 1px 3px rgba(0,0,0,.06); }

.msb-int-head { display: flex; align-items: center; gap: 12px; padding: 10px 12px; min-height: 56px; box-sizing: border-box; }

/* Fixed-size tile so all four icons optically align regardless of
   each glyph's own bounding box. */
.msb-int-icon {
	flex: 0 0 auto; width: 34px; height: 34px; border-radius: 9px;
	display: flex; align-items: center; justify-content: center;
	background: var(--msb-bg-alt, #f5f4f2);
	transition: background .15s ease;
}
.msb-int-icon svg { width: 18px; height: 18px; fill: var(--msb-muted); transition: fill .15s ease; }
.msb-int-row-on .msb-int-icon { background: var(--msb-fg); }
.msb-int-row-on .msb-int-icon svg { fill: #fff; }

/* min-width:0 is what lets the status line's ellipsis actually
   trigger -- without it the flex item refuses to shrink below its
   content and a long address widens the row instead of truncating. */
.msb-int-meta { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.msb-int-name { font-size: 14px; font-weight: 600; color: var(--msb-fg); line-height: 1.3; }
.msb-int-status {
	font-size: 12px; color: var(--msb-muted); line-height: 1.3;
	white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ---- Toggle switch ---- */
.msb-int-switch { position: relative; flex: 0 0 auto; width: 42px; height: 24px; cursor: pointer; }
.msb-int-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.msb-int-slider {
	position: absolute; inset: 0; border-radius: 999px;
	background: #d6d3ce; transition: background .18s ease;
}
.msb-int-slider::after {
	content: ""; position: absolute; top: 3px; left: 3px;
	width: 18px; height: 18px; border-radius: 50%; background: #fff;
	box-shadow: 0 1px 2px rgba(0,0,0,.25); transition: transform .18s ease;
}
.msb-int-switch input:checked + .msb-int-slider { background: var(--msb-fg); }
.msb-int-switch input:checked + .msb-int-slider::after { transform: translateX(18px); }
.msb-int-switch input:focus-visible + .msb-int-slider { outline: 2px solid var(--msb-fg); outline-offset: 2px; }

/* ---- Expanded body ---- */
.msb-int-body { padding: 0 12px 12px; display: flex; flex-direction: column; gap: 8px; }
.msb-int-body input[type="text"], .msb-int-body input[type="email"] {
	width: 100%; padding: 9px 10px; border: 1px solid var(--msb-border);
	border-radius: 8px; font-size: 14px; box-sizing: border-box;
}
.msb-int-hint { font-size: 12px; color: var(--msb-muted); margin: 0; line-height: 1.45; }
.msb-int-visibility { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 12.5px; font-weight: 600; margin-top: 8px; }
.msb-int-visibility select { font-size: 12.5px; font-weight: 400; padding: 5px 8px; border-radius: 6px; }
.msb-int-link-btn {
	align-self: flex-start; background: none; border: none; padding: 0;
	font-size: 13px; font-weight: 600; color: var(--msb-fg);
	text-decoration: underline; cursor: pointer;
}

/* ---- Google Map Location picker ---- */
.msb-map-picker { display: flex; flex-direction: column; gap: 8px; }
.msb-map-chosen {
	display: flex; align-items: center; gap: 10px;
	border: 1px solid var(--msb-border); border-radius: 10px;
	padding: 9px 10px; background: var(--msb-bg-alt, #f7f5f3);
}
.msb-map-chosen-pin { flex: 0 0 auto; display: flex; }
.msb-map-chosen-pin svg { width: 16px; height: 16px; fill: var(--msb-fg); }
.msb-map-chosen-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
/* Two lines max, then ellipsis: a full geocoded address is long
   enough to dominate the panel if left to wrap freely. */
.msb-map-chosen-label {
	font-size: 13px; color: var(--msb-fg); line-height: 1.35;
	display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.msb-map-chosen-sub { font-size: 11px; color: var(--msb-muted); }
.msb-map-change {
	flex: 0 0 auto; background: none; border: none; padding: 0;
	font-size: 12px; font-weight: 600; color: var(--msb-fg);
	text-decoration: underline; cursor: pointer;
}

.msb-map-search { position: relative; }
.msb-map-results {
	margin-top: 6px; border: 1px solid var(--msb-border); border-radius: 10px;
	background: #fff; overflow: hidden; max-height: 230px; overflow-y: auto;
}
.msb-map-result {
	display: flex; flex-direction: column; gap: 2px; width: 100%;
	padding: 9px 11px; background: none; border: none;
	border-bottom: 1px solid var(--msb-border);
	text-align: left; cursor: pointer;
}
.msb-map-result:last-child { border-bottom: none; }
.msb-map-result:hover { background: var(--msb-bg-alt, #f7f5f3); }
.msb-map-result-title { font-size: 13px; font-weight: 600; color: var(--msb-fg); }
.msb-map-result-context {
	font-size: 11px; color: var(--msb-muted); line-height: 1.35;
	display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.msb-map-empty { font-size: 12px; color: var(--msb-muted); margin: 0; padding: 10px 11px; }

/* =========================================================
   CUSTOMIZATION SUB-TAB POLISH (v7.19.14) -- Typography, Buttons,
   Layout and Presets all shared the same problem: a single column of
   full-width controls with a heading and an explanatory paragraph
   above each one, so every tab was a long scroll and nothing was
   comparable at a glance. The primitives below are shared across all
   four rather than styled per tab, so a control row looks the same
   wherever it appears.
   ========================================================= */

/* ---- Labelled control row: label left, control right ---- */
.msb-control-row {
	display: flex; align-items: center; justify-content: space-between;
	gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--msb-border);
}
.msb-control-row:last-child { border-bottom: none; }
/* Corners has six options -- too many to sit beside a label at panel
   width, so that row stacks while Size (three) stays inline. */
.msb-control-row-stack { flex-direction: column; align-items: stretch; gap: 8px; }
.msb-control-label { font-size: 13px; font-weight: 600; color: var(--msb-fg); flex: 0 0 auto; }

/* ---- Segmented control ---- */
.msb-segmented {
	display: inline-flex; background: var(--msb-bg-alt, #f2f1ef);
	border-radius: 9px; padding: 3px; gap: 2px;
}
.msb-segmented-wrap { display: flex; flex-wrap: wrap; }
.msb-segmented-wrap .msb-seg-btn { flex: 1 1 auto; min-width: 48px; }
.msb-seg-btn {
	border: none; background: none; cursor: pointer; font: inherit;
	font-size: 12px; font-weight: 600; color: var(--msb-muted);
	padding: 6px 12px; border-radius: 7px; white-space: nowrap;
	transition: background .15s ease, color .15s ease;
}
.msb-seg-btn:hover { color: var(--msb-fg); }
.msb-seg-btn-active { background: #fff; color: var(--msb-fg); box-shadow: 0 1px 2px rgba(0,0,0,.10); }

/* ---- Buttons tab: preview stage ---- */
/* Framed and centred so it reads as a sample rather than a live action
   the customer is meant to press -- the old bare button floating at the
   top of the panel looked like the tab's primary control. */
.msb-btn-stage {
	display: flex; align-items: center; justify-content: center;
	padding: 22px 12px; margin-bottom: 4px;
	background: var(--msb-bg-alt, #f7f5f3);
	border: 1px solid var(--msb-border); border-radius: 12px;
}
.msb-btn-preview { pointer-events: none; }

/* ---- Layout tab: grouped cards ---- */
.msb-layout-tab { gap: 10px; }
.msb-layout-card {
	border: 1px solid var(--msb-border); border-radius: 12px;
	background: #fff; padding: 12px;
	display: flex; flex-direction: column; gap: 8px;
}
.msb-layout-card > .msb-section-title { margin-top: 0; }
.msb-layout-card .msb-settings-hint { margin: 0; }
.msb-layout-card select { width: 100%; }
/* v7.53.1 REDESIGN: Icon Settings card -- previously a live preview
   chip sitting BESIDE the three segmented-option groups. Two real,
   confirmed bugs there, both fixed here rather than patched in place:
   (1) "not clearly visible" -- the chip's colors are plain
   var(--msb-role-badges)/var(--msb-role-badges-text) custom
   properties, which are only ever DEFINED inside a rendered site's
   own <style> block (MSB_Global_Chrome::style_block(), injected into
   the live site / preview iframe) -- never on this Layout tab's own
   parent document. With no fallback, that left the chip effectively
   transparent-on-transparent here. iconStyleVars() in frontend.js now
   supplies the same 2-level var() fallback chain already proven out
   just below for .msb-icon-settings-option-active (var(--msb-role-
   badges, var(--msb-primary, #171717))), so the preview always
   renders a real, visible color regardless of context.
   (2) "icon position not centered" -- the glyph's own centering
   (display:flex;align-items:center;justify-content:center) is part
   of the .msb-icon-chip rule, which -- same root cause as (1) -- is
   also only ever injected into a rendered site/preview iframe, not
   this parent document, so the chip had no centering at all here and
   the glyph sat at its default top-left position. Given directly
   below.
   Redesign: preview promoted to its own full-width "stage" ABOVE the
   controls (was a small dashed box to their left) -- reuses the same
   framed/centered visual language .msb-btn-stage already established
   for the Button preview above, so every live-preview card on this
   tab now looks and behaves consistently, and the chip has generous
   room to be seen clearly at any of its four sizes (28-88px). */
.msb-icon-settings-stage {
	display: flex; align-items: center; justify-content: center;
	min-height: 116px; padding: 20px 12px; margin-bottom: 2px;
	background: var(--msb-bg-alt, #f7f5f3);
	border: 1px solid var(--msb-border); border-radius: 12px;
}
/* The chip itself: centering + rounding supplied directly here since
   (per the comment above) the real site's own .msb-icon-chip rule
   supplying these never reaches this document. Width/height/
   background/border/color are still computed and kept live by
   updateIconSettingsPreview() in frontend.js -- this only supplies
   the static shape. */
#msb-icon-settings-preview-chip {
	display: flex; align-items: center; justify-content: center;
	border-radius: 14px; flex-shrink: 0;
	transition: width .15s ease, height .15s ease, background-color .15s ease, border-color .15s ease;
}
#msb-icon-settings-preview-glyph { display: block; }
.msb-icon-settings-live-controls { display: flex; flex-direction: column; gap: 14px; }
.msb-icon-settings-group { margin-bottom: 0; }
.msb-icon-settings-label { font-size: 12px; font-weight: 600; color: var(--msb-muted); text-transform: uppercase; letter-spacing: .03em; margin: 0 0 6px; }
.msb-icon-settings-row { display: flex; flex-wrap: wrap; gap: 6px; }
.msb-icon-settings-option {
	padding: 6px 12px; border-radius: 999px; border: 1px solid var(--msb-border);
	background: #fff; color: var(--msb-fg); font-size: 12.5px; cursor: pointer; transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}
.msb-icon-settings-option:hover { border-color: var(--msb-primary, #111); }
.msb-icon-settings-option-active {
	background: var(--msb-role-badges, var(--msb-primary, #111)); color: var(--msb-role-badges-text, #fff);
	border-color: var(--msb-role-badges, var(--msb-primary, #111));
}
/* The two-column width pair collapses on a phone, where two selects
   side by side would each be too narrow to read their own values. */
@media (max-width: 420px) {
	.msb-layout-tab .msb-two-col { grid-template-columns: 1fr; }
}

/* ---- Typography tab: scale table ---- */
.msb-scale-controls select { width: 100%; }
/* Header row dropped (v7.19.14): three columns whose content is
   self-evident don't need labels spending a row of the panel. */
.msb-type-scale-table td { padding: 7px 4px; }
.msb-ts-name { font-size: 12px; font-weight: 600; color: var(--msb-fg); white-space: nowrap; }
.msb-ts-size { font-size: 11px; color: var(--msb-muted); white-space: nowrap; }
.msb-ts-size span { display: block; opacity: .75; }
/* Fixed-width preview cell so the size steps read as a ladder: without
   it the column width tracks each sample's own rendered width and the
   progression is much harder to see. */
.msb-ts-preview { width: 64px; text-align: right; color: var(--msb-fg); line-height: 1.1; }

/* ---- Map picker: typed-text result + area-only tag ---- */
.msb-map-results-head {
	font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
	color: var(--msb-muted); margin: 0; padding: 8px 11px 6px;
}
/* The typed-name row is the reliable path (Google resolves it), so it
   gets the tinted background and sits first -- see geo_search()'s note
   on why a search match can't be required. */
.msb-map-result-typed { background: var(--msb-bg-alt, #f7f5f3); }
.msb-map-result-typed .msb-map-result-title { display: flex; align-items: center; gap: 6px; }
.msb-map-result-tag {
	display: inline-block; font-size: 10px; font-weight: 700; text-transform: uppercase;
	letter-spacing: .03em; color: var(--msb-muted);
	background: var(--msb-bg-alt, #f2f1ef); border-radius: 4px; padding: 1px 5px; margin-left: 4px;
}

/* ---- Build (AI Website Builder wizard, v7.47.0) ---- */
.msb-build-page .msb-topbar { border-bottom: 1px solid var(--msb-border); }
/* v7.48.0 BUGFIX: confirmed real, live-reported issue -- 640px read as
   noticeably cramped on an actual desktop monitor (Screenshot_85), and
   combined with the stepper's own centered flex track (see
   .msb-build-stepper below) it was ALSO the direct cause of the first
   and last step being invisible by default: with more steps than a
   640px-wide track can show at once, the browser's initial scroll
   position for a horizontally-overflowing centered flex row lands
   scrolled to the middle, hiding roughly equal amounts off BOTH edges
   -- exactly "step 1 and step 9 not visible" as reported. Widened to
   960px (a comfortable middle ground: wide enough that most step
   trackers fit with no scrolling needed on an ordinary laptop screen,
   without stretching the actual form card into uncomfortably long
   input-field line lengths at very wide viewports).
*/
.msb-build-wrap { max-width: 960px; margin: 0 auto; padding: 32px 20px 96px; }
.msb-build-head { text-align: center; margin-bottom: 28px; }
.msb-build-head h1 { font-size: 26px; margin: 0 0 8px; }
.msb-build-head p { color: var(--msb-muted); font-size: 15px; margin: 0; max-width: 480px; margin: 0 auto; }

.msb-build-stepper {
	display: flex; align-items: center; justify-content: flex-start; gap: 2px;
	margin-bottom: 24px; overflow-x: auto; padding: 4px 8px 10px;
	/* v7.48.0: was justify-content:center -- see .msb-build-wrap's own
	   comment above for why that centering was the actual root cause
	   of both edge steps starting off-screen. flex-start means the
	   track's natural, unscrolled position is "step 1 fully visible,
	   scroll right for more" -- the same predictable starting point a
	   horizontal scroller anywhere else on the platform already uses.
	   scroll-snap turns that same overflow-x:auto into the "clean
	   horizontal scroller" behavior requested for mobile specifically,
	   rather than a raw, snap-less drag. */
	scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
	scrollbar-width: thin;
}
.msb-build-step { display: flex; flex-direction: column; align-items: center; gap: 6px; flex-shrink: 0; width: 64px; scroll-snap-align: center; }
.msb-build-step-dot {
	display: flex; align-items: center; justify-content: center;
	width: 28px; height: 28px; border-radius: 50%; border: 2px solid var(--msb-border);
	font-size: 12px; font-weight: 700; color: var(--msb-muted); background: #fff;
	transition: border-color .15s ease, background .15s ease, color .15s ease;
}
.msb-build-step-done .msb-build-step-dot, .msb-build-step-done .msb-build-step-label { cursor: pointer; }
.msb-build-step-dot svg { width: 14px; height: 14px; }
.msb-build-step-active .msb-build-step-dot { border-color: var(--msb-accent); color: var(--msb-accent); }
.msb-build-step-done .msb-build-step-dot { border-color: var(--msb-accent); background: var(--msb-accent); color: #fff; }
.msb-build-step-label { font-size: 10px; font-weight: 600; color: var(--msb-muted); text-align: center; }
.msb-build-step-active .msb-build-step-label { color: var(--msb-fg); }
.msb-build-step-line { flex: 1; min-width: 12px; max-width: 32px; height: 2px; background: var(--msb-border); margin-top: -18px; }

/* v7.47.11 BUGFIX: confirmed live-reported ("Wizard UI needs to be
   fixed for tablet and mobile version") -- the stepper's fixed 64px-
   per-step width was tuned for desktop and never had an intermediate
   tablet-width adjustment, only a separate mobile one far below. At
   real tablet widths (§42 of the Section Block Rulebook confirms 834px
   as this platform's own reference Tablet preview width) 6 steps at
   64px plus their connecting lines can still feel cramped even though
   there's technically room -- narrower steps and a smaller label give
   more breathing room without needing the mobile tier's more drastic
   label-hiding fix. */
@media (max-width: 900px) and (min-width: 481px) {
	.msb-build-step { width: 52px; }
	.msb-build-step-label { font-size: 9px; }
	.msb-build-stepper { gap: 1px; }
}

.msb-build-card {
	background: #fff; border: 1px solid var(--msb-border); border-radius: var(--msb-radius);
	padding: 28px 24px; box-shadow: 0 2px 10px rgba(0,0,0,.04);
}
.msb-build-card h2 { font-size: 19px; margin: 0 0 6px; }
.msb-build-hint { color: var(--msb-muted); font-size: 13.5px; margin: 0 0 18px; line-height: 1.5; }

.msb-build-field { margin-bottom: 18px; }
/* v7.50.1: a real, reusable 2-column field row for short-input pairs
   (Phone/Email, and any future pair like it) -- collapses to a single
   stacked column on mobile, same breakpoint the rest of this wizard's
   own multi-column layouts already use. */
.msb-build-field-row { display: flex; gap: 16px; }
.msb-build-field-row .msb-build-field { flex: 1; min-width: 0; margin-bottom: 18px; }
@media (max-width: 560px) {
	.msb-build-field-row { flex-direction: column; gap: 0; }
}
.msb-build-field label { display: block; font-size: 13px; font-weight: 700; margin-bottom: 6px; }
.msb-build-optional-tag { font-size: 11px; font-weight: 600; color: var(--msb-muted); text-transform: none; margin-left: 4px; }

/* ---- Build: Business step's custom country combobox (v7.64.0) ---- */
.msb-build-country-picker { position: relative; }
.msb-build-country-trigger {
	width: 100%; display: flex; align-items: center; gap: 8px; text-align: left;
	padding: 10px 12px; border-radius: 8px; border: 1px solid var(--msb-border); background: #fff;
	font-family: inherit; font-size: 14px; color: var(--msb-fg); cursor: pointer;
}
.msb-build-country-trigger:hover, .msb-build-country-trigger:focus { border-color: var(--msb-accent); outline: none; }
.msb-build-country-trigger-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msb-build-country-placeholder { color: var(--msb-muted); }
.msb-build-country-caret { width: 16px; height: 16px; flex: 0 0 auto; color: var(--msb-muted); }
.msb-build-country-flag { width: 20px; height: 15px; flex: 0 0 auto; border-radius: 2px; object-fit: cover; box-shadow: 0 0 0 1px rgba(0,0,0,.08); }
.msb-build-country-panel {
	position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 20;
	background: #fff; border: 1px solid var(--msb-border); border-radius: 10px;
	box-shadow: 0 8px 24px rgba(0,0,0,.12); overflow: hidden;
}
.msb-build-country-search {
	width: 100%; box-sizing: border-box; padding: 10px 12px; border: none; border-bottom: 1px solid var(--msb-border);
	font-family: inherit; font-size: 14px; outline: none;
}
.msb-build-country-list { max-height: 240px; overflow-y: auto; padding: 4px; }
.msb-build-country-row {
	width: 100%; display: flex; align-items: center; gap: 10px; text-align: left;
	padding: 8px 10px; border: none; background: none; border-radius: 6px;
	font-family: inherit; font-size: 13.5px; color: var(--msb-fg); cursor: pointer;
}
.msb-build-country-row:hover { background: var(--msb-bg); }
.msb-build-country-empty { padding: 14px 12px; color: var(--msb-muted); font-size: 13px; margin: 0; text-align: center; }

/* v7.50.0: Design step's Reference Website field. */
.msb-build-reference-checks { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.msb-build-checkbox-row { display: flex; align-items: center; gap: 8px; font-size: 13.5px; cursor: pointer; }
.msb-build-checkbox-row input[type="checkbox"] { width: 16px; height: 16px; flex-shrink: 0; }
/* v7.50.4 BUGFIX: confirmed real, live-reported UI issue -- Phone,
   Email, and Reference Website use type="tel"/"email"/"url" (correct,
   for the right mobile keyboard/validation), but this selector only
   ever matched type="text" -- those three fields rendered with zero
   custom styling at all (bare browser-default borders/padding),
   visibly inconsistent next to the Business Name field right above
   them. Expanded to match every text-shaped input type this wizard
   actually uses. */
.msb-build-field input[type="text"],
.msb-build-field input[type="tel"],
.msb-build-field input[type="email"],
.msb-build-field input[type="url"],
.msb-build-field select,
.msb-build-field textarea {
	width: 100%; box-sizing: border-box; padding: 11px 13px; border: 1px solid var(--msb-border);
	border-radius: 10px; font-size: 15px; font-family: inherit; background: #fff; color: var(--msb-fg);
}
/* v7.48.0 BUGFIX: confirmed real, live-reported issue (Screenshot_87) --
   the native dropdown chevron sat flush against the field's own right
   edge with no breathing room at all. Selects get their own explicit
   right-hand padding (clear of the chevron) plus a custom-drawn arrow
   inset from the edge, replacing the platform-default chevron most
   browsers render with zero built-in margin of its own. */
.msb-build-field select {
	padding-right: 40px; -webkit-appearance: none; -moz-appearance: none; appearance: none;
	background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23666' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat; background-position: right 14px center;
}
.msb-build-field textarea { resize: vertical; min-height: 90px; }
.msb-build-field input:focus, .msb-build-field select:focus, .msb-build-field textarea:focus {
	outline: none; border-color: var(--msb-accent); box-shadow: 0 0 0 3px rgba(0,0,0,.06);
}

/* Tag input (Services / Products) */
.msb-build-tag-input { display: flex; gap: 8px; margin-bottom: 14px; }
.msb-build-tag-input input { flex: 1; box-sizing: border-box; padding: 11px 13px; border: 1px solid var(--msb-border); border-radius: 10px; font-size: 15px; }
.msb-build-tags { display: flex; flex-wrap: wrap; gap: 8px; min-height: 32px; }
.msb-build-tag {
	display: inline-flex; align-items: center; gap: 6px; background: var(--msb-bg);
	border: 1px solid var(--msb-border); border-radius: 20px; padding: 6px 8px 6px 14px; font-size: 13.5px; font-weight: 600;
}
.msb-build-tag-remove {
	display: flex; align-items: center; justify-content: center; width: 18px; height: 18px;
	border-radius: 50%; border: none; background: rgba(0,0,0,.08); color: inherit; font-size: 13px;
	line-height: 1; cursor: pointer; padding: 0;
}
.msb-build-tag-remove:hover { background: rgba(0,0,0,.16); }
.msb-build-tags-empty { color: var(--msb-muted); font-size: 13px; font-style: italic; padding: 6px 0; }

/* Tone chips */
.msb-build-chip-row { display: flex; flex-wrap: wrap; gap: 8px; }
.msb-build-chip {
	padding: 8px 14px; border-radius: 20px; border: 1px solid var(--msb-border); background: #fff;
	font-size: 13.5px; font-weight: 600; cursor: pointer; color: var(--msb-fg);
}
.msb-build-chip-active { border-color: var(--msb-accent); background: var(--msb-accent); color: #fff; }

/* Color swatches + mode toggle */
.msb-build-swatches { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.msb-build-swatch {
	width: 38px; height: 38px; border-radius: 50%; border: 2px solid transparent; cursor: pointer;
	padding: 0; position: relative; box-shadow: inset 0 0 0 1px rgba(0,0,0,.08);
}
.msb-build-swatch-active { border-color: var(--msb-fg); box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--msb-fg); }
.msb-build-swatch-custom {
	display: flex; align-items: center; justify-content: center; overflow: hidden;
	background: conic-gradient(from 90deg, #ef4444, #f59e0b, #eab308, #22c55e, #06b6d4, #6366f1, #ec4899, #ef4444);
}
.msb-build-swatch-custom svg { width: 16px; height: 16px; pointer-events: none; filter: drop-shadow(0 1px 1px rgba(0,0,0,.35)); }
.msb-build-swatch-custom input[type="color"] {
	position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; cursor: pointer; border: none; padding: 0;
}
.msb-build-mode-toggle { display: flex; gap: 10px; }
.msb-build-mode-btn {
	flex: 1; display: flex; align-items: center; justify-content: center; gap: 8px;
	padding: 12px; border-radius: 10px; border: 1px solid var(--msb-border); background: #fff;
	font-size: 14px; font-weight: 700; cursor: pointer; color: var(--msb-fg);
}
.msb-build-mode-btn svg { width: 18px; height: 18px; }
.msb-build-mode-btn-active { border-color: var(--msb-accent); background: var(--msb-bg); }

/* Review */
.msb-build-summary { border: 1px solid var(--msb-border); border-radius: 12px; padding: 4px 16px; margin-bottom: 20px; }
.msb-build-summary-row {
	display: flex; align-items: center; justify-content: space-between; gap: 12px;
	padding: 11px 0; border-bottom: 1px solid var(--msb-border); font-size: 14px;
}
.msb-build-summary-row:last-child { border-bottom: none; }
.msb-build-summary-row span { color: var(--msb-muted); }
.msb-build-summary-row strong { text-align: right; font-weight: 700; display: inline-flex; align-items: center; gap: 6px; }
.msb-build-color-dot { width: 14px; height: 14px; border-radius: 50%; display: inline-block; box-shadow: inset 0 0 0 1px rgba(0,0,0,.15); }
.msb-build-slug-row {
	display: flex; align-items: center; border: 1px solid var(--msb-border); border-radius: 10px; overflow: hidden;
}
.msb-build-slug-row span { padding: 11px 0 11px 13px; color: var(--msb-muted); font-size: 14px; white-space: nowrap; }
.msb-build-slug-row input { border: none; flex: 1; min-width: 0; padding: 11px 13px 11px 2px; font-size: 15px; }
.msb-build-slug-row input:focus { outline: none; }

.msb-build-generate-btn { width: 100%; padding: 15px; font-size: 16px; gap: 8px; }
.msb-build-generate-btn svg { width: 18px; height: 18px; }

/* Nav row (Back / Next) */
.msb-build-nav { display: flex; align-items: center; justify-content: space-between; margin-top: 22px; gap: 12px; }
.msb-build-nav .msb-btn-primary { min-width: 120px; }
.msb-build-nav .msb-btn-primary:disabled { opacity: .45; cursor: not-allowed; }

/* Loading state */
/* v7.64.3 BUGFIX, confirmed real and live-reported (Screenshot_84: the
   whole loading state sitting near the top of the screen with a huge
   empty gap below it) -- text-align:center only ever centered content
   horizontally; there was no vertical centering at all, just a fixed
   60px top padding regardless of viewport height. Flexbox-centered
   within a real min-height now instead -- 70vh is a safe, viewport-
   height-relative value that doesn't need to know the exact topbar
   height in px, and still leaves comfortable breathing room above and
   below on any real screen size. */
.msb-build-loading {
	display: flex; flex-direction: column; align-items: center; justify-content: center;
	text-align: center; min-height: 70vh; padding-top: 0;
}
/* v7.64.1 (direct request, Screenshot_82: "increase that percentage
   number even bigger with some loading effect, must be super cool and
   pleasant to watch"): the icon now sits inside a real animated SVG
   progress ring (circumference-based stroke-dashoffset, driven by
   pollBuildStatus() in frontend.js -- setRingPercent()) instead of a
   plain pulsing circle with no relationship to actual progress, and
   the percent itself is now a large standalone number underneath
   (was a small 15px label squeezed above a thin bar). Replaces
   v7.63.0's own .msb-build-loading-progress/-percent/-fill entirely. */
.msb-build-loading-ring-wrap { position: relative; width: 140px; height: 140px; margin: 0 auto 8px; }
.msb-build-loading-ring { width: 140px; height: 140px; transform: rotate(-90deg); }
.msb-build-loading-ring-track { fill: none; stroke: var(--msb-border); stroke-width: 8; }
.msb-build-loading-ring-fill {
	fill: none; stroke: var(--msb-accent); stroke-width: 8; stroke-linecap: round;
	stroke-dasharray: 326.7; stroke-dashoffset: 326.7; /* 2*pi*52 -- starts fully unfilled */
	transition: stroke-dashoffset 0.6s ease;
}
.msb-build-loading-icon {
	position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
	width: 72px; height: 72px; border-radius: 50%;
	display: flex; align-items: center; justify-content: center;
	background: var(--msb-bg); color: var(--msb-accent);
	animation: msb-build-loading-pulse 1.8s ease-in-out infinite;
	transition: opacity .18s ease, transform .18s ease;
}
.msb-build-loading-icon svg { width: 30px; height: 30px; }
.msb-build-loading-icon-swap { opacity: 0; transform: translate(-50%, -50%) scale(.85); }
@keyframes msb-build-loading-pulse {
	0%, 100% { box-shadow: 0 0 0 0 rgba(0,0,0,.06); }
	50% { box-shadow: 0 0 0 10px rgba(0,0,0,0); }
}
/* Big, bold, animates in as it climbs -- tabular-nums keeps the digits
   from jittering side to side as the width of "9%" vs "89%" changes. */
.msb-build-loading-percent-big {
	font-size: 44px; font-weight: 800; color: var(--msb-accent); letter-spacing: -.02em;
	font-variant-numeric: tabular-nums; margin: 4px 0 4px; line-height: 1;
}
.msb-build-loading h2 { font-size: 19px; margin: 0 0 6px; }
.msb-build-loading p { color: var(--msb-muted); font-size: 14px; margin: 0; }

/* v7.63.0 (direct request, Screenshot_67: "in mobile version it's not
   looking right, can we use multi step form for mobile... display one
   after another... make sure UI UX is super clean"): the mobile-only
   progress indicator itself -- see render()'s own doc comment
   (frontend.js) for exactly what this markup is and why it exists
   alongside .msb-build-stepper rather than replacing it outright.
   Hidden by default (desktop/tablet keep the numbered-dot stepper
   exactly as it always was, per direct request) -- the media query
   below is the ONLY place this ever becomes visible, and it hides
   .msb-build-stepper at the exact same breakpoint, so precisely one of
   the two is ever on screen at once. */
.msb-build-stepper-mobile { display: none; }
.msb-build-stepper-mobile-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.msb-build-stepper-mobile-row span { font-size: 11.5px; font-weight: 700; color: var(--msb-muted); text-transform: uppercase; letter-spacing: .03em; }
.msb-build-stepper-mobile-row strong { font-size: 14px; font-weight: 700; color: var(--msb-fg); }
.msb-build-stepper-mobile-track { height: 5px; border-radius: 3px; background: var(--msb-border); overflow: hidden; }
.msb-build-stepper-mobile-fill { height: 100%; border-radius: 3px; background: var(--msb-accent); transition: width .2s ease; }

@media (max-width: 480px) {
	.msb-build-card { padding: 22px 16px; }
	/* v7.63.0 BUGFIX, confirmed real and live-reported (Screenshot_67):
	   v7.47.11's own fix (labels hidden, dots-only) still packed all 9
	   steps' own dots + connecting lines into one crowded, hard-to-read
	   horizontally-scrolling row on a real phone -- direct follow-up
	   feedback was that this still "isn't looking right." The numbered-
	   dot stepper is now hidden completely below this width, replaced
	   by .msb-build-stepper-mobile above: a single clean "Step X of Y:
	   <name>" line plus a thin progress bar -- exactly one step's worth
	   of information at a time, matching the wizard's own actual
	   behavior (it already shows one step's card at a time; only the
	   OVERVIEW strip was ever showing all 9 steps at once). Desktop and
	   tablet are completely unaffected -- .msb-build-stepper's own
	   numbered dots stay exactly as they've always been above 480px,
	   per direct request. */
	.msb-build-stepper { display: none; }
	.msb-build-stepper-mobile { display: block; margin-bottom: 20px; }
}

/* ---- Build: provider-status notice (v7.47.3) ---- */
.msb-build-provider-notice {
	display: flex; align-items: flex-start; gap: 10px;
	background: #fff8e6; border: 1px solid #f0d896; border-radius: 10px;
	padding: 12px 14px; margin-bottom: 20px; font-size: 13.5px; line-height: 1.5; color: #6b5615;
}
.msb-build-provider-notice svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px; color: #b8860b; }
.msb-build-provider-notice strong { display: block; margin-bottom: 2px; color: #4a3a0a; }
.msb-build-provider-notice a { color: #6b5615; font-weight: 700; text-decoration: underline; }

/* ---- Build: Hero layout picker, real live-block-preview cards (v7.47.12) ---- */
/* v7.47.14 BUGFIX: confirmed real, live-reported issue -- a 2-column
   grid with a fixed 170px frame never showed a full Hero section, just
   a cropped sliver. Rebuilt single-column, reusing the SAME dynamic-
   height-scaling technique the existing "Choose a Layout" editor
   feature already uses (see scaleThumbDynamic() in frontend.js) -- the
   height below is only the INITIAL/loading-state height; once each
   iframe's real content loads, JS measures it and sets the container's
   actual height directly (capped at the 320px maxHeight passed to
   scaleAllThumbsDynamic()), so what's shown is a genuine, proportional
   view of the whole Hero section, not an arbitrary crop. */
/* v7.50.4 BUGFIX: confirmed real, live-reported issue (Screenshot_2) --
   a real CSS Grid stretches every cell in a row to match the TALLEST
   cell in that row (align-items:stretch is Grid's own default), so
   pairing a short Hero design next to a much taller one left a large
   empty gap below the shorter card, matching its taller row-mate's
   height instead of its own real content height. Switched to a real
   CSS multi-column ("masonry-ish") layout -- each card sizes to its
   own real content height and the next one flows immediately below it
   in the same column, no shared row height forcing empty space onto a
   shorter card. break-inside:avoid keeps a single card from ever being
   visually split across the column break.
*/
.msb-build-hero-grid {
	column-count: 2; column-gap: 14px; margin-bottom: 4px;
}
.msb-build-hero-card {
	display: flex; flex-direction: column; text-align: left; padding: 0; overflow: hidden;
	border: 2px solid var(--msb-border); border-radius: 12px; background: #fff; cursor: pointer;
	transition: border-color .12s ease, box-shadow .12s ease;
	break-inside: avoid; margin-bottom: 14px;
	width: 100%; min-width: 0; /* v7.50.5: defensive -- a flex item's default min-width:auto can still let an oversized child push it wider than its column; this pins it to the column's own real width no matter what any child does. */
}
.msb-build-hero-card:hover { border-color: var(--msb-accent); }
.msb-build-hero-card-active { border-color: var(--msb-accent); box-shadow: 0 0 0 3px rgba(0,0,0,.06); }
.msb-build-hero-frame { display: block; width: 100%; height: 260px; overflow: hidden; background: var(--msb-bg); border-bottom: 1px solid var(--msb-border); pointer-events: none; position: relative; }
/* v7.50.5 BUGFIX (URGENT): confirmed real, live-reported regression --
   switching .msb-build-hero-grid to a multi-column masonry layout
   (v7.50.4) exposed a bug that was ALWAYS present here but happened to
   be masked before: this iframe is scaled down purely with CSS
   transform (see scaleThumbDynamic() in frontend.js), which does NOT
   shrink an element's LAYOUT box, only its visual rendering -- the
   iframe's real, un-transformed width (1440px, set directly in JS)
   was always still technically present in normal document flow. A
   fixed-track CSS Grid (grid-template-columns: 1fr 1fr, the v7.50.0-
   v7.50.3 layout) is resistant enough to this that it never visibly
   broke; a multi-column layout's own column-width computation is not,
   so cards started rendering far too wide the instant the layout
   changed, cascading into the broken/overlapping mess seen live.
   Fixed at the actual root: position:absolute takes the iframe
   COMPLETELY out of normal flow, so its real 1440px width can never
   influence any ancestor's layout width again, matching the exact
   same already-working pattern .msb-site-thumb iframe (the My Sites
   card) uses for this identical scale-a-big-iframe-down technique.
*/
.msb-build-hero-frame iframe { border: none; display: block; position: absolute; top: 0; left: 0; }
.msb-build-hero-name { display: block; padding: 10px 12px; font-size: 13px; font-weight: 700; }

/* "Auto-select for me" card -- compact, no iframe, sits above the real preview cards, spans both columns via column-span (the multi-column-layout equivalent of grid-column:1/-1) */
.msb-build-hero-card-auto {
	column-span: all;
	flex-direction: row; align-items: center; justify-content: flex-start; text-align: left; gap: 12px;
	padding: 16px 18px; background: var(--msb-bg);
}
.msb-build-hero-auto-icon { width: 24px; height: 24px; color: var(--msb-accent); flex-shrink: 0; }
.msb-build-hero-auto-icon svg { width: 100%; height: 100%; }
.msb-build-hero-card-auto .msb-build-hero-name { padding: 0; }
.msb-build-hero-sub { font-size: 12px; color: var(--msb-muted); }

@media (max-width: 700px) {
	.msb-build-hero-grid { column-count: 1; }
}
@media (max-width: 560px) {
	.msb-build-hero-frame { height: 220px; }
}

/* ---- Build: Images step (customer's own photos, v7.47.13) ---- */
.msb-build-image-toggle { display: flex; gap: 10px; margin-bottom: 20px; }
.msb-build-image-toggle-btn {
	flex: 1; display: flex; align-items: center; justify-content: center; gap: 8px;
	padding: 12px; border-radius: 10px; border: 1px solid var(--msb-border); background: #fff;
	font-size: 13.5px; font-weight: 700; cursor: pointer; color: var(--msb-fg);
}
.msb-build-image-toggle-btn svg { width: 16px; height: 16px; }
.msb-build-image-toggle-active { border-color: var(--msb-accent); background: var(--msb-bg); }

.msb-build-image-dropzone {
	display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
	border: 2px dashed var(--msb-border); border-radius: 12px; padding: 28px 16px;
	cursor: pointer; text-align: center; color: var(--msb-muted); font-size: 13.5px; font-weight: 600;
	transition: border-color .12s ease, background .12s ease;
}
.msb-build-image-dropzone svg { width: 24px; height: 24px; color: var(--msb-accent); }
.msb-build-image-dropzone:hover, .msb-build-image-dropzone-over { border-color: var(--msb-accent); background: var(--msb-bg); }
.msb-build-image-dropzone-disabled { opacity: .5; cursor: not-allowed; }
.msb-build-image-dropzone input[type="file"] { display: none; }
#msb-build-image-status { font-size: 12.5px; color: var(--msb-muted); margin-top: 8px; min-height: 16px; }

.msb-build-image-grid {
	display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; margin-top: 14px;
}
.msb-build-image-thumb {
	border: 1px solid var(--msb-border); border-radius: 10px; overflow: hidden; background: #fff; position: relative;
}
.msb-build-image-thumb-photo { width: 100%; height: 88px; background-size: cover; background-position: center; background-color: var(--msb-bg); }
.msb-build-image-thumb select.msb-build-image-tag {
	width: 100%; border: none; border-top: 1px solid var(--msb-border); background: #fff; font-size: 11px;
	font-weight: 400; padding: 6px 6px; color: var(--msb-muted); cursor: pointer; font-family: inherit; -webkit-appearance: none; appearance: none;
}
.msb-build-image-remove {
	position: absolute; top: 6px; right: 6px; width: 22px; height: 22px; border-radius: 50%;
	border: none; background: rgba(0,0,0,.55); color: #fff; font-size: 14px; line-height: 1;
	cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.msb-build-image-remove:hover { background: rgba(0,0,0,.8); }

@media (max-width: 480px) {
	.msb-build-image-toggle { flex-direction: column; }
	.msb-build-image-grid { grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); }
}

/* ---- Build: Goals step's quick-pick primary-goal cards (v7.63.0) ---- */
.msb-build-goal-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 24px; }
.msb-build-goal-card {
	display: flex; align-items: center; gap: 14px; width: 100%; text-align: left;
	padding: 14px 16px; border-radius: 12px; border: 1px solid var(--msb-border); background: #fff;
	cursor: pointer; font-family: inherit; transition: border-color .12s ease, background .12s ease;
}
.msb-build-goal-card:hover { border-color: var(--msb-accent); }
.msb-build-goal-card-active { border-color: var(--msb-accent); background: var(--msb-bg); }
/* v7.64.0 BUGFIX, confirmed real and live-reported (Screenshot_73:
   icon too small, circle border not visible enough) -- bumped the
   swatch from 40px to 48px and the icon itself from 20px to 24px
   (both scale together so the icon still sits centered), and gave
   the swatch its own real 1px border so the circle edge reads clearly
   against the card's own white/tinted background instead of relying
   on a faint background-color difference alone. */
.msb-build-goal-icon {
	flex: 0 0 auto; width: 48px; height: 48px; border-radius: 10px; background: var(--msb-bg);
	border: 1px solid var(--msb-border);
	display: flex; align-items: center; justify-content: center; color: var(--msb-accent);
}
.msb-build-goal-card-active .msb-build-goal-icon { background: #fff; border-color: var(--msb-accent); }
.msb-build-goal-icon svg { width: 24px; height: 24px; }
.msb-build-goal-text { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.msb-build-goal-text strong { font-size: 14px; font-weight: 700; color: var(--msb-fg); }
.msb-build-goal-text span { font-size: 12.5px; color: var(--msb-muted); line-height: 1.4; }
.msb-build-goal-check { flex: 0 0 auto; width: 22px; height: 22px; color: var(--msb-accent); }
.msb-build-goal-check svg { width: 22px; height: 22px; display: block; }
/* v7.64.0: "Something else" own free-text field (Screenshot_71) --
   sits inside the same card list, indented slightly less than a full
   card so it visually reads as "belonging to" the card above it. */
.msb-build-goal-other-field { margin: -4px 0 0; }
.msb-build-goal-other-field input {
	width: 100%; box-sizing: border-box; padding: 10px 12px; border-radius: 10px;
	border: 1px solid var(--msb-border); font-family: inherit; font-size: 14px;
}
.msb-build-goal-other-field input:focus { border-color: var(--msb-accent); outline: none; }

/* ---- Build: Typography picker, real Google Font live previews (v7.47.14) ---- */
/* v7.50.0 BUGFIX: confirmed real, live-reported issue (Screenshot_9) --
   every font pairing an admin has curated rendered in one
   unconstrained grid, so a library of any real size pushed the page's
   own Next/Back buttons far off screen, forcing the customer to
   scroll the WHOLE PAGE up and down repeatedly just to compare
   options. Capped to a fixed height with its own internal scroll --
   the customer now stays in one viewport position (Next/Back always
   in reach) and scrolls just this panel to see every option.
*/
.msb-build-font-grid {
	display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 4px;
	max-height: 480px; overflow-y: auto; padding-right: 4px; -webkit-overflow-scrolling: touch;
}
.msb-build-font-card {
	display: flex; flex-direction: column; text-align: left; padding: 14px; gap: 6px;
	border: 2px solid var(--msb-border); border-radius: 12px; background: #fff; cursor: pointer;
	transition: border-color .12s ease, box-shadow .12s ease;
}
.msb-build-font-card:hover { border-color: var(--msb-accent); }
.msb-build-font-card-active { border-color: var(--msb-accent); box-shadow: 0 0 0 3px rgba(0,0,0,.06); }
.msb-build-font-preview { display: flex; align-items: baseline; gap: 10px; overflow: hidden; }
.msb-build-font-heading { font-size: 28px; font-weight: 700; line-height: 1; flex-shrink: 0; }
.msb-build-font-body { font-size: 13px; color: var(--msb-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.msb-build-font-label { font-size: 12.5px; font-weight: 700; margin-top: 2px; }
.msb-build-font-mood { font-size: 11.5px; color: var(--msb-muted); }
.msb-build-font-card-auto {
	flex-direction: row; align-items: center; justify-content: center; gap: 10px;
	grid-column: 1 / -1; background: var(--msb-bg); padding: 16px;
	position: sticky; top: 0; z-index: 2; /* stays reachable even while scrolling the rest of the list */
}
.msb-build-font-card-auto .msb-build-hero-auto-icon { width: 20px; height: 20px; }

@media (max-width: 480px) {
	.msb-build-font-grid { grid-template-columns: 1fr; max-height: 420px; }
}
